Skip to content

Commit

Permalink
handle protocol version in api: rootless-containers#253 rootless-cont…
Browse files Browse the repository at this point in the history
…ainers#259

Signed-off-by: fassl <[email protected]>
  • Loading branch information
pfandl committed Sep 11, 2021
1 parent a2e566b commit fcb7f2e
Show file tree
Hide file tree
Showing 5 changed files with 343 additions and 68 deletions.
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ AM_CFLAGS = @GLIB_CFLAGS@ @SLIRP_CFLAGS@ @LIBCAP_CFLAGS@ @LIBSECCOMP_CFLAGS@
noinst_LIBRARIES = libparson.a

AM_TESTS_ENVIRONMENT = PATH="$(abs_top_builddir):$(PATH)"
TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh tests/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 = 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

EXTRA_DIST = \
slirp4netns.1.md \
Expand Down
166 changes: 99 additions & 67 deletions api.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ struct api_hostfwd {
int is_udp;
int is_ipv4;
int is_ipv6;
struct in_addr host_addr;
struct in6_addr host_addr6;
struct sockaddr_in host;
struct sockaddr_in guest;
struct sockaddr_in6 host6;
struct sockaddr_in6 guest6;
int host_port;
struct in_addr guest_addr;
struct in6_addr guest_addr6;
int guest_port;
};

Expand Down Expand Up @@ -103,10 +103,8 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
const char *proto_s = json_object_dotget_string(jo, "arguments.proto");
const char *host_addr_s =
json_object_dotget_string(jo, "arguments.host_addr");
char *host_addr6_s = NULL;
const char *guest_addr_s =
json_object_dotget_string(jo, "arguments.guest_addr");
char *guest_addr6_s = NULL;
struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd));
if (fwd == NULL) {
perror("fatal: malloc");
Expand All @@ -127,10 +125,46 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
}
int flags = (fwd->is_udp ? SLIRP_HOSTFWD_UDP : 0);

if (host_addr_s == NULL || host_addr_s[0] == '\0') {
host_addr_s = "0.0.0.0";
}
if (strcmp("0.0.0.0", host_addr_s) == 0 ||
strcmp("::", host_addr_s) == 0 ||
strcmp("::0", host_addr_s) == 0) {
fwd->is_ipv4 = 1;
fwd->is_ipv6 = ctx->cfg->enable_ipv6;
host_addr_s = "0.0.0.0";
} else {
if (strchr(host_addr_s, '.')) {
fwd->is_ipv4 = 1;
}
else if (strchr(host_addr_s, ':')) {
if (!ctx->cfg->enable_ipv6) {
const char *err =
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"bad arguments.host_addr\"}}";
wrc = write(fd, err, strlen(err));
free(fwd);
goto finish;
}
fwd->is_ipv6 = 1;
}
}

if (strlen(proto_s) == 4) {
if (proto_s[3] == '4') {
fwd->is_ipv4 = 1;
fwd->is_ipv6 = 0;
} else if (proto_s[3] == '6') {
if (!ctx->cfg->enable_ipv6) {
const char *err =
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"bad arguments.proto\"}}";
wrc = write(fd, err, strlen(err));
free(fwd);
goto finish;
}
fwd->is_ipv4 = 0;
fwd->is_ipv6 = 1;
} else {
const char *err =
Expand All @@ -140,9 +174,6 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
free(fwd);
goto finish;
}
} else {
fwd->is_ipv4 = 1;
fwd->is_ipv6 = 1;
}

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

if (fwd->is_ipv4) {
fwd->host.sin_family = AF_INET;
fwd->guest.sin_family = AF_INET;
fwd->host.sin_port = htons(fwd->host_port);
fwd->guest.sin_port = htons(fwd->guest_port);
if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
fwd->guest_addr = ctx->cfg->recommended_vguest;
} else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest_addr) != 1) {
fwd->guest.sin_addr = ctx->cfg->recommended_vguest;
} else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest.sin_addr) != 1) {
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"bad arguments.guest_addr\"}}";
wrc = write(fd, err, strlen(err));
free(fwd);
goto finish;
}
if (host_addr_s == NULL || host_addr_s[0] == '\0') {
host_addr_s = "0.0.0.0";
}
if (inet_pton(AF_INET, host_addr_s, &fwd->host_addr) != 1) {
if (inet_pton(AF_INET, host_addr_s, &fwd->host.sin_addr) != 1) {
const char *err =
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"bad arguments.host_addr\"}}";
wrc = write(fd, err, strlen(err));
free(fwd);
goto finish;
}
struct sockaddr_in host;
memset(&host, 0, sizeof(host));
host.sin_family = AF_INET;
host.sin_addr.s_addr = fwd->host_addr.s_addr;
host.sin_port = htons(fwd->host_port);
struct sockaddr_in guest;
memset(&guest, 0, sizeof(guest));
guest.sin_family = AF_INET;
guest.sin_addr.s_addr = fwd->guest_addr.s_addr;
guest.sin_port = htons(fwd->guest_port);
if (slirp_add_hostxfwd(slirp,
(const struct sockaddr*)&host, sizeof(host),
(const struct sockaddr*)&guest, sizeof(guest), flags) < 0) {
(const struct sockaddr*)&fwd->host, sizeof(fwd->host),
(const struct sockaddr*)&fwd->guest, sizeof(fwd->guest), flags) < 0) {
const char *err =
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"slirp_add_hostxfwd failed\"}}";
Expand All @@ -205,44 +227,39 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
goto finish;
}
}

if (fwd->is_ipv6) {
fwd->host6.sin6_family = AF_INET6;
fwd->guest6.sin6_family = AF_INET6;
fwd->host6.sin6_port = htons(fwd->host_port);
fwd->guest6.sin6_port = htons(fwd->guest_port);
if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
fwd->guest_addr6 = ctx->cfg->recommended_vguest6;
} else if (inet_pton(AF_INET6, guest_addr_s, &fwd->guest_addr6) != 1) {
fwd->guest6.sin6_addr = ctx->cfg->recommended_vguest6;
} else if (inet_pton(AF_INET6, guest_addr_s, &fwd->guest6.sin6_addr) != 1) {
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"bad arguments.guest_addr\"}}";
wrc = write(fd, err, strlen(err));
free(fwd);
goto finish;
}
if (host_addr_s == NULL || host_addr_s[0] == '\0' ||
strcmp(host_addr_s, "0.0.0.0") == 0) {
if (strcmp(host_addr_s, "0.0.0.0") == 0) {
host_addr_s = "::";
}
if (inet_pton(AF_INET6, host_addr_s, &fwd->host_addr6) != 1) {
if (inet_pton(AF_INET6, host_addr_s, &fwd->host6.sin6_addr) != 1) {
const char *err =
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"bad arguments.host_addr\"}}";
wrc = write(fd, err, strlen(err));
free(fwd);
goto finish;
}
struct sockaddr_in6 host;
memset(&host, 0, sizeof(host));
host.sin6_family = AF_INET6;
host.sin6_addr = fwd->host_addr6;
host.sin6_port = htons(fwd->host_port);
struct sockaddr_in6 guest;
memset(&guest, 0, sizeof(guest));
guest.sin6_family = AF_INET6;
guest.sin6_addr = fwd->guest_addr6;
guest.sin6_port = htons(fwd->guest_port);
flags |= SLIRP_HOSTFWD_V6ONLY;
if (slirp_add_hostxfwd(slirp,
(const struct sockaddr*)&host, sizeof(host),
(const struct sockaddr*)&guest, sizeof(guest), flags) < 0) {
(const struct sockaddr*)&fwd->host6, sizeof(fwd->host6),
(const struct sockaddr*)&fwd->guest6, sizeof(fwd->guest6), flags) < 0) {
const char *err =
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
"slirp_add_hostfwd failed\"}}";
"slirp_add_hostxfwd failed\"}}";
wrc = write(fd, err, strlen(err));
free(fwd);
goto finish;
Expand Down Expand Up @@ -270,36 +287,42 @@ static void api_handle_req_list_hostfwd_foreach(gpointer data,
JSON_Object *entry_object = json_value_get_object(entry_value);
char host_addr[INET_ADDRSTRLEN], guest_addr[INET_ADDRSTRLEN];
char host_addr6[INET6_ADDRSTRLEN], guest_addr6[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET, &fwd->host_addr, host_addr, sizeof(host_addr)) ==
NULL) {
perror("fatal: inet_ntop");
exit(EXIT_FAILURE);
}
if (inet_ntop(AF_INET, &fwd->guest_addr, guest_addr, sizeof(guest_addr)) ==
NULL) {
perror("fatal: inet_ntop");
exit(EXIT_FAILURE);
if (fwd->is_ipv4) {
if (inet_ntop(AF_INET, &fwd->host.sin_addr, host_addr, sizeof(host_addr)) ==
NULL) {
perror("fatal: inet_ntop");
exit(EXIT_FAILURE);
}
if (inet_ntop(AF_INET, &fwd->guest.sin_addr, guest_addr, sizeof(guest_addr)) ==
NULL) {
perror("fatal: inet_ntop");
exit(EXIT_FAILURE);
}
}
if (fwd->is_ipv6) {
if (inet_ntop(AF_INET6, &fwd->host_addr6, host_addr6, sizeof(host_addr6)) ==
if (inet_ntop(AF_INET6, &fwd->host6.sin6_addr, host_addr6, sizeof(host_addr6)) ==
NULL) {
perror("fatal: inet_ntop");
exit(EXIT_FAILURE);
}
if (inet_ntop(AF_INET6, &fwd->guest_addr6, guest_addr6, sizeof(guest_addr6)) ==
if (inet_ntop(AF_INET6, &fwd->guest6.sin6_addr, guest_addr6, sizeof(guest_addr6)) ==
NULL) {
perror("fatal: inet_ntop");
exit(EXIT_FAILURE);
}
}
json_object_set_number(entry_object, "id", fwd->id);
json_object_set_string(entry_object, "proto", fwd->is_udp ? "udp" : "tcp");
json_object_set_string(entry_object, "host_addr", host_addr);
if (fwd->is_ipv4) {
json_object_set_string(entry_object, "host_addr", host_addr);
}
if (fwd->is_ipv6) {
json_object_set_string(entry_object, "host_addr6", host_addr6);
}
json_object_set_number(entry_object, "host_port", fwd->host_port);
json_object_set_string(entry_object, "guest_addr", guest_addr);
if (fwd->is_ipv4) {
json_object_set_string(entry_object, "guest_addr", guest_addr);
}
if (fwd->is_ipv6) {
json_object_set_string(entry_object, "guest_addr6", guest_addr6);
}
Expand Down Expand Up @@ -360,16 +383,25 @@ static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd,
} else {
struct api_hostfwd *fwd = found->data;
const char *api_ok = "{\"return\":{}}";
if (slirp_remove_hostfwd(slirp, fwd->is_udp, fwd->host_addr,
fwd->host_port) < 0) {
const char *err = "{\"error\":{\"desc\":\"bad request: "
"remove_hostfwd: slirp_remove_hostfwd failed\"}}";
wrc = write(fd, err, strlen(err));
} else {
ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
g_free(fwd);
wrc = write(fd, api_ok, strlen(api_ok));
if (fwd->is_ipv4) {
if (slirp_remove_hostxfwd(slirp,
(const struct sockaddr*)&fwd->host, sizeof(fwd->host), 0) < 0) {
const char *err = "{\"error\":{\"desc\":\"bad request: "
"remove_hostfwd: slirp_remove_hostxfwd failed\"}}";
return write(fd, err, strlen(err));
}
}
if (fwd->is_ipv6) {
if (slirp_remove_hostxfwd(slirp,
(const struct sockaddr*)&fwd->host6, sizeof(fwd->host6), 0) < 0) {
const char *err = "{\"error\":{\"desc\":\"bad request: "
"remove_hostfwd: slirp_remove_hostxfwd failed\"}}";
return write(fd, err, strlen(err));
}
}
ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
g_free(fwd);
wrc = write(fd, api_ok, strlen(api_ok));
}
return wrc;
}
Expand Down
62 changes: 62 additions & 0 deletions tests/test-slirp4netns-hostfwd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/bash
set -xeuo pipefail

. $(dirname $0)/common.sh

host_port=8080
guest_port=1080
cidr=fd00:a1e1:1724:1a

unshare -r -n socat tcp6-listen:$guest_port,reuseaddr,fork exec:cat,nofork &
child=$!

#nsenter -a -t $child socat tcp-listen:$guest_port,reuseaddr,fork exec:cat,nofork &

wait_for_network_namespace $child

tmpdir=$(mktemp -d /tmp/slirp4netns-bench.XXXXXXXXXX)
apisocket=${tmpdir}/slirp4netns.sock

slirp4netns -c $child --enable-ipv6 --cidr6=$cidr::/64 --api-socket $apisocket tun11 &
slirp_pid=$!

wait_for_network_device $child tun11

function cleanup() {
kill -9 $child $slirp_pid
rm -rf $tmpdir
}
trap cleanup EXIT

set +e
result=$(cat /dev/zero | ncat -U $apisocket || true)
set set -e
echo $result | jq .error.desc | grep "bad request: too large message"

set -e
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket)
[[ $(echo $result | jq .error) == null ]]
id=$(echo $result | jq .return.id)
[[ $id == 1 ]]

result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
[[ $(echo $result | jq .error) == null ]]
[[ $(echo $result | jq .entries[0].id) == $id ]]
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
[[ $(echo $result | jq .entries[0].host_addr) == '"0.0.0.0"' ]]
[[ $(echo $result | jq .entries[0].host_addr6) == '"::"' ]]
[[ $(echo $result | jq .entries[0].host_port) == $host_port ]]
[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]]
[[ $(echo $result | jq .entries[0].guest_addr6) == '"'$cidr'::100"' ]]
[[ $(echo $result | jq .entries[0].guest_port) == $guest_port ]]

result=$(echo works | nc -w 10 localhost6 $host_port)
[[ "$result" == "works" ]]

result=$(echo works | nc -w 10 localhost $host_port)
[[ "$result" == "works" ]]

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

# see also: benchmarks/benchmark-iperf3-reverse.sh
Loading

0 comments on commit fcb7f2e

Please sign in to comment.