diff --git a/alpine/packages/nc-vsock/Dockerfile b/alpine/packages/nc-vsock/Dockerfile index 9a86af443..e03507c7d 100644 --- a/alpine/packages/nc-vsock/Dockerfile +++ b/alpine/packages/nc-vsock/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.3 -RUN apk update && apk upgrade && apk add alpine-sdk +RUN apk update && apk upgrade && apk add alpine-sdk util-linux-dev RUN mkdir -p /nc-vsock WORKDIR /nc-vsock diff --git a/alpine/packages/nc-vsock/Makefile b/alpine/packages/nc-vsock/Makefile index f58fab3ec..84dcdc812 100644 --- a/alpine/packages/nc-vsock/Makefile +++ b/alpine/packages/nc-vsock/Makefile @@ -8,7 +8,7 @@ all: Dockerfile $(DEPS) chmod 755 nc-vsock nc-vsock: $(DEPS) - gcc -Wall -Werror -o nc-vsock nc-vsock.c + gcc -Wall -Werror -o nc-vsock nc-vsock.c -luuid clean: rm -f nc-vsock diff --git a/alpine/packages/nc-vsock/nc-vsock.c b/alpine/packages/nc-vsock/nc-vsock.c index e2d961312..14c449f33 100644 --- a/alpine/packages/nc-vsock/nc-vsock.c +++ b/alpine/packages/nc-vsock/nc-vsock.c @@ -9,16 +9,56 @@ #include #include #include +#include #include "include/uapi/linux/vm_sockets.h" #define MODE_READ 1 /* From the vsock */ #define MODE_WRITE 2 /* To the vsock */ #define MODE_RDWR (MODE_READ|MODE_WRITE) +/* + * Hyper-V Sockets headerfile pull in too much other stuff. Replicate + * the bits we need here. + */ +#ifndef AF_HYPERV +#define AF_HYPERV 42 +#endif + +struct sockaddr_hv { + unsigned short shv_family; /* Address family */ + unsigned short reserved; /* Must be Zero */ + uuid_t shv_vm_id; /* Not used. Must be Zero. */ + uuid_t shv_service_id; /* Service ID */ +}; +UUID_DEFINE(SHV_VMID_GUEST,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); +#define SHV_PROTO_RAW 1 + +/* + * MSFT's GUIDs are a bonkers mix of native and big endian byte + * order. The uuid library uses RFC 4122, which is always big endian. + * The Linux kernel uuid.h actually looks more like it should be + * called guid.h. We use the uuid library for ease of parsing/printing + * and then this function to convert between UUID and GUID. + * https://en.wikipedia.org/wiki/Globally_unique_identifier + */ +static void uuid2guid(uuid_t u) +{ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + char t; + t = u[0]; u[0] = u[3]; u[3] = t; + t = u[1]; u[1] = u[2]; u[2] = t; + + t = u[4]; u[4] = u[5]; u[5] = t; + + t = u[6]; u[6] = u[7]; u[7] = t; +#endif +} + static int parse_cid(const char *cid_str) { char *end = NULL; long cid = strtol(cid_str, &end, 10); + if (cid_str != end && *end == '\0') { return cid; } else { @@ -31,6 +71,7 @@ static int parse_port(const char *port_str) { char *end = NULL; long port = strtol(port_str, &end, 10); + if (port_str != end && *end == '\0') { return port; } else { @@ -49,10 +90,12 @@ static int vsock_listen(const char *port_str) }; struct sockaddr_vm sa_client; socklen_t socklen_client = sizeof(sa_client); - int port = parse_port(port_str); - if (port < 0) { + int port; + int ret; + + port = parse_port(port_str); + if (port < 0) return -1; - } sa_listen.svm_port = port; @@ -62,26 +105,87 @@ static int vsock_listen(const char *port_str) return -1; } - if (bind(listen_fd, (struct sockaddr*)&sa_listen, sizeof(sa_listen)) != 0) { + ret = bind(listen_fd, (struct sockaddr*)&sa_listen, sizeof(sa_listen)); + if (ret != 0) { perror("bind"); close(listen_fd); return -1; } - if (listen(listen_fd, 1) != 0) { + ret = listen(listen_fd, 1); + if (ret != 0) { perror("listen"); close(listen_fd); return -1; } - client_fd = accept(listen_fd, (struct sockaddr*)&sa_client, &socklen_client); + client_fd = accept(listen_fd, + (struct sockaddr*)&sa_client, &socklen_client); if (client_fd < 0) { perror("accept"); close(listen_fd); return -1; } - fprintf(stderr, "Connection from cid %u port %u...\n", sa_client.svm_cid, sa_client.svm_port); + fprintf(stderr, "Connection from cid %u port %u...\n", + sa_client.svm_cid, sa_client.svm_port); + + close(listen_fd); + return client_fd; +} + +static int hvsock_listen(const char *port_str) +{ + int listen_fd; + int client_fd; + struct sockaddr_hv sa_listen = { + .shv_family = AF_HYPERV, + .reserved = 0, + }; + struct sockaddr_hv sa_client; + socklen_t socklen_client = sizeof(sa_client); + char vm_str[128], svc_str[128]; + int ret; + + uuid_copy(sa_listen.shv_vm_id, SHV_VMID_GUEST); + + ret = uuid_parse(port_str, sa_listen.shv_service_id); + if (ret != 0) + return -1; + + uuid2guid(sa_listen.shv_service_id); + + listen_fd = socket(AF_HYPERV, SOCK_STREAM, SHV_PROTO_RAW); + if (listen_fd < 0) { + perror("socket"); + return -1; + } + + ret = bind(listen_fd, (struct sockaddr*)&sa_listen, sizeof(sa_listen)); + if (ret != 0) { + perror("bind"); + close(listen_fd); + return -1; + } + + ret = listen(listen_fd, 1); + if (ret != 0) { + perror("listen"); + close(listen_fd); + return -1; + } + + client_fd = accept(listen_fd, + (struct sockaddr*)&sa_client, &socklen_client); + if (client_fd < 0) { + perror("accept"); + close(listen_fd); + return -1; + } + + uuid_unparse(sa_client.shv_vm_id, vm_str); + uuid_unparse(sa_client.shv_service_id, svc_str); + fprintf(stderr, "Connection from %s port %s...\n", vm_str, svc_str); close(listen_fd); return client_fd; @@ -105,13 +209,15 @@ static int tcp_connect(const char *node, const char *service) } for (addrinfo = res; addrinfo; addrinfo = addrinfo->ai_next) { - fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); + fd = socket(addrinfo->ai_family, + addrinfo->ai_socktype, addrinfo->ai_protocol); if (fd < 0) { perror("socket"); continue; } - if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) { + ret = connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen); + if (ret != 0) { perror("connect"); close(fd); continue; @@ -132,17 +238,18 @@ static int vsock_connect(const char *cid_str, const char *port_str) struct sockaddr_vm sa = { .svm_family = AF_VSOCK, }; + int ret; cid = parse_cid(cid_str); - if (cid < 0) { + if (cid < 0) return -1; - } + sa.svm_cid = cid; port = parse_port(port_str); - if (port < 0) { + if (port < 0) return -1; - } + sa.svm_port = port; fd = socket(AF_VSOCK, SOCK_STREAM, 0); @@ -151,7 +258,47 @@ static int vsock_connect(const char *cid_str, const char *port_str) return -1; } - if (connect(fd, (struct sockaddr*)&sa, sizeof(sa)) != 0) { + ret = connect(fd, (struct sockaddr*)&sa, sizeof(sa)); + if (ret != 0) { + perror("connect"); + close(fd); + return -1; + } + + return fd; +} + +static int hvsock_connect(const char *vm_str, const char *svc_str) +{ + int fd; + int ret; + struct sockaddr_hv sa = { + .shv_family = AF_HYPERV, + .reserved = 0, + }; + + ret = uuid_parse(vm_str, sa.shv_vm_id); + if (ret != 0) { + fprintf(stderr, "VM GUID parse error: %s\n", vm_str); + return -1; + } + uuid2guid(sa.shv_vm_id); + + ret = uuid_parse(svc_str, sa.shv_service_id); + if (ret != 0) { + fprintf(stderr, "Service GUID parse error: %s\n", svc_str); + return -1; + } + uuid2guid(sa.shv_service_id); + + fd = socket(AF_HYPERV, SOCK_STREAM, SHV_PROTO_RAW); + if (fd < 0) { + perror("socket"); + return -1; + } + + ret = connect(fd, (struct sockaddr*)&sa, sizeof(sa)); + if (ret != 0) { perror("connect"); close(fd); return -1; @@ -166,10 +313,12 @@ static int get_fds(int argc, char **argv, int fds[2]) fds[1] = -1; if (argc >= 3 && strcmp(argv[1], "-l") == 0) { - fds[1] = vsock_listen(argv[2]); - if (fds[1] < 0) { + if (strstr(argv[2], "-")) + fds[1] = hvsock_listen(argv[2]); + else + fds[1] = vsock_listen(argv[2]); + if (fds[1] < 0) return -1; - } if (argc == 6 && strcmp(argv[3], "-t") == 0) { fds[0] = tcp_connect(argv[4], argv[5]); @@ -179,10 +328,12 @@ static int get_fds(int argc, char **argv, int fds[2]) } return 0; } else if (argc == 3) { - fds[1] = vsock_connect(argv[1], argv[2]); - if (fds[1] < 0) { + if (strstr(argv[1], "-") || strstr(argv[2], "-")) + fds[1] = hvsock_connect(argv[1], argv[2]); + else + fds[1] = vsock_connect(argv[1], argv[2]); + if (fds[1] < 0) return -1; - } return 0; } else { fprintf(stderr, "usage: %s [-r|-w] [-l [-t ] | ]\n", argv[0]); @@ -202,9 +353,8 @@ static void set_nonblock(int fd, bool enable) } flags = ret & ~O_NONBLOCK; - if (enable) { + if (enable) flags |= O_NONBLOCK; - } fcntl(fd, F_SETFL, flags); } @@ -215,18 +365,20 @@ static int xfer_data(int in_fd, int out_fd) char *send_ptr = buf; ssize_t nbytes; ssize_t remaining; + int ret; if (out_fd == STDIN_FILENO) out_fd = STDOUT_FILENO; nbytes = read(in_fd, buf, sizeof(buf)); - if (nbytes < 0) { + if (nbytes < 0) return -1; - } + if (nbytes == 0) { - int rc; - if (out_fd == STDOUT_FILENO) return 0; - rc = shutdown(out_fd, SHUT_WR); - if (rc == 0) return 0; + if (out_fd == STDOUT_FILENO) + return 0; + ret = shutdown(out_fd, SHUT_WR); + if (ret == 0) + return 0; perror("shutdown"); return -1; } @@ -234,11 +386,10 @@ static int xfer_data(int in_fd, int out_fd) remaining = nbytes; while (remaining > 0) { nbytes = write(out_fd, send_ptr, remaining); - if (nbytes < 0 && errno == EAGAIN) { + if (nbytes < 0 && errno == EAGAIN) nbytes = 0; - } else if (nbytes <= 0) { + else if (nbytes <= 0) return -1; - } if (remaining > nbytes) { /* Wait for fd to become writeable again */ @@ -246,7 +397,9 @@ static int xfer_data(int in_fd, int out_fd) fd_set wfds; FD_ZERO(&wfds); FD_SET(out_fd, &wfds); - if (select(out_fd + 1, NULL, &wfds, NULL, NULL) < 0) { + ret = select(out_fd + 1, NULL, + &wfds, NULL, NULL); + if (ret < 0) { if (errno == EINTR) { continue; } else { @@ -255,9 +408,8 @@ static int xfer_data(int in_fd, int out_fd) } } - if (FD_ISSET(out_fd, &wfds)) { + if (FD_ISSET(out_fd, &wfds)) break; - } } } @@ -273,6 +425,7 @@ static void main_loop(int fds[2], int mode) int nfds = fds[fds[0] > fds[1] ? 0 : 1] + 1; /* Which fd's are readable */ bool rfd0 = !!(mode&MODE_WRITE), rfd1 = !!(mode&MODE_READ); + int ret; set_nonblock(fds[0], true); set_nonblock(fds[1], true); @@ -282,10 +435,13 @@ static void main_loop(int fds[2], int mode) return; FD_ZERO(&rfds); - if (rfd0) FD_SET(fds[0], &rfds); - if (rfd1) FD_SET(fds[1], &rfds); + if (rfd0) + FD_SET(fds[0], &rfds); + if (rfd1) + FD_SET(fds[1], &rfds); - if (select(nfds, &rfds, NULL, NULL, NULL) < 0) { + ret = select(nfds, &rfds, NULL, NULL, NULL); + if (ret < 0) { if (errno == EINTR) { continue; } else { @@ -327,9 +483,8 @@ int main(int argc, char **argv) } } - if (get_fds(argc, argv, fds) < 0) { + if (get_fds(argc, argv, fds) < 0) return EXIT_FAILURE; - } main_loop(fds, mode); return EXIT_SUCCESS; diff --git a/alpine/packages/vsudd/9pudc b/alpine/packages/vsudd/9pudc deleted file mode 100755 index 8b8c67921..000000000 Binary files a/alpine/packages/vsudd/9pudc and /dev/null differ diff --git a/alpine/packages/vsudd/etc/init.d/vsudd b/alpine/packages/vsudd/etc/init.d/vsudd index ee787ab69..686fc030c 100755 --- a/alpine/packages/vsudd/etc/init.d/vsudd +++ b/alpine/packages/vsudd/etc/init.d/vsudd @@ -11,6 +11,12 @@ start() { ebegin "Starting docker socket vsock passthrough" + if [ -d /sys/bus/vmbus ]; then + PORT=23a432c2-537a-4291-bcb5-d62504644739 + else + PORT=2376 + fi + [ -n "${PIDFILE}" ] || PIDFILE=/var/run/vsudd.pid [ -n "${LOGFILE}" ] || LOGFILE=/var/log/vsudd.log @@ -19,7 +25,7 @@ start() --exec /sbin/vsudd \ --make-pidfile --pidfile ${PIDFILE} \ --stderr "${LOGFILE}" --stdout "${LOGFILE}" \ - -- -port 2376 -sock /var/run/docker.sock + -- -port "${PORT}" -sock /var/run/docker.sock eend $? "Failed to start vsudd" } diff --git a/alpine/packages/vsudd/main.go b/alpine/packages/vsudd/main.go index 1dc535fa1..634a3fd9b 100644 --- a/alpine/packages/vsudd/main.go +++ b/alpine/packages/vsudd/main.go @@ -7,6 +7,8 @@ import ( "log" "net" "os" + "strconv" + "strings" "syscall" "time" ) @@ -14,6 +16,7 @@ import ( /* No way to teach net or syscall about vsock sockaddr, so go right to C */ /* +#include #include #include "include/uapi/linux/vm_sockets.h" int bind_sockaddr_vm(int fd, const struct sockaddr_vm *sa_vm) { @@ -25,22 +28,44 @@ int connect_sockaddr_vm(int fd, const struct sockaddr_vm *sa_vm) { int accept_vm(int fd, struct sockaddr_vm *sa_vm, socklen_t *sa_vm_len) { return accept4(fd, (struct sockaddr *)sa_vm, sa_vm_len, 0); } + +struct sockaddr_hv { + unsigned short shv_family; + unsigned short reserved; + unsigned char shv_vm_id[16]; + unsigned char shv_service_id[16]; +}; +int bind_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) { + return bind(fd, (const struct sockaddr*)sa_hv, sizeof(*sa_hv)); +} +int connect_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) { + return connect(fd, (const struct sockaddr*)sa_hv, sizeof(*sa_hv)); +} +int accept_hv(int fd, struct sockaddr_hv *sa_hv, socklen_t *sa_hv_len) { + return accept4(fd, (struct sockaddr *)sa_hv, sa_hv_len, 0); +} */ import "C" +type GUID [16]byte + const ( - AF_VSOCK = 40 - VSOCK_CID_ANY = 4294967295 /* 2^32-1 */ + AF_VSOCK = 40 + VSOCK_CID_ANY = 4294967295 /* 2^32-1 */ + + AF_HYPERV = 42 + SHV_PROTO_RAW = 1 ) var ( - port uint - sock string - detach bool + portstr string + sock string + detach bool + SHV_VMID_GUEST = GUID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ) func init() { - flag.UintVar(&port, "port", 2376, "vsock port to forward") + flag.StringVar(&portstr, "port", "2376", "vsock port to forward") flag.StringVar(&sock, "sock", "/var/run/docker.sock", "path of the local Unix domain socket to forward to") flag.BoolVar(&detach, "detach", false, "detach from terminal") } @@ -65,6 +90,72 @@ func main() { syscall.Dup2(int(fd), int(os.Stderr.Fd())) } + if strings.Contains(portstr, "-") { + guid, err := guidFromString(portstr) + if err != nil { + log.Fatalln("Failed to parse GUID", portstr, err) + } + hvsockListen(guid) + } else { + port, err := strconv.ParseUint(portstr, 10, 32) + if err != nil { + log.Fatalln("Can't convert %s to a uint.", portstr, err) + } + vsockListen(uint(port)) + } +} + +func hvsockListen(port GUID) { + accept_fd, err := syscall.Socket(AF_HYPERV, syscall.SOCK_STREAM, SHV_PROTO_RAW) + if err != nil { + log.Fatal(err) + } + + sa := C.struct_sockaddr_hv{} + sa.shv_family = AF_HYPERV + sa.reserved = 0 + /* TODO: Turn this into a function */ + for i := 0; i < 16; i++ { + sa.shv_vm_id[i] = C.uchar(SHV_VMID_GUEST[i]) + } + for i := 0; i < 16; i++ { + sa.shv_service_id[i] = C.uchar(port[i]) + } + + if ret := C.bind_sockaddr_hv(C.int(accept_fd), &sa); ret != 0 { + log.Fatal(fmt.Sprintf("failed bind hvsock connection to %s.%s, returned %d", + SHV_VMID_GUEST.toString(), port.toString(), ret)) + } + + err = syscall.Listen(accept_fd, syscall.SOMAXCONN) + if err != nil { + log.Fatalln("Failed to listen to VSOCK", err) + } + + log.Printf("Listening on fd %d", accept_fd) + + connid := 0 + + for { + var accept_sa C.struct_sockaddr_hv + var accept_sa_len C.socklen_t + + connid++ + accept_sa_len = C.sizeof_struct_sockaddr_hv + fd, err := C.accept_hv(C.int(accept_fd), &accept_sa, &accept_sa_len) + if err != nil { + log.Fatalln("Error accepting connection", err) + } + + accept_vm_id := guidFromC(accept_sa.shv_vm_id) + accept_svc_id := guidFromC(accept_sa.shv_service_id) + log.Printf("%d Accepted connection on fd %d from %s.%s", + connid, fd, accept_vm_id.toString(), accept_svc_id.toString()) + go handleOne(connid, int(fd)) + } +} + +func vsockListen(port uint) { accept_fd, err := syscall.Socket(AF_VSOCK, syscall.SOCK_STREAM, 0) if err != nil { log.Fatal(err) @@ -76,7 +167,8 @@ func main() { sa.svm_cid = VSOCK_CID_ANY if ret := C.bind_sockaddr_vm(C.int(accept_fd), &sa); ret != 0 { - log.Fatal(fmt.Sprintf("failed bind vsock connection to %08x.%08x, returned %d", sa.svm_cid, sa.svm_port, ret)) + log.Fatal(fmt.Sprintf("failed bind vsock connection to %08x.%08x, returned %d", + sa.svm_cid, sa.svm_port, ret)) } err = syscall.Listen(accept_fd, syscall.SOMAXCONN) @@ -98,17 +190,18 @@ func main() { if err != nil { log.Fatalln("Error accepting connection", err) } - go handleOne(connid, int(fd), uint(accept_sa.svm_cid), uint(accept_sa.svm_port)) + log.Printf("%d Accepted connection on fd %d from %08x.%08x", + connid, fd, uint(accept_sa.svm_cid), uint(accept_sa.svm_port)) + go handleOne(connid, int(fd)) } } -func handleOne(connid int, fd int, cid, port uint) { +func handleOne(connid int, fd int) { vsock := os.NewFile(uintptr(fd), fmt.Sprintf("vsock:%d", fd)) - log.Printf("%d Accepted connection on fd %d from %08x.%08x", connid, fd, cid, port) defer func() { log.Println(connid, "Closing vsock", vsock) - if err := vsock.Close() ; err != nil { + if err := vsock.Close(); err != nil { log.Println(connid, "Error closing", vsock, ":", err) } }() @@ -131,7 +224,7 @@ func handleOne(connid int, fd int, cid, port uint) { } defer func() { log.Println(connid, "Closing docker", docker) - if err := docker.Close() ; err != nil { + if err := docker.Close(); err != nil { log.Println(connid, "Error closing", docker, ":", err) } }() @@ -175,3 +268,33 @@ func handleOne(connid int, fd int, cid, port uint) { totalWritten := <-w log.Println(connid, "Done. read:", totalRead, "written:", totalWritten) } + +func (g *GUID) toString() string { + /* XXX This assume little endian */ + return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + g[3], g[2], g[1], g[0], + g[5], g[4], + g[7], g[6], + g[8], g[9], + g[10], g[11], g[12], g[13], g[14], g[15]) +} + +func guidFromString(s string) (GUID, error) { + var g GUID + var err error + _, err = fmt.Sscanf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &g[3], &g[2], &g[1], &g[0], + &g[5], &g[4], + &g[7], &g[6], + &g[8], &g[9], + &g[10], &g[11], &g[12], &g[13], &g[14], &g[15]) + return g, err +} + +func guidFromC(cg [16]C.uchar) GUID { + var g GUID + for i := 0; i < 16; i++ { + g[i] = byte(cg[i]) + } + return g +}