diff --git a/.clang-format b/.clang-format new file mode 120000 index 0000000..7fdae58 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +vendor/libslirp/.clang-format \ No newline at end of file diff --git a/Dockerfile.tests b/Dockerfile.tests index 85c1066..9f93f66 100644 --- a/Dockerfile.tests +++ b/Dockerfile.tests @@ -8,6 +8,6 @@ RUN ./autogen.sh && ./configure && make -j $(nproc) FROM build AS test USER 0 -RUN apt update && apt install -y git indent libtool iproute2 clang clang-tidy iputils-ping iperf3 nmap jq +RUN apt update && apt install -y git libtool iproute2 clang clang-format clang-tidy iputils-ping iperf3 nmap jq USER 1000:1000 CMD ["make", "ci"] diff --git a/Makefile.am b/Makefile.am index 3587a7e..2bf05ca 100644 --- a/Makefile.am +++ b/Makefile.am @@ -100,6 +100,8 @@ generate-man: CLANGTIDY = clang-tidy -warnings-as-errors='*' +CLANGFORMAT = clang-format + lint: $(CLANGTIDY) $(slirp4netns_SOURCES) -- $(AM_CFLAGS) @@ -107,16 +109,7 @@ lint-full: $(CLANGTIDY) $(slirp4netns_SOURCES) $(libslirp_a_SOURCES) $(libparson_a_SOURCES) -- $(AM_CFLAGS) indent: -# 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." - indent -linux -l120 \ - -T ssize_t -T pid_t \ - -T GArray -T GList -T GSList -T GPollFD -T gpointer -T gconstpointer \ - -T Slirp -T SlirpCb -T SlirpConfig -T SlirpTimerCb \ - -T JSON_Object -T JSON_Value \ - -T regex_t -T regmatch_t \ - $(slirp4netns_SOURCES) - $(RM) *.c~ - + $(CLANGFORMAT) -i $(slirp4netns_SOURCES) benchmark: benchmarks/benchmark-iperf3.sh diff --git a/api.c b/api.c index fc034f7..9bacbc1 100644 --- a/api.c +++ b/api.c @@ -11,278 +11,306 @@ int api_bindlisten(const char *api_socket) { - int fd; - struct sockaddr_un addr; - unlink(api_socket); /* avoid EADDRINUSE */ - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - perror("api_bindlisten: socket"); - return -1; - } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, api_socket, sizeof(addr.sun_path) - 1); - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - perror("api_bindlisten: bind"); - return -1; - } - if (listen(fd, 0) < 0) { - perror("api_bindlisten: listen"); - return -1; - } - return fd; + int fd; + struct sockaddr_un addr; + unlink(api_socket); /* avoid EADDRINUSE */ + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("api_bindlisten: socket"); + return -1; + } + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, api_socket, sizeof(addr.sun_path) - 1); + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("api_bindlisten: bind"); + return -1; + } + if (listen(fd, 0) < 0) { + perror("api_bindlisten: listen"); + return -1; + } + return fd; } struct api_hostfwd { - int id; - int is_udp; - struct in_addr host_addr; - int host_port; - struct in_addr guest_addr; - int guest_port; + int id; + int is_udp; + struct in_addr host_addr; + int host_port; + struct in_addr guest_addr; + int guest_port; }; struct api_ctx { - uint8_t *buf; - size_t buflen; - GList *hostfwds; - int hostfwds_nextid; - struct slirp4netns_config *cfg; + uint8_t *buf; + size_t buflen; + GList *hostfwds; + int hostfwds_nextid; + struct slirp4netns_config *cfg; }; struct api_ctx *api_ctx_alloc(struct slirp4netns_config *cfg) { - struct api_ctx *ctx = (struct api_ctx *)g_malloc0(sizeof(*ctx)); - if (ctx == NULL) { - return NULL; - } - ctx->buflen = 4096; - ctx->buf = malloc(ctx->buflen); /* FIXME: realloc */ - if (ctx->buf == NULL) { - free(ctx); - return NULL; - } - ctx->cfg = cfg; - ctx->hostfwds = NULL; - ctx->hostfwds_nextid = 1; - return ctx; + struct api_ctx *ctx = (struct api_ctx *)g_malloc0(sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + ctx->buflen = 4096; + ctx->buf = malloc(ctx->buflen); /* FIXME: realloc */ + if (ctx->buf == NULL) { + free(ctx); + return NULL; + } + ctx->cfg = cfg; + ctx->hostfwds = NULL; + ctx->hostfwds_nextid = 1; + return ctx; } void api_ctx_free(struct api_ctx *ctx) { - if (ctx != NULL) { - if (ctx->buf != NULL) { - free(ctx->buf); - } - g_list_free_full(ctx->hostfwds, g_free); - free(ctx); - } + if (ctx != NULL) { + if (ctx->buf != NULL) { + free(ctx->buf); + } + g_list_free_full(ctx->hostfwds, g_free); + free(ctx); + } } /* Handler for add_hostfwd. - 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}} - This function returns the return value of write(2), not the return value of slirp_add_hostfwd(). + 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}} + This function returns the return value of write(2), not the return value of + slirp_add_hostfwd(). */ -static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo) +static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, + JSON_Object *jo) { - int wrc = 0, slirprc = 0; - int id = -1; - char idbuf[64]; - const char *proto_s = json_object_dotget_string(jo, "arguments.proto"); - const char *host_addr_s = json_object_dotget_string(jo, "arguments.host_addr"); - const char *guest_addr_s = json_object_dotget_string(jo, "arguments.guest_addr"); - struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd)); - if (fwd == NULL) { - perror("fatal: malloc"); - exit(EXIT_FAILURE); - } - fwd->is_udp = -1; /* TODO: support SCTP */ - if (strcmp(proto_s, "udp") == 0) { - fwd->is_udp = 1; - } else if (strcmp(proto_s, "tcp") == 0) { - fwd->is_udp = 0; - } - if (fwd->is_udp == -1) { - const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.proto\"}}"; - 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) { - const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.host_addr\"}}"; - wrc = write(fd, err, strlen(err)); - free(fwd); - goto finish; - } - fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port"); - if (fwd->host_port == 0) { - const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.host_port\"}}"; - wrc = write(fd, err, strlen(err)); - free(fwd); - goto finish; - } + int wrc = 0, slirprc = 0; + int id = -1; + char idbuf[64]; + const char *proto_s = json_object_dotget_string(jo, "arguments.proto"); + const char *host_addr_s = + json_object_dotget_string(jo, "arguments.host_addr"); + const char *guest_addr_s = + json_object_dotget_string(jo, "arguments.guest_addr"); + struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd)); + if (fwd == NULL) { + perror("fatal: malloc"); + exit(EXIT_FAILURE); + } + fwd->is_udp = -1; /* TODO: support SCTP */ + if (strcmp(proto_s, "udp") == 0) { + fwd->is_udp = 1; + } else if (strcmp(proto_s, "tcp") == 0) { + fwd->is_udp = 0; + } + if (fwd->is_udp == -1) { + const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " + "bad arguments.proto\"}}"; + 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) { + const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " + "bad arguments.host_addr\"}}"; + wrc = write(fd, err, strlen(err)); + free(fwd); + goto finish; + } + fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port"); + if (fwd->host_port == 0) { + const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " + "bad arguments.host_port\"}}"; + wrc = write(fd, err, strlen(err)); + free(fwd); + goto finish; + } - 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) { - const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.guest_addr\"}}"; - wrc = write(fd, err, strlen(err)); - free(fwd); - goto finish; - } - fwd->guest_port = (int)json_object_dotget_number(jo, "arguments.guest_port"); - if (fwd->guest_port == 0) { - const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.guest_port\"}}"; - wrc = write(fd, err, strlen(err)); - free(fwd); - goto finish; - } - if ((slirprc = - slirp_add_hostfwd(slirp, fwd->is_udp, fwd->host_addr, fwd->host_port, fwd->guest_addr, - fwd->guest_port)) < 0) { - const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: slirp_add_hostfwd failed\"}}"; - wrc = write(fd, err, strlen(err)); - free(fwd); - goto finish; - } - fwd->id = ctx->hostfwds_nextid; - ctx->hostfwds_nextid++; - ctx->hostfwds = g_list_append(ctx->hostfwds, fwd); - if (snprintf(idbuf, sizeof(idbuf), "{\"return\":{\"id\":%d}}", fwd->id) > sizeof(idbuf)) { - fprintf(stderr, "fatal: unexpected id=%d\n", fwd->id); - exit(EXIT_FAILURE); - } - wrc = write(fd, idbuf, strlen(idbuf)); - finish: - return wrc; + 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) { + const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " + "bad arguments.guest_addr\"}}"; + wrc = write(fd, err, strlen(err)); + free(fwd); + goto finish; + } + fwd->guest_port = + (int)json_object_dotget_number(jo, "arguments.guest_port"); + if (fwd->guest_port == 0) { + const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " + "bad arguments.guest_port\"}}"; + wrc = write(fd, err, strlen(err)); + free(fwd); + goto finish; + } + if ((slirprc = slirp_add_hostfwd(slirp, fwd->is_udp, fwd->host_addr, + fwd->host_port, fwd->guest_addr, + fwd->guest_port)) < 0) { + const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " + "slirp_add_hostfwd failed\"}}"; + wrc = write(fd, err, strlen(err)); + free(fwd); + goto finish; + } + fwd->id = ctx->hostfwds_nextid; + ctx->hostfwds_nextid++; + ctx->hostfwds = g_list_append(ctx->hostfwds, fwd); + if (snprintf(idbuf, sizeof(idbuf), "{\"return\":{\"id\":%d}}", fwd->id) > + sizeof(idbuf)) { + fprintf(stderr, "fatal: unexpected id=%d\n", fwd->id); + exit(EXIT_FAILURE); + } + wrc = write(fd, idbuf, strlen(idbuf)); +finish: + return wrc; } -static void api_handle_req_list_hostfwd_foreach(gpointer data, gpointer user_data) +static void api_handle_req_list_hostfwd_foreach(gpointer data, + gpointer user_data) { - struct api_hostfwd *fwd = data; - JSON_Array *entries_array = (JSON_Array *) user_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]; - 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); - } - 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); - json_object_set_number(entry_object, "host_port", fwd->host_port); - json_object_set_string(entry_object, "guest_addr", guest_addr); - 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) { - fprintf(stderr, "fatal: json_array_append_value\n"); - exit(EXIT_FAILURE); - } + struct api_hostfwd *fwd = data; + JSON_Array *entries_array = (JSON_Array *)user_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]; + 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); + } + 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); + json_object_set_number(entry_object, "host_port", fwd->host_port); + json_object_set_string(entry_object, "guest_addr", guest_addr); + 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) { + fprintf(stderr, "fatal: json_array_append_value\n"); + exit(EXIT_FAILURE); + } } /* Handler for list_hostfwd. e.g. {"execute": "list_hostfwd"} */ -static int api_handle_req_list_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo) +static int api_handle_req_list_hostfwd(Slirp *slirp, int fd, + struct api_ctx *ctx, JSON_Object *jo) { - int wrc = 0; - struct api_hostfwd *fwd = NULL; - JSON_Value *root_value = json_value_init_object(), *entries_value = json_value_init_array(); - JSON_Object *root_object = json_value_get_object(root_value); - JSON_Array *entries_array = json_array(entries_value); - char *serialized_string = NULL; - g_list_foreach(ctx->hostfwds, api_handle_req_list_hostfwd_foreach, entries_array); - json_object_set_value(root_object, "entries", entries_value); - serialized_string = json_serialize_to_string(root_value); - wrc = write(fd, serialized_string, strlen(serialized_string)); - finish: - json_free_serialized_string(serialized_string); - json_value_free(root_value); - return wrc; + int wrc = 0; + struct api_hostfwd *fwd = NULL; + JSON_Value *root_value = json_value_init_object(), + *entries_value = json_value_init_array(); + JSON_Object *root_object = json_value_get_object(root_value); + JSON_Array *entries_array = json_array(entries_value); + char *serialized_string = NULL; + g_list_foreach(ctx->hostfwds, api_handle_req_list_hostfwd_foreach, + entries_array); + json_object_set_value(root_object, "entries", entries_value); + serialized_string = json_serialize_to_string(root_value); + wrc = write(fd, serialized_string, strlen(serialized_string)); +finish: + json_free_serialized_string(serialized_string); + json_value_free(root_value); + return wrc; } -static int api_handle_remove_list_hostfwd_find(gconstpointer gcp_fwd, gconstpointer gcp_id) +static int api_handle_remove_list_hostfwd_find(gconstpointer gcp_fwd, + gconstpointer gcp_id) { - struct api_hostfwd *fwd = (struct api_hostfwd *)gcp_fwd; - int id = *(int *)gcp_id; - return id == fwd->id ? 0 : 1; + struct api_hostfwd *fwd = (struct api_hostfwd *)gcp_fwd; + int id = *(int *)gcp_id; + return id == fwd->id ? 0 : 1; } /* Handler for remove_hostfwd. e.g. {"execute": "remove_hostfwd", "arguments": {"id": 42}} */ -static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo) +static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd, + struct api_ctx *ctx, JSON_Object *jo) { - int wrc = 0; - int id = (int)json_object_dotget_number(jo, "arguments.id"); - GList *found = g_list_find_custom(ctx->hostfwds, &id, api_handle_remove_list_hostfwd_find); - if (found == NULL) { - const char *err = "{\"error\":{\"desc\":\"bad request: remove_hostfwd: bad arguments.id\"}}"; - wrc = write(fd, err, strlen(err)); - } 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)); - } - } - return wrc; + int wrc = 0; + int id = (int)json_object_dotget_number(jo, "arguments.id"); + GList *found = g_list_find_custom(ctx->hostfwds, &id, + api_handle_remove_list_hostfwd_find); + if (found == NULL) { + const char *err = "{\"error\":{\"desc\":\"bad request: remove_hostfwd: " + "bad arguments.id\"}}"; + wrc = write(fd, err, strlen(err)); + } 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)); + } + } + return wrc; } static int api_handle_req(Slirp *slirp, int fd, struct api_ctx *ctx) { - JSON_Value *jv = NULL; - JSON_Object *jo = NULL; - const char *execute = NULL; - int wrc = 0; - if ((jv = json_parse_string((const char *)ctx->buf)) == NULL) { - const char *err = "{\"error\":{\"desc\":\"bad request: cannot parse JSON\"}}"; - wrc = write(fd, err, strlen(err)); - goto finish; - } - if ((jo = json_object(jv)) == NULL) { - const char *err = "{\"error\":{\"desc\":\"bad request: json_object() failed\"}}"; - wrc = write(fd, err, strlen(err)); - goto finish; - } - /* TODO: json_validate */ - if ((execute = json_object_get_string(jo, "execute")) == NULL) { - const char *err = "{\"error\":{\"desc\":\"bad request: no execute found\"}}"; - wrc = write(fd, err, strlen(err)); - goto finish; - } - if ((strcmp(execute, "add_hostfwd")) == 0) { - wrc = api_handle_req_add_hostfwd(slirp, fd, ctx, jo); - } else if ((strcmp(execute, "list_hostfwd")) == 0) { - wrc = api_handle_req_list_hostfwd(slirp, fd, ctx, jo); - } else if ((strcmp(execute, "remove_hostfwd")) == 0) { - wrc = api_handle_req_remove_hostfwd(slirp, fd, ctx, jo); - } else { - const char *err = "{\"error\":{\"desc\":\"bad request: unknown execute\"}}"; - wrc = write(fd, err, strlen(err)); - goto finish; - } - finish: - if (jv != NULL) { - json_value_free(jv); - } - return wrc; + JSON_Value *jv = NULL; + JSON_Object *jo = NULL; + const char *execute = NULL; + int wrc = 0; + if ((jv = json_parse_string((const char *)ctx->buf)) == NULL) { + const char *err = + "{\"error\":{\"desc\":\"bad request: cannot parse JSON\"}}"; + wrc = write(fd, err, strlen(err)); + goto finish; + } + if ((jo = json_object(jv)) == NULL) { + const char *err = + "{\"error\":{\"desc\":\"bad request: json_object() failed\"}}"; + wrc = write(fd, err, strlen(err)); + goto finish; + } + /* TODO: json_validate */ + if ((execute = json_object_get_string(jo, "execute")) == NULL) { + const char *err = + "{\"error\":{\"desc\":\"bad request: no execute found\"}}"; + wrc = write(fd, err, strlen(err)); + goto finish; + } + if ((strcmp(execute, "add_hostfwd")) == 0) { + wrc = api_handle_req_add_hostfwd(slirp, fd, ctx, jo); + } else if ((strcmp(execute, "list_hostfwd")) == 0) { + wrc = api_handle_req_list_hostfwd(slirp, fd, ctx, jo); + } else if ((strcmp(execute, "remove_hostfwd")) == 0) { + wrc = api_handle_req_remove_hostfwd(slirp, fd, ctx, jo); + } else { + const char *err = + "{\"error\":{\"desc\":\"bad request: unknown execute\"}}"; + wrc = write(fd, err, strlen(err)); + goto finish; + } +finish: + if (jv != NULL) { + json_value_free(jv); + } + return wrc; } /* @@ -291,36 +319,37 @@ static int api_handle_req(Slirp *slirp, int fd, struct api_ctx *ctx) */ int api_handler(Slirp *slirp, int listenfd, struct api_ctx *ctx) { - struct sockaddr_un addr; - socklen_t addrlen = sizeof(struct sockaddr_un); - int fd; - int rc = 0, wrc = 0; - ssize_t len; - memset(&addr, 0, sizeof(addr)); - if ((fd = accept(listenfd, (struct sockaddr *)&addr, &addrlen)) < 0) { - perror("api_handler: accept"); - return -1; - } - if ((len = read(fd, ctx->buf, ctx->buflen)) < 0) { - perror("api_handler: read"); - rc = len; - goto finish; - } - if (len == ctx->buflen) { - const char *err = "{\"error\":{\"desc\":\"bad request: too large message\"}}"; - fprintf(stderr, "api_handler: too large message (>= %ld bytes)\n", len); - wrc = write(fd, err, strlen(err)); - rc = -1; - goto finish; - } - ctx->buf[len] = 0; - fprintf(stderr, "api_handler: got request: %s\n", ctx->buf); - wrc = api_handle_req(slirp, fd, ctx); - finish: - shutdown(fd, SHUT_RDWR); - if (rc == 0 && wrc != 0) { - rc = wrc; - } - close(fd); - return rc; + struct sockaddr_un addr; + socklen_t addrlen = sizeof(struct sockaddr_un); + int fd; + int rc = 0, wrc = 0; + ssize_t len; + memset(&addr, 0, sizeof(addr)); + if ((fd = accept(listenfd, (struct sockaddr *)&addr, &addrlen)) < 0) { + perror("api_handler: accept"); + return -1; + } + if ((len = read(fd, ctx->buf, ctx->buflen)) < 0) { + perror("api_handler: read"); + rc = len; + goto finish; + } + if (len == ctx->buflen) { + const char *err = + "{\"error\":{\"desc\":\"bad request: too large message\"}}"; + fprintf(stderr, "api_handler: too large message (>= %ld bytes)\n", len); + wrc = write(fd, err, strlen(err)); + rc = -1; + goto finish; + } + ctx->buf[len] = 0; + fprintf(stderr, "api_handler: got request: %s\n", ctx->buf); + wrc = api_handle_req(slirp, fd, ctx); +finish: + shutdown(fd, SHUT_RDWR); + if (rc == 0 && wrc != 0) { + rc = wrc; + } + close(fd); + return rc; } diff --git a/main.c b/main.c index 45ca81d..2fa3c06 100644 --- a/main.c +++ b/main.c @@ -22,329 +22,342 @@ #define DEFAULT_MTU (1500) #define DEFAULT_CIDR ("10.0.2.0/24") -#define DEFAULT_VHOST_OFFSET (2) // 10.0.2.2 -#define DEFAULT_VDHCPSTART_OFFSET (15) // 10.0.2.15 -#define DEFAULT_VNAMESERVER_OFFSET (3) // 10.0.2.3 -#define DEFAULT_RECOMMENDED_VGUEST_OFFSET (100) // 10.0.2.100 +#define DEFAULT_VHOST_OFFSET (2) // 10.0.2.2 +#define DEFAULT_VDHCPSTART_OFFSET (15) // 10.0.2.15 +#define DEFAULT_VNAMESERVER_OFFSET (3) // 10.0.2.3 +#define DEFAULT_RECOMMENDED_VGUEST_OFFSET (100) // 10.0.2.100 #define DEFAULT_NETNS_TYPE ("pid") #define NETWORK_PREFIX_MIN (1) -// >=26 is not supported because the recommended guest IP is set to network addr + 100 . +// >=26 is not supported because the recommended guest IP is set to network addr +// + 100 . #define NETWORK_PREFIX_MAX (25) static int nsenter(pid_t target_pid, char *netns, char *userns) { - int usernsfd = -1, netnsfd; - if (!netns) { - if (asprintf(&netns, "/proc/%d/ns/net", target_pid) < 0) { - perror("cannot get netns path"); - return -1; - } - } - if (!userns && target_pid) { - if (asprintf(&userns, "/proc/%d/ns/user", target_pid) < 0) { - perror("cannot get userns path"); - return -1; - } - } - if ((netnsfd = open(netns, O_RDONLY)) < 0) { - perror(netns); - return netnsfd; - } - if (userns && (usernsfd = open(userns, O_RDONLY)) < 0) { - perror(userns); - return usernsfd; - } + int usernsfd = -1, netnsfd; + if (!netns) { + if (asprintf(&netns, "/proc/%d/ns/net", target_pid) < 0) { + perror("cannot get netns path"); + return -1; + } + } + if (!userns && target_pid) { + if (asprintf(&userns, "/proc/%d/ns/user", target_pid) < 0) { + perror("cannot get userns path"); + return -1; + } + } + if ((netnsfd = open(netns, O_RDONLY)) < 0) { + perror(netns); + return netnsfd; + } + if (userns && (usernsfd = open(userns, O_RDONLY)) < 0) { + perror(userns); + return usernsfd; + } - if (usernsfd != -1) { - setns(usernsfd, CLONE_NEWUSER); - close(usernsfd); - } - if (setns(netnsfd, CLONE_NEWNET) < 0) { - perror("setns(CLONE_NEWNET)"); - return -1; - } - close(netnsfd); - free(netns); - free(userns); - return 0; + if (usernsfd != -1) { + setns(usernsfd, CLONE_NEWUSER); + close(usernsfd); + } + if (setns(netnsfd, CLONE_NEWNET) < 0) { + perror("setns(CLONE_NEWNET)"); + return -1; + } + close(netnsfd); + free(netns); + free(userns); + return 0; } static int open_tap(const char *tapname) { - int fd; - struct ifreq ifr; - if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { - perror("open(\"/dev/net/tun\")"); - return fd; - } - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); - if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { - perror("ioctl(TUNSETIFF)"); - close(fd); - return -1; - } - return fd; + int fd; + struct ifreq ifr; + if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { + perror("open(\"/dev/net/tun\")"); + return fd; + } + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + perror("ioctl(TUNSETIFF)"); + close(fd); + return -1; + } + return fd; } static int sendfd(int sock, int fd) { - ssize_t rc; - struct msghdr msg; - struct cmsghdr *cmsg; - char cmsgbuf[CMSG_SPACE(sizeof(fd))]; - struct iovec iov; - char dummy = '\0'; - memset(&msg, 0, sizeof(msg)); - iov.iov_base = &dummy; - iov.iov_len = 1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); - memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); - msg.msg_controllen = cmsg->cmsg_len; - if ((rc = sendmsg(sock, &msg, 0)) < 0) { - perror("sendmsg"); - } - return rc; + ssize_t rc; + struct msghdr msg; + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE(sizeof(fd))]; + struct iovec iov; + char dummy = '\0'; + memset(&msg, 0, sizeof(msg)); + iov.iov_base = &dummy; + iov.iov_len = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + msg.msg_controllen = cmsg->cmsg_len; + if ((rc = sendmsg(sock, &msg, 0)) < 0) { + perror("sendmsg"); + } + return rc; } -static int configure_network(const char *tapname, struct slirp4netns_config *cfg) +static int configure_network(const char *tapname, + struct slirp4netns_config *cfg) { - struct rtentry route; - struct ifreq ifr; - struct sockaddr_in *sai = (struct sockaddr_in *)&ifr.ifr_addr; - int sockfd; + struct rtentry route; + struct ifreq ifr; + struct sockaddr_in *sai = (struct sockaddr_in *)&ifr.ifr_addr; + int sockfd; - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - perror("cannot create socket"); - return -1; - } + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + perror("cannot create socket"); + return -1; + } - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_UP | IFF_RUNNING; - strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_UP | IFF_RUNNING; + strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); - if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { - perror("cannot set device up"); - return -1; - } + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { + perror("cannot set device up"); + return -1; + } - ifr.ifr_mtu = (int)cfg->mtu; - if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { - perror("cannot set MTU"); - return -1; - } + ifr.ifr_mtu = (int)cfg->mtu; + if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { + perror("cannot set MTU"); + return -1; + } - sai->sin_family = AF_INET; - sai->sin_port = 0; - sai->sin_addr = cfg->recommended_vguest; + sai->sin_family = AF_INET; + sai->sin_port = 0; + sai->sin_addr = cfg->recommended_vguest; - if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { - perror("cannot set device address"); - return -1; - } + if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { + perror("cannot set device address"); + return -1; + } - sai->sin_addr = cfg->vnetmask; - if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { - perror("cannot set device netmask"); - return -1; - } + sai->sin_addr = cfg->vnetmask; + if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { + perror("cannot set device netmask"); + return -1; + } - memset(&route, 0, sizeof(route)); - sai = (struct sockaddr_in *)&route.rt_gateway; - sai->sin_family = AF_INET; - sai->sin_addr = cfg->vhost; - sai = (struct sockaddr_in *)&route.rt_dst; - sai->sin_family = AF_INET; - sai->sin_addr.s_addr = INADDR_ANY; - sai = (struct sockaddr_in *)&route.rt_genmask; - sai->sin_family = AF_INET; - sai->sin_addr.s_addr = INADDR_ANY; + memset(&route, 0, sizeof(route)); + sai = (struct sockaddr_in *)&route.rt_gateway; + sai->sin_family = AF_INET; + sai->sin_addr = cfg->vhost; + sai = (struct sockaddr_in *)&route.rt_dst; + sai->sin_family = AF_INET; + sai->sin_addr.s_addr = INADDR_ANY; + sai = (struct sockaddr_in *)&route.rt_genmask; + sai->sin_family = AF_INET; + sai->sin_addr.s_addr = INADDR_ANY; - route.rt_flags = RTF_UP | RTF_GATEWAY; - route.rt_metric = 0; - route.rt_dev = (char *)tapname; + route.rt_flags = RTF_UP | RTF_GATEWAY; + route.rt_metric = 0; + route.rt_dev = (char *)tapname; - if (ioctl(sockfd, SIOCADDRT, &route) < 0) { - perror("set route"); - return -1; - } - return 0; + if (ioctl(sockfd, SIOCADDRT, &route) < 0) { + perror("set route"); + return -1; + } + return 0; } -static int child(int sock, pid_t target_pid, bool do_config_network, const char *tapname, int ready_fd, char *netns_path, char *userns_path, - struct slirp4netns_config *cfg) +static int child(int sock, pid_t target_pid, bool do_config_network, + const char *tapname, int ready_fd, char *netns_path, + char *userns_path, struct slirp4netns_config *cfg) { - int rc, tapfd; - if ((rc = nsenter(target_pid, netns_path, userns_path)) < 0) { - return rc; - } - if ((tapfd = open_tap(tapname)) < 0) { - return tapfd; - } - if (do_config_network && configure_network(tapname, cfg) < 0) { - return -1; - } - if (ready_fd >= 0) { - do - rc = write(ready_fd, "1", 1); - while (rc < 0 && errno == EINTR); - close(ready_fd); - } - if (sendfd(sock, tapfd) < 0) { - close(tapfd); - close(sock); - return -1; - } - fprintf(stderr, "sent tapfd=%d for %s\n", tapfd, tapname); - close(sock); - return 0; + int rc, tapfd; + if ((rc = nsenter(target_pid, netns_path, userns_path)) < 0) { + return rc; + } + if ((tapfd = open_tap(tapname)) < 0) { + return tapfd; + } + if (do_config_network && configure_network(tapname, cfg) < 0) { + return -1; + } + if (ready_fd >= 0) { + do + rc = write(ready_fd, "1", 1); + while (rc < 0 && errno == EINTR); + close(ready_fd); + } + if (sendfd(sock, tapfd) < 0) { + close(tapfd); + close(sock); + return -1; + } + fprintf(stderr, "sent tapfd=%d for %s\n", tapfd, tapname); + close(sock); + return 0; } static int recvfd(int sock) { - int fd; - ssize_t rc; - struct msghdr msg; - struct cmsghdr *cmsg; - char cmsgbuf[CMSG_SPACE(sizeof(fd))]; - struct iovec iov; - char dummy = '\0'; - memset(&msg, 0, sizeof(msg)); - iov.iov_base = &dummy; - iov.iov_len = 1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - if ((rc = recvmsg(sock, &msg, 0)) < 0) { - perror("recvmsg"); - return (int)rc; - } - if (rc == 0) { - fprintf(stderr, "the message is empty\n"); - return -1; - } - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS) { - fprintf(stderr, "the message does not contain fd\n"); - return -1; - } - memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); - return fd; + int fd; + ssize_t rc; + struct msghdr msg; + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE(sizeof(fd))]; + struct iovec iov; + char dummy = '\0'; + memset(&msg, 0, sizeof(msg)); + iov.iov_base = &dummy; + iov.iov_len = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + if ((rc = recvmsg(sock, &msg, 0)) < 0) { + perror("recvmsg"); + return (int)rc; + } + if (rc == 0) { + fprintf(stderr, "the message is empty\n"); + return -1; + } + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS) { + fprintf(stderr, "the message does not contain fd\n"); + return -1; + } + memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + return fd; } -static int parent(int sock, int exit_fd, const char *api_socket, struct slirp4netns_config *cfg) +static int parent(int sock, int exit_fd, const char *api_socket, + struct slirp4netns_config *cfg) { - int rc, tapfd; - if ((tapfd = recvfd(sock)) < 0) { - return tapfd; - } - fprintf(stderr, "received tapfd=%d\n", tapfd); - close(sock); - printf("Starting slirp\n"); - printf("* MTU: %d\n", cfg->mtu); - printf("* Network: %s\n", inet_ntoa(cfg->vnetwork)); - printf("* Netmask: %s\n", inet_ntoa(cfg->vnetmask)); - printf("* Gateway: %s\n", inet_ntoa(cfg->vhost)); - printf("* DNS: %s\n", inet_ntoa(cfg->vnameserver)); - printf("* Recommended IP: %s\n", inet_ntoa(cfg->recommended_vguest)); - if (api_socket != NULL) { - printf("* API Socket: %s\n", api_socket); - } - if (!cfg->disable_host_loopback) { - printf - ("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", - inet_ntoa(cfg->vhost)); - } - if ((rc = do_slirp(tapfd, exit_fd, api_socket, cfg)) < 0) { - fprintf(stderr, "do_slirp failed\n"); - close(tapfd); - return rc; - } - /* NOT REACHED */ - return 0; + int rc, tapfd; + if ((tapfd = recvfd(sock)) < 0) { + return tapfd; + } + fprintf(stderr, "received tapfd=%d\n", tapfd); + close(sock); + printf("Starting slirp\n"); + printf("* MTU: %d\n", cfg->mtu); + printf("* Network: %s\n", inet_ntoa(cfg->vnetwork)); + printf("* Netmask: %s\n", inet_ntoa(cfg->vnetmask)); + printf("* Gateway: %s\n", inet_ntoa(cfg->vhost)); + printf("* DNS: %s\n", inet_ntoa(cfg->vnameserver)); + printf("* Recommended IP: %s\n", inet_ntoa(cfg->recommended_vguest)); + if (api_socket != NULL) { + printf("* API Socket: %s\n", api_socket); + } + if (!cfg->disable_host_loopback) { + printf( + "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", + inet_ntoa(cfg->vhost)); + } + if ((rc = do_slirp(tapfd, exit_fd, api_socket, cfg)) < 0) { + fprintf(stderr, "do_slirp failed\n"); + close(tapfd); + return rc; + } + /* NOT REACHED */ + return 0; } static void usage(const char *argv0) { - printf("Usage: %s [OPTION]... PID|PATH TAPNAME\n", argv0); - printf("User-mode networking for unprivileged network namespaces.\n\n"); - printf("-c, --configure bring up the interface\n"); - printf("-e, --exit-fd=FD specify the FD for terminating slirp4netns\n"); - printf("-r, --ready-fd=FD specify the FD to write to when the network is configured\n"); - printf("-m, --mtu=MTU specify MTU (default=%d, max=65521)\n", DEFAULT_MTU); - printf("--cidr=CIDR specify network address CIDR (default=%s)\n", DEFAULT_CIDR); - printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the host namespace\n"); - printf("--netns-type=TYPE specify network namespace type ([path|pid], default=%s)\n", DEFAULT_NETNS_TYPE); - printf("--userns-path=PATH specify user namespace path\n"); - printf("-a, --api-socket=PATH specify API socket path\n"); - printf("-6, --enable-ipv6 enable IPv6 (experimental)\n"); - printf("-h, --help show this help and exit\n"); - printf("-v, --version show version and exit\n"); + printf("Usage: %s [OPTION]... PID|PATH TAPNAME\n", argv0); + printf("User-mode networking for unprivileged network namespaces.\n\n"); + printf("-c, --configure bring up the interface\n"); + printf("-e, --exit-fd=FD specify the FD for terminating " + "slirp4netns\n"); + printf("-r, --ready-fd=FD specify the FD to write to when the " + "network is configured\n"); + printf("-m, --mtu=MTU specify MTU (default=%d, max=65521)\n", + DEFAULT_MTU); + printf( + "--cidr=CIDR specify network address CIDR (default=%s)\n", + DEFAULT_CIDR); + printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the " + "host namespace\n"); + printf("--netns-type=TYPE specify network namespace type ([path|pid], " + "default=%s)\n", + DEFAULT_NETNS_TYPE); + printf("--userns-path=PATH specify user namespace path\n"); + printf("-a, --api-socket=PATH specify API socket path\n"); + printf("-6, --enable-ipv6 enable IPv6 (experimental)\n"); + printf("-h, --help show this help and exit\n"); + printf("-v, --version show version and exit\n"); } // version output is runc-compatible and machine-parsable static void version() { - printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION); + printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION); #ifdef COMMIT - printf("commit: %s\n", COMMIT); + printf("commit: %s\n", COMMIT); #endif } struct options { - pid_t target_pid; // argv[1] - char *tapname; // argv[2] - bool do_config_network; // -c - int exit_fd; // -e - int ready_fd; // -r - unsigned int mtu; // -m - bool disable_host_loopback; // --disable-host-loopback - char *cidr; // --cidr - bool enable_ipv6; // -6 - char *api_socket; // -a - char *netns_type; // argv[1] - char *netns_path; // --netns-path - char *userns_path; // --userns-path + pid_t target_pid; // argv[1] + char *tapname; // argv[2] + bool do_config_network; // -c + int exit_fd; // -e + int ready_fd; // -r + unsigned int mtu; // -m + bool disable_host_loopback; // --disable-host-loopback + char *cidr; // --cidr + bool enable_ipv6; // -6 + char *api_socket; // -a + char *netns_type; // argv[1] + char *netns_path; // --netns-path + char *userns_path; // --userns-path }; static void options_init(struct options *options) { - memset(options, 0, sizeof(*options)); - options->exit_fd = options->ready_fd = -1; - options->mtu = DEFAULT_MTU; + memset(options, 0, sizeof(*options)); + options->exit_fd = options->ready_fd = -1; + options->mtu = DEFAULT_MTU; } static void options_destroy(struct options *options) { - if (options->tapname != NULL) { - free(options->tapname); - options->tapname = NULL; - } - if (options->cidr != NULL) { - free(options->cidr); - options->cidr = NULL; - } - if (options->api_socket != NULL) { - free(options->api_socket); - options->api_socket = NULL; - } - if (options->netns_type != NULL) { - free(options->netns_type); - options->netns_type = NULL; - } - // options->netns_path and options->userns_path are - // getting freed in the nsenter function - // options itself is not freed, because it can be on the stack. + if (options->tapname != NULL) { + free(options->tapname); + options->tapname = NULL; + } + if (options->cidr != NULL) { + free(options->cidr); + options->cidr = NULL; + } + if (options->api_socket != NULL) { + free(options->api_socket); + options->api_socket = NULL; + } + if (options->netns_type != NULL) { + free(options->netns_type); + options->netns_type = NULL; + } + // options->netns_path and options->userns_path are + // getting freed in the nsenter function + // options itself is not freed, because it can be on the stack. } // * caller does not need to call options_init() @@ -352,288 +365,306 @@ static void options_destroy(struct options *options) // * this function calls exit() on an error. static void parse_args(int argc, char *const argv[], struct options *options) { - int opt; + int opt; #define CIDR -42 #define DISABLE_HOST_LOOPBACK -43 #define NETNS_TYPE -44 #define USERNS_PATH -45 -#define _DEPRECATED_NO_HOST_LOOPBACK -10043 // deprecated in favor of disable-host-loopback - const struct option longopts[] = { - {"configure", no_argument, NULL, 'c'}, - {"exit-fd", required_argument, NULL, 'e'}, - {"ready-fd", required_argument, NULL, 'r'}, - {"mtu", required_argument, NULL, 'm'}, - {"cidr", required_argument, NULL, CIDR}, - {"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}, - {"userns-path", required_argument, NULL, USERNS_PATH}, - {"api-socket", required_argument, NULL, 'a'}, - {"enable-ipv6", no_argument, NULL, '6'}, - {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'v'}, - {0, 0, 0, 0}, - }; - options_init(options); - while ((opt = getopt_long(argc, argv, "ce:r:m:a:6hv", longopts, NULL)) != -1) { - switch (opt) { - case 'c': - options->do_config_network = true; - break; - case 'e': - errno = 0; - options->exit_fd = strtol(optarg, NULL, 10); - if (errno) { - perror("strtol"); - usage(argv[0]); - exit(EXIT_FAILURE); - } - break; - case 'r': - errno = 0; - options->ready_fd = strtol(optarg, NULL, 10); - if (errno) { - perror("strtol"); - usage(argv[0]); - exit(EXIT_FAILURE); - } - break; - case 'm': - errno = 0; - options->mtu = strtol(optarg, NULL, 10); - if (errno) { - perror("strtol"); - usage(argv[0]); - exit(EXIT_FAILURE); - } - break; - case CIDR: - options->cidr = strdup(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. - printf - ("WARNING: --no-host-loopback is deprecated and will be removed in future releases, please use --disable-host-loopback instead.\n"); - /* FALLTHROUGH */ - case DISABLE_HOST_LOOPBACK: - options->disable_host_loopback = true; - break; - case NETNS_TYPE: - options->netns_type = strdup(optarg); - break; - case USERNS_PATH: - options->userns_path = strdup(optarg); - if (access(options->userns_path, F_OK) == -1) { - fprintf(stderr, "userns path doesn't exist: %s\n", options->userns_path); - usage(argv[0]); - exit(EXIT_FAILURE); - } - break; - case 'a': - options->api_socket = strdup(optarg); - break; - case '6': - options->enable_ipv6 = true; - printf("WARNING: Support for IPv6 is experimental\n"); - break; - case 'h': - usage(argv[0]); - exit(EXIT_SUCCESS); - case 'v': - version(); - exit(EXIT_SUCCESS); - default: - usage(argv[0]); - exit(EXIT_FAILURE); - } - } +#define _DEPRECATED_NO_HOST_LOOPBACK \ + -10043 // deprecated in favor of disable-host-loopback + const struct option longopts[] = { + { "configure", no_argument, NULL, 'c' }, + { "exit-fd", required_argument, NULL, 'e' }, + { "ready-fd", required_argument, NULL, 'r' }, + { "mtu", required_argument, NULL, 'm' }, + { "cidr", required_argument, NULL, CIDR }, + { "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 }, + { "userns-path", required_argument, NULL, USERNS_PATH }, + { "api-socket", required_argument, NULL, 'a' }, + { "enable-ipv6", no_argument, NULL, '6' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 }, + }; + options_init(options); + while ((opt = getopt_long(argc, argv, "ce:r:m:a:6hv", longopts, NULL)) != + -1) { + switch (opt) { + case 'c': + options->do_config_network = true; + break; + case 'e': + errno = 0; + options->exit_fd = strtol(optarg, NULL, 10); + if (errno) { + perror("strtol"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + break; + case 'r': + errno = 0; + options->ready_fd = strtol(optarg, NULL, 10); + if (errno) { + perror("strtol"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + break; + case 'm': + errno = 0; + options->mtu = strtol(optarg, NULL, 10); + if (errno) { + perror("strtol"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + break; + case CIDR: + options->cidr = strdup(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. + printf("WARNING: --no-host-loopback is deprecated and will be " + "removed in future releases, please use " + "--disable-host-loopback instead.\n"); + /* FALLTHROUGH */ + case DISABLE_HOST_LOOPBACK: + options->disable_host_loopback = true; + break; + case NETNS_TYPE: + options->netns_type = strdup(optarg); + break; + case USERNS_PATH: + options->userns_path = strdup(optarg); + if (access(options->userns_path, F_OK) == -1) { + fprintf(stderr, "userns path doesn't exist: %s\n", + options->userns_path); + usage(argv[0]); + exit(EXIT_FAILURE); + } + break; + case 'a': + options->api_socket = strdup(optarg); + break; + case '6': + options->enable_ipv6 = true; + printf("WARNING: Support for IPv6 is experimental\n"); + break; + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + case 'v': + version(); + exit(EXIT_SUCCESS); + default: + usage(argv[0]); + exit(EXIT_FAILURE); + } + } #undef CIDR #undef DISABLE_HOST_LOOPBACK #undef NETNS_TYPE #undef USERNS_PATH #undef _DEPRECATED_NO_HOST_LOOPBACK - if (options->ready_fd >= 0 && !options->do_config_network) { - fprintf(stderr, "the option -r FD requires -c\n"); - exit(EXIT_FAILURE); - } - if (options->mtu == 0 || options->mtu > 65521) { - fprintf(stderr, "MTU must be a positive integer (< 65522)\n"); - exit(EXIT_FAILURE); - } - if (argc - optind < 2) { - usage(argv[0]); - exit(EXIT_FAILURE); - } - if (!options->netns_type || strcmp(options->netns_type, DEFAULT_NETNS_TYPE) == 0) { - errno = 0; - options->target_pid = strtol(argv[optind], NULL, 10); - if (errno != 0) { - perror("strtol"); - usage(argv[0]); - exit(EXIT_FAILURE); - } - } else { - options->netns_path = strdup(argv[optind]); - if (access(options->netns_path, F_OK) == -1) { - perror("existing path expected when --netns-type=path"); - usage(argv[0]); - exit(EXIT_FAILURE); - } - } - options->tapname = strdup(argv[optind + 1]); + if (options->ready_fd >= 0 && !options->do_config_network) { + fprintf(stderr, "the option -r FD requires -c\n"); + exit(EXIT_FAILURE); + } + if (options->mtu == 0 || options->mtu > 65521) { + fprintf(stderr, "MTU must be a positive integer (< 65522)\n"); + exit(EXIT_FAILURE); + } + if (argc - optind < 2) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + if (!options->netns_type || + strcmp(options->netns_type, DEFAULT_NETNS_TYPE) == 0) { + errno = 0; + options->target_pid = strtol(argv[optind], NULL, 10); + if (errno != 0) { + perror("strtol"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + } else { + options->netns_path = strdup(argv[optind]); + if (access(options->netns_path, F_OK) == -1) { + perror("existing path expected when --netns-type=path"); + usage(argv[0]); + exit(EXIT_FAILURE); + } + } + options->tapname = strdup(argv[optind + 1]); } -static int from_regmatch(char *buf, size_t buf_len, regmatch_t match, const char *orig) +static int from_regmatch(char *buf, size_t buf_len, regmatch_t match, + const char *orig) { - size_t len = match.rm_eo - match.rm_so; - if (len > buf_len - 1) { - return -1; - } - memset(buf, 0, buf_len); - strncpy(buf, &orig[match.rm_so], len); - return 0; + size_t len = match.rm_eo - match.rm_so; + if (len > buf_len - 1) { + return -1; + } + memset(buf, 0, buf_len); + strncpy(buf, &orig[match.rm_so], len); + return 0; } -static int parse_cidr(struct in_addr *network, struct in_addr *netmask, const char *cidr) +static int parse_cidr(struct in_addr *network, struct in_addr *netmask, + const char *cidr) { - int rc = 0; - regex_t r; - regmatch_t matches[4]; - size_t nmatch = sizeof(matches) / sizeof(matches[0]); - const char *cidr_regex = "^(([0-9]{1,3}\\.){3}[0-9]{1,3})/([0-9]{1,2})$"; - char snetwork[16], sprefix[16]; - int prefix; - rc = regcomp(&r, cidr_regex, REG_EXTENDED); - if (rc != 0) { - fprintf(stderr, "internal regex error\n"); - rc = -1; - goto finish; - } - rc = regexec(&r, cidr, nmatch, matches, 0); - if (rc != 0) { - fprintf(stderr, "invalid CIDR: %s\n", cidr); - rc = -1; - goto finish; - } - rc = from_regmatch(snetwork, sizeof(snetwork), matches[1], cidr); - if (rc < 0) { - fprintf(stderr, "invalid CIDR: %s\n", cidr); - goto finish; - } - rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr); - if (rc < 0) { - fprintf(stderr, "invalid CIDR: %s\n", cidr); - goto finish; - } - if (inet_pton(AF_INET, snetwork, network) != 1) { - fprintf(stderr, "invalid network address: %s\n", snetwork); - rc = -1; - goto finish; - } - errno = 0; - prefix = strtoul(sprefix, NULL, 10); - if (errno) { - fprintf(stderr, "invalid prefix length: %s\n", sprefix); - rc = -1; - goto finish; - } - if (prefix < NETWORK_PREFIX_MIN || prefix > NETWORK_PREFIX_MAX) { - fprintf(stderr, "prefix length needs to be %d-%d\n", NETWORK_PREFIX_MIN, NETWORK_PREFIX_MAX); - rc = -1; - goto finish; - } - netmask->s_addr = htonl(~((1 << (32 - prefix)) - 1)); - if ((network->s_addr & netmask->s_addr) != network->s_addr) { - fprintf(stderr, "CIDR needs to be a network address like 10.0.2.0/24, not like 10.0.2.100/24\n"); - rc = -1; - goto finish; - } - finish: - regfree(&r); - return rc; + int rc = 0; + regex_t r; + regmatch_t matches[4]; + size_t nmatch = sizeof(matches) / sizeof(matches[0]); + const char *cidr_regex = "^(([0-9]{1,3}\\.){3}[0-9]{1,3})/([0-9]{1,2})$"; + char snetwork[16], sprefix[16]; + int prefix; + rc = regcomp(&r, cidr_regex, REG_EXTENDED); + if (rc != 0) { + fprintf(stderr, "internal regex error\n"); + rc = -1; + goto finish; + } + rc = regexec(&r, cidr, nmatch, matches, 0); + if (rc != 0) { + fprintf(stderr, "invalid CIDR: %s\n", cidr); + rc = -1; + goto finish; + } + rc = from_regmatch(snetwork, sizeof(snetwork), matches[1], cidr); + if (rc < 0) { + fprintf(stderr, "invalid CIDR: %s\n", cidr); + goto finish; + } + rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr); + if (rc < 0) { + fprintf(stderr, "invalid CIDR: %s\n", cidr); + goto finish; + } + if (inet_pton(AF_INET, snetwork, network) != 1) { + fprintf(stderr, "invalid network address: %s\n", snetwork); + rc = -1; + goto finish; + } + errno = 0; + prefix = strtoul(sprefix, NULL, 10); + if (errno) { + fprintf(stderr, "invalid prefix length: %s\n", sprefix); + rc = -1; + goto finish; + } + if (prefix < NETWORK_PREFIX_MIN || prefix > NETWORK_PREFIX_MAX) { + fprintf(stderr, "prefix length needs to be %d-%d\n", NETWORK_PREFIX_MIN, + NETWORK_PREFIX_MAX); + rc = -1; + goto finish; + } + netmask->s_addr = htonl(~((1 << (32 - prefix)) - 1)); + if ((network->s_addr & netmask->s_addr) != network->s_addr) { + fprintf(stderr, "CIDR needs to be a network address like 10.0.2.0/24, " + "not like 10.0.2.100/24\n"); + rc = -1; + goto finish; + } +finish: + regfree(&r); + return rc; } -static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg, const char *cidr) +static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg, + const char *cidr) { - int rc; - rc = parse_cidr(&cfg->vnetwork, &cfg->vnetmask, cidr); - if (rc < 0) { - goto finish; - } - cfg->vhost.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VHOST_OFFSET); - cfg->vdhcp_start.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VDHCPSTART_OFFSET); - cfg->vnameserver.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VNAMESERVER_OFFSET); - cfg->recommended_vguest.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_RECOMMENDED_VGUEST_OFFSET); - finish: - return rc; + int rc; + rc = parse_cidr(&cfg->vnetwork, &cfg->vnetmask, cidr); + if (rc < 0) { + goto finish; + } + cfg->vhost.s_addr = + htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VHOST_OFFSET); + cfg->vdhcp_start.s_addr = + htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VDHCPSTART_OFFSET); + cfg->vnameserver.s_addr = + htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VNAMESERVER_OFFSET); + cfg->recommended_vguest.s_addr = + htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_RECOMMENDED_VGUEST_OFFSET); +finish: + return rc; } -static int slirp4netns_config_from_options(struct slirp4netns_config *cfg, struct options *opt) +static int slirp4netns_config_from_options(struct slirp4netns_config *cfg, + struct options *opt) { - int rc = 0; - cfg->mtu = opt->mtu; - rc = slirp4netns_config_from_cidr(cfg, opt->cidr == NULL ? DEFAULT_CIDR : opt->cidr); - if (rc < 0) { - goto finish; - } - cfg->enable_ipv6 = cfg->enable_ipv6; - cfg->disable_host_loopback = opt->disable_host_loopback; - finish: - return rc; + int rc = 0; + cfg->mtu = opt->mtu; + rc = slirp4netns_config_from_cidr(cfg, opt->cidr == NULL ? DEFAULT_CIDR : + opt->cidr); + if (rc < 0) { + goto finish; + } + cfg->enable_ipv6 = cfg->enable_ipv6; + cfg->disable_host_loopback = opt->disable_host_loopback; +finish: + return rc; } int main(int argc, char *const argv[]) { - int sv[2]; - pid_t child_pid; - struct options options; - struct slirp4netns_config slirp4netns_config; - int exit_status = 0; + int sv[2]; + pid_t child_pid; + struct options options; + struct slirp4netns_config slirp4netns_config; + int exit_status = 0; - parse_args(argc, argv, &options); - if (slirp4netns_config_from_options(&slirp4netns_config, &options) < 0) { - exit_status = EXIT_FAILURE; - goto finish; - } - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) { - perror("socketpair"); - exit_status = EXIT_FAILURE; - goto finish; - } - if ((child_pid = fork()) < 0) { - perror("fork"); - exit_status = EXIT_FAILURE; - goto finish; - } - if (child_pid == 0) { - if (child(sv[1], options.target_pid, options.do_config_network, options.tapname, options.ready_fd, options.netns_path, options.userns_path, - &slirp4netns_config) < 0) { - exit_status = EXIT_FAILURE; - goto finish; - } - } else { - int child_wstatus, child_status; - waitpid(child_pid, &child_wstatus, 0); - if (!WIFEXITED(child_wstatus)) { - fprintf(stderr, "child failed\n"); - exit_status = EXIT_FAILURE; - goto finish; - } - child_status = WEXITSTATUS(child_wstatus); - if (child_status != 0) { - fprintf(stderr, "child failed(%d)\n", child_status); - exit_status = child_status; - goto finish; - } - if (parent(sv[0], options.exit_fd, options.api_socket, &slirp4netns_config) < 0) { - fprintf(stderr, "parent failed\n"); - exit_status = EXIT_FAILURE; - goto finish; - } - } - finish: - options_destroy(&options); - exit(exit_status); - return 0; + parse_args(argc, argv, &options); + if (slirp4netns_config_from_options(&slirp4netns_config, &options) < 0) { + exit_status = EXIT_FAILURE; + goto finish; + } + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) { + perror("socketpair"); + exit_status = EXIT_FAILURE; + goto finish; + } + if ((child_pid = fork()) < 0) { + perror("fork"); + exit_status = EXIT_FAILURE; + goto finish; + } + if (child_pid == 0) { + if (child(sv[1], options.target_pid, options.do_config_network, + options.tapname, options.ready_fd, options.netns_path, + options.userns_path, &slirp4netns_config) < 0) { + exit_status = EXIT_FAILURE; + goto finish; + } + } else { + int child_wstatus, child_status; + waitpid(child_pid, &child_wstatus, 0); + if (!WIFEXITED(child_wstatus)) { + fprintf(stderr, "child failed\n"); + exit_status = EXIT_FAILURE; + goto finish; + } + child_status = WEXITSTATUS(child_wstatus); + if (child_status != 0) { + fprintf(stderr, "child failed(%d)\n", child_status); + exit_status = child_status; + goto finish; + } + if (parent(sv[0], options.exit_fd, options.api_socket, + &slirp4netns_config) < 0) { + fprintf(stderr, "parent failed\n"); + exit_status = EXIT_FAILURE; + goto finish; + } + } +finish: + options_destroy(&options); + exit(exit_status); + return 0; } diff --git a/slirp4netns.c b/slirp4netns.c index 5629c44..f947111 100644 --- a/slirp4netns.c +++ b/slirp4netns.c @@ -13,162 +13,166 @@ /* opaque for SlirpCb */ struct libslirp_data { - int tapfd; - GSList *timers; + int tapfd; + GSList *timers; }; /* implements SlirpCb.send_packet */ -static ssize_t libslirp_send_packet(const void *pkt, size_t pkt_len, void *opaque) +static ssize_t libslirp_send_packet(const void *pkt, size_t pkt_len, + void *opaque) { - struct libslirp_data *data = (struct libslirp_data *)opaque; - return write(data->tapfd, pkt, pkt_len); + struct libslirp_data *data = (struct libslirp_data *)opaque; + return write(data->tapfd, pkt, pkt_len); } /* implements SlirpCb.guest_error */ static void libslirp_guest_error(const char *msg, void *opaque) { - fprintf(stderr, "libslirp: %s\n", msg); + fprintf(stderr, "libslirp: %s\n", msg); } /* implements SlirpCb.clock_get_ns */ static int64_t libslirp_clock_get_ns(void *opaque) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * 1000000000LL + ts.tv_nsec; + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; } /* timer for SlirpCb */ struct timer { - SlirpTimerCb cb; - void *cb_opaque; - int64_t expire_timer_msec; + SlirpTimerCb cb; + void *cb_opaque; + int64_t expire_timer_msec; }; /* implements SlirpCb.timer_new */ static void *libslirp_timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) { - struct libslirp_data *data = (struct libslirp_data *)opaque; - struct timer *t = g_malloc0(sizeof(*t)); - t->cb = cb; - t->cb_opaque = cb_opaque; - t->expire_timer_msec = -1; - data->timers = g_slist_append(data->timers, t); - return t; + struct libslirp_data *data = (struct libslirp_data *)opaque; + struct timer *t = g_malloc0(sizeof(*t)); + t->cb = cb; + t->cb_opaque = cb_opaque; + t->expire_timer_msec = -1; + data->timers = g_slist_append(data->timers, t); + return t; } /* implements SlirpCb.timer_free */ static void libslirp_timer_free(void *timer, void *opaque) { - struct libslirp_data *data = (struct libslirp_data *)opaque; - data->timers = g_slist_remove(data->timers, timer); - g_free(timer); + struct libslirp_data *data = (struct libslirp_data *)opaque; + data->timers = g_slist_remove(data->timers, timer); + g_free(timer); } /* implements SlirpCb.timer_mod */ -static void libslirp_timer_mod(void *timer, int64_t expire_timer_msec, void *opaque) +static void libslirp_timer_mod(void *timer, int64_t expire_timer_msec, + void *opaque) { - struct timer *t = (struct timer *)timer; - t->expire_timer_msec = expire_timer_msec; + struct timer *t = (struct timer *)timer; + t->expire_timer_msec = expire_timer_msec; } /* implements SlirpCb.register_poll_fd */ static void libslirp_register_poll_fd(int fd, void *opaque) { - /* - * NOP - * - * This is NOP on QEMU upstream on Linux as well, see: - * * qemu/net/slirp.c: net_slirp_register_poll_fd (calls qemu_fd_register) - * * qemu/stubs/fd-register.c: qemu_fd_register (NOP on Linux) - * - * See also: - * * qemu/util/main-loop.c: qemu_fd_register (Win32 only) - */ + /* + * NOP + * + * This is NOP on QEMU upstream on Linux as well, see: + * * qemu/net/slirp.c: net_slirp_register_poll_fd (calls + * qemu_fd_register) + * * qemu/stubs/fd-register.c: qemu_fd_register (NOP on Linux) + * + * See also: + * * qemu/util/main-loop.c: qemu_fd_register (Win32 only) + */ } /* implements SlirpCb.unregister_poll_fd */ static void libslirp_unregister_poll_fd(int fd, void *opaque) { - /* - * NOP - * - * This is NOP on QEMU upstream as well, see: - * * qemu/net/slirp.c: net_slirp_unregister_poll_fd (NOP) - */ + /* + * NOP + * + * This is NOP on QEMU upstream as well, see: + * * qemu/net/slirp.c: net_slirp_unregister_poll_fd (NOP) + */ } /* implements SlirpCb.notify */ static void libslirp_notify(void *opaque) { - /* - * NOP - * - * This can be NOP on QEMU upstream as well, see: - * * qemu/net/slirp.c: net_slirp_notify (calls qemu_notify_event) - * * qemu/stubs/notify-event.c: qemu_notify_event (NOP) - * - * See also: - * * qemu/util/main-loop.c: qemu_notify_event (NOP if !qemu_aio_context) - */ + /* + * NOP + * + * This can be NOP on QEMU upstream as well, see: + * * qemu/net/slirp.c: net_slirp_notify (calls qemu_notify_event) + * * qemu/stubs/notify-event.c: qemu_notify_event (NOP) + * + * See also: + * * qemu/util/main-loop.c: qemu_notify_event (NOP if + * !qemu_aio_context) + */ } static int libslirp_poll_to_gio(int events) { - int ret = 0; - if (events & SLIRP_POLL_IN) { - ret |= G_IO_IN; - } - if (events & SLIRP_POLL_OUT) { - ret |= G_IO_OUT; - } - if (events & SLIRP_POLL_PRI) { - ret |= G_IO_PRI; - } - if (events & SLIRP_POLL_ERR) { - ret |= G_IO_ERR; - } - if (events & SLIRP_POLL_HUP) { - ret |= G_IO_HUP; - } - return ret; + int ret = 0; + if (events & SLIRP_POLL_IN) { + ret |= G_IO_IN; + } + if (events & SLIRP_POLL_OUT) { + ret |= G_IO_OUT; + } + if (events & SLIRP_POLL_PRI) { + ret |= G_IO_PRI; + } + if (events & SLIRP_POLL_ERR) { + ret |= G_IO_ERR; + } + if (events & SLIRP_POLL_HUP) { + ret |= G_IO_HUP; + } + return ret; } /* * implements SlirpAddPollCb used in slirp_pollfds_fill. - * originally from qemu/net/slirp.c:net_slirp_add_poll + * originally from qemu/net/slirp.c:net_slirp_add_poll */ static int libslirp_add_poll(int fd, int events, void *opaque) { - GArray *pollfds = opaque; - GPollFD pfd = { - .fd = fd, - .events = libslirp_poll_to_gio(events), - }; - int idx = pollfds->len; - g_array_append_val(pollfds, pfd); - return idx; + GArray *pollfds = opaque; + GPollFD pfd = { + .fd = fd, + .events = libslirp_poll_to_gio(events), + }; + int idx = pollfds->len; + g_array_append_val(pollfds, pfd); + return idx; } static int libslirp_gio_to_poll(int events) { - int ret = 0; - if (events & G_IO_IN) { - ret |= SLIRP_POLL_IN; - } - if (events & G_IO_OUT) { - ret |= SLIRP_POLL_OUT; - } - if (events & G_IO_PRI) { - ret |= SLIRP_POLL_PRI; - } - if (events & G_IO_ERR) { - ret |= SLIRP_POLL_ERR; - } - if (events & G_IO_HUP) { - ret |= SLIRP_POLL_HUP; - } - return ret; + int ret = 0; + if (events & G_IO_IN) { + ret |= SLIRP_POLL_IN; + } + if (events & G_IO_OUT) { + ret |= SLIRP_POLL_OUT; + } + if (events & G_IO_PRI) { + ret |= SLIRP_POLL_PRI; + } + if (events & G_IO_ERR) { + ret |= SLIRP_POLL_ERR; + } + if (events & G_IO_HUP) { + ret |= SLIRP_POLL_HUP; + } + return ret; } /* @@ -177,199 +181,206 @@ static int libslirp_gio_to_poll(int events) */ static int libslirp_get_revents(int idx, void *opaque) { - GArray *pollfds = opaque; - return libslirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents); + GArray *pollfds = opaque; + return libslirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents); } /* * updates timeout_msec for data->timers - * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L66 + * originally from + * https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L66 */ -static void update_ra_timeout(uint32_t * timeout_msec, struct libslirp_data *data) +static void update_ra_timeout(uint32_t *timeout_msec, + struct libslirp_data *data) { - int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; - GSList *f; - for (f = data->timers; f != NULL; f = f->next) { - struct timer *t = f->data; - if (t->expire_timer_msec != -1) { - int64_t diff = t->expire_timer_msec - now_msec; - if (diff < 0) - diff = 0; - if (diff < *timeout_msec) - *timeout_msec = diff; - } - } + int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; + GSList *f; + for (f = data->timers; f != NULL; f = f->next) { + struct timer *t = f->data; + if (t->expire_timer_msec != -1) { + int64_t diff = t->expire_timer_msec - now_msec; + if (diff < 0) + diff = 0; + if (diff < *timeout_msec) + *timeout_msec = diff; + } + } } /* * calls SlirpTimerCb if timed out - * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L78 + * originally from + * https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L78 */ static void check_ra_timeout(struct libslirp_data *data) { - int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; - GSList *f; - for (f = data->timers; f != NULL; f = f->next) { - struct timer *t = f->data; - if (t->expire_timer_msec != -1) { - int64_t diff = t->expire_timer_msec - now_msec; - if (diff <= 0) { - t->expire_timer_msec = -1; - t->cb(t->cb_opaque); - } - } - } + int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; + GSList *f; + for (f = data->timers; f != NULL; f = f->next) { + struct timer *t = f->data; + if (t->expire_timer_msec != -1) { + int64_t diff = t->expire_timer_msec - now_msec; + if (diff <= 0) { + t->expire_timer_msec = -1; + t->cb(t->cb_opaque); + } + } + } } static const SlirpCb libslirp_cb = { - .send_packet = libslirp_send_packet, - .guest_error = libslirp_guest_error, - .clock_get_ns = libslirp_clock_get_ns, - .timer_new = libslirp_timer_new, - .timer_free = libslirp_timer_free, - .timer_mod = libslirp_timer_mod, - .register_poll_fd = libslirp_register_poll_fd, - .unregister_poll_fd = libslirp_unregister_poll_fd, - .notify = libslirp_notify, + .send_packet = libslirp_send_packet, + .guest_error = libslirp_guest_error, + .clock_get_ns = libslirp_clock_get_ns, + .timer_new = libslirp_timer_new, + .timer_free = libslirp_timer_free, + .timer_mod = libslirp_timer_mod, + .register_poll_fd = libslirp_register_poll_fd, + .unregister_poll_fd = libslirp_unregister_poll_fd, + .notify = libslirp_notify, }; Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn) { - Slirp *slirp = NULL; - SlirpConfig cfg; - memset(&cfg, 0, sizeof(cfg)); - cfg.restricted = 0; - cfg.in_enabled = 1; - cfg.vnetwork = s4nn->vnetwork; - cfg.vnetmask = s4nn->vnetmask; - cfg.vhost = s4nn->vhost; - cfg.in6_enabled = (int)(s4nn->enable_ipv6); - inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6); - cfg.vprefix_len = 64; - inet_pton(AF_INET6, "fd00::2", &cfg.vhost6); - cfg.vhostname = NULL; - cfg.tftp_server_name = NULL; - cfg.tftp_path = NULL; - cfg.bootfile = NULL; - cfg.vdhcp_start = s4nn->vdhcp_start; - cfg.vnameserver = s4nn->vnameserver; - inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6); - cfg.vdnssearch = NULL; - cfg.vdomainname = NULL; - cfg.if_mtu = s4nn->mtu; - cfg.if_mru = s4nn->mtu; - cfg.disable_host_loopback = s4nn->disable_host_loopback; - slirp = slirp_initx(&cfg, &libslirp_cb, opaque); - if (slirp == NULL) { - fprintf(stderr, "slirp_initx failed\n"); - } - return slirp; + Slirp *slirp = NULL; + SlirpConfig cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.restricted = 0; + cfg.in_enabled = 1; + cfg.vnetwork = s4nn->vnetwork; + cfg.vnetmask = s4nn->vnetmask; + cfg.vhost = s4nn->vhost; + cfg.in6_enabled = (int)(s4nn->enable_ipv6); + inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6); + cfg.vprefix_len = 64; + inet_pton(AF_INET6, "fd00::2", &cfg.vhost6); + cfg.vhostname = NULL; + cfg.tftp_server_name = NULL; + cfg.tftp_path = NULL; + cfg.bootfile = NULL; + cfg.vdhcp_start = s4nn->vdhcp_start; + cfg.vnameserver = s4nn->vnameserver; + inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6); + cfg.vdnssearch = NULL; + cfg.vdomainname = NULL; + cfg.if_mtu = s4nn->mtu; + cfg.if_mru = s4nn->mtu; + cfg.disable_host_loopback = s4nn->disable_host_loopback; + slirp = slirp_initx(&cfg, &libslirp_cb, opaque); + if (slirp == NULL) { + fprintf(stderr, "slirp_initx failed\n"); + } + return slirp; } #define ETH_BUF_SIZE (65536) -int do_slirp(int tapfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg) +int do_slirp(int tapfd, int exitfd, const char *api_socket, + struct slirp4netns_config *cfg) { - int ret = -1; - Slirp *slirp = NULL; - uint8_t *buf = NULL; - struct libslirp_data opaque = {.tapfd = tapfd,.timers = NULL }; - int apifd = -1; - struct api_ctx *apictx = NULL; - GArray *pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); - int pollfds_exitfd_idx = -1; - int pollfds_apifd_idx = -1; - size_t n_fds = 1; - GPollFD tap_pollfd = {.fd = tapfd,.events = G_IO_IN | G_IO_HUP,.revents = 0 - }; - GPollFD exit_pollfd = {.fd = exitfd,.events = G_IO_HUP,.revents = 0 - }; - GPollFD api_pollfd = {.fd = -1,.events = G_IO_IN | G_IO_HUP,.revents = 0 - }; + int ret = -1; + Slirp *slirp = NULL; + uint8_t *buf = NULL; + struct libslirp_data opaque = { .tapfd = tapfd, .timers = NULL }; + int apifd = -1; + struct api_ctx *apictx = NULL; + GArray *pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); + int pollfds_exitfd_idx = -1; + int pollfds_apifd_idx = -1; + size_t n_fds = 1; + GPollFD tap_pollfd = { .fd = tapfd, + .events = G_IO_IN | G_IO_HUP, + .revents = 0 }; + GPollFD exit_pollfd = { .fd = exitfd, .events = G_IO_HUP, .revents = 0 }; + GPollFD api_pollfd = { .fd = -1, + .events = G_IO_IN | G_IO_HUP, + .revents = 0 }; - slirp = create_slirp((void *)&opaque, cfg); - if (slirp == NULL) { - fprintf(stderr, "create_slirp failed\n"); - goto err; - } - buf = malloc(ETH_BUF_SIZE); - if (buf == NULL) { - goto err; - } - g_array_append_val(pollfds, tap_pollfd); - if (exitfd >= 0) { - n_fds++; - g_array_append_val(pollfds, exit_pollfd); - pollfds_exitfd_idx = n_fds - 1; - } - if (api_socket != NULL) { - if ((apifd = api_bindlisten(api_socket)) < 0) { - goto err; - } - if ((apictx = api_ctx_alloc(cfg)) == NULL) { - fprintf(stderr, "api_ctx_alloc failed\n"); - goto err; - } - api_pollfd.fd = apifd; - n_fds++; - g_array_append_val(pollfds, api_pollfd); - pollfds_apifd_idx = n_fds - 1; - } - signal(SIGPIPE, SIG_IGN); - while (1) { - int pollout; - GPollFD *pollfds_data; - uint32_t timeout = -1; /* msec */ - g_array_set_size(pollfds, n_fds); - slirp_pollfds_fill(slirp, &timeout, libslirp_add_poll, pollfds); - update_ra_timeout(&timeout, &opaque); - pollfds_data = (GPollFD *)pollfds->data; - do { - pollout = g_poll(pollfds_data, pollfds->len, timeout); - } while (pollout < 0 && errno == EINTR); - if (pollout < 0) { - goto err; - } + slirp = create_slirp((void *)&opaque, cfg); + if (slirp == NULL) { + fprintf(stderr, "create_slirp failed\n"); + goto err; + } + buf = malloc(ETH_BUF_SIZE); + if (buf == NULL) { + goto err; + } + g_array_append_val(pollfds, tap_pollfd); + if (exitfd >= 0) { + n_fds++; + g_array_append_val(pollfds, exit_pollfd); + pollfds_exitfd_idx = n_fds - 1; + } + if (api_socket != NULL) { + if ((apifd = api_bindlisten(api_socket)) < 0) { + goto err; + } + if ((apictx = api_ctx_alloc(cfg)) == NULL) { + fprintf(stderr, "api_ctx_alloc failed\n"); + goto err; + } + api_pollfd.fd = apifd; + n_fds++; + g_array_append_val(pollfds, api_pollfd); + pollfds_apifd_idx = n_fds - 1; + } + signal(SIGPIPE, SIG_IGN); + while (1) { + int pollout; + GPollFD *pollfds_data; + uint32_t timeout = -1; /* msec */ + g_array_set_size(pollfds, n_fds); + slirp_pollfds_fill(slirp, &timeout, libslirp_add_poll, pollfds); + update_ra_timeout(&timeout, &opaque); + pollfds_data = (GPollFD *)pollfds->data; + do { + pollout = g_poll(pollfds_data, pollfds->len, timeout); + } while (pollout < 0 && errno == EINTR); + if (pollout < 0) { + goto err; + } - if (pollfds_data[0].revents) { - ssize_t rc = read(tapfd, buf, ETH_BUF_SIZE); - if (rc < 0) { - perror("do_slirp: read"); - goto after_slirp_input; - } - slirp_input(slirp, buf, (int)rc); - after_slirp_input: - pollout = -1; - } + if (pollfds_data[0].revents) { + ssize_t rc = read(tapfd, buf, ETH_BUF_SIZE); + if (rc < 0) { + perror("do_slirp: read"); + goto after_slirp_input; + } + slirp_input(slirp, buf, (int)rc); + after_slirp_input: + pollout = -1; + } - /* The exitfd is closed. */ - if (pollfds_exitfd_idx >= 0 && pollfds_data[pollfds_exitfd_idx].revents) { - fprintf(stderr, "exitfd event\n"); - goto success; - } + /* The exitfd is closed. */ + if (pollfds_exitfd_idx >= 0 && + pollfds_data[pollfds_exitfd_idx].revents) { + fprintf(stderr, "exitfd event\n"); + goto success; + } - if (pollfds_apifd_idx >= 0 && pollfds_data[pollfds_apifd_idx].revents) { - int rc; - fprintf(stderr, "apifd event\n"); - if ((rc = api_handler(slirp, apifd, apictx)) < 0) { - fprintf(stderr, "api_handler: rc=%d\n", rc); - } - } + if (pollfds_apifd_idx >= 0 && pollfds_data[pollfds_apifd_idx].revents) { + int rc; + fprintf(stderr, "apifd event\n"); + if ((rc = api_handler(slirp, apifd, apictx)) < 0) { + fprintf(stderr, "api_handler: rc=%d\n", rc); + } + } - slirp_pollfds_poll(slirp, (pollout <= 0), libslirp_get_revents, pollfds); - check_ra_timeout(&opaque); - } - success: - ret = 0; - err: - fprintf(stderr, "do_slirp is exiting\n"); - if (buf != NULL) { - free(buf); - } - if (apictx != NULL) { - api_ctx_free(apictx); - unlink(api_socket); - } - g_array_free(pollfds, TRUE); - return ret; + slirp_pollfds_poll(slirp, (pollout <= 0), libslirp_get_revents, + pollfds); + check_ra_timeout(&opaque); + } +success: + ret = 0; +err: + fprintf(stderr, "do_slirp is exiting\n"); + if (buf != NULL) { + free(buf); + } + if (apictx != NULL) { + api_ctx_free(apictx); + unlink(api_socket); + } + g_array_free(pollfds, TRUE); + return ret; } diff --git a/vendor.sh b/vendor.sh index 79842e2..3a8394c 100755 --- a/vendor.sh +++ b/vendor.sh @@ -24,7 +24,7 @@ git clone $LIBSLIRP_REPO $tmp_git/libslirp # run make to generate src/libslirp-version.h make mkdir -p $tmp_vendor/libslirp/src - cp -a COPYRIGHT README.md $tmp_vendor/libslirp + cp -a .clang-format COPYRIGHT README.md $tmp_vendor/libslirp cp -a src/{*.c,*.h} $tmp_vendor/libslirp/src ) diff --git a/vendor/libslirp/.clang-format b/vendor/libslirp/.clang-format new file mode 100644 index 0000000..17fb49f --- /dev/null +++ b/vendor/libslirp/.clang-format @@ -0,0 +1,58 @@ +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +--- +Language: Cpp +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false # although we like it, it creates churn +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: false # churn +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account +AlwaysBreakBeforeMultilineStrings: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterStruct: false + AfterUnion: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: false +BreakStringLiterals: true +ColumnLimit: 80 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ? +MacroBlockEnd: '.*_END$' +MaxEmptyLinesToKeep: 2 +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +UseTab: Never +...