diff --git a/Makefile.am b/Makefile.am index 1f4a8cd..41b9ea7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 = 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 EXTRA_DIST = \ slirp4netns.1.md \ diff --git a/api.c b/api.c index 0ba2305..16b306f 100644 --- a/api.c +++ b/api.c @@ -207,9 +207,8 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, } if (fwd->is_ipv6) { if (guest_addr_s == NULL || guest_addr_s[0] == '\0') { - guest_addr_s = "fd00::100"; - } - if (inet_pton(AF_INET6, guest_addr_s, &fwd->guest_addr6) != 1) { + fwd->guest_addr6 = ctx->cfg->recommended_vguest6; + } else if (inet_pton(AF_INET6, guest_addr_s, &fwd->guest_addr6) != 1) { const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " "bad arguments.guest_addr\"}}"; wrc = write(fd, err, strlen(err)); @@ -270,6 +269,7 @@ static void api_handle_req_list_hostfwd_foreach(gpointer data, JSON_Value *entry_value = json_value_init_object(); 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"); @@ -280,11 +280,29 @@ static void api_handle_req_list_hostfwd_foreach(gpointer data, perror("fatal: inet_ntop"); exit(EXIT_FAILURE); } + if (fwd->is_ipv6) { + if (inet_ntop(AF_INET6, &fwd->host_addr6, 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)) == + 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_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_ipv6) { + json_object_set_string(entry_object, "guest_addr6", guest_addr6); + } json_object_set_number(entry_object, "guest_port", fwd->guest_port); /* json_array_append_value does not copy passed value */ if (json_array_append_value(entries_array, entry_value) != JSONSuccess) { diff --git a/main.c b/main.c index de49394..c7cc343 100644 --- a/main.c +++ b/main.c @@ -524,6 +524,9 @@ static void usage(const char *argv0) printf( "--cidr=CIDR specify network address CIDR (default=%s)\n", DEFAULT_CIDR); + printf( + "--cidr6=CIDR specify network address CIDR (default=%s)\n", + DEFAULT_CIDR6); printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the " "host namespace\n"); /* v0.4.0 */ @@ -611,6 +614,10 @@ static void options_destroy(struct options *options) free(options->cidr); options->cidr = NULL; } + if (options->cidr6 != NULL) { + free(options->cidr6); + options->cidr6 = NULL; + } if (options->api_socket != NULL) { free(options->api_socket); options->api_socket = NULL; @@ -649,13 +656,15 @@ static void parse_args(int argc, char *const argv[], struct options *options) int opt; char *strtol_e = NULL; char *optarg_cidr = NULL; + char *optarg_cidr6 = NULL; char *optarg_netns_type = NULL; char *optarg_userns_path = NULL; char *optarg_api_socket = NULL; char *optarg_outbound_addr = NULL; char *optarg_outbound_addr6 = NULL; char *optarg_macaddress = NULL; -#define CIDR -42 +#define CIDR -41 +#define CIDR6 -42 #define DISABLE_HOST_LOOPBACK -43 #define NETNS_TYPE -44 #define USERNS_PATH -45 @@ -675,6 +684,7 @@ static void parse_args(int argc, char *const argv[], struct options *options) { "ready-fd", required_argument, NULL, 'r' }, { "mtu", required_argument, NULL, 'm' }, { "cidr", required_argument, NULL, CIDR }, + { "cidr6", required_argument, NULL, CIDR6 }, { "disable-host-loopback", no_argument, NULL, DISABLE_HOST_LOOPBACK }, { "no-host-loopback", no_argument, NULL, _DEPRECATED_NO_HOST_LOOPBACK }, { "netns-type", required_argument, NULL, NETNS_TYPE }, @@ -728,6 +738,9 @@ static void parse_args(int argc, char *const argv[], struct options *options) case CIDR: optarg_cidr = optarg; break; + case CIDR6: + optarg_cidr6 = optarg; + break; case _DEPRECATED_NO_HOST_LOOPBACK: // There was no tagged release with support for --no-host-loopback. // So no one will be affected by removal of --no-host-loopback. @@ -800,6 +813,9 @@ static void parse_args(int argc, char *const argv[], struct options *options) if (optarg_cidr != NULL) { options->cidr = strdup(optarg_cidr); } + if (optarg_cidr6 != NULL) { + options->cidr6 = strdup(optarg_cidr6); + } if (optarg_netns_type != NULL) { options->netns_type = strdup(optarg_netns_type); } @@ -825,6 +841,7 @@ static void parse_args(int argc, char *const argv[], struct options *options) } } #undef CIDR +#undef CIDR6 #undef DISABLE_HOST_LOOPBACK #undef NETNS_TYPE #undef USERNS_PATH @@ -940,10 +957,12 @@ static int parse_cidr6(struct in6_addr *network, struct in6_addr *netmask, { int rc = 0; regex_t r; - regmatch_t matches[4]; + regmatch_t matches[5]; size_t nmatch = sizeof(matches) / sizeof(matches[0]); - const char *cidr_regex = "^(([a-fA-F0-9]{1,4}):){1,4}:/([0-9]{1,3})"; - char snetwork[INET6_ADDRSTRLEN], sprefix[INET6_ADDRSTRLEN]; + const char *cidr_regex = "^((([a-fA-F0-9]{1,4}):){1,4}):/([0-9]{1,3})"; + char snetwork[INET6_ADDRSTRLEN], + snetwork_end[INET6_ADDRSTRLEN], + sprefix[INET6_ADDRSTRLEN]; int prefix; const char *random; rc = regcomp(&r, cidr_regex, REG_EXTENDED); @@ -963,19 +982,28 @@ static int parse_cidr6(struct in6_addr *network, struct in6_addr *netmask, fprintf(stderr, "invalid CIDR: %s\n", cidr); goto finish; } - rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr); + rc = from_regmatch(snetwork_end, sizeof(snetwork_end), matches[2], cidr); if (rc < 0) { fprintf(stderr, "invalid CIDR: %s\n", cidr); goto finish; } - random = pseudo_random_global_id(NULL); - if (random == NULL) { - fprintf(stderr, "cannot create pseudo random global id\n"); - rc = -1; + rc = from_regmatch(sprefix, sizeof(sprefix), matches[4], cidr); + if (rc < 0) { + fprintf(stderr, "invalid CIDR: %s\n", cidr); goto finish; } - strcpy(snetwork, random); - strcat(snetwork, "0"); + if (strcmp(snetwork, snetwork_end) == 0) { + random = pseudo_random_global_id(NULL); + if (random == NULL) { + fprintf(stderr, "cannot create pseudo random global id\n"); + rc = -1; + goto finish; + } + strcpy(snetwork, random); + strcat(snetwork, "0"); + } else { + strcat(snetwork, ":0"); + } if (inet_pton(AF_INET6, snetwork, network) != 1) { fprintf(stderr, "invalid network address: %s\n", snetwork); diff --git a/tests/test-slirp4netns-api-socket.sh b/tests/test-slirp4netns-api-socket.sh index 8de0c21..c73592f 100755 --- a/tests/test-slirp4netns-api-socket.sh +++ b/tests/test-slirp4netns-api-socket.sh @@ -47,6 +47,7 @@ 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":8080,"guest_port":80}}' | ncat -U $apisocket) [[ $(echo $result | jq .error) == null ]] id=$(echo $result | jq .return.id) @@ -54,12 +55,12 @@ id=$(echo $result | jq .return.id) result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket) [[ $(echo $result | jq .error) == null ]] -[[ $(echo $result | jq .return.entries[0].id) == $id ]] -[[ $(echo $result | jq .return.entries[0].proto) == '"tcp"' ]] -[[ $(echo $result | jq .return.entries[0].host_addr) == '"0.0.0.0"' ]] -[[ $(echo $result | jq .return.entries[0].host_port) == 8080 ]] -[[ $(echo $result | jq .return.entries[0].guest_addr) == '"10.0.2.100"' ]] -[[ $(echo $result | jq .return.entries[0].guest_port) == 80 ]] +[[ $(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_port) == 8080 ]] +[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]] +[[ $(echo $result | jq .entries[0].guest_port) == 80 ]] result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 1}}' | ncat -U $apisocket) [[ $(echo $result | jq .error) == null ]] diff --git a/tests/test-slirp4netns-ipv6.sh b/tests/test-slirp4netns-ipv6.sh new file mode 100755 index 0000000..60f6af3 --- /dev/null +++ b/tests/test-slirp4netns-ipv6.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -xeuo pipefail + +. $(dirname $0)/common.sh + +unshare -r -n sleep infinity & +child=$! + +wait_for_network_namespace $child + +tmpdir=$(mktemp -d /tmp/slirp4netns-bench.XXXXXXXXXX) +apisocket=${tmpdir}/slirp4netns.sock + +slirp4netns -c $child --enable-ipv6 --cidr6=fd00:fb14:63ee:b::/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":8080,"guest_port":80}}' | 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) == 8080 ]] +[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]] +[[ $(echo $result | jq .entries[0].guest_addr6) == '"fd00:fb14:63ee:b::100"' ]] +[[ $(echo $result | jq .entries[0].guest_port) == 80 ]] + +result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 1}}' | ncat -U $apisocket) +[[ $(echo $result | jq .error) == null ]] + +# see also: benchmarks/benchmark-iperf3-reverse.sh