Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions common/include/linux_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ struct cmsghdr {
#define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))

#define SCM_RIGHTS 1
#define SCM_CREDENTIALS 2
#define SCM_SECURITY 3

#define AF_UNSPEC 0
#define AF_UNIX 1
Expand Down Expand Up @@ -96,9 +98,14 @@ struct cmsghdr {
#define SO_RCVTIMEO 20
#define SO_SNDTIMEO 21
#define SO_ACCEPTCONN 30
#define SO_MARK 36
#define SO_TIMESTAMPING_OLD 37
#define SO_PROTOCOL 38
#define SO_DOMAIN 39

#define SO_TXTIME 61
#define SCM_TXTIME SO_TXTIME

/* TCP options. */
#define TCP_NODELAY 1 /* Turn off Nagle's algorithm */
#define TCP_CORK 3 /* Never send partially complete segments */
Expand Down
27 changes: 18 additions & 9 deletions libos/include/libos_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ struct libos_sock_ops {
* \param handle A handle.
* \param iov An array of buffers to write from.
* \param iov_len The length of \p iov.
* \param msg_control An ancillary data buffer.
* \param msg_controllen The length of \p msg_control.
* \param[out] out_size On success contains the number of bytes sent.
* \param addr An address to send to. May be NULL. It's up to
* the implementation to decide what to do with it (which might
Expand All @@ -88,28 +90,33 @@ struct libos_sock_ops {
* \param force_nonblocking If `true` this request should not block. Otherwise just use
* whatever mode the handle is in.
*/
int (*send)(struct libos_handle* handle, struct iovec* iov, size_t iov_len, size_t* out_size,
void* addr, size_t addrlen, bool force_nonblocking);
int (*send)(struct libos_handle* handle, struct iovec* iov, size_t iov_len, void* msg_control,
size_t msg_controllen, size_t* out_size, void* addr, size_t addrlen,
bool force_nonblocking);

/*!
* \brief Receive continuous data into an array of buffers.
*
* \param handle A handle.
* \param iov An array of buffers to read to.
* \param iov_len The length of \p iov.
* \param msg_control An ancillary data buffer to populate.
* \param[in,out] msg_controllen_ptr The length of \p msg_control. On success updated to the
* actual length of the received ancillary data.
* \param[out] out_total_size On success contains the number of bytes received (STREAM)
* or the datagram size (DGRAM), which might be bigger than
* the total size of buffers in \p iov array.
* \param[out] addr On success contains the address data was received from. May
* be NULL.
* \param[in,out] addrlen The length of \p addr. On success updated to the actual
* \param[in,out] addrlen_ptr The length of \p addr. On success updated to the actual
* length of the address. Bigger than original value indicates
* that truncation has happened.
* \param force_nonblocking If `true` this request should not block. Otherwise just use
* whatever mode the handle is in.
*/
int (*recv)(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
size_t* out_total_size, void* addr, size_t* addrlen, bool force_nonblocking);
int (*recv)(struct libos_handle* handle, struct iovec* iov, size_t iov_len, void* msg_control,
size_t* msg_controllen_ptr, size_t* out_total_size, void* addr, size_t* addrlen_ptr,
bool force_nonblocking);
};

struct libos_handle* get_new_socket_handle(int family, int type, int protocol,
Expand All @@ -118,7 +125,9 @@ struct libos_handle* get_new_socket_handle(int family, int type, int protocol,
extern struct libos_sock_ops sock_unix_ops;
extern struct libos_sock_ops sock_ip_ops;

ssize_t do_recvmsg(struct libos_handle* handle, struct iovec* iov, size_t iov_len, void* addr,
size_t* addrlen, unsigned int* flags);
ssize_t do_sendmsg(struct libos_handle* handle, struct iovec* iov, size_t iov_len, void* addr,
size_t addrlen, unsigned int flags);
ssize_t do_recvmsg(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
void* msg_control, size_t* msg_controllen_ptr, void* addr, size_t* addrlen_ptr,
unsigned int* flags);
ssize_t do_sendmsg(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
void* msg_control, size_t msg_controllen, void* addr, size_t addrlen,
unsigned int flags);
12 changes: 8 additions & 4 deletions libos/src/fs/socket/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ static ssize_t read(struct libos_handle* handle, void* buf, size_t size, file_of
.iov_len = size,
};
unsigned int flags = 0;
return do_recvmsg(handle, &iov, /*iov_len=*/1, /*addr=*/NULL, /*addrlen=*/NULL, &flags);
return do_recvmsg(handle, &iov, /*iov_len=*/1, /*msg_control=*/NULL,
/*msg_controllen_ptr=*/NULL, /*addr=*/NULL, /*addrlen_ptr=*/NULL, &flags);
}

static ssize_t write(struct libos_handle* handle, const void* buf, size_t size, file_off_t* pos) {
Expand All @@ -45,20 +46,23 @@ static ssize_t write(struct libos_handle* handle, const void* buf, size_t size,
.iov_base = (void*)buf,
.iov_len = size,
};
return do_sendmsg(handle, &iov, /*iov_len=*/1, /*addr=*/NULL, /*addrlen=*/0, /*flags=*/0);
return do_sendmsg(handle, &iov, /*iov_len=*/1, /*msg_control=*/NULL, /*msg_controllen=*/0,
/*addr=*/NULL, /*addrlen=*/0, /*flags=*/0);
}

static ssize_t readv(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
file_off_t* pos) {
__UNUSED(pos);
unsigned int flags = 0;
return do_recvmsg(handle, iov, iov_len, /*addr=*/NULL, /*addrlen=*/NULL, &flags);
return do_recvmsg(handle, iov, iov_len, /*msg_control=*/NULL, /*msg_controllen_ptr=*/NULL,
/*addr=*/NULL, /*addrlen_ptr=*/NULL, &flags);
}

static ssize_t writev(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
file_off_t* pos) {
__UNUSED(pos);
return do_sendmsg(handle, iov, iov_len, /*addr=*/NULL, /*addrlen=*/0, /*flags=*/0);
return do_sendmsg(handle, iov, iov_len, /*msg_control=*/NULL, /*msg_controllen=*/0,
/*addr=*/NULL, /*addrlen=*/0, /*flags=*/0);
}

static int hstat(struct libos_handle* handle, struct stat* stat) {
Expand Down
84 changes: 71 additions & 13 deletions libos/src/net/ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@

#include "libos_fs.h"
#include "libos_socket.h"
#include "linux_socket.h"
#include "pal.h"
#include "socket_utils.h"

static int verify_sockaddr(int expected_family, void* addr, size_t* addrlen) {
static int verify_sockaddr(int expected_family, void* addr, size_t* addrlen_ptr) {
unsigned short family;
switch (expected_family) {
case AF_INET:
if (*addrlen < sizeof(struct sockaddr_in)) {
if (*addrlen_ptr < sizeof(struct sockaddr_in)) {
return -EINVAL;
}
memcpy(&family, (char*)addr + offsetof(struct sockaddr_in, sin_family), sizeof(family));
Expand All @@ -27,10 +28,10 @@ static int verify_sockaddr(int expected_family, void* addr, size_t* addrlen) {
}
/* Cap the address at the maximal possible size - rest of the input buffer (if any) is
* ignored. */
*addrlen = sizeof(struct sockaddr_in);
*addrlen_ptr = sizeof(struct sockaddr_in);
break;
case AF_INET6:
if (*addrlen < sizeof(struct sockaddr_in6)) {
if (*addrlen_ptr < sizeof(struct sockaddr_in6)) {
return -EINVAL;
}
memcpy(&family, (char*)addr + offsetof(struct sockaddr_in6, sin6_family),
Expand All @@ -40,7 +41,7 @@ static int verify_sockaddr(int expected_family, void* addr, size_t* addrlen) {
}
/* Cap the address at the maximal possible size - rest of the input buffer (if any) is
* ignored. */
*addrlen = sizeof(struct sockaddr_in6);
*addrlen_ptr = sizeof(struct sockaddr_in6);
break;
default:
BUG();
Expand Down Expand Up @@ -662,13 +663,54 @@ static int getsockopt(struct libos_handle* handle, int level, int optname, void*
}
}

static int send(struct libos_handle* handle, struct iovec* iov, size_t iov_len, size_t* out_size,
void* addr, size_t addrlen, bool force_nonblocking) {
static int send(struct libos_handle* handle, struct iovec* iov, size_t iov_len, void* msg_control,
size_t msg_controllen, size_t* out_size, void* addr, size_t addrlen,
bool force_nonblocking) {
assert(handle->type == TYPE_SOCK);

struct libos_sock_handle* sock = &handle->info.sock;
struct sockaddr_storage sock_addr;

struct cmsghdr* cmsg = (struct cmsghdr*)msg_control;
size_t rest_msg_controllen = msg_controllen;
while (cmsg && rest_msg_controllen >= sizeof(struct cmsghdr)) {
if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
CMSG_ALIGN(cmsg->cmsg_len) > rest_msg_controllen) {
return -EINVAL;
}

if (cmsg->cmsg_level != SOL_SOCKET) {
/*
* We currently don't support:
* - SOL_UDP: UDP_SEGMENT
* - SOL_IPV6: IPV6_PKTINFO
* - SOL_IP: IP_RETOPTS, IP_PKTINFO, IP_TTL, IP_TOS
*
* Note that there are no cmsgs for TCP (SOL_TCP) in Linux (as of v6.0).
*/
return -EINVAL;
}

switch (cmsg->cmsg_type) {
/* We currently don't support below SOL_SOCKET types. */
case SO_MARK:
case SO_TIMESTAMPING_OLD:
case SCM_TXTIME:
return -EINVAL;

/* SCM_RIGHTS and SCM_CREDENTIALS are semantically in SOL_UNIX, simply ignored */
case SCM_RIGHTS:
case SCM_CREDENTIALS:
break;

default:
return -EINVAL;
}

rest_msg_controllen -= CMSG_ALIGN(cmsg->cmsg_len);
cmsg = (struct cmsghdr*)((char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len));
}

switch (sock->type) {
case SOCK_STREAM:
/* TCP sockets ignore destination address - they must have been connected. */
Expand Down Expand Up @@ -709,15 +751,16 @@ static int send(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
return ret;
}

static int recv(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
size_t* out_total_size, void* addr, size_t* addrlen, bool force_nonblocking) {
static int recv(struct libos_handle* handle, struct iovec* iov, size_t iov_len, void* msg_control,
size_t* msg_controllen_ptr, size_t* out_total_size, void* addr, size_t* addrlen_ptr,
bool force_nonblocking) {
assert(handle->type == TYPE_SOCK);

switch (handle->info.sock.type) {
case SOCK_STREAM:
/* TCP - not interested in remote address (we know it already). */
addr = NULL;
addrlen = NULL;
addrlen_ptr = NULL;
break;
case SOCK_DGRAM:
break;
Expand All @@ -731,14 +774,29 @@ static int recv(struct libos_handle* handle, struct iovec* iov, size_t iov_len,
if (ret < 0) {
return pal_to_unix_errno(ret);
}

if (msg_control && msg_controllen_ptr) {
/*
* We currently don't support:
* - SOL_TCP: TCP_CM_INQ
* - SOL_SOCKET: SO_TIMESTAMPNS_NEW, SO_TIMESTAMPNS_OLD, SO_TIMESTAMP_NEW, SO_TIMESTAMP_OLD
* - SOL_IPV6: IPV6_PKTINFO
* - SOL_IP: IP_RETOPTS, IP_RECVOPTS, IP_PKTINFO, IP_TTL, IP_TOS, IP_RECVFRAGSIZE,
* IP_CHECKSUM, SCM_SECURITY, IP_ORIGDSTADDR, IP_RECVERR
*
* Note that SCM_RIGHTS and SCM_CREDENTIALS are not possible on TCP/UDP sockets.
*/
*msg_controllen_ptr = 0;
}

if (addr) {
struct sockaddr_storage linux_addr;
size_t linux_addr_len = sizeof(linux_addr);
pal_to_linux_sockaddr(&pal_ip_addr, &linux_addr, &linux_addr_len);
/* If the user provided buffer is too small, the address is truncated, but we report
* the actual address size in `addrlen`. */
memcpy(addr, &linux_addr, MIN(*addrlen, linux_addr_len));
*addrlen = linux_addr_len;
* the actual address size in `addrlen_ptr`. */
memcpy(addr, &linux_addr, MIN(*addrlen_ptr, linux_addr_len));
*addrlen_ptr = linux_addr_len;
}
return 0;
}
Expand Down
Loading