From 17ced40bf418fba4f7ccb5e23fe1f0d47eab9bc4 Mon Sep 17 00:00:00 2001 From: David Scott Date: Wed, 11 May 2016 21:29:57 +0100 Subject: [PATCH 01/16] tap-vsockd: add skeleton with hyper-v sockets Signed-off-by: David Scott --- alpine/packages/Makefile | 3 + alpine/packages/tap-vsockd/.gitignore | 1 + alpine/packages/tap-vsockd/Dockerfile | 10 ++ alpine/packages/tap-vsockd/Makefile | 15 ++ alpine/packages/tap-vsockd/compat.h | 118 ++++++++++++++ alpine/packages/tap-vsockd/tap-vsockd.c | 207 ++++++++++++++++++++++++ 6 files changed, 354 insertions(+) create mode 100644 alpine/packages/tap-vsockd/.gitignore create mode 100644 alpine/packages/tap-vsockd/Dockerfile create mode 100644 alpine/packages/tap-vsockd/Makefile create mode 100644 alpine/packages/tap-vsockd/compat.h create mode 100644 alpine/packages/tap-vsockd/tap-vsockd.c diff --git a/alpine/packages/Makefile b/alpine/packages/Makefile index 1b9aaffe3..2bce01e8d 100644 --- a/alpine/packages/Makefile +++ b/alpine/packages/Makefile @@ -2,6 +2,7 @@ all: $(MAKE) -C proxy OS=linux $(MAKE) -C diagnostics OS=linux $(MAKE) -C transfused OS=linux + $(MAKE) -C tap-vsockd OS=linux $(MAKE) -C hupper OS=linux $(MAKE) -C hvtools OS=linux $(MAKE) -C docker OS=Linux @@ -15,6 +16,7 @@ arm: $(MAKE) -C hupper OS=linux ARCH=arm $(MAKE) -C docker arm OS=Linux ARCH=arm # Not cross building at present (C code) + # $(MAKE) -C tap-vsockd OS=linux ARCH=arm # $(MAKE) -C diagnostics OS=linux ARCH=arm # $(MAKE) -C proxy OS=linux ARCH=arm # $(MAKE) -C nc-vsock OS=linux ARCH=arm @@ -24,6 +26,7 @@ clean: $(MAKE) -C proxy clean $(MAKE) -C diagnostics clean $(MAKE) -C transfused clean + $(MAKE) -C tap-vsockd clean $(MAKE) -C docker clean $(MAKE) -C hupper clean $(MAKE) -C hvtools clean diff --git a/alpine/packages/tap-vsockd/.gitignore b/alpine/packages/tap-vsockd/.gitignore new file mode 100644 index 000000000..5cf0d45b4 --- /dev/null +++ b/alpine/packages/tap-vsockd/.gitignore @@ -0,0 +1 @@ +tap-vsockd diff --git a/alpine/packages/tap-vsockd/Dockerfile b/alpine/packages/tap-vsockd/Dockerfile new file mode 100644 index 000000000..7f08ede6e --- /dev/null +++ b/alpine/packages/tap-vsockd/Dockerfile @@ -0,0 +1,10 @@ +FROM alpine:3.3 + +RUN apk update && apk upgrade && apk add alpine-sdk util-linux-dev + +RUN mkdir -p /tap-vsockd +WORKDIR /tap-vsockd + +COPY . /tap-vsockd + +RUN make tap-vsockd diff --git a/alpine/packages/tap-vsockd/Makefile b/alpine/packages/tap-vsockd/Makefile new file mode 100644 index 000000000..19f1a7917 --- /dev/null +++ b/alpine/packages/tap-vsockd/Makefile @@ -0,0 +1,15 @@ +.PHONY: all + +DEPS=tap-vsockd.c compat.h + +all: Dockerfile $(DEPS) + docker build -t tap-vsockd:build . + docker run --rm tap-vsockd:build cat tap-vsockd > tap-vsockd + chmod 755 tap-vsockd + +tap-vsockd: $(DEPS) + gcc -Wall -Werror -o tap-vsockd tap-vsockd.c -lpthread + +clean: + rm -f tap-vsockd + docker images -q tap-vsockd:build | xargs docker rmi -f diff --git a/alpine/packages/tap-vsockd/compat.h b/alpine/packages/tap-vsockd/compat.h new file mode 100644 index 000000000..02f9f7b84 --- /dev/null +++ b/alpine/packages/tap-vsockd/compat.h @@ -0,0 +1,118 @@ +/* + * Compatibility layer between Windows and Linux + */ +#ifdef _MSC_VER +#undef UNICODE +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#pragma comment (lib, "Ws2_32.lib") +#pragma comment (lib, "Mswsock.lib") +#pragma comment (lib, "AdvApi32.lib") + +#else +#include +#include +#include +#include +#endif /* !_MSC_VER */ + +#ifdef _MSC_VER +typedef int socklen_t; +#endif + +#ifndef _MSC_VER +/* Compat layer for Linux/Unix */ +typedef int SOCKET; + +#ifndef SOCKET_ERROR +#define SOCKET_ERROR -1 +#endif + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif + +#define closesocket(_fd) close(_fd) + +/* Shutdown flags are different too */ +#define SD_SEND SHUT_WR +#define SD_RECEIVE SHUT_RD +#define SD_BOTH SHUT_RDWR + +#define __cdecl + +/* GUID handling */ +typedef struct _GUID { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} GUID; + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} + + +/* HV Socket definitions */ +#define AF_HYPERV 43 +#define HV_PROTOCOL_RAW 1 + +typedef struct _SOCKADDR_HV +{ + unsigned short Family; + unsigned short Reserved; + GUID VmId; + GUID ServiceId; +} SOCKADDR_HV; + +DEFINE_GUID(HV_GUID_ZERO, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); +DEFINE_GUID(HV_GUID_BROADCAST, + 0xFFFFFFFF, 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); +DEFINE_GUID(HV_GUID_WILDCARD, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +DEFINE_GUID(HV_GUID_CHILDREN, + 0x90db8b89, 0x0d35, 0x4f79, 0x8c, 0xe9, 0x49, 0xea, 0x0a, 0xc8, 0xb7, 0xcd); +DEFINE_GUID(HV_GUID_LOOPBACK, + 0xe0e16197, 0xdd56, 0x4a10, 0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38); +DEFINE_GUID(HV_GUID_PARENT, + 0xa42e7cda, 0xd03f, 0x480c, 0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78); + +#endif + +/* Thread wrappers */ +#ifdef _MSC_VER +typedef HANDLE THREAD_HANDLE; + +static inline int thread_create(THREAD_HANDLE *t, void *(*f)(void *), void *arg) +{ + *t = CreateThread(NULL, 0, f, arg, 0, NULL); + return 0; +} + +static inline int thread_join(THREAD_HANDLE t) +{ + WaitForSingleObject(t, INFINITE); + return 0; +} + +#else +#include + +typedef pthread_t THREAD_HANDLE; + +static inline int thread_create(THREAD_HANDLE *t, void *(*f)(void *), void *arg) +{ + return pthread_create(t, NULL, f, arg); +} + +static inline int thread_join(THREAD_HANDLE t) +{ + return pthread_join(t, NULL); +} +#endif diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c new file mode 100644 index 000000000..c982f5d2a --- /dev/null +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -0,0 +1,207 @@ +/* + */ + +#include +#include +#include +#include + +#include "compat.h" + +int verbose_flag = 0; + +#define SVR_BUF_LEN (3 * 4096) +#define MAX_BUF_LEN (2 * 1024 * 1024) + + +/* Helper macros for parsing/printing GUIDs */ +#define GUID_FMT "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x" +#define GUID_ARGS(_g) \ + (_g).Data1, (_g).Data2, (_g).Data3, \ + (_g).Data4[0], (_g).Data4[1], (_g).Data4[2], (_g).Data4[3], \ + (_g).Data4[4], (_g).Data4[5], (_g).Data4[6], (_g).Data4[7] +#define GUID_SARGS(_g) \ + &(_g).Data1, &(_g).Data2, &(_g).Data3, \ + &(_g).Data4[0], &(_g).Data4[1], &(_g).Data4[2], &(_g).Data4[3], \ + &(_g).Data4[4], &(_g).Data4[5], &(_g).Data4[6], &(_g).Data4[7] + + +int parseguid(const char *s, GUID *g) +{ + int res; + int p0, p1, p2, p3, p4, p5, p6, p7; + + res = sscanf(s, GUID_FMT, + &g->Data1, &g->Data2, &g->Data3, + &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7); + if (res != 11) + return 1; + g->Data4[0] = p0; + g->Data4[1] = p1; + g->Data4[2] = p2; + g->Data4[3] = p3; + g->Data4[4] = p4; + g->Data4[5] = p5; + g->Data4[6] = p6; + g->Data4[7] = p7; + return 0; +} + +/* Slightly different error handling between Windows and Linux */ +void sockerr(const char *msg) +{ + fprintf(stderr, "%s Error: %d. %s", msg, errno, strerror(errno)); +} + +/* Argument passed to Client send thread */ +struct client_args { + SOCKET fd; + int tosend; +}; + +/* Handle a connection. Echo back anything sent to us and when the + * connection is closed send a bye message. + */ +static void handle(SOCKET fd) +{ + char recvbuf[SVR_BUF_LEN]; + int recvbuflen = SVR_BUF_LEN; + int received; + int sent; + int res; + + for (;;) { + received = recv(fd, recvbuf, recvbuflen, 0); + if (received == 0) { + printf("Peer closed\n"); + break; + } else if (received == SOCKET_ERROR) { + sockerr("recv()"); + return; + } + + /* No error, echo */ + printf("RX: %d Bytes\n", received); + + sent = 0; + while (sent < received) { + res = send(fd, recvbuf + sent, received - sent, 0); + if (sent == SOCKET_ERROR) { + sockerr("send()"); + return; + } + printf("TX: %d Bytes\n", res); + sent += res; + } + } + + /* Dummy read to wait till other end closes */ + recv(fd, recvbuf, recvbuflen, 0); +} + + +/* Server: + * accept() in an endless loop, handle a connection at a time + */ +static int server(GUID serviceid) +{ + SOCKET lsock = INVALID_SOCKET; + SOCKET csock = INVALID_SOCKET; + SOCKADDR_HV sa, sac; + socklen_t socklen = sizeof(sac); + int res; + + lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); + if (lsock == INVALID_SOCKET) { + sockerr("socket()"); + return 1; + } + + sa.Family = AF_HYPERV; + sa.Reserved = 0; + sa.VmId = HV_GUID_WILDCARD; + sa.ServiceId = serviceid; + + res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa)); + if (res == SOCKET_ERROR) { + sockerr("bind()"); + closesocket(lsock); + return 1; + } + + res = listen(lsock, SOMAXCONN); + if (res == SOCKET_ERROR) { + sockerr("listen()"); + closesocket(lsock); + return 1; + } + + while(1) { + csock = accept(lsock, (struct sockaddr *)&sac, &socklen); + if (csock == INVALID_SOCKET) { + sockerr("accept()"); + closesocket(lsock); + return 1; + } + + printf("Connect from: "GUID_FMT":"GUID_FMT"\n", + GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); + + handle(csock); + closesocket(csock); + } +} + +void usage(char *name) +{ + printf("%s: --verbose | --service id | --tap \n", name); + printf(": Hyper-V socket serviceId to bind\n"); + printf(": tap device to connect to\n"); +} + +int __cdecl main(int argc, char **argv) +{ + int res = 0; + GUID target = HV_GUID_PARENT; + int c; + char *serviceid = NULL; + char *tap = "tap0"; + + opterr = 0; + while (1) { + static struct option long_options[] = { + /* These options set a flag. */ + {"verbose", no_argument, &verbose_flag, 1}, + {"serviceid", required_argument, NULL, 's'}, + {"tap", required_argument, NULL, 't'}, + {0, 0, 0, 0} + }; + int option_index = 0; + + c = getopt_long (argc, argv, "vs:t:", long_options, &option_index); + if (c == -1) break; + + switch (c) { + case 'v': + verbose_flag = 1; + break; + case 's': + serviceid = optarg; + break; + case 't': + tap = optarg; + break; + case 0: + break; + default: + usage (argv[0]); + exit (1); + } + } + fprintf(stderr, "serviceid=%s\n", serviceid); + /* 3049197C-9A4E-4FBF-9367-97F792F16994 */ + fprintf(stderr, "tap=%s\n", tap); + server(target); + + return res; +} From cd441c5f29433a3bffeedd9d10e46eb3eeef7d63 Mon Sep 17 00:00:00 2001 From: David Scott Date: Thu, 12 May 2016 15:38:33 +0100 Subject: [PATCH 02/16] tap-vsockd: add a function to open a TAP device Signed-off-by: David Scott --- alpine/packages/tap-vsockd/Dockerfile | 2 +- alpine/packages/tap-vsockd/tap-vsockd.c | 42 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/alpine/packages/tap-vsockd/Dockerfile b/alpine/packages/tap-vsockd/Dockerfile index 7f08ede6e..74f5ce00d 100644 --- a/alpine/packages/tap-vsockd/Dockerfile +++ b/alpine/packages/tap-vsockd/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.3 -RUN apk update && apk upgrade && apk add alpine-sdk util-linux-dev +RUN apk update && apk upgrade && apk add alpine-sdk util-linux-dev linux-headers RUN mkdir -p /tap-vsockd WORKDIR /tap-vsockd diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index c982f5d2a..449591ef3 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -6,10 +6,52 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + #include "compat.h" int verbose_flag = 0; +int alloc_tap(char *dev) { + int fd; + struct ifreq ifr; + const char *clonedev = "/dev/net/tun"; + if ((fd = open(clonedev, O_RDWR)) == -1) { + perror("Failed to open /dev/net/tun"); + exit(1); + } + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(fd, TUNSETIFF, (void*) &ifr) < 0) { + perror("TUNSETIFF failed"); + exit(1); + } + int persist = 1; + if (ioctl(fd, TUNSETPERSIST, persist) < 0) { + perror("TUNSETPERSIST failed"); + exit(1); + } + fprintf(stderr, "successfully created TAP device %s\n", dev); + return fd; +} + + #define SVR_BUF_LEN (3 * 4096) #define MAX_BUF_LEN (2 * 1024 * 1024) From 66534816a75cd031ee1ca31c3d53c87b43be8c71 Mon Sep 17 00:00:00 2001 From: David Scott Date: Thu, 12 May 2016 15:42:51 +0100 Subject: [PATCH 03/16] tap-vsockd: add the vmnet protocol definitions Signed-off-by: David Scott --- alpine/packages/tap-vsockd/Makefile | 12 +- alpine/packages/tap-vsockd/protocol.c | 262 ++++++++++++++++++++++++++ alpine/packages/tap-vsockd/protocol.h | 76 ++++++++ 3 files changed, 347 insertions(+), 3 deletions(-) create mode 100644 alpine/packages/tap-vsockd/protocol.c create mode 100644 alpine/packages/tap-vsockd/protocol.h diff --git a/alpine/packages/tap-vsockd/Makefile b/alpine/packages/tap-vsockd/Makefile index 19f1a7917..f2fe55ec2 100644 --- a/alpine/packages/tap-vsockd/Makefile +++ b/alpine/packages/tap-vsockd/Makefile @@ -1,14 +1,20 @@ .PHONY: all -DEPS=tap-vsockd.c compat.h +DEPS=tap-vsockd.c compat.h protocol.c protocol.h all: Dockerfile $(DEPS) docker build -t tap-vsockd:build . docker run --rm tap-vsockd:build cat tap-vsockd > tap-vsockd chmod 755 tap-vsockd -tap-vsockd: $(DEPS) - gcc -Wall -Werror -o tap-vsockd tap-vsockd.c -lpthread +tap-vsockd: protocol.o tap-vsockd.o + gcc -Wall -Werror -o tap-vsockd tap-vsockd.o protocol.o -lpthread + +protocol.o: protocol.c + gcc -Wall -Werror -c protocol.c + +tap-vsockd.o: tap-vsockd.c compat.h + gcc -Wall -Werror -c tap-vsockd.c clean: rm -f tap-vsockd diff --git a/alpine/packages/tap-vsockd/protocol.c b/alpine/packages/tap-vsockd/protocol.c new file mode 100644 index 000000000..0a423995e --- /dev/null +++ b/alpine/packages/tap-vsockd/protocol.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "protocol.h" +#include "commit.h" + +#include "utils.h" + +/* Version 0 of the protocol used this */ +char expected_hello_old[5] = { 'V', 'M', 'N', 'E', 'T' }; + +/* Version 1 and later of the protocol used this */ +char expected_hello[5] = { 'V', 'M', 'N', '3', 'T' }; + +int really_read(int fd, uint8_t *buffer, size_t total){ + size_t remaining = total; + ssize_t n; + while (remaining > 0){ + n = read(fd, buffer, remaining); + if (n == 0){ + aslLog(ASL_LEVEL_NOTICE, "EOF reading packet from Unix domain socket: closing"); + goto err; + } + if (n < 0){ + aslLog(ASL_LEVEL_NOTICE, "failure reading packet from Unix domain socket, closing: %s", strerror (errno)); + goto err; + } + remaining -= (size_t)n; + buffer = buffer + n; + } + return 0; +err: + /* On error: stop reading from the socket and trigger a clean shutdown */ + shutdown(fd, SHUT_RD); + return -1; +} + +int really_write(int fd, uint8_t *buffer, size_t total){ + size_t remaining = total; + ssize_t n; + while (remaining > 0){ + n = write(fd, buffer, remaining); + if (n == 0){ + aslLog(ASL_LEVEL_INFO, "EOF writing to Unix domain socket"); + goto err; + } + if (n < 0){ + aslLog(ASL_LEVEL_NOTICE, "failure writing packet from Unix domain socket, closing: %s", strerror (errno)); + goto err; + } + remaining -= (size_t) n; + buffer = buffer + n; + } + return 0; +err: + /* On error: stop listening to the socket */ + shutdown(fd, SHUT_WR); + return -1; +} + +int really_writev(int fd, struct iovec *iov, int iovcnt){ + ssize_t n = 0; + int pos = 0; + + while (pos < iovcnt) { + iov[pos].iov_base = (char *)iov[pos].iov_base + n; + iov[pos].iov_len -= (size_t)n; + + n = writev(fd, iov + pos, iovcnt - pos); + + if (n == 0){ + aslLog(ASL_LEVEL_INFO, "EOF writing to Unix domain socket"); + goto err; + } + if (n < 0){ + aslLog(ASL_LEVEL_NOTICE, "failure writing packet from Unix domain socket, closing: %s", strerror (errno)); + goto err; + } + + // advance pos, reduce n + for (; pos < iovcnt && (size_t)n >= iov[pos].iov_len; pos++) { + n -= iov[pos].iov_len; + } + } + return 0; +err: + shutdown(fd, SHUT_WR); + return -1; +} + +struct init_message *create_init_message(){ + struct init_message *m = (struct init_message*) malloc(sizeof(struct init_message)); + bzero(m, sizeof(struct init_message)); + memcpy(&m->hello[0], &expected_hello[0], sizeof(m->hello)); + m->version = CURRENT_VERSION; + memcpy(&m->commit[0], &commit[0], sizeof(m->commit)); + return m; +} + +char *print_init_message(struct init_message *m) { + char tmp[41]; + memcpy(&tmp[0], &m->commit[0], 40); + tmp[40] = '\000'; + char *buffer = (char*) malloc(80); + int n = snprintf(buffer, 80, "version %d, commit %s", m->version, tmp); + if (n < 0) { + aslLog(ASL_LEVEL_ERR, "Failed to format init_message: %s", strerror (errno)); + exit(1); + } + return buffer; +} + +int read_init_message(int fd, struct init_message *ci) { + bzero(ci, sizeof(struct init_message)); + if (really_read(fd, (uint8_t*) &ci->hello[0], sizeof(ci->hello)) == -1){ + aslLog(ASL_LEVEL_CRIT, "Failed to read hello from client"); + return -1; + } + if (memcmp(&ci->hello[0], &expected_hello_old[0], sizeof(expected_hello_old)) == 0) { + ci->version = 0; + return 0; + } + if (memcmp(&ci->hello[0], &expected_hello[0], sizeof(expected_hello)) != 0) { + aslLog(ASL_LEVEL_ERR, "Failed to read header magic from client"); + return -1; + } + if (really_read(fd, (uint8_t*) &ci->version, sizeof(ci->version)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to read header version from client"); + return -1; + } + if (really_read(fd, (uint8_t*) &ci->commit[0], sizeof(ci->commit)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to read header hash from client"); + return -1; + } + return 0; +} + +int write_init_message(int fd, struct init_message *ci) { + if (really_write(fd, (uint8_t*) &ci->hello[0], sizeof(ci->hello)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write hello to client"); + return -1; + } + if (ci->version > 0) { + if (really_write(fd, (uint8_t*) &ci->version, sizeof(ci->version)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write version to client"); + return -1; + } + if (really_write(fd, (uint8_t*) &ci->commit[0], sizeof(ci->commit)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write hash to client"); + return -1; + } + } + return 0; +} + +int read_vif_info(int fd, struct vif_info *vif) { + uint8_t buffer[10]; + if (really_read(fd, &buffer[0], sizeof(buffer)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to read vif info from client"); + return -1; + } + vif->mtu = (size_t) (buffer[0] | (buffer[1] << 8)); + vif->max_packet_size = (size_t) (buffer[2] | (buffer[3] << 8)); + memcpy(vif->mac, &buffer[4], 6); + return 0; +} + + +int write_vif_info(int fd, struct vif_info *vif) { + uint8_t buffer[10]; + buffer[0] = (uint8_t) ((vif->mtu >> 0) & 0xff); + buffer[1] = (uint8_t) ((vif->mtu >> 8) & 0xff); + buffer[2] = (uint8_t) ((vif->max_packet_size >> 0) & 0xff); + buffer[3] = (uint8_t) ((vif->max_packet_size >> 8) & 0xff); + memcpy(&buffer[0] + 4, &(vif->mac)[0], 6); + if (really_write(fd, &buffer[0], sizeof(buffer)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write vif info to client"); + return -1; + } + return 0; +} + +int read_command(int fd, enum command *c) { + uint8_t command; + if (really_read(fd, (uint8_t*) &command, sizeof(command)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to read command from client"); + return -1; + } + if ((command != ethernet) && (command != uninstall) && (command != install_symlinks) && (command != uninstall_symlinks) && (command != uninstall_sockets) && (command != bind_ipv4)) { + aslLog(ASL_LEVEL_CRIT, "Client sent unknown command: %d", command); + return -1; + } + *c = command; + return 0; +} + +int write_command(int fd, enum command *c) { + uint8_t command = *c; + if (really_write(fd, (uint8_t*) &command, sizeof(command)) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write command to client"); + return -1; + } + return 0; +} + +int read_ethernet_args(int fd, struct ethernet_args *args){ + if (really_read(fd, (uint8_t*) &args->uuid_string[0], 36) == -1){ + aslLog(ASL_LEVEL_CRIT, "Failed to read ethernet args from client"); + return -1; + } + return 0; +} + +int write_ethernet_args(int fd, struct ethernet_args *args){ + if (really_write(fd, (uint8_t*) &args->uuid_string[0], 36) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write ethernet args to client"); + return -1; + } + return 0; +} + +int read_bind_ipv4(int fd, struct bind_ipv4 *ip) { + if (really_read(fd, (uint8_t*) &ip->ipv4, 4) == -1){ + aslLog(ASL_LEVEL_CRIT, "Failed to read IPv4 address from client"); + return -1; + } + if (really_read(fd, (uint8_t*) &ip->port, 2) == -1){ + aslLog(ASL_LEVEL_CRIT, "Failed to read port from client"); + return -1; + } + if (really_read(fd, (uint8_t*) &ip->stream, 1) == -1){ + aslLog(ASL_LEVEL_CRIT, "Failed to read stream from client"); + return -1; + } + if ((ip->stream != 0) && (ip->stream != 1)) { + aslLog(ASL_LEVEL_CRIT, "Failed to parse stream value: %d", ip->stream); + return -1; + } + + return 0; +} + +int write_bind_ipv4(int fd, struct bind_ipv4 *ip) { + if (really_write(fd, (uint8_t*) &ip->ipv4, 4) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write IPv4 to server"); + return -1; + } + if (really_write(fd, (uint8_t*) &ip->port, 2) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write port to server"); + return -1; + } + if (really_write(fd, (uint8_t*) &ip->stream, 1) == -1){ + aslLog(ASL_LEVEL_ERR, "Failed to write stream to server"); + return -1; + } + return 0; +} diff --git a/alpine/packages/tap-vsockd/protocol.h b/alpine/packages/tap-vsockd/protocol.h new file mode 100644 index 000000000..57389382b --- /dev/null +++ b/alpine/packages/tap-vsockd/protocol.h @@ -0,0 +1,76 @@ +#ifndef _VMNET_PROTOCOL_H_ +#define _VMNET_PROTOCOL_H_ + +#include +#include + +/* Client -> Server init_message */ +/* Server -> Client init_message */ +struct init_message { + char hello[5]; + uint8_t _padding[3]; + uint32_t version; + char commit[40]; /* git sha of the compiled commit */ +}; + +/* This should be bumped whenever we add something (like a feature or a bugfix) + and we wish the UI to be able to detect when to trigger a reinstall. */ +#define CURRENT_VERSION 13 + +extern struct init_message *create_init_message(void); +extern int read_init_message(int fd, struct init_message *ci); +extern int write_init_message(int fd, struct init_message *ci); +extern char *print_init_message(struct init_message *m); + +/* Client -> Server command */ +enum command { + ethernet = 1, + uninstall = 2, + install_symlinks = 3, + uninstall_symlinks = 4, + uninstall_sockets = 5, // to uninstall all sockets but com.docker.vmnetd.socket + bind_ipv4 = 6, +}; + +extern int read_command(int fd, enum command *c); +extern int write_command(int fd, enum command *c); + +/* Client -> Server command arguments */ +struct ethernet_args { + char uuid_string[36]; +}; + +extern int read_ethernet_args(int fd, struct ethernet_args *args); +extern int write_ethernet_args(int fd, struct ethernet_args *args); + +/* Server -> Client: details of a vif */ +struct vif_info { + uint8_t mac[6]; + short _padding; + size_t max_packet_size; + size_t mtu; +}; + +extern int read_vif_info(int fd, struct vif_info *vif); +extern int write_vif_info(int fd, struct vif_info *vif); + +extern char expected_hello[5]; +extern char expected_hello_old[5]; + +extern int really_read(int fd, uint8_t *buffer, size_t total); +extern int really_write(int fd, uint8_t *buffer, size_t total); +extern int really_writev(int fd, struct iovec *iov, int iovcnt); + +// Client -> Server: requested IPv4 address and port +struct bind_ipv4 { + uint32_t ipv4; + short _padding; + uint16_t port; + uint8_t stream; /* 0 = stream; 1 = dgram */ + uint8_t _padding2[3]; +}; + +extern int read_bind_ipv4(int fd, struct bind_ipv4 *vif); +extern int write_bind_ipv4(int fd, struct bind_ipv4 *vif); + +#endif /* _VMNET_PROTOCOL_H_ */ From 3533ff526e05768eeedb2a82200169d1efeb63f6 Mon Sep 17 00:00:00 2001 From: David Scott Date: Thu, 12 May 2016 16:07:51 +0100 Subject: [PATCH 04/16] tap-vsockd: remove dead code and ASL references Signed-off-by: David Scott --- alpine/packages/tap-vsockd/protocol.c | 126 ++++---------------------- alpine/packages/tap-vsockd/protocol.h | 20 ---- 2 files changed, 17 insertions(+), 129 deletions(-) diff --git a/alpine/packages/tap-vsockd/protocol.c b/alpine/packages/tap-vsockd/protocol.c index 0a423995e..c5893e01f 100644 --- a/alpine/packages/tap-vsockd/protocol.c +++ b/alpine/packages/tap-vsockd/protocol.c @@ -7,9 +7,6 @@ #include #include "protocol.h" -#include "commit.h" - -#include "utils.h" /* Version 0 of the protocol used this */ char expected_hello_old[5] = { 'V', 'M', 'N', 'E', 'T' }; @@ -23,11 +20,11 @@ int really_read(int fd, uint8_t *buffer, size_t total){ while (remaining > 0){ n = read(fd, buffer, remaining); if (n == 0){ - aslLog(ASL_LEVEL_NOTICE, "EOF reading packet from Unix domain socket: closing"); + fprintf(stderr, "EOF reading from socket: closing\n"); goto err; } if (n < 0){ - aslLog(ASL_LEVEL_NOTICE, "failure reading packet from Unix domain socket, closing: %s", strerror (errno)); + perror("Failure reading from socket: closing"); goto err; } remaining -= (size_t)n; @@ -46,11 +43,11 @@ int really_write(int fd, uint8_t *buffer, size_t total){ while (remaining > 0){ n = write(fd, buffer, remaining); if (n == 0){ - aslLog(ASL_LEVEL_INFO, "EOF writing to Unix domain socket"); + fprintf(stderr, "EOF writing to socket: closing\n"); goto err; } if (n < 0){ - aslLog(ASL_LEVEL_NOTICE, "failure writing packet from Unix domain socket, closing: %s", strerror (errno)); + perror("Failure writing to socket: closing\n"); goto err; } remaining -= (size_t) n; @@ -63,42 +60,12 @@ err: return -1; } -int really_writev(int fd, struct iovec *iov, int iovcnt){ - ssize_t n = 0; - int pos = 0; - - while (pos < iovcnt) { - iov[pos].iov_base = (char *)iov[pos].iov_base + n; - iov[pos].iov_len -= (size_t)n; - - n = writev(fd, iov + pos, iovcnt - pos); - - if (n == 0){ - aslLog(ASL_LEVEL_INFO, "EOF writing to Unix domain socket"); - goto err; - } - if (n < 0){ - aslLog(ASL_LEVEL_NOTICE, "failure writing packet from Unix domain socket, closing: %s", strerror (errno)); - goto err; - } - - // advance pos, reduce n - for (; pos < iovcnt && (size_t)n >= iov[pos].iov_len; pos++) { - n -= iov[pos].iov_len; - } - } - return 0; -err: - shutdown(fd, SHUT_WR); - return -1; -} - struct init_message *create_init_message(){ struct init_message *m = (struct init_message*) malloc(sizeof(struct init_message)); bzero(m, sizeof(struct init_message)); memcpy(&m->hello[0], &expected_hello[0], sizeof(m->hello)); m->version = CURRENT_VERSION; - memcpy(&m->commit[0], &commit[0], sizeof(m->commit)); + memset(&m->commit[0], 0, sizeof(m->commit)); return m; } @@ -109,7 +76,7 @@ char *print_init_message(struct init_message *m) { char *buffer = (char*) malloc(80); int n = snprintf(buffer, 80, "version %d, commit %s", m->version, tmp); if (n < 0) { - aslLog(ASL_LEVEL_ERR, "Failed to format init_message: %s", strerror (errno)); + perror("Failed to format init_message"); exit(1); } return buffer; @@ -118,7 +85,7 @@ char *print_init_message(struct init_message *m) { int read_init_message(int fd, struct init_message *ci) { bzero(ci, sizeof(struct init_message)); if (really_read(fd, (uint8_t*) &ci->hello[0], sizeof(ci->hello)) == -1){ - aslLog(ASL_LEVEL_CRIT, "Failed to read hello from client"); + fprintf(stderr, "Failed to read hello from client\n"); return -1; } if (memcmp(&ci->hello[0], &expected_hello_old[0], sizeof(expected_hello_old)) == 0) { @@ -126,15 +93,15 @@ int read_init_message(int fd, struct init_message *ci) { return 0; } if (memcmp(&ci->hello[0], &expected_hello[0], sizeof(expected_hello)) != 0) { - aslLog(ASL_LEVEL_ERR, "Failed to read header magic from client"); + fprintf(stderr, "Failed to read header magic from client\n"); return -1; } if (really_read(fd, (uint8_t*) &ci->version, sizeof(ci->version)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to read header version from client"); + fprintf(stderr, "Failed to read header version from client\n"); return -1; } if (really_read(fd, (uint8_t*) &ci->commit[0], sizeof(ci->commit)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to read header hash from client"); + fprintf(stderr, "Failed to read header hash from client\n"); return -1; } return 0; @@ -142,16 +109,16 @@ int read_init_message(int fd, struct init_message *ci) { int write_init_message(int fd, struct init_message *ci) { if (really_write(fd, (uint8_t*) &ci->hello[0], sizeof(ci->hello)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write hello to client"); + fprintf(stderr, "Failed to write hello to client\n"); return -1; } if (ci->version > 0) { if (really_write(fd, (uint8_t*) &ci->version, sizeof(ci->version)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write version to client"); + fprintf(stderr, "Failed to write version to client\n"); return -1; } if (really_write(fd, (uint8_t*) &ci->commit[0], sizeof(ci->commit)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write hash to client"); + fprintf(stderr, "Failed to write header hash to client\n"); return -1; } } @@ -161,7 +128,7 @@ int write_init_message(int fd, struct init_message *ci) { int read_vif_info(int fd, struct vif_info *vif) { uint8_t buffer[10]; if (really_read(fd, &buffer[0], sizeof(buffer)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to read vif info from client"); + fprintf(stderr, "Failed to read vif info from client\n"); return -1; } vif->mtu = (size_t) (buffer[0] | (buffer[1] << 8)); @@ -179,38 +146,16 @@ int write_vif_info(int fd, struct vif_info *vif) { buffer[3] = (uint8_t) ((vif->max_packet_size >> 8) & 0xff); memcpy(&buffer[0] + 4, &(vif->mac)[0], 6); if (really_write(fd, &buffer[0], sizeof(buffer)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write vif info to client"); + fprintf(stderr, "Failed to write vif into to client\n"); return -1; } return 0; } -int read_command(int fd, enum command *c) { - uint8_t command; - if (really_read(fd, (uint8_t*) &command, sizeof(command)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to read command from client"); - return -1; - } - if ((command != ethernet) && (command != uninstall) && (command != install_symlinks) && (command != uninstall_symlinks) && (command != uninstall_sockets) && (command != bind_ipv4)) { - aslLog(ASL_LEVEL_CRIT, "Client sent unknown command: %d", command); - return -1; - } - *c = command; - return 0; -} - int write_command(int fd, enum command *c) { uint8_t command = *c; if (really_write(fd, (uint8_t*) &command, sizeof(command)) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write command to client"); - return -1; - } - return 0; -} - -int read_ethernet_args(int fd, struct ethernet_args *args){ - if (really_read(fd, (uint8_t*) &args->uuid_string[0], 36) == -1){ - aslLog(ASL_LEVEL_CRIT, "Failed to read ethernet args from client"); + fprintf(stderr, "Failed to write command to client\n"); return -1; } return 0; @@ -218,44 +163,7 @@ int read_ethernet_args(int fd, struct ethernet_args *args){ int write_ethernet_args(int fd, struct ethernet_args *args){ if (really_write(fd, (uint8_t*) &args->uuid_string[0], 36) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write ethernet args to client"); - return -1; - } - return 0; -} - -int read_bind_ipv4(int fd, struct bind_ipv4 *ip) { - if (really_read(fd, (uint8_t*) &ip->ipv4, 4) == -1){ - aslLog(ASL_LEVEL_CRIT, "Failed to read IPv4 address from client"); - return -1; - } - if (really_read(fd, (uint8_t*) &ip->port, 2) == -1){ - aslLog(ASL_LEVEL_CRIT, "Failed to read port from client"); - return -1; - } - if (really_read(fd, (uint8_t*) &ip->stream, 1) == -1){ - aslLog(ASL_LEVEL_CRIT, "Failed to read stream from client"); - return -1; - } - if ((ip->stream != 0) && (ip->stream != 1)) { - aslLog(ASL_LEVEL_CRIT, "Failed to parse stream value: %d", ip->stream); - return -1; - } - - return 0; -} - -int write_bind_ipv4(int fd, struct bind_ipv4 *ip) { - if (really_write(fd, (uint8_t*) &ip->ipv4, 4) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write IPv4 to server"); - return -1; - } - if (really_write(fd, (uint8_t*) &ip->port, 2) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write port to server"); - return -1; - } - if (really_write(fd, (uint8_t*) &ip->stream, 1) == -1){ - aslLog(ASL_LEVEL_ERR, "Failed to write stream to server"); + fprintf(stderr, "Failed to write ethernet args to client\n"); return -1; } return 0; diff --git a/alpine/packages/tap-vsockd/protocol.h b/alpine/packages/tap-vsockd/protocol.h index 57389382b..1ce330086 100644 --- a/alpine/packages/tap-vsockd/protocol.h +++ b/alpine/packages/tap-vsockd/protocol.h @@ -25,14 +25,8 @@ extern char *print_init_message(struct init_message *m); /* Client -> Server command */ enum command { ethernet = 1, - uninstall = 2, - install_symlinks = 3, - uninstall_symlinks = 4, - uninstall_sockets = 5, // to uninstall all sockets but com.docker.vmnetd.socket - bind_ipv4 = 6, }; -extern int read_command(int fd, enum command *c); extern int write_command(int fd, enum command *c); /* Client -> Server command arguments */ @@ -40,7 +34,6 @@ struct ethernet_args { char uuid_string[36]; }; -extern int read_ethernet_args(int fd, struct ethernet_args *args); extern int write_ethernet_args(int fd, struct ethernet_args *args); /* Server -> Client: details of a vif */ @@ -59,18 +52,5 @@ extern char expected_hello_old[5]; extern int really_read(int fd, uint8_t *buffer, size_t total); extern int really_write(int fd, uint8_t *buffer, size_t total); -extern int really_writev(int fd, struct iovec *iov, int iovcnt); - -// Client -> Server: requested IPv4 address and port -struct bind_ipv4 { - uint32_t ipv4; - short _padding; - uint16_t port; - uint8_t stream; /* 0 = stream; 1 = dgram */ - uint8_t _padding2[3]; -}; - -extern int read_bind_ipv4(int fd, struct bind_ipv4 *vif); -extern int write_bind_ipv4(int fd, struct bind_ipv4 *vif); #endif /* _VMNET_PROTOCOL_H_ */ From 853d17e04f972f512a017c87488f708089e7c5da Mon Sep 17 00:00:00 2001 From: David Scott Date: Thu, 12 May 2016 16:25:05 +0100 Subject: [PATCH 05/16] tap-vsockd: open the tap device, connect the hvsock and negotiate Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 63 +++++++++++++++++++++---- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index 449591ef3..4d259b1f3 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -24,6 +24,7 @@ #include "compat.h" +#include "protocol.h" int verbose_flag = 0; @@ -101,10 +102,43 @@ struct client_args { int tosend; }; -/* Handle a connection. Echo back anything sent to us and when the - * connection is closed send a bye message. +void negotiate(SOCKET fd, struct vif_info *vif) +{ + /* Negotiate with com.docker.slirp */ + struct init_message *me = create_init_message(); + if (write_init_message(fd, me) == -1) { + goto err; + } + struct init_message you; + if (read_init_message(fd, &you) == -1) { + goto err; + } + char *txt = print_init_message(&you); + fprintf(stderr, "Server reports %s\n", txt); + free(txt); + enum command command = ethernet; + if (write_command(fd, &command) == -1) { + goto err; + } + struct ethernet_args args; + /* We don't need a uuid */ + memset(&args.uuid_string[0], 0, sizeof(args.uuid_string)); + if (write_ethernet_args(fd, &args) == -1) { + goto err; + } + if (read_vif_info(fd, vif) == -1) { + goto err; + } + return; +err: + fprintf(stderr, "Failed to negotiate with com.docker.slirp\n"); + exit(1); +} + +/* Handle a connection. Handshake with the com.docker.slirp process and start + * exchanging ethernet frames between the socket and the tap device. */ -static void handle(SOCKET fd) +static void handle(SOCKET fd, int tapfd) { char recvbuf[SVR_BUF_LEN]; int recvbuflen = SVR_BUF_LEN; @@ -112,6 +146,9 @@ static void handle(SOCKET fd) int sent; int res; + struct vif_info vif; + negotiate(fd, &vif); + for (;;) { received = recv(fd, recvbuf, recvbuflen, 0); if (received == 0) { @@ -145,7 +182,7 @@ static void handle(SOCKET fd) /* Server: * accept() in an endless loop, handle a connection at a time */ -static int server(GUID serviceid) +static int server(GUID serviceid, int tapfd) { SOCKET lsock = INVALID_SOCKET; SOCKET csock = INVALID_SOCKET; @@ -189,7 +226,7 @@ static int server(GUID serviceid) printf("Connect from: "GUID_FMT":"GUID_FMT"\n", GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); - handle(csock); + handle(csock, tapfd); closesocket(csock); } } @@ -204,7 +241,7 @@ void usage(char *name) int __cdecl main(int argc, char **argv) { int res = 0; - GUID target = HV_GUID_PARENT; + GUID sid; int c; char *serviceid = NULL; char *tap = "tap0"; @@ -243,7 +280,15 @@ int __cdecl main(int argc, char **argv) fprintf(stderr, "serviceid=%s\n", serviceid); /* 3049197C-9A4E-4FBF-9367-97F792F16994 */ fprintf(stderr, "tap=%s\n", tap); - server(target); - - return res; + res = parseguid(serviceid, &sid); + if (res) { + fprintf(stderr, "Failed to parse serviceid as GUID: %s\n", serviceid); + usage(argv[0]); + exit(1); + } + int tapfd = alloc_tap(tap); + + server(sid, tapfd); + + return 0; } From b45ac6d9a8f6cad46aa45522cdc687a1f13cf30f Mon Sep 17 00:00:00 2001 From: David Scott Date: Thu, 12 May 2016 17:10:32 +0100 Subject: [PATCH 06/16] tap-vsockd: proxy between the hyper-v socket and the tap device Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 127 ++++++++++++++++-------- 1 file changed, 88 insertions(+), 39 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index 4d259b1f3..8cbb61c0a 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -96,12 +96,6 @@ void sockerr(const char *msg) fprintf(stderr, "%s Error: %d. %s", msg, errno, strerror(errno)); } -/* Argument passed to Client send thread */ -struct client_args { - SOCKET fd; - int tosend; -}; - void negotiate(SOCKET fd, struct vif_info *vif) { /* Negotiate with com.docker.slirp */ @@ -135,47 +129,102 @@ err: exit(1); } + +/* Argument passed to proxy threads */ +struct connection { + SOCKET fd; /* Hyper-V socket with vmnet protocol */ + int tapfd; /* TAP device with ethernet frames */ + struct vif_info vif; /* Contains VIF MAC, MTU etc, received from server */ +}; + +static void* vmnet_to_tap(void *arg) +{ + int length, n; + struct connection *connection = (struct connection*) arg; + uint8_t header[2]; + uint8_t buffer[2048]; + + for (;;) { + if (really_read(connection->fd, &header[0], 2) == -1){ + fprintf(stderr, "Failed to read a packet header from host\n"); + exit(1); + } + length = (header[0] & 0xff) | ((header[1] & 0xff) << 8); + if (length > sizeof(buffer)) { + fprintf(stderr, "Received an over-large packet: %d > %ld\n", length, sizeof(buffer)); + exit(1); + } + if (really_read(connection->fd, &buffer[0], length) == -1){ + fprintf(stderr, "Failed to read packet contents from host\n"); + exit(1); + } + n = write(connection->tapfd, &buffer[0], length); + if (n != length) { + fprintf(stderr, "Failed to write %d bytes to tap device (wrote %d)\n", length, n); + exit(1); + } + } +} + +static void* tap_to_vmnet(void *arg) +{ + int length; + struct connection *connection = (struct connection*) arg; + uint8_t header[2]; + uint8_t buffer[2048]; + + for (;;) { + length = read(connection->tapfd, &buffer[0], sizeof(buffer)); + if (length == -1) { + if (errno == ENXIO) { + fprintf(stderr, "tap device has gone down\n"); + exit(0); + } + fprintf(stderr, "ignoring error %d\n", errno); + /* This is what mirage-net-unix does. Is it a good idea really? */ + continue; + } + header[0] = (length >> 0) & 0xff; + header[1] = (length >> 8) & 0xff; + if (really_write(connection->fd, &header[0], 2) == -1){ + fprintf(stderr, "Failed to write packet header\n"); + exit(1); + } + if (really_write(connection->fd, &buffer[0], length) == -1) { + fprintf(stderr, "Failed to write packet body\n"); + exit(1); + } + } +} + /* Handle a connection. Handshake with the com.docker.slirp process and start * exchanging ethernet frames between the socket and the tap device. */ static void handle(SOCKET fd, int tapfd) { - char recvbuf[SVR_BUF_LEN]; - int recvbuflen = SVR_BUF_LEN; - int received; - int sent; - int res; + struct connection connection; + pthread_t v2t, t2v; - struct vif_info vif; - negotiate(fd, &vif); + connection.fd = fd; + connection.tapfd = tapfd; + negotiate(fd, &connection.vif); - for (;;) { - received = recv(fd, recvbuf, recvbuflen, 0); - if (received == 0) { - printf("Peer closed\n"); - break; - } else if (received == SOCKET_ERROR) { - sockerr("recv()"); - return; - } - - /* No error, echo */ - printf("RX: %d Bytes\n", received); - - sent = 0; - while (sent < received) { - res = send(fd, recvbuf + sent, received - sent, 0); - if (sent == SOCKET_ERROR) { - sockerr("send()"); - return; - } - printf("TX: %d Bytes\n", res); - sent += res; - } + if (pthread_create(&v2t, NULL, vmnet_to_tap, &connection) != 0){ + fprintf(stderr, "Failed to create the vmnet_to_tap thread\n"); + exit(1); + } + if (pthread_create(&t2v, NULL, tap_to_vmnet, &connection) != 0){ + fprintf(stderr, "Failed to create the tap_to_vmnet thread\n"); + exit(1); + } + if (pthread_join(v2t, NULL) != 0){ + fprintf(stderr, "Failed to join the vmnet_to_tap thread\n"); + exit(1); + } + if (pthread_join(t2v, NULL) != 0){ + fprintf(stderr, "Failed to join the tap_to_vmnet thread\n"); + exit(1); } - - /* Dummy read to wait till other end closes */ - recv(fd, recvbuf, recvbuflen, 0); } From 4f4c8072457a5bdd197215aa9cf9d07eb1e6a555 Mon Sep 17 00:00:00 2001 From: David Scott Date: Fri, 13 May 2016 11:55:04 +0100 Subject: [PATCH 07/16] tap-vsockd: set the MAC address on the TAP device The vmnet protocol expects to tell the client what the MAC address is. This patch sets it on the TAP device. Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 43 +++++++++++++++++++------ 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index 8cbb61c0a..06d230794 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,7 +29,7 @@ int verbose_flag = 0; -int alloc_tap(char *dev) { +int alloc_tap(const char *dev) { int fd; struct ifreq ifr; const char *clonedev = "/dev/net/tun"; @@ -52,6 +53,22 @@ int alloc_tap(char *dev) { return fd; } +void set_macaddr(const char *dev, uint8_t *mac) { + int fd; + struct ifreq ifq; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + strcpy(ifq.ifr_name, dev); + memcpy(&ifq.ifr_hwaddr.sa_data[0], mac, 6); + ifq.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(fd, SIOCSIFHWADDR, &ifq) == -1) { + perror("SIOCSIFHWADDR failed"); + exit(1); + } + + close(fd); +} #define SVR_BUF_LEN (3 * 4096) #define MAX_BUF_LEN (2 * 1024 * 1024) @@ -200,14 +217,21 @@ static void* tap_to_vmnet(void *arg) /* Handle a connection. Handshake with the com.docker.slirp process and start * exchanging ethernet frames between the socket and the tap device. */ -static void handle(SOCKET fd, int tapfd) +static void handle(SOCKET fd, const char *tap) { struct connection connection; pthread_t v2t, t2v; connection.fd = fd; - connection.tapfd = tapfd; negotiate(fd, &connection.vif); + fprintf(stderr, "VMNET VIF has MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + connection.vif.mac[0], connection.vif.mac[1], connection.vif.mac[2], + connection.vif.mac[3], connection.vif.mac[4], connection.vif.mac[5] + ); + + int tapfd = alloc_tap(tap); + set_macaddr(tap, &connection.vif.mac[0]); + connection.tapfd = tapfd; if (pthread_create(&v2t, NULL, vmnet_to_tap, &connection) != 0){ fprintf(stderr, "Failed to create the vmnet_to_tap thread\n"); @@ -231,7 +255,7 @@ static void handle(SOCKET fd, int tapfd) /* Server: * accept() in an endless loop, handle a connection at a time */ -static int server(GUID serviceid, int tapfd) +static int server(GUID serviceid, const char *tap) { SOCKET lsock = INVALID_SOCKET; SOCKET csock = INVALID_SOCKET; @@ -275,7 +299,7 @@ static int server(GUID serviceid, int tapfd) printf("Connect from: "GUID_FMT":"GUID_FMT"\n", GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); - handle(csock, tapfd); + handle(csock, tap); closesocket(csock); } } @@ -292,8 +316,9 @@ int __cdecl main(int argc, char **argv) int res = 0; GUID sid; int c; - char *serviceid = NULL; - char *tap = "tap0"; + /* Defaults to a testing GUID */ + char *serviceid = "3049197C-9A4E-4FBF-9367-97F792F16994"; + char *tap = "eth1"; opterr = 0; while (1) { @@ -327,7 +352,6 @@ int __cdecl main(int argc, char **argv) } } fprintf(stderr, "serviceid=%s\n", serviceid); - /* 3049197C-9A4E-4FBF-9367-97F792F16994 */ fprintf(stderr, "tap=%s\n", tap); res = parseguid(serviceid, &sid); if (res) { @@ -335,9 +359,8 @@ int __cdecl main(int argc, char **argv) usage(argv[0]); exit(1); } - int tapfd = alloc_tap(tap); - server(sid, tapfd); + server(sid, tap); return 0; } From 4ffd1742dda8fe27ff2bf188fc795f7f91ce8e2a Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 14 May 2016 18:08:53 +0100 Subject: [PATCH 08/16] tap-vsockd: add basic logging support By default we write to syslog. If the `--debug` argument is provided then we also write to stderr. Signed-off-by: David Scott --- alpine/packages/tap-vsockd/protocol.c | 31 ++++++------- alpine/packages/tap-vsockd/tap-vsockd.c | 60 ++++++++++++++----------- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/alpine/packages/tap-vsockd/protocol.c b/alpine/packages/tap-vsockd/protocol.c index c5893e01f..22a143a58 100644 --- a/alpine/packages/tap-vsockd/protocol.c +++ b/alpine/packages/tap-vsockd/protocol.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "protocol.h" @@ -20,11 +21,11 @@ int really_read(int fd, uint8_t *buffer, size_t total){ while (remaining > 0){ n = read(fd, buffer, remaining); if (n == 0){ - fprintf(stderr, "EOF reading from socket: closing\n"); + syslog(LOG_CRIT, "EOF reading from socket: closing\n"); goto err; } if (n < 0){ - perror("Failure reading from socket: closing"); + syslog(LOG_CRIT, "Failure reading from socket: closing: %s", strerror(errno)); goto err; } remaining -= (size_t)n; @@ -43,11 +44,11 @@ int really_write(int fd, uint8_t *buffer, size_t total){ while (remaining > 0){ n = write(fd, buffer, remaining); if (n == 0){ - fprintf(stderr, "EOF writing to socket: closing\n"); + syslog(LOG_CRIT, "EOF writing to socket: closing"); goto err; } if (n < 0){ - perror("Failure writing to socket: closing\n"); + syslog(LOG_CRIT, "Failure writing to socket: closing: %s", strerror(errno)); goto err; } remaining -= (size_t) n; @@ -85,7 +86,7 @@ char *print_init_message(struct init_message *m) { int read_init_message(int fd, struct init_message *ci) { bzero(ci, sizeof(struct init_message)); if (really_read(fd, (uint8_t*) &ci->hello[0], sizeof(ci->hello)) == -1){ - fprintf(stderr, "Failed to read hello from client\n"); + syslog(LOG_CRIT, "Failed to read hello from client"); return -1; } if (memcmp(&ci->hello[0], &expected_hello_old[0], sizeof(expected_hello_old)) == 0) { @@ -93,15 +94,15 @@ int read_init_message(int fd, struct init_message *ci) { return 0; } if (memcmp(&ci->hello[0], &expected_hello[0], sizeof(expected_hello)) != 0) { - fprintf(stderr, "Failed to read header magic from client\n"); + syslog(LOG_CRIT, "Failed to read header magic from client"); return -1; } if (really_read(fd, (uint8_t*) &ci->version, sizeof(ci->version)) == -1){ - fprintf(stderr, "Failed to read header version from client\n"); + syslog(LOG_CRIT, "Failed to read header version from client"); return -1; } if (really_read(fd, (uint8_t*) &ci->commit[0], sizeof(ci->commit)) == -1){ - fprintf(stderr, "Failed to read header hash from client\n"); + syslog(LOG_CRIT, "Failed to read header hash from client"); return -1; } return 0; @@ -109,16 +110,16 @@ int read_init_message(int fd, struct init_message *ci) { int write_init_message(int fd, struct init_message *ci) { if (really_write(fd, (uint8_t*) &ci->hello[0], sizeof(ci->hello)) == -1){ - fprintf(stderr, "Failed to write hello to client\n"); + syslog(LOG_CRIT, "Failed to write hello to client"); return -1; } if (ci->version > 0) { if (really_write(fd, (uint8_t*) &ci->version, sizeof(ci->version)) == -1){ - fprintf(stderr, "Failed to write version to client\n"); + syslog(LOG_CRIT, "Failed to write version to client"); return -1; } if (really_write(fd, (uint8_t*) &ci->commit[0], sizeof(ci->commit)) == -1){ - fprintf(stderr, "Failed to write header hash to client\n"); + syslog(LOG_CRIT, "Failed to write header hash to client"); return -1; } } @@ -128,7 +129,7 @@ int write_init_message(int fd, struct init_message *ci) { int read_vif_info(int fd, struct vif_info *vif) { uint8_t buffer[10]; if (really_read(fd, &buffer[0], sizeof(buffer)) == -1){ - fprintf(stderr, "Failed to read vif info from client\n"); + syslog(LOG_CRIT, "Failed to read vif info from client"); return -1; } vif->mtu = (size_t) (buffer[0] | (buffer[1] << 8)); @@ -146,7 +147,7 @@ int write_vif_info(int fd, struct vif_info *vif) { buffer[3] = (uint8_t) ((vif->max_packet_size >> 8) & 0xff); memcpy(&buffer[0] + 4, &(vif->mac)[0], 6); if (really_write(fd, &buffer[0], sizeof(buffer)) == -1){ - fprintf(stderr, "Failed to write vif into to client\n"); + syslog(LOG_CRIT, "Failed to write vif into to client"); return -1; } return 0; @@ -155,7 +156,7 @@ int write_vif_info(int fd, struct vif_info *vif) { int write_command(int fd, enum command *c) { uint8_t command = *c; if (really_write(fd, (uint8_t*) &command, sizeof(command)) == -1){ - fprintf(stderr, "Failed to write command to client\n"); + syslog(LOG_CRIT, "Failed to write command to client"); return -1; } return 0; @@ -163,7 +164,7 @@ int write_command(int fd, enum command *c) { int write_ethernet_args(int fd, struct ethernet_args *args){ if (really_write(fd, (uint8_t*) &args->uuid_string[0], 36) == -1){ - fprintf(stderr, "Failed to write ethernet args to client\n"); + syslog(LOG_CRIT, "Failed to write ethernet args to client"); return -1; } return 0; diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index 06d230794..a1a3d76d8 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -5,7 +5,7 @@ #include #include #include - +#include #include #include #include @@ -27,7 +27,7 @@ #include "compat.h" #include "protocol.h" -int verbose_flag = 0; +int debug_flag = 0; int alloc_tap(const char *dev) { int fd; @@ -49,7 +49,7 @@ int alloc_tap(const char *dev) { perror("TUNSETPERSIST failed"); exit(1); } - fprintf(stderr, "successfully created TAP device %s\n", dev); + syslog(LOG_INFO, "successfully created TAP device %s", dev); return fd; } @@ -110,7 +110,7 @@ int parseguid(const char *s, GUID *g) /* Slightly different error handling between Windows and Linux */ void sockerr(const char *msg) { - fprintf(stderr, "%s Error: %d. %s", msg, errno, strerror(errno)); + syslog(LOG_CRIT, "%s Error: %d. %s", msg, errno, strerror(errno)); } void negotiate(SOCKET fd, struct vif_info *vif) @@ -125,7 +125,7 @@ void negotiate(SOCKET fd, struct vif_info *vif) goto err; } char *txt = print_init_message(&you); - fprintf(stderr, "Server reports %s\n", txt); + syslog(LOG_INFO, "Server reports %s", txt); free(txt); enum command command = ethernet; if (write_command(fd, &command) == -1) { @@ -142,7 +142,7 @@ void negotiate(SOCKET fd, struct vif_info *vif) } return; err: - fprintf(stderr, "Failed to negotiate with com.docker.slirp\n"); + syslog(LOG_CRIT, "Failed to negotiate with com.docker.slirp"); exit(1); } @@ -163,21 +163,21 @@ static void* vmnet_to_tap(void *arg) for (;;) { if (really_read(connection->fd, &header[0], 2) == -1){ - fprintf(stderr, "Failed to read a packet header from host\n"); + syslog(LOG_CRIT, "Failed to read a packet header from host"); exit(1); } length = (header[0] & 0xff) | ((header[1] & 0xff) << 8); if (length > sizeof(buffer)) { - fprintf(stderr, "Received an over-large packet: %d > %ld\n", length, sizeof(buffer)); + syslog(LOG_CRIT, "Received an over-large packet: %d > %ld", length, sizeof(buffer)); exit(1); } if (really_read(connection->fd, &buffer[0], length) == -1){ - fprintf(stderr, "Failed to read packet contents from host\n"); + syslog(LOG_CRIT, "Failed to read packet contents from host"); exit(1); } n = write(connection->tapfd, &buffer[0], length); if (n != length) { - fprintf(stderr, "Failed to write %d bytes to tap device (wrote %d)\n", length, n); + syslog(LOG_CRIT, "Failed to write %d bytes to tap device (wrote %d)", length, n); exit(1); } } @@ -194,21 +194,21 @@ static void* tap_to_vmnet(void *arg) length = read(connection->tapfd, &buffer[0], sizeof(buffer)); if (length == -1) { if (errno == ENXIO) { - fprintf(stderr, "tap device has gone down\n"); + syslog(LOG_CRIT, "tap device has gone down"); exit(0); } - fprintf(stderr, "ignoring error %d\n", errno); + syslog(LOG_WARNING, "ignoring error %d", errno); /* This is what mirage-net-unix does. Is it a good idea really? */ continue; } header[0] = (length >> 0) & 0xff; header[1] = (length >> 8) & 0xff; if (really_write(connection->fd, &header[0], 2) == -1){ - fprintf(stderr, "Failed to write packet header\n"); + syslog(LOG_CRIT, "Failed to write packet header"); exit(1); } if (really_write(connection->fd, &buffer[0], length) == -1) { - fprintf(stderr, "Failed to write packet body\n"); + syslog(LOG_CRIT, "Failed to write packet body"); exit(1); } } @@ -224,7 +224,7 @@ static void handle(SOCKET fd, const char *tap) connection.fd = fd; negotiate(fd, &connection.vif); - fprintf(stderr, "VMNET VIF has MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + syslog(LOG_INFO, "VMNET VIF has MAC %02x:%02x:%02x:%02x:%02x:%02x", connection.vif.mac[0], connection.vif.mac[1], connection.vif.mac[2], connection.vif.mac[3], connection.vif.mac[4], connection.vif.mac[5] ); @@ -234,19 +234,19 @@ static void handle(SOCKET fd, const char *tap) connection.tapfd = tapfd; if (pthread_create(&v2t, NULL, vmnet_to_tap, &connection) != 0){ - fprintf(stderr, "Failed to create the vmnet_to_tap thread\n"); + syslog(LOG_CRIT, "Failed to create the vmnet_to_tap thread"); exit(1); } if (pthread_create(&t2v, NULL, tap_to_vmnet, &connection) != 0){ - fprintf(stderr, "Failed to create the tap_to_vmnet thread\n"); + syslog(LOG_CRIT, "Failed to create the tap_to_vmnet thread"); exit(1); } if (pthread_join(v2t, NULL) != 0){ - fprintf(stderr, "Failed to join the vmnet_to_tap thread\n"); + syslog(LOG_CRIT, "Failed to join the vmnet_to_tap thread"); exit(1); } if (pthread_join(t2v, NULL) != 0){ - fprintf(stderr, "Failed to join the tap_to_vmnet thread\n"); + syslog(LOG_CRIT, "Failed to join the tap_to_vmnet thread"); exit(1); } } @@ -306,7 +306,7 @@ static int server(GUID serviceid, const char *tap) void usage(char *name) { - printf("%s: --verbose | --service id | --tap \n", name); + printf("%s: --debug | --service id | --tap \n", name); printf(": Hyper-V socket serviceId to bind\n"); printf(": tap device to connect to\n"); } @@ -324,19 +324,20 @@ int __cdecl main(int argc, char **argv) while (1) { static struct option long_options[] = { /* These options set a flag. */ - {"verbose", no_argument, &verbose_flag, 1}, + {"debug", no_argument, &debug_flag, 1}, {"serviceid", required_argument, NULL, 's'}, {"tap", required_argument, NULL, 't'}, + {0, 0, 0, 0} }; int option_index = 0; - c = getopt_long (argc, argv, "vs:t:", long_options, &option_index); + c = getopt_long (argc, argv, "ds:t:", long_options, &option_index); if (c == -1) break; switch (c) { - case 'v': - verbose_flag = 1; + case 'd': + debug_flag = 1; break; case 's': serviceid = optarg; @@ -351,11 +352,16 @@ int __cdecl main(int argc, char **argv) exit (1); } } - fprintf(stderr, "serviceid=%s\n", serviceid); - fprintf(stderr, "tap=%s\n", tap); + int log_flags = LOG_CONS | LOG_NDELAY; + if (debug_flag) { + log_flags |= LOG_PERROR; + } + openlog(argv[0], log_flags, LOG_DAEMON); + + syslog(LOG_INFO, "starting with serviceid=%s and tap=%s", serviceid, tap); res = parseguid(serviceid, &sid); if (res) { - fprintf(stderr, "Failed to parse serviceid as GUID: %s\n", serviceid); + syslog(LOG_CRIT, "Failed to parse serviceid as GUID: %s", serviceid); usage(argv[0]); exit(1); } From a4a8d025600dd1a20c1751a1965c1a1efe4a5a23 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 14 May 2016 19:35:58 +0100 Subject: [PATCH 09/16] tap-vsockd: exit non-zero if the server cannot start Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index a1a3d76d8..5eabb8987 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -255,7 +255,7 @@ static void handle(SOCKET fd, const char *tap) /* Server: * accept() in an endless loop, handle a connection at a time */ -static int server(GUID serviceid, const char *tap) +static void server(GUID serviceid, const char *tap) { SOCKET lsock = INVALID_SOCKET; SOCKET csock = INVALID_SOCKET; @@ -266,7 +266,7 @@ static int server(GUID serviceid, const char *tap) lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); if (lsock == INVALID_SOCKET) { sockerr("socket()"); - return 1; + exit(1); } sa.Family = AF_HYPERV; @@ -278,14 +278,14 @@ static int server(GUID serviceid, const char *tap) if (res == SOCKET_ERROR) { sockerr("bind()"); closesocket(lsock); - return 1; + exit(1); } res = listen(lsock, SOMAXCONN); if (res == SOCKET_ERROR) { sockerr("listen()"); closesocket(lsock); - return 1; + exit(1); } while(1) { @@ -293,7 +293,7 @@ static int server(GUID serviceid, const char *tap) if (csock == INVALID_SOCKET) { sockerr("accept()"); closesocket(lsock); - return 1; + exit(1); } printf("Connect from: "GUID_FMT":"GUID_FMT"\n", From f74d52574e5c62c1ed8add73e118b0a771809323 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 14 May 2016 19:36:35 +0100 Subject: [PATCH 10/16] tap-vsockd: add a --pidfile argument Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 46 ++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index 5eabb8987..c8b13ada2 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -304,11 +304,41 @@ static void server(GUID serviceid, const char *tap) } } +void write_pidfile(const char *pidfile) { + pid_t pid = getpid(); + char * pid_s; + FILE *file; + int len; + + if (asprintf(&pid_s, "%lld", (long long) pid) == -1) { + syslog(LOG_CRIT, "Failed to allocate pidfile string"); + exit(1); + } + len = strlen(pid_s); + file = fopen(pidfile, "w"); + if (file == NULL) { + syslog(LOG_CRIT, "Failed to open pidfile %s", pidfile); + exit(1); + } + + if (fwrite(pid_s, 1, len, file) != len) { + syslog(LOG_CRIT, "Failed to write pid to pidfile"); + exit(1); + } + fclose(file); + free(pid_s); +} + void usage(char *name) { - printf("%s: --debug | --service id | --tap \n", name); - printf(": Hyper-V socket serviceId to bind\n"); - printf(": tap device to connect to\n"); + printf("%s: [--debug] [--tap ] [--serviceid ] [--pid ]\n", name); + printf("\t[--listen | --connect]\n\n"); + printf("--debug: log to stderr as well as syslog\n"); + printf("--tap : create a tap device with the given name (defaults to eth1)\n"); + printf("--serviceid : use as the well-known service GUID\n"); + printf("--pid : write a pid to the given file\n"); + printf("--listen: listen forever for incoming AF_HVSOCK connections\n"); + printf("--connect: connect to the parent partition\n"); } int __cdecl main(int argc, char **argv) @@ -319,6 +349,7 @@ int __cdecl main(int argc, char **argv) /* Defaults to a testing GUID */ char *serviceid = "3049197C-9A4E-4FBF-9367-97F792F16994"; char *tap = "eth1"; + char *pidfile = NULL; opterr = 0; while (1) { @@ -327,12 +358,13 @@ int __cdecl main(int argc, char **argv) {"debug", no_argument, &debug_flag, 1}, {"serviceid", required_argument, NULL, 's'}, {"tap", required_argument, NULL, 't'}, + {"pidfile", required_argument, NULL, 'p'}, {0, 0, 0, 0} }; int option_index = 0; - c = getopt_long (argc, argv, "ds:t:", long_options, &option_index); + c = getopt_long (argc, argv, "ds:t:p:", long_options, &option_index); if (c == -1) break; switch (c) { @@ -345,6 +377,9 @@ int __cdecl main(int argc, char **argv) case 't': tap = optarg; break; + case 'p': + pidfile = optarg; + break; case 0: break; default: @@ -365,6 +400,9 @@ int __cdecl main(int argc, char **argv) usage(argv[0]); exit(1); } + if (pidfile) { + write_pidfile(pidfile); + } server(sid, tap); From f636321b81db243b7184602409cf79cb079b37ec Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 14 May 2016 19:54:34 +0100 Subject: [PATCH 11/16] tap-vsockd: support both connect() and accept() Older versions of the Linux AF_HVSOCK can only accept() as they don't allow connections to the parent partition. Support both modes for now. Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 117 ++++++++++++++++-------- 1 file changed, 79 insertions(+), 38 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index c8b13ada2..55bdc80f6 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -28,6 +28,8 @@ #include "protocol.h" int debug_flag = 0; +int listen_flag = 0; +int connect_flag = 0; int alloc_tap(const char *dev) { int fd; @@ -251,42 +253,73 @@ static void handle(SOCKET fd, const char *tap) } } +static int create_listening_socket(GUID serviceid) { + SOCKET lsock = INVALID_SOCKET; + SOCKADDR_HV sa; + int res; + + lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); + if (lsock == INVALID_SOCKET) { + sockerr("socket()"); + exit(1); + } + + sa.Family = AF_HYPERV; + sa.Reserved = 0; + sa.VmId = HV_GUID_WILDCARD; + sa.ServiceId = serviceid; + + res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa)); + if (res == SOCKET_ERROR) { + sockerr("bind()"); + closesocket(lsock); + exit(1); + } + + res = listen(lsock, SOMAXCONN); + if (res == SOCKET_ERROR) { + sockerr("listen()"); + closesocket(lsock); + exit(1); + } + return lsock; +} + +static int connect_socket(GUID serviceid) { + SOCKET sock = INVALID_SOCKET; + SOCKADDR_HV sa; + int res; + + sock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); + if (sock == INVALID_SOCKET) { + sockerr("socket()"); + exit(1); + } + + sa.Family = AF_HYPERV; + sa.Reserved = 0; + sa.VmId = HV_GUID_PARENT; + sa.ServiceId = serviceid; + + res = connect(sock, (const struct sockaddr *)&sa, sizeof(sa)); + if (res == SOCKET_ERROR) { + sockerr("connect()"); + closesocket(sock); + exit(1); + } + + return sock; +} + /* Server: * accept() in an endless loop, handle a connection at a time */ -static void server(GUID serviceid, const char *tap) +static void accept_forever(SOCKET lsock, const char *tap) { - SOCKET lsock = INVALID_SOCKET; - SOCKET csock = INVALID_SOCKET; - SOCKADDR_HV sa, sac; - socklen_t socklen = sizeof(sac); - int res; - - lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); - if (lsock == INVALID_SOCKET) { - sockerr("socket()"); - exit(1); - } - - sa.Family = AF_HYPERV; - sa.Reserved = 0; - sa.VmId = HV_GUID_WILDCARD; - sa.ServiceId = serviceid; - - res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa)); - if (res == SOCKET_ERROR) { - sockerr("bind()"); - closesocket(lsock); - exit(1); - } - - res = listen(lsock, SOMAXCONN); - if (res == SOCKET_ERROR) { - sockerr("listen()"); - closesocket(lsock); - exit(1); - } + SOCKET csock = INVALID_SOCKET; + SOCKADDR_HV sac; + socklen_t socklen = sizeof(sac); while(1) { csock = accept(lsock, (struct sockaddr *)&sac, &socklen); @@ -359,7 +392,8 @@ int __cdecl main(int argc, char **argv) {"serviceid", required_argument, NULL, 's'}, {"tap", required_argument, NULL, 't'}, {"pidfile", required_argument, NULL, 'p'}, - + {"listen", no_argument, &listen_flag, 1}, + {"connect", no_argument, &connect_flag, 1}, {0, 0, 0, 0} }; int option_index = 0; @@ -387,24 +421,31 @@ int __cdecl main(int argc, char **argv) exit (1); } } + if ((listen_flag && connect_flag) || !(listen_flag || connect_flag)){ + fprintf(stderr, "Please supply either the --listen or --connect flag, but not both.\n"); + exit(1); + } int log_flags = LOG_CONS | LOG_NDELAY; if (debug_flag) { log_flags |= LOG_PERROR; } openlog(argv[0], log_flags, LOG_DAEMON); - syslog(LOG_INFO, "starting with serviceid=%s and tap=%s", serviceid, tap); res = parseguid(serviceid, &sid); if (res) { syslog(LOG_CRIT, "Failed to parse serviceid as GUID: %s", serviceid); usage(argv[0]); exit(1); } - if (pidfile) { - write_pidfile(pidfile); + + if (listen_flag) { + syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap); + int socket = create_listening_socket(sid); + accept_forever(socket, tap); + exit(0); } - - server(sid, tap); - + syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap); + int socket = connect_socket(sid); + handle(socket, tap); return 0; } From a1d91d2a3d459bae7cea677c59f539ef095a1f6c Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 14 May 2016 20:38:56 +0100 Subject: [PATCH 12/16] tap-vsockd: support daemonization via a --daemon arg We daemonize after checking the command-line and binding/connecting the socket and creating the tap device. Only the ethernet frame processing happens afterwards. This patch also includes a new well-known service GUID for the VPN-proxy ethernet service. Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 109 +++++++++++++++--------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index 55bdc80f6..ae1dd9792 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -27,10 +27,12 @@ #include "compat.h" #include "protocol.h" -int debug_flag = 0; +int daemon_flag = 0; int listen_flag = 0; int connect_flag = 0; +char *default_sid = "30D48B34-7D27-4B0B-AAAF-BBBED334DD59"; + int alloc_tap(const char *dev) { int fd; struct ifreq ifr; @@ -219,7 +221,7 @@ static void* tap_to_vmnet(void *arg) /* Handle a connection. Handshake with the com.docker.slirp process and start * exchanging ethernet frames between the socket and the tap device. */ -static void handle(SOCKET fd, const char *tap) +static void handle(SOCKET fd, char *tap, int tapfd) { struct connection connection; pthread_t v2t, t2v; @@ -231,7 +233,6 @@ static void handle(SOCKET fd, const char *tap) connection.vif.mac[3], connection.vif.mac[4], connection.vif.mac[5] ); - int tapfd = alloc_tap(tap); set_macaddr(tap, &connection.vif.mac[0]); connection.tapfd = tapfd; @@ -311,30 +312,21 @@ static int connect_socket(GUID serviceid) { return sock; } - -/* Server: - * accept() in an endless loop, handle a connection at a time - */ -static void accept_forever(SOCKET lsock, const char *tap) -{ +static int accept_socket(SOCKET lsock) { SOCKET csock = INVALID_SOCKET; SOCKADDR_HV sac; socklen_t socklen = sizeof(sac); - while(1) { - csock = accept(lsock, (struct sockaddr *)&sac, &socklen); - if (csock == INVALID_SOCKET) { - sockerr("accept()"); - closesocket(lsock); - exit(1); - } + csock = accept(lsock, (struct sockaddr *)&sac, &socklen); + if (csock == INVALID_SOCKET) { + sockerr("accept()"); + closesocket(lsock); + exit(1); + } - printf("Connect from: "GUID_FMT":"GUID_FMT"\n", - GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); - - handle(csock, tap); - closesocket(csock); - } + printf("Connect from: "GUID_FMT":"GUID_FMT"\n", + GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); + return csock; } void write_pidfile(const char *pidfile) { @@ -362,16 +354,44 @@ void write_pidfile(const char *pidfile) { free(pid_s); } +void daemonize(const char *pidfile){ + pid_t pid = fork (); + if (pid == -1) { + syslog(LOG_CRIT, "Failed to fork()"); + exit(1); + } + else if (pid != 0) + exit(0); + if (setsid () == -1) { + syslog(LOG_CRIT, "Failed to setsid()"); + exit(1); + } + if (chdir ("/") == -1) { + syslog(LOG_CRIT, "Failed to chdir()"); + exit(1); + } + int null = open("/dev/null", O_RDWR); + dup2(null, STDIN_FILENO); + dup2(null, STDOUT_FILENO); + dup2(null, STDERR_FILENO); + close(null); + if (pidfile) write_pidfile(pidfile); +} + void usage(char *name) { - printf("%s: [--debug] [--tap ] [--serviceid ] [--pid ]\n", name); + printf("%s usage:\n", name); + printf("\t[--daemon] [--tap ] [--serviceid ] [--pid ]\n"); printf("\t[--listen | --connect]\n\n"); - printf("--debug: log to stderr as well as syslog\n"); - printf("--tap : create a tap device with the given name (defaults to eth1)\n"); - printf("--serviceid : use as the well-known service GUID\n"); - printf("--pid : write a pid to the given file\n"); - printf("--listen: listen forever for incoming AF_HVSOCK connections\n"); - printf("--connect: connect to the parent partition\n"); + printf("where\n"); + printf("\t--daemonize: run as a background daemon\n"); + printf("\t--tap : create a tap device with the given name\n"); + printf("\t (defaults to eth1)\n"); + printf("\t--serviceid : use as the well-known service GUID\n"); + printf("\t (defaults to %s)\n", default_sid); + printf("\t--pid : write a pid to the given file\n"); + printf("\t--listen: listen forever for incoming AF_HVSOCK connections\n"); + printf("\t--connect: connect to the parent partition\n"); } int __cdecl main(int argc, char **argv) @@ -380,7 +400,7 @@ int __cdecl main(int argc, char **argv) GUID sid; int c; /* Defaults to a testing GUID */ - char *serviceid = "3049197C-9A4E-4FBF-9367-97F792F16994"; + char *serviceid = default_sid; char *tap = "eth1"; char *pidfile = NULL; @@ -388,7 +408,7 @@ int __cdecl main(int argc, char **argv) while (1) { static struct option long_options[] = { /* These options set a flag. */ - {"debug", no_argument, &debug_flag, 1}, + {"daemon", no_argument, &daemon_flag, 1}, {"serviceid", required_argument, NULL, 's'}, {"tap", required_argument, NULL, 't'}, {"pidfile", required_argument, NULL, 'p'}, @@ -403,7 +423,7 @@ int __cdecl main(int argc, char **argv) switch (c) { case 'd': - debug_flag = 1; + daemon_flag = 1; break; case 's': serviceid = optarg; @@ -425,8 +445,12 @@ int __cdecl main(int argc, char **argv) fprintf(stderr, "Please supply either the --listen or --connect flag, but not both.\n"); exit(1); } + if (daemon_flag && !pidfile){ + fprintf(stderr, "For daemon mode, please supply a --pidfile argument.\n"); + exit(1); + } int log_flags = LOG_CONS | LOG_NDELAY; - if (debug_flag) { + if (!daemon_flag) { log_flags |= LOG_PERROR; } openlog(argv[0], log_flags, LOG_DAEMON); @@ -438,14 +462,19 @@ int __cdecl main(int argc, char **argv) exit(1); } + SOCKET sock = INVALID_SOCKET; if (listen_flag) { syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap); - int socket = create_listening_socket(sid); - accept_forever(socket, tap); - exit(0); + SOCKET lsocket = create_listening_socket(sid); + sock = accept_socket(lsocket); + } else { + syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap); + sock = connect_socket(sid); } - syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap); - int socket = connect_socket(sid); - handle(socket, tap); - return 0; + /* Bring up the tap device before we daemonize */ + int tapfd = alloc_tap(tap); + if (daemon_flag) daemonize(pidfile); + + handle(sock, tap, tapfd); + exit(0); } From 1805c803a9262177e8ee33e25202246b79e74d35 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sun, 15 May 2016 19:51:26 +0100 Subject: [PATCH 13/16] tap-vsockd: remove the Windows compatibility shim Signed-off-by: David Scott --- alpine/packages/tap-vsockd/Makefile | 11 +- alpine/packages/tap-vsockd/compat.h | 118 ----------------- alpine/packages/tap-vsockd/hvsock.c | 38 ++++++ alpine/packages/tap-vsockd/hvsock.h | 49 +++++++ alpine/packages/tap-vsockd/tap-vsockd.c | 162 ++++++++---------------- 5 files changed, 144 insertions(+), 234 deletions(-) delete mode 100644 alpine/packages/tap-vsockd/compat.h create mode 100644 alpine/packages/tap-vsockd/hvsock.c create mode 100644 alpine/packages/tap-vsockd/hvsock.h diff --git a/alpine/packages/tap-vsockd/Makefile b/alpine/packages/tap-vsockd/Makefile index f2fe55ec2..77ebf467b 100644 --- a/alpine/packages/tap-vsockd/Makefile +++ b/alpine/packages/tap-vsockd/Makefile @@ -1,19 +1,22 @@ .PHONY: all -DEPS=tap-vsockd.c compat.h protocol.c protocol.h +DEPS=tap-vsockd.c hvsock.c hvsock.h protocol.c protocol.h all: Dockerfile $(DEPS) docker build -t tap-vsockd:build . docker run --rm tap-vsockd:build cat tap-vsockd > tap-vsockd chmod 755 tap-vsockd -tap-vsockd: protocol.o tap-vsockd.o - gcc -Wall -Werror -o tap-vsockd tap-vsockd.o protocol.o -lpthread +tap-vsockd: hvsock.o protocol.o tap-vsockd.o + gcc -Wall -Werror -o tap-vsockd tap-vsockd.o protocol.o hvsock.o -lpthread + +hvsock.o: hvsock.c hvsock.h + gcc -Wall -Werror -c hvsock.c protocol.o: protocol.c gcc -Wall -Werror -c protocol.c -tap-vsockd.o: tap-vsockd.c compat.h +tap-vsockd.o: tap-vsockd.c hvsock.h gcc -Wall -Werror -c tap-vsockd.c clean: diff --git a/alpine/packages/tap-vsockd/compat.h b/alpine/packages/tap-vsockd/compat.h deleted file mode 100644 index 02f9f7b84..000000000 --- a/alpine/packages/tap-vsockd/compat.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Compatibility layer between Windows and Linux - */ -#ifdef _MSC_VER -#undef UNICODE -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include - -#pragma comment (lib, "Ws2_32.lib") -#pragma comment (lib, "Mswsock.lib") -#pragma comment (lib, "AdvApi32.lib") - -#else -#include -#include -#include -#include -#endif /* !_MSC_VER */ - -#ifdef _MSC_VER -typedef int socklen_t; -#endif - -#ifndef _MSC_VER -/* Compat layer for Linux/Unix */ -typedef int SOCKET; - -#ifndef SOCKET_ERROR -#define SOCKET_ERROR -1 -#endif - -#ifndef INVALID_SOCKET -#define INVALID_SOCKET -1 -#endif - -#define closesocket(_fd) close(_fd) - -/* Shutdown flags are different too */ -#define SD_SEND SHUT_WR -#define SD_RECEIVE SHUT_RD -#define SD_BOTH SHUT_RDWR - -#define __cdecl - -/* GUID handling */ -typedef struct _GUID { - uint32_t Data1; - uint16_t Data2; - uint16_t Data3; - uint8_t Data4[8]; -} GUID; - -#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} - - -/* HV Socket definitions */ -#define AF_HYPERV 43 -#define HV_PROTOCOL_RAW 1 - -typedef struct _SOCKADDR_HV -{ - unsigned short Family; - unsigned short Reserved; - GUID VmId; - GUID ServiceId; -} SOCKADDR_HV; - -DEFINE_GUID(HV_GUID_ZERO, - 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); -DEFINE_GUID(HV_GUID_BROADCAST, - 0xFFFFFFFF, 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); -DEFINE_GUID(HV_GUID_WILDCARD, - 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); - -DEFINE_GUID(HV_GUID_CHILDREN, - 0x90db8b89, 0x0d35, 0x4f79, 0x8c, 0xe9, 0x49, 0xea, 0x0a, 0xc8, 0xb7, 0xcd); -DEFINE_GUID(HV_GUID_LOOPBACK, - 0xe0e16197, 0xdd56, 0x4a10, 0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38); -DEFINE_GUID(HV_GUID_PARENT, - 0xa42e7cda, 0xd03f, 0x480c, 0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78); - -#endif - -/* Thread wrappers */ -#ifdef _MSC_VER -typedef HANDLE THREAD_HANDLE; - -static inline int thread_create(THREAD_HANDLE *t, void *(*f)(void *), void *arg) -{ - *t = CreateThread(NULL, 0, f, arg, 0, NULL); - return 0; -} - -static inline int thread_join(THREAD_HANDLE t) -{ - WaitForSingleObject(t, INFINITE); - return 0; -} - -#else -#include - -typedef pthread_t THREAD_HANDLE; - -static inline int thread_create(THREAD_HANDLE *t, void *(*f)(void *), void *arg) -{ - return pthread_create(t, NULL, f, arg); -} - -static inline int thread_join(THREAD_HANDLE t) -{ - return pthread_join(t, NULL); -} -#endif diff --git a/alpine/packages/tap-vsockd/hvsock.c b/alpine/packages/tap-vsockd/hvsock.c new file mode 100644 index 000000000..47c924d63 --- /dev/null +++ b/alpine/packages/tap-vsockd/hvsock.c @@ -0,0 +1,38 @@ +#include + +#include "hvsock.h" + +int parseguid(const char *s, GUID *g) +{ + int res; + int p0, p1, p2, p3, p4, p5, p6, p7; + + res = sscanf(s, GUID_FMT, + &g->Data1, &g->Data2, &g->Data3, + &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7); + if (res != 11) + return 1; + g->Data4[0] = p0; + g->Data4[1] = p1; + g->Data4[2] = p2; + g->Data4[3] = p3; + g->Data4[4] = p4; + g->Data4[5] = p5; + g->Data4[6] = p6; + g->Data4[7] = p7; + return 0; +} + +DEFINE_GUID(HV_GUID_ZERO, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); +DEFINE_GUID(HV_GUID_BROADCAST, + 0xFFFFFFFF, 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); +DEFINE_GUID(HV_GUID_WILDCARD, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +DEFINE_GUID(HV_GUID_CHILDREN, + 0x90db8b89, 0x0d35, 0x4f79, 0x8c, 0xe9, 0x49, 0xea, 0x0a, 0xc8, 0xb7, 0xcd); +DEFINE_GUID(HV_GUID_LOOPBACK, + 0xe0e16197, 0xdd56, 0x4a10, 0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38); +DEFINE_GUID(HV_GUID_PARENT, + 0xa42e7cda, 0xd03f, 0x480c, 0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78); diff --git a/alpine/packages/tap-vsockd/hvsock.h b/alpine/packages/tap-vsockd/hvsock.h new file mode 100644 index 000000000..6e1ca6695 --- /dev/null +++ b/alpine/packages/tap-vsockd/hvsock.h @@ -0,0 +1,49 @@ +/* AF_HYPERV definitions and utilities */ + +#include +#include +#include +#include + +/* GUID handling */ +typedef struct _GUID { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} GUID; + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} + +/* Helper macros for parsing/printing GUIDs */ +#define GUID_FMT "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x" +#define GUID_ARGS(_g) \ + (_g).Data1, (_g).Data2, (_g).Data3, \ + (_g).Data4[0], (_g).Data4[1], (_g).Data4[2], (_g).Data4[3], \ + (_g).Data4[4], (_g).Data4[5], (_g).Data4[6], (_g).Data4[7] +#define GUID_SARGS(_g) \ + &(_g).Data1, &(_g).Data2, &(_g).Data3, \ + &(_g).Data4[0], &(_g).Data4[1], &(_g).Data4[2], &(_g).Data4[3], \ + &(_g).Data4[4], &(_g).Data4[5], &(_g).Data4[6], &(_g).Data4[7] + +extern int parseguid(const char *s, GUID *g); + +/* HV Socket definitions */ +#define AF_HYPERV 43 +#define HV_PROTOCOL_RAW 1 + +typedef struct _SOCKADDR_HV +{ + unsigned short Family; + unsigned short Reserved; + GUID VmId; + GUID ServiceId; +} SOCKADDR_HV; + +extern const GUID HV_GUID_ZERO; +extern const GUID HV_GUID_BROADCAST; +extern const GUID HV_GUID_WILDCARD; +extern const GUID HV_GUID_CHILDREN; +extern const GUID HV_GUID_LOOPBACK; +extern const GUID HV_GUID_PARENT; diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index ae1dd9792..c24edbac9 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,7 @@ #include -#include "compat.h" +#include "hvsock.h" #include "protocol.h" int daemon_flag = 0; @@ -33,25 +34,28 @@ int connect_flag = 0; char *default_sid = "30D48B34-7D27-4B0B-AAAF-BBBED334DD59"; +void fatal(const char *msg) +{ + syslog(LOG_CRIT, "%s Error: %d. %s", msg, errno, strerror(errno)); + exit(1); +} + int alloc_tap(const char *dev) { int fd; struct ifreq ifr; const char *clonedev = "/dev/net/tun"; if ((fd = open(clonedev, O_RDWR)) == -1) { - perror("Failed to open /dev/net/tun"); - exit(1); + fatal("Failed to open /dev/net/tun"); } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy(ifr.ifr_name, dev, IFNAMSIZ); if (ioctl(fd, TUNSETIFF, (void*) &ifr) < 0) { - perror("TUNSETIFF failed"); - exit(1); + fatal("TUNSETIFF failed"); } int persist = 1; if (ioctl(fd, TUNSETPERSIST, persist) < 0) { - perror("TUNSETPERSIST failed"); - exit(1); + fatal("TUNSETPERSIST failed"); } syslog(LOG_INFO, "successfully created TAP device %s", dev); return fd; @@ -67,57 +71,14 @@ void set_macaddr(const char *dev, uint8_t *mac) { ifq.ifr_hwaddr.sa_family = ARPHRD_ETHER; if (ioctl(fd, SIOCSIFHWADDR, &ifq) == -1) { - perror("SIOCSIFHWADDR failed"); - exit(1); + fatal("SIOCSIFHWADDR failed"); } close(fd); } -#define SVR_BUF_LEN (3 * 4096) -#define MAX_BUF_LEN (2 * 1024 * 1024) - -/* Helper macros for parsing/printing GUIDs */ -#define GUID_FMT "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x" -#define GUID_ARGS(_g) \ - (_g).Data1, (_g).Data2, (_g).Data3, \ - (_g).Data4[0], (_g).Data4[1], (_g).Data4[2], (_g).Data4[3], \ - (_g).Data4[4], (_g).Data4[5], (_g).Data4[6], (_g).Data4[7] -#define GUID_SARGS(_g) \ - &(_g).Data1, &(_g).Data2, &(_g).Data3, \ - &(_g).Data4[0], &(_g).Data4[1], &(_g).Data4[2], &(_g).Data4[3], \ - &(_g).Data4[4], &(_g).Data4[5], &(_g).Data4[6], &(_g).Data4[7] - - -int parseguid(const char *s, GUID *g) -{ - int res; - int p0, p1, p2, p3, p4, p5, p6, p7; - - res = sscanf(s, GUID_FMT, - &g->Data1, &g->Data2, &g->Data3, - &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7); - if (res != 11) - return 1; - g->Data4[0] = p0; - g->Data4[1] = p1; - g->Data4[2] = p2; - g->Data4[3] = p3; - g->Data4[4] = p4; - g->Data4[5] = p5; - g->Data4[6] = p6; - g->Data4[7] = p7; - return 0; -} - -/* Slightly different error handling between Windows and Linux */ -void sockerr(const char *msg) -{ - syslog(LOG_CRIT, "%s Error: %d. %s", msg, errno, strerror(errno)); -} - -void negotiate(SOCKET fd, struct vif_info *vif) +void negotiate(int fd, struct vif_info *vif) { /* Negotiate with com.docker.slirp */ struct init_message *me = create_init_message(); @@ -146,14 +107,13 @@ void negotiate(SOCKET fd, struct vif_info *vif) } return; err: - syslog(LOG_CRIT, "Failed to negotiate with com.docker.slirp"); - exit(1); + fatal("Failed to negotiate with com.docker.slirp"); } /* Argument passed to proxy threads */ struct connection { - SOCKET fd; /* Hyper-V socket with vmnet protocol */ + int fd; /* Hyper-V socket with vmnet protocol */ int tapfd; /* TAP device with ethernet frames */ struct vif_info vif; /* Contains VIF MAC, MTU etc, received from server */ }; @@ -167,8 +127,7 @@ static void* vmnet_to_tap(void *arg) for (;;) { if (really_read(connection->fd, &header[0], 2) == -1){ - syslog(LOG_CRIT, "Failed to read a packet header from host"); - exit(1); + fatal("Failed to read a packet header from host"); } length = (header[0] & 0xff) | ((header[1] & 0xff) << 8); if (length > sizeof(buffer)) { @@ -198,8 +157,7 @@ static void* tap_to_vmnet(void *arg) length = read(connection->tapfd, &buffer[0], sizeof(buffer)); if (length == -1) { if (errno == ENXIO) { - syslog(LOG_CRIT, "tap device has gone down"); - exit(0); + fatal("tap device has gone down"); } syslog(LOG_WARNING, "ignoring error %d", errno); /* This is what mirage-net-unix does. Is it a good idea really? */ @@ -208,20 +166,19 @@ static void* tap_to_vmnet(void *arg) header[0] = (length >> 0) & 0xff; header[1] = (length >> 8) & 0xff; if (really_write(connection->fd, &header[0], 2) == -1){ - syslog(LOG_CRIT, "Failed to write packet header"); - exit(1); + fatal("Failed to write packet header"); } if (really_write(connection->fd, &buffer[0], length) == -1) { - syslog(LOG_CRIT, "Failed to write packet body"); - exit(1); + fatal("Failed to write packet body"); } } + return NULL; } /* Handle a connection. Handshake with the com.docker.slirp process and start * exchanging ethernet frames between the socket and the tap device. */ -static void handle(SOCKET fd, char *tap, int tapfd) +static void handle(int fd, char *tap, int tapfd) { struct connection connection; pthread_t v2t, t2v; @@ -237,32 +194,27 @@ static void handle(SOCKET fd, char *tap, int tapfd) connection.tapfd = tapfd; if (pthread_create(&v2t, NULL, vmnet_to_tap, &connection) != 0){ - syslog(LOG_CRIT, "Failed to create the vmnet_to_tap thread"); - exit(1); + fatal("Failed to create the vmnet_to_tap thread"); } if (pthread_create(&t2v, NULL, tap_to_vmnet, &connection) != 0){ - syslog(LOG_CRIT, "Failed to create the tap_to_vmnet thread"); - exit(1); + fatal("Failed to create the tap_to_vmnet thread"); } if (pthread_join(v2t, NULL) != 0){ - syslog(LOG_CRIT, "Failed to join the vmnet_to_tap thread"); - exit(1); + fatal("Failed to join the vmnet_to_tap thread"); } if (pthread_join(t2v, NULL) != 0){ - syslog(LOG_CRIT, "Failed to join the tap_to_vmnet thread"); - exit(1); + fatal("Failed to join the tap_to_vmnet thread"); } } static int create_listening_socket(GUID serviceid) { - SOCKET lsock = INVALID_SOCKET; + int lsock = -1; SOCKADDR_HV sa; int res; lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); - if (lsock == INVALID_SOCKET) { - sockerr("socket()"); - exit(1); + if (lsock == -1) { + fatal("socket()"); } sa.Family = AF_HYPERV; @@ -271,30 +223,25 @@ static int create_listening_socket(GUID serviceid) { sa.ServiceId = serviceid; res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa)); - if (res == SOCKET_ERROR) { - sockerr("bind()"); - closesocket(lsock); - exit(1); + if (res == -1) { + fatal("bind()"); } res = listen(lsock, SOMAXCONN); - if (res == SOCKET_ERROR) { - sockerr("listen()"); - closesocket(lsock); - exit(1); + if (res == -1) { + fatal("listen()"); } return lsock; } static int connect_socket(GUID serviceid) { - SOCKET sock = INVALID_SOCKET; + int sock = -1; SOCKADDR_HV sa; int res; sock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); - if (sock == INVALID_SOCKET) { - sockerr("socket()"); - exit(1); + if (sock == -1) { + fatal("socket()"); } sa.Family = AF_HYPERV; @@ -303,28 +250,24 @@ static int connect_socket(GUID serviceid) { sa.ServiceId = serviceid; res = connect(sock, (const struct sockaddr *)&sa, sizeof(sa)); - if (res == SOCKET_ERROR) { - sockerr("connect()"); - closesocket(sock); - exit(1); + if (res == -1) { + fatal("connect()"); } return sock; } -static int accept_socket(SOCKET lsock) { - SOCKET csock = INVALID_SOCKET; +static int accept_socket(int lsock) { + int csock = -1; SOCKADDR_HV sac; socklen_t socklen = sizeof(sac); csock = accept(lsock, (struct sockaddr *)&sac, &socklen); - if (csock == INVALID_SOCKET) { - sockerr("accept()"); - closesocket(lsock); - exit(1); + if (csock == -1) { + fatal("accept()"); } - printf("Connect from: "GUID_FMT":"GUID_FMT"\n", + syslog(LOG_INFO, "Connect from: "GUID_FMT":"GUID_FMT"\n", GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); return csock; } @@ -336,8 +279,7 @@ void write_pidfile(const char *pidfile) { int len; if (asprintf(&pid_s, "%lld", (long long) pid) == -1) { - syslog(LOG_CRIT, "Failed to allocate pidfile string"); - exit(1); + fatal("Failed to allocate pidfile string"); } len = strlen(pid_s); file = fopen(pidfile, "w"); @@ -347,8 +289,7 @@ void write_pidfile(const char *pidfile) { } if (fwrite(pid_s, 1, len, file) != len) { - syslog(LOG_CRIT, "Failed to write pid to pidfile"); - exit(1); + fatal("Failed to write pid to pidfile"); } fclose(file); free(pid_s); @@ -357,18 +298,15 @@ void write_pidfile(const char *pidfile) { void daemonize(const char *pidfile){ pid_t pid = fork (); if (pid == -1) { - syslog(LOG_CRIT, "Failed to fork()"); - exit(1); + fatal("Failed to fork()"); } else if (pid != 0) exit(0); if (setsid () == -1) { - syslog(LOG_CRIT, "Failed to setsid()"); - exit(1); + fatal("Failed to setsid()"); } if (chdir ("/") == -1) { - syslog(LOG_CRIT, "Failed to chdir()"); - exit(1); + fatal("Failed to chdir()"); } int null = open("/dev/null", O_RDWR); dup2(null, STDIN_FILENO); @@ -394,7 +332,7 @@ void usage(char *name) printf("\t--connect: connect to the parent partition\n"); } -int __cdecl main(int argc, char **argv) +int main(int argc, char **argv) { int res = 0; GUID sid; @@ -462,10 +400,10 @@ int __cdecl main(int argc, char **argv) exit(1); } - SOCKET sock = INVALID_SOCKET; + int sock = -1; if (listen_flag) { syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap); - SOCKET lsocket = create_listening_socket(sid); + int lsocket = create_listening_socket(sid); sock = accept_socket(lsocket); } else { syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap); From 1e96379e3f908b3ee29f80faf3efb0129ef05d1d Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 21 May 2016 14:10:16 +0100 Subject: [PATCH 14/16] tap-vsockd: use stderr rather than syslog if service guid is wrong Our convention is to print command-line parse failures to the terminal to allow easier interactive debugging, rather than sending them to the syslog. Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index c24edbac9..1a6f30704 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -387,19 +387,19 @@ int main(int argc, char **argv) fprintf(stderr, "For daemon mode, please supply a --pidfile argument.\n"); exit(1); } + res = parseguid(serviceid, &sid); + if (res) { + fprintf(stderr, "Failed to parse serviceid as GUID: %s\n", serviceid); + usage(argv[0]); + exit(1); + } + int log_flags = LOG_CONS | LOG_NDELAY; if (!daemon_flag) { log_flags |= LOG_PERROR; } openlog(argv[0], log_flags, LOG_DAEMON); - res = parseguid(serviceid, &sid); - if (res) { - syslog(LOG_CRIT, "Failed to parse serviceid as GUID: %s", serviceid); - usage(argv[0]); - exit(1); - } - int sock = -1; if (listen_flag) { syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap); From 4fa937d11de7b330cac20a37684dfdc3a113c156 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 21 May 2016 14:42:21 +0100 Subject: [PATCH 15/16] tap-vsockd: if the client closes, be prepared to reconnect In `connect` mode the client looping calling `connect` and `close` on a timer. This could result in the server accepting the connection and the client accidentally closing it immediately. This patch makes `tap-vsockd` keep trying to (re-)establish the connection. Note that daemonization is delayed until after the first successful negotiation, so attempts to send a packet afterwards should succeed. Signed-off-by: David Scott --- alpine/packages/tap-vsockd/tap-vsockd.c | 80 +++++++++++++++---------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/alpine/packages/tap-vsockd/tap-vsockd.c b/alpine/packages/tap-vsockd/tap-vsockd.c index 1a6f30704..d1f7db502 100644 --- a/alpine/packages/tap-vsockd/tap-vsockd.c +++ b/alpine/packages/tap-vsockd/tap-vsockd.c @@ -77,8 +77,8 @@ void set_macaddr(const char *dev, uint8_t *mac) { close(fd); } - -void negotiate(int fd, struct vif_info *vif) +/* Negotiate a vmnet connection, returns 0 on success and 1 on error. */ +int negotiate(int fd, struct vif_info *vif) { /* Negotiate with com.docker.slirp */ struct init_message *me = create_init_message(); @@ -105,9 +105,10 @@ void negotiate(int fd, struct vif_info *vif) if (read_vif_info(fd, vif) == -1) { goto err; } - return; + return 0; err: - fatal("Failed to negotiate with com.docker.slirp"); + syslog(LOG_CRIT, "Failed to negotiate vmnet connection"); + return 1; } @@ -175,28 +176,16 @@ static void* tap_to_vmnet(void *arg) return NULL; } -/* Handle a connection. Handshake with the com.docker.slirp process and start - * exchanging ethernet frames between the socket and the tap device. +/* Handle a connection by exchanging ethernet frames forever. */ -static void handle(int fd, char *tap, int tapfd) +static void handle(struct connection *connection) { - struct connection connection; pthread_t v2t, t2v; - connection.fd = fd; - negotiate(fd, &connection.vif); - syslog(LOG_INFO, "VMNET VIF has MAC %02x:%02x:%02x:%02x:%02x:%02x", - connection.vif.mac[0], connection.vif.mac[1], connection.vif.mac[2], - connection.vif.mac[3], connection.vif.mac[4], connection.vif.mac[5] - ); - - set_macaddr(tap, &connection.vif.mac[0]); - connection.tapfd = tapfd; - - if (pthread_create(&v2t, NULL, vmnet_to_tap, &connection) != 0){ + if (pthread_create(&v2t, NULL, vmnet_to_tap, connection) != 0){ fatal("Failed to create the vmnet_to_tap thread"); } - if (pthread_create(&t2v, NULL, tap_to_vmnet, &connection) != 0){ + if (pthread_create(&t2v, NULL, tap_to_vmnet, connection) != 0){ fatal("Failed to create the tap_to_vmnet thread"); } if (pthread_join(v2t, NULL) != 0){ @@ -400,19 +389,44 @@ int main(int argc, char **argv) } openlog(argv[0], log_flags, LOG_DAEMON); - int sock = -1; - if (listen_flag) { - syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap); - int lsocket = create_listening_socket(sid); - sock = accept_socket(lsocket); - } else { - syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap); - sock = connect_socket(sid); - } - /* Bring up the tap device before we daemonize */ int tapfd = alloc_tap(tap); - if (daemon_flag) daemonize(pidfile); - handle(sock, tap, tapfd); - exit(0); + struct connection connection; + connection.tapfd = tapfd; + + int sock = -1; + + for (;;) { + if (sock != -1) { + close(sock); + sock = -1; + } + if (listen_flag) { + syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap); + int lsocket = create_listening_socket(sid); + sock = accept_socket(lsocket); + close(lsocket); + } else { + syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap); + sock = connect_socket(sid); + } + + connection.fd = sock; + if (negotiate(sock, &connection.vif) != 0) { + sleep(1); + continue; + } + syslog(LOG_INFO, "VMNET VIF has MAC %02x:%02x:%02x:%02x:%02x:%02x", + connection.vif.mac[0], connection.vif.mac[1], connection.vif.mac[2], + connection.vif.mac[3], connection.vif.mac[4], connection.vif.mac[5] + ); + set_macaddr(tap, &connection.vif.mac[0]); + + /* Daemonize after we've made our first reliable connection */ + if (daemon_flag) { + daemon_flag = 0; + daemonize(pidfile); + } + handle(&connection); + } } From d7863c8882d86873c660e2b8fe2c8481eefb65a6 Mon Sep 17 00:00:00 2001 From: David Scott Date: Sat, 14 May 2016 21:06:55 +0100 Subject: [PATCH 16/16] tap-vsockd: add to /sbin Install the daemon in /sbin, but don't start it up yet. Signed-off-by: David Scott --- alpine/Dockerfile | 1 + alpine/packages/diagnostics/diagnostics | 2 ++ 2 files changed, 3 insertions(+) diff --git a/alpine/Dockerfile b/alpine/Dockerfile index ed52e428a..7517f3bb9 100644 --- a/alpine/Dockerfile +++ b/alpine/Dockerfile @@ -40,6 +40,7 @@ COPY packages/proxy/proxy /sbin/ COPY packages/proxy/etc /etc/ COPY packages/transfused/transfused /sbin/ COPY packages/transfused/etc /etc/ +COPY packages/tap-vsockd/tap-vsockd /sbin/ COPY packages/llmnrd/llmnrd /sbin/ COPY packages/llmnrd/etc /etc/ COPY packages/llmnrd/llmnrd.tar.gz /usr/share/src/ diff --git a/alpine/packages/diagnostics/diagnostics b/alpine/packages/diagnostics/diagnostics index 850ef4a59..250daef8c 100755 --- a/alpine/packages/diagnostics/diagnostics +++ b/alpine/packages/diagnostics/diagnostics @@ -9,6 +9,8 @@ INET=$(ifconfig eth0 2> /dev/null | grep 'inet addr') [ $? -eq 0 ] && printf "✓ Network connected: $INET\n" || printf "✗ No network connection\n" FUSE=$(ps -eo args | grep '^/sbin/transfused') [ $? -eq 0 ] && printf "✓ Process transfused running\n" || printf "✗ No transfused process\n" +FUSE=$(ps -eo args | grep '^/sbin/tap-vsockd') +[ $? -eq 0 ] && printf "✓ Process tap-vsockd running\n" || printf "✗ No tap-vsockd process\n" HUPPER=$(ps -eo args | grep '^/bin/hupper') [ $? -eq 0 ] && printf "✓ Process hupper running: $HUPPER\n" || printf "✗ No hupper process\n" DOCKER=$(ps -eo args | grep '^/usr/bin/docker')