Skip to content

Commit

Permalink
Merge pull request #295 from petreeftime/master
Browse files Browse the repository at this point in the history
add initial vsock support
  • Loading branch information
graebm authored Jul 23, 2020
2 parents 3e51124 + 0bf6e77 commit 56d38b6
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 6 deletions.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ if (WIN32)
set(TLS_STACK_DETERMINED ON)

elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android")
option(USE_VSOCK
"Build in support for VSOCK sockets"
OFF)

file(GLOB AWS_IO_OS_HEADERS
)

Expand Down Expand Up @@ -194,6 +198,10 @@ if (BUILD_RELOCATABLE_BINARIES)
target_compile_definitions(${PROJECT_NAME} PRIVATE "-DCOMPAT_MODE")
endif()

if (USE_VSOCK)
target_compile_definitions(${PROJECT_NAME} PUBLIC "-DUSE_VSOCK")
endif()

target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ notifications.
AWS_SOCKET_IPV4,
AWS_SOCKET_IPV6,
AWS_SOCKET_LOCAL,
AWS_SOCKET_VSOCK,
} aws_socket_domain;

`AWS_SOCKET_IPV4` means an IPv4 address will be used.
Expand All @@ -681,6 +682,7 @@ notifications.
AWS_SOCKET_DGRAM
} aws_socket_type;

`AWS_SOCKET_VSOCK` means a CID address will be used. Note: VSOCK is currently only available on Linux with an appropriate VSOCK kernel driver installed. `-DUSE_VSOCK` needs to be passed during compilation to enable VSOCK support.

`AWS_SOCKET_STREAM` is TCP or a connection oriented socket.

Expand Down Expand Up @@ -709,7 +711,7 @@ with it.
char port[10];
};

`address` can be either an IPv4 or IPv6 address. This can be used for UDP or TCP.
`address` can be either an IPv4, IPv6 or VSOCK CID address. This can be used for UDP or TCP.
`socket_name` is only used in LOCAL mode.
`port` can be used for TCP or UDP.

Expand Down
12 changes: 7 additions & 5 deletions include/aws/io/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ enum aws_socket_domain {
AWS_SOCKET_IPV6,
/* Unix domain sockets (or at least something like them) */
AWS_SOCKET_LOCAL,
/* VSOCK used in inter-VM communication */
AWS_SOCKET_VSOCK,
};

enum aws_socket_type {
Expand All @@ -22,7 +24,7 @@ enum aws_socket_type {
AWS_SOCKET_STREAM,
/* A datagram socket is connectionless and sends unreliable messages.
* This means UDP when used with IPV4/6.
* LOCAL sockets are not compatible with DGRAM.*/
* LOCAL and VSOCK sockets are not compatible with DGRAM.*/
AWS_SOCKET_DGRAM,
};

Expand Down Expand Up @@ -152,7 +154,7 @@ AWS_IO_API void aws_socket_clean_up(struct aws_socket *socket);
* Connects to a remote endpoint. In UDP, this simply binds the socket to a remote address for use with
* `aws_socket_write()`, and if the operation is successful, the socket can immediately be used for write operations.
*
* In TCP amd LOCAL, this function will not block. If the return value is successful, then you must wait on the
* In TCP, LOCAL and VSOCK this function will not block. If the return value is successful, then you must wait on the
* `on_connection_result()` callback to be invoked before using the socket.
*
* If an event_loop is provided for UDP sockets, a notification will be sent on
Expand All @@ -175,12 +177,12 @@ AWS_IO_API int aws_socket_connect(
AWS_IO_API int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint);

/**
* TCP and LOCAL only. Sets up the socket to listen on the address bound to in `aws_socket_bind()`.
* TCP, LOCAL and VSOCK only. Sets up the socket to listen on the address bound to in `aws_socket_bind()`.
*/
AWS_IO_API int aws_socket_listen(struct aws_socket *socket, int backlog_size);

/**
* TCP and LOCAL only. The socket will begin accepting new connections. This is an asynchronous operation. New
* TCP, LOCAL and VSOCK only. The socket will begin accepting new connections. This is an asynchronous operation. New
* connections or errors will arrive via the `on_accept_result` callback.
*
* aws_socket_bind() and aws_socket_listen() must be called before calling this function.
Expand All @@ -192,7 +194,7 @@ AWS_IO_API int aws_socket_start_accept(
void *user_data);

/**
* TCP and LOCAL only. The listening socket will stop accepting new connections.
* TCP, LOCAL and VSOCK only. The listening socket will stop accepting new connections.
* It is safe to call `aws_socket_start_accept()` again after
* this operation. This can be called from any thread but be aware,
* on some platforms, if you call this from outside of the current event loop's thread, it will block
Expand Down
63 changes: 63 additions & 0 deletions source/posix/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@
# define O_CLOEXEC 02000000
#endif

#ifdef USE_VSOCK
# if defined(__linux__) && defined(AF_VSOCK)
# include <linux/vm_sockets.h>
# else
# error "USE_VSOCK not supported on current platform"
# endif
#endif

/* other than CONNECTED_READ | CONNECTED_WRITE
* a socket is only in one of these states at a time. */
enum socket_state {
Expand All @@ -61,6 +69,10 @@ static int s_convert_domain(enum aws_socket_domain domain) {
return AF_INET6;
case AWS_SOCKET_LOCAL:
return AF_UNIX;
#ifdef USE_VSOCK
case AWS_SOCKET_VSOCK:
return AF_VSOCK;
#endif
default:
AWS_ASSERT(0);
return AF_INET;
Expand Down Expand Up @@ -498,9 +510,46 @@ struct socket_address {
struct sockaddr_in addr_in;
struct sockaddr_in6 addr_in6;
struct sockaddr_un un_addr;
#ifdef USE_VSOCK
struct sockaddr_vm vm_addr;
#endif
} sock_addr_types;
};

#ifdef USE_VSOCK
/** Convert a string to a VSOCK CID. Respects the calling convetion of inet_pton:
* 0 on error, 1 on success. */
static int parse_cid(const char *cid_str, unsigned int *value) {
if (cid_str == NULL || value == NULL) {
errno = EINVAL;
return 0;
}
/* strtoll returns 0 as both error and correct value */
errno = 0;
/* unsigned long long to handle edge cases in convention explicitly */
long long cid = strtoll(cid_str, NULL, 10);
if (errno != 0) {
return 0;
}

/* -1U means any, so it's a valid value, but it needs to be converted to
* unsigned int. */
if (cid == -1) {
*value = VMADDR_CID_ANY;
return 1;
}

if (cid < 0 || cid > UINT_MAX) {
errno = ERANGE;
return 0;
}

/* cast is safe here, edge cases already checked */
*value = (unsigned int)cid;
return 1;
}
#endif

int aws_socket_connect(
struct aws_socket *socket,
const struct aws_socket_endpoint *remote_endpoint,
Expand Down Expand Up @@ -551,6 +600,13 @@ int aws_socket_connect(
address.sock_addr_types.un_addr.sun_family = AF_UNIX;
strncpy(address.sock_addr_types.un_addr.sun_path, remote_endpoint->address, AWS_ADDRESS_MAX_LEN);
sock_size = sizeof(address.sock_addr_types.un_addr);
#ifdef USE_VSOCK
} else if (socket->options.domain == AWS_SOCKET_VSOCK) {
pton_err = parse_cid(remote_endpoint->address, &address.sock_addr_types.vm_addr.svm_cid);
address.sock_addr_types.vm_addr.svm_family = AF_VSOCK;
address.sock_addr_types.vm_addr.svm_port = (unsigned int)remote_endpoint->port;
sock_size = sizeof(address.sock_addr_types.vm_addr);
#endif
} else {
AWS_ASSERT(0);
return aws_raise_error(AWS_IO_SOCKET_UNSUPPORTED_ADDRESS_FAMILY);
Expand Down Expand Up @@ -718,6 +774,13 @@ int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint
address.sock_addr_types.un_addr.sun_family = AF_UNIX;
strncpy(address.sock_addr_types.un_addr.sun_path, local_endpoint->address, AWS_ADDRESS_MAX_LEN);
sock_size = sizeof(address.sock_addr_types.un_addr);
#ifdef USE_VSOCK
} else if (socket->options.domain == AWS_SOCKET_VSOCK) {
pton_err = parse_cid(local_endpoint->address, &address.sock_addr_types.vm_addr.svm_cid);
address.sock_addr_types.vm_addr.svm_family = AF_VSOCK;
address.sock_addr_types.vm_addr.svm_port = (unsigned int)local_endpoint->port;
sock_size = sizeof(address.sock_addr_types.vm_addr);
#endif
} else {
AWS_ASSERT(0);
return aws_raise_error(AWS_IO_SOCKET_UNSUPPORTED_ADDRESS_FAMILY);
Expand Down
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ add_test_case(udp_socket_communication)
add_test_case(udp_bind_connect_communication)
add_net_test_case(connect_timeout)
add_net_test_case(connect_timeout_cancelation)
if (USE_VSOCK)
add_test_case(vsock_loopback_socket_communication)
endif ()

add_test_case(outgoing_local_sock_errors)
add_test_case(outgoing_tcp_sock_error)
Expand Down
29 changes: 29 additions & 0 deletions tests/socket_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
# pragma warning(disable : 4996) /* sprintf */
#endif

#if USE_VSOCK
# include <linux/vm_sockets.h>
#endif

struct local_listener_args {
struct aws_socket *incoming;
struct aws_mutex *mutex;
Expand Down Expand Up @@ -432,6 +436,31 @@ static int s_test_tcp_socket_communication(struct aws_allocator *allocator, void

AWS_TEST_CASE(tcp_socket_communication, s_test_tcp_socket_communication)

#if defined(USE_VSOCK)
static int s_test_vsock_loopback_socket_communication(struct aws_allocator *allocator, void *ctx) {
/* Without vsock loopback it's difficult to test vsock functionality.
* Also note that having this defined does not guarantee that it's available
* for use and there's no path to figure out dynamically if it can be used. */
# if defined(VMADDR_CID_LOCAL)
(void)ctx;

struct aws_socket_options options;
AWS_ZERO_STRUCT(options);
options.connect_timeout_ms = 3000;
options.type = AWS_SOCKET_STREAM;
options.domain = AWS_SOCKET_VSOCK;

struct aws_socket_endpoint endpoint = {.address = "1" /* VMADDR_CID_LOCAL */, .port = 8127};

return s_test_socket(allocator, &options, &endpoint);
# else
return 0;
# endif
}

AWS_TEST_CASE(vsock_loopback_socket_communication, s_test_vsock_loopback_socket_communication)
#endif

static int s_test_udp_socket_communication(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

Expand Down

0 comments on commit 56d38b6

Please sign in to comment.