From 17ced40bf418fba4f7ccb5e23fe1f0d47eab9bc4 Mon Sep 17 00:00:00 2001 From: David Scott Date: Wed, 11 May 2016 21:29:57 +0100 Subject: [PATCH] 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; +}