Skip to content

Commit a3ca11a

Browse files
committed
handle protocol version in api: rootless-containers#253 rootless-containers#259
1 parent 6a1975d commit a3ca11a

5 files changed

+343
-68
lines changed

Makefile.am

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ AM_CFLAGS = @GLIB_CFLAGS@ @SLIRP_CFLAGS@ @LIBCAP_CFLAGS@ @LIBSECCOMP_CFLAGS@
55
noinst_LIBRARIES = libparson.a
66

77
AM_TESTS_ENVIRONMENT = PATH="$(abs_top_builddir):$(PATH)"
8-
TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh tests/test-slirp4netns-outbound-addr.sh tests/test-slirp4netns-disable-dns.sh tests/test-slirp4netns-seccomp.sh tests/test-slirp4netns-macaddress.sh tests/test-slirp4netns-ipv6.sh
8+
TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh tests/test-slirp4netns-outbound-addr.sh tests/test-slirp4netns-disable-dns.sh tests/test-slirp4netns-seccomp.sh tests/test-slirp4netns-macaddress.sh tests/test-slirp4netns-ipv6.sh tests/test-slirp4netns-hostfwd4.sh tests/test-slirp4netns-hostfwd6.sh tests/test-slirp4netns-hostfwd.sh
99

1010
EXTRA_DIST = \
1111
slirp4netns.1.md \

api.c

+99-67
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ struct api_hostfwd {
4343
int is_udp;
4444
int is_ipv4;
4545
int is_ipv6;
46-
struct in_addr host_addr;
47-
struct in6_addr host_addr6;
46+
struct sockaddr_in host;
47+
struct sockaddr_in guest;
48+
struct sockaddr_in6 host6;
49+
struct sockaddr_in6 guest6;
4850
int host_port;
49-
struct in_addr guest_addr;
50-
struct in6_addr guest_addr6;
5151
int guest_port;
5252
};
5353

@@ -103,10 +103,8 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
103103
const char *proto_s = json_object_dotget_string(jo, "arguments.proto");
104104
const char *host_addr_s =
105105
json_object_dotget_string(jo, "arguments.host_addr");
106-
char *host_addr6_s = NULL;
107106
const char *guest_addr_s =
108107
json_object_dotget_string(jo, "arguments.guest_addr");
109-
char *guest_addr6_s = NULL;
110108
struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd));
111109
if (fwd == NULL) {
112110
perror("fatal: malloc");
@@ -127,10 +125,46 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
127125
}
128126
int flags = (fwd->is_udp ? SLIRP_HOSTFWD_UDP : 0);
129127

128+
if (host_addr_s == NULL || host_addr_s[0] == '\0') {
129+
host_addr_s = "0.0.0.0";
130+
}
131+
if (strcmp("0.0.0.0", host_addr_s) == 0 ||
132+
strcmp("::", host_addr_s) == 0 ||
133+
strcmp("::0", host_addr_s) == 0) {
134+
fwd->is_ipv4 = 1;
135+
fwd->is_ipv6 = ctx->cfg->enable_ipv6;
136+
host_addr_s = "0.0.0.0";
137+
} else {
138+
if (strchr(host_addr_s, '.')) {
139+
fwd->is_ipv4 = 1;
140+
}
141+
else if (strchr(host_addr_s, ':')) {
142+
if (!ctx->cfg->enable_ipv6) {
143+
const char *err =
144+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
145+
"bad arguments.host_addr\"}}";
146+
wrc = write(fd, err, strlen(err));
147+
free(fwd);
148+
goto finish;
149+
}
150+
fwd->is_ipv6 = 1;
151+
}
152+
}
153+
130154
if (strlen(proto_s) == 4) {
131155
if (proto_s[3] == '4') {
132156
fwd->is_ipv4 = 1;
157+
fwd->is_ipv6 = 0;
133158
} else if (proto_s[3] == '6') {
159+
if (!ctx->cfg->enable_ipv6) {
160+
const char *err =
161+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
162+
"bad arguments.proto\"}}";
163+
wrc = write(fd, err, strlen(err));
164+
free(fwd);
165+
goto finish;
166+
}
167+
fwd->is_ipv4 = 0;
134168
fwd->is_ipv6 = 1;
135169
} else {
136170
const char *err =
@@ -140,9 +174,6 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
140174
free(fwd);
141175
goto finish;
142176
}
143-
} else {
144-
fwd->is_ipv4 = 1;
145-
fwd->is_ipv6 = 1;
146177
}
147178

148179
fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port");
@@ -164,39 +195,30 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
164195
}
165196

166197
if (fwd->is_ipv4) {
198+
fwd->host.sin_family = AF_INET;
199+
fwd->guest.sin_family = AF_INET;
200+
fwd->host.sin_port = htons(fwd->host_port);
201+
fwd->guest.sin_port = htons(fwd->guest_port);
167202
if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
168-
fwd->guest_addr = ctx->cfg->recommended_vguest;
169-
} else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest_addr) != 1) {
203+
fwd->guest.sin_addr = ctx->cfg->recommended_vguest;
204+
} else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest.sin_addr) != 1) {
170205
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
171206
"bad arguments.guest_addr\"}}";
172207
wrc = write(fd, err, strlen(err));
173208
free(fwd);
174209
goto finish;
175210
}
176-
if (host_addr_s == NULL || host_addr_s[0] == '\0') {
177-
host_addr_s = "0.0.0.0";
178-
}
179-
if (inet_pton(AF_INET, host_addr_s, &fwd->host_addr) != 1) {
211+
if (inet_pton(AF_INET, host_addr_s, &fwd->host.sin_addr) != 1) {
180212
const char *err =
181213
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
182214
"bad arguments.host_addr\"}}";
183215
wrc = write(fd, err, strlen(err));
184216
free(fwd);
185217
goto finish;
186218
}
187-
struct sockaddr_in host;
188-
memset(&host, 0, sizeof(host));
189-
host.sin_family = AF_INET;
190-
host.sin_addr.s_addr = fwd->host_addr.s_addr;
191-
host.sin_port = htons(fwd->host_port);
192-
struct sockaddr_in guest;
193-
memset(&guest, 0, sizeof(guest));
194-
guest.sin_family = AF_INET;
195-
guest.sin_addr.s_addr = fwd->guest_addr.s_addr;
196-
guest.sin_port = htons(fwd->guest_port);
197219
if (slirp_add_hostxfwd(slirp,
198-
(const struct sockaddr*)&host, sizeof(host),
199-
(const struct sockaddr*)&guest, sizeof(guest), flags) < 0) {
220+
(const struct sockaddr*)&fwd->host, sizeof(fwd->host),
221+
(const struct sockaddr*)&fwd->guest, sizeof(fwd->guest), flags) < 0) {
200222
const char *err =
201223
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
202224
"slirp_add_hostxfwd failed\"}}";
@@ -205,44 +227,39 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
205227
goto finish;
206228
}
207229
}
230+
208231
if (fwd->is_ipv6) {
232+
fwd->host6.sin6_family = AF_INET6;
233+
fwd->guest6.sin6_family = AF_INET6;
234+
fwd->host6.sin6_port = htons(fwd->host_port);
235+
fwd->guest6.sin6_port = htons(fwd->guest_port);
209236
if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
210-
fwd->guest_addr6 = ctx->cfg->recommended_vguest6;
211-
} else if (inet_pton(AF_INET6, guest_addr_s, &fwd->guest_addr6) != 1) {
237+
fwd->guest6.sin6_addr = ctx->cfg->recommended_vguest6;
238+
} else if (inet_pton(AF_INET6, guest_addr_s, &fwd->guest6.sin6_addr) != 1) {
212239
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
213240
"bad arguments.guest_addr\"}}";
214241
wrc = write(fd, err, strlen(err));
215242
free(fwd);
216243
goto finish;
217244
}
218-
if (host_addr_s == NULL || host_addr_s[0] == '\0' ||
219-
strcmp(host_addr_s, "0.0.0.0") == 0) {
245+
if (strcmp(host_addr_s, "0.0.0.0") == 0) {
220246
host_addr_s = "::";
221247
}
222-
if (inet_pton(AF_INET6, host_addr_s, &fwd->host_addr6) != 1) {
248+
if (inet_pton(AF_INET6, host_addr_s, &fwd->host6.sin6_addr) != 1) {
223249
const char *err =
224250
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
225251
"bad arguments.host_addr\"}}";
226252
wrc = write(fd, err, strlen(err));
227253
free(fwd);
228254
goto finish;
229255
}
230-
struct sockaddr_in6 host;
231-
memset(&host, 0, sizeof(host));
232-
host.sin6_family = AF_INET6;
233-
host.sin6_addr = fwd->host_addr6;
234-
host.sin6_port = htons(fwd->host_port);
235-
struct sockaddr_in6 guest;
236-
memset(&guest, 0, sizeof(guest));
237-
guest.sin6_family = AF_INET6;
238-
guest.sin6_addr = fwd->guest_addr6;
239-
guest.sin6_port = htons(fwd->guest_port);
256+
flags |= SLIRP_HOSTFWD_V6ONLY;
240257
if (slirp_add_hostxfwd(slirp,
241-
(const struct sockaddr*)&host, sizeof(host),
242-
(const struct sockaddr*)&guest, sizeof(guest), flags) < 0) {
258+
(const struct sockaddr*)&fwd->host6, sizeof(fwd->host6),
259+
(const struct sockaddr*)&fwd->guest6, sizeof(fwd->guest6), flags) < 0) {
243260
const char *err =
244261
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
245-
"slirp_add_hostfwd failed\"}}";
262+
"slirp_add_hostxfwd failed\"}}";
246263
wrc = write(fd, err, strlen(err));
247264
free(fwd);
248265
goto finish;
@@ -270,36 +287,42 @@ static void api_handle_req_list_hostfwd_foreach(gpointer data,
270287
JSON_Object *entry_object = json_value_get_object(entry_value);
271288
char host_addr[INET_ADDRSTRLEN], guest_addr[INET_ADDRSTRLEN];
272289
char host_addr6[INET6_ADDRSTRLEN], guest_addr6[INET6_ADDRSTRLEN];
273-
if (inet_ntop(AF_INET, &fwd->host_addr, host_addr, sizeof(host_addr)) ==
274-
NULL) {
275-
perror("fatal: inet_ntop");
276-
exit(EXIT_FAILURE);
277-
}
278-
if (inet_ntop(AF_INET, &fwd->guest_addr, guest_addr, sizeof(guest_addr)) ==
279-
NULL) {
280-
perror("fatal: inet_ntop");
281-
exit(EXIT_FAILURE);
290+
if (fwd->is_ipv4) {
291+
if (inet_ntop(AF_INET, &fwd->host.sin_addr, host_addr, sizeof(host_addr)) ==
292+
NULL) {
293+
perror("fatal: inet_ntop");
294+
exit(EXIT_FAILURE);
295+
}
296+
if (inet_ntop(AF_INET, &fwd->guest.sin_addr, guest_addr, sizeof(guest_addr)) ==
297+
NULL) {
298+
perror("fatal: inet_ntop");
299+
exit(EXIT_FAILURE);
300+
}
282301
}
283302
if (fwd->is_ipv6) {
284-
if (inet_ntop(AF_INET6, &fwd->host_addr6, host_addr6, sizeof(host_addr6)) ==
303+
if (inet_ntop(AF_INET6, &fwd->host6.sin6_addr, host_addr6, sizeof(host_addr6)) ==
285304
NULL) {
286305
perror("fatal: inet_ntop");
287306
exit(EXIT_FAILURE);
288307
}
289-
if (inet_ntop(AF_INET6, &fwd->guest_addr6, guest_addr6, sizeof(guest_addr6)) ==
308+
if (inet_ntop(AF_INET6, &fwd->guest6.sin6_addr, guest_addr6, sizeof(guest_addr6)) ==
290309
NULL) {
291310
perror("fatal: inet_ntop");
292311
exit(EXIT_FAILURE);
293312
}
294313
}
295314
json_object_set_number(entry_object, "id", fwd->id);
296315
json_object_set_string(entry_object, "proto", fwd->is_udp ? "udp" : "tcp");
297-
json_object_set_string(entry_object, "host_addr", host_addr);
316+
if (fwd->is_ipv4) {
317+
json_object_set_string(entry_object, "host_addr", host_addr);
318+
}
298319
if (fwd->is_ipv6) {
299320
json_object_set_string(entry_object, "host_addr6", host_addr6);
300321
}
301322
json_object_set_number(entry_object, "host_port", fwd->host_port);
302-
json_object_set_string(entry_object, "guest_addr", guest_addr);
323+
if (fwd->is_ipv4) {
324+
json_object_set_string(entry_object, "guest_addr", guest_addr);
325+
}
303326
if (fwd->is_ipv6) {
304327
json_object_set_string(entry_object, "guest_addr6", guest_addr6);
305328
}
@@ -360,16 +383,25 @@ static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd,
360383
} else {
361384
struct api_hostfwd *fwd = found->data;
362385
const char *api_ok = "{\"return\":{}}";
363-
if (slirp_remove_hostfwd(slirp, fwd->is_udp, fwd->host_addr,
364-
fwd->host_port) < 0) {
365-
const char *err = "{\"error\":{\"desc\":\"bad request: "
366-
"remove_hostfwd: slirp_remove_hostfwd failed\"}}";
367-
wrc = write(fd, err, strlen(err));
368-
} else {
369-
ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
370-
g_free(fwd);
371-
wrc = write(fd, api_ok, strlen(api_ok));
386+
if (fwd->is_ipv4) {
387+
if (slirp_remove_hostxfwd(slirp,
388+
(const struct sockaddr*)&fwd->host, sizeof(fwd->host), 0) < 0) {
389+
const char *err = "{\"error\":{\"desc\":\"bad request: "
390+
"remove_hostfwd: slirp_remove_hostxfwd failed\"}}";
391+
return write(fd, err, strlen(err));
392+
}
393+
}
394+
if (fwd->is_ipv6) {
395+
if (slirp_remove_hostxfwd(slirp,
396+
(const struct sockaddr*)&fwd->host6, sizeof(fwd->host6), 0) < 0) {
397+
const char *err = "{\"error\":{\"desc\":\"bad request: "
398+
"remove_hostfwd: slirp_remove_hostxfwd failed\"}}";
399+
return write(fd, err, strlen(err));
400+
}
372401
}
402+
ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
403+
g_free(fwd);
404+
wrc = write(fd, api_ok, strlen(api_ok));
373405
}
374406
return wrc;
375407
}

tests/test-slirp4netns-hostfwd.sh

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

0 commit comments

Comments
 (0)