drivers: net: nsos: new driver for Native Simulator offloaded sockets

Add driver for 'native_sim' target that implements offloaded socket
networking by the use of host networking stack and wrapped BSD sockets API.

This driver has following advantages over existing networking drivers for
emulated platforms that are already in tree:
 * no TUN/TAP use means that no additional setup is required on the host
   side:
   * possible to use it within unpriviledged Docker containers, either for
     development or in CI
 * possibility to use and test offloaded sockets
   (CONFIG_NET_SOCKETS_OFFLOAD=y) with emulated target, which allows
   to increase tests coverage of this feature, without requirement of using
   hardware

Native Simulator host libc has different error codes than embedded libc
used by Zephyr. Convert between those.

Signed-off-by: Marcin Niestroj <m.niestroj@emb.dev>
This commit is contained in:
Marcin Niestroj 2023-11-10 15:46:04 +01:00 committed by Carles Cufí
parent b29d73fcc4
commit 07edc9a070
7 changed files with 1211 additions and 0 deletions

View file

@ -9,3 +9,13 @@ if(CONFIG_NET_NATIVE)
zephyr_library_sources_ifdef(CONFIG_SLIP slip.c)
zephyr_library_sources_ifdef(CONFIG_NET_PPP ppp.c)
endif()
if(CONFIG_NET_NATIVE_OFFLOADED_SOCKETS)
zephyr_library_include_directories(
${ZEPHYR_BASE}/subsys/net/lib/sockets
)
zephyr_library_sources(nsos_errno.c)
zephyr_library_sources(nsos_sockets.c)
target_sources(native_simulator INTERFACE nsos_adapt.c)
target_sources(native_simulator INTERFACE nsos_errno.c)
endif()

View file

@ -224,4 +224,20 @@ config NET_CANBUS_INIT_PRIORITY
endif # NET_CAN
#
# Native simulator offloaded sockets
#
config NET_NATIVE_OFFLOADED_SOCKETS
bool "Native Simulator offloaded sockets"
depends on ARCH_POSIX
depends on NATIVE_LIBRARY
depends on NET_SOCKETS_OFFLOAD
help
Offloaded sockets for Native Simulator utilize host BSD sockets API (like socket(),
connect(), send(), recvfrom(), etc.) in order to provide networking capability.
This driver main advantage is that it is possible to use this driver without any
additional setup on the host side, unlike with the native TAP Ethernet driver.
endif # NET_DRIVERS

100
drivers/net/nsos.h Normal file
View file

@ -0,0 +1,100 @@
/**
* Copyright (c) 2023-2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __DRIVERS_NET_NSOS_H__
#define __DRIVERS_NET_NSOS_H__
#include <stddef.h>
#include <stdint.h>
/* Protocol families. */
#define NSOS_MID_PF_UNSPEC 0 /**< Unspecified protocol family. */
#define NSOS_MID_PF_INET 1 /**< IP protocol family version 4. */
/* Address families. */
#define NSOS_MID_AF_UNSPEC NSOS_MID_PF_UNSPEC /**< Unspecified address family. */
#define NSOS_MID_AF_INET NSOS_MID_PF_INET /**< IP protocol family version 4. */
/** Protocol numbers from IANA/BSD */
enum nsos_mid_net_ip_protocol {
NSOS_MID_IPPROTO_IP = 0, /**< IP protocol (pseudo-val for setsockopt() */
NSOS_MID_IPPROTO_ICMP = 1, /**< ICMP protocol */
NSOS_MID_IPPROTO_IGMP = 2, /**< IGMP protocol */
NSOS_MID_IPPROTO_IPIP = 4, /**< IPIP tunnels */
NSOS_MID_IPPROTO_TCP = 6, /**< TCP protocol */
NSOS_MID_IPPROTO_UDP = 17, /**< UDP protocol */
NSOS_MID_IPPROTO_IPV6 = 41, /**< IPv6 protocol */
NSOS_MID_IPPROTO_ICMPV6 = 58, /**< ICMPv6 protocol */
NSOS_MID_IPPROTO_RAW = 255, /**< RAW IP packets */
};
/** Socket type */
enum nsos_mid_net_sock_type {
NSOS_MID_SOCK_STREAM = 1, /**< Stream socket type */
NSOS_MID_SOCK_DGRAM, /**< Datagram socket type */
NSOS_MID_SOCK_RAW /**< RAW socket type */
};
#define NSOS_MID_MSG_PEEK 0x02
#define NSOS_MID_MSG_TRUNC 0x20
#define NSOS_MID_MSG_DONTWAIT 0x40
#define NSOS_MID_MSG_WAITALL 0x100
struct nsos_mid_sockaddr {
uint16_t sa_family; /* Address family */
char sa_data[]; /* Socket address */
};
struct nsos_mid_sockaddr_in {
uint16_t sin_family; /* AF_INET */
uint16_t sin_port; /* Port number */
uint32_t sin_addr; /* IPv4 address */
};
struct nsos_mid_sockaddr_in6 {
uint16_t sin6_family; /* AF_INET6 */
uint16_t sin6_port; /* Port number */
uint32_t sin6_flowinfo; /* IPv6 flow info */
uint8_t sin6_addr[16];
uint32_t sin6_scope_id; /* Set of interfaces for a scope */
};
struct nsos_mid_sockaddr_storage {
union {
struct nsos_mid_sockaddr_in sockaddr_in;
struct nsos_mid_sockaddr_in6 sockaddr_in6;
};
};
struct nsos_mid_pollfd {
int fd;
short events;
short revents;
};
static inline void nsos_socket_flag_convert(int *flags_a, int flag_a,
int *flags_b, int flag_b)
{
if ((*flags_a) & flag_a) {
*flags_a &= ~flag_a;
*flags_b |= flag_b;
}
}
int nsos_adapt_get_errno(void);
int nsos_adapt_socket(int family, int type, int proto);
int nsos_adapt_bind(int fd, const struct nsos_mid_sockaddr *addr, size_t addrlen);
int nsos_adapt_connect(int fd, const struct nsos_mid_sockaddr *addr, size_t addrlen);
int nsos_adapt_listen(int fd, int backlog);
int nsos_adapt_accept(int fd, struct nsos_mid_sockaddr *addr, size_t *addrlen);
int nsos_adapt_sendto(int fd, const void *buf, size_t len, int flags,
const struct nsos_mid_sockaddr *addr, size_t addrlen);
int nsos_adapt_recvfrom(int fd, void *buf, size_t len, int flags,
struct nsos_mid_sockaddr *addr, size_t *addrlen);
#endif /* __DRIVERS_NET_NSOS_H__ */

333
drivers/net/nsos_adapt.c Normal file
View file

@ -0,0 +1,333 @@
/**
* Copyright (c) 2023-2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* Linux (bottom) side of NSOS (Native Simulator Offloaded Sockets).
*/
#include <errno.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include "nsos.h"
#include "nsos_errno.h"
#include "nsi_tracing.h"
#include <stdio.h>
int nsos_adapt_get_errno(void)
{
return errno_to_nsos_mid(errno);
}
static int socket_family_from_nsos_mid(int family_mid, int *family)
{
switch (family_mid) {
case NSOS_MID_AF_UNSPEC:
*family = AF_UNSPEC;
break;
case NSOS_MID_AF_INET:
*family = AF_INET;
break;
default:
nsi_print_warning("%s: socket family %d not supported\n", __func__, family_mid);
return -NSOS_MID_EAFNOSUPPORT;
}
return 0;
}
static int socket_proto_from_nsos_mid(int proto_mid, int *proto)
{
switch (proto_mid) {
case NSOS_MID_IPPROTO_IP:
*proto = IPPROTO_IP;
break;
case NSOS_MID_IPPROTO_ICMP:
*proto = IPPROTO_ICMP;
break;
case NSOS_MID_IPPROTO_IGMP:
*proto = IPPROTO_IGMP;
break;
case NSOS_MID_IPPROTO_IPIP:
*proto = IPPROTO_IPIP;
break;
case NSOS_MID_IPPROTO_TCP:
*proto = IPPROTO_TCP;
break;
case NSOS_MID_IPPROTO_UDP:
*proto = IPPROTO_UDP;
break;
case NSOS_MID_IPPROTO_IPV6:
*proto = IPPROTO_IPV6;
break;
case NSOS_MID_IPPROTO_RAW:
*proto = IPPROTO_RAW;
break;
default:
nsi_print_warning("%s: socket protocol %d not supported\n", __func__, proto_mid);
return -NSOS_MID_EPROTONOSUPPORT;
}
return 0;
}
static int socket_type_from_nsos_mid(int type_mid, int *type)
{
switch (type_mid) {
case NSOS_MID_SOCK_STREAM:
*type = SOCK_STREAM;
break;
case NSOS_MID_SOCK_DGRAM:
*type = SOCK_DGRAM;
break;
case NSOS_MID_SOCK_RAW:
*type = SOCK_RAW;
break;
default:
nsi_print_warning("%s: socket type %d not supported\n", __func__, type_mid);
return -NSOS_MID_ESOCKTNOSUPPORT;
}
return 0;
}
static int socket_flags_from_nsos_mid(int flags_mid)
{
int flags = 0;
nsos_socket_flag_convert(&flags_mid, NSOS_MID_MSG_PEEK,
&flags, MSG_PEEK);
nsos_socket_flag_convert(&flags_mid, NSOS_MID_MSG_TRUNC,
&flags, MSG_TRUNC);
nsos_socket_flag_convert(&flags_mid, NSOS_MID_MSG_DONTWAIT,
&flags, MSG_DONTWAIT);
nsos_socket_flag_convert(&flags_mid, NSOS_MID_MSG_WAITALL,
&flags, MSG_WAITALL);
if (flags_mid != 0) {
return -NSOS_MID_EINVAL;
}
return flags;
}
int nsos_adapt_socket(int family_mid, int type_mid, int proto_mid)
{
int family;
int type;
int proto;
int ret;
ret = socket_family_from_nsos_mid(family_mid, &family);
if (ret < 0) {
return ret;
}
ret = socket_type_from_nsos_mid(type_mid, &type);
if (ret < 0) {
return ret;
}
ret = socket_proto_from_nsos_mid(proto_mid, &proto);
if (ret < 0) {
return ret;
}
ret = socket(family, type, proto);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
return ret;
}
static int sockaddr_from_nsos_mid(struct sockaddr **addr, socklen_t *addrlen,
const struct nsos_mid_sockaddr *addr_mid, size_t addrlen_mid)
{
if (!addr_mid || addrlen_mid == 0) {
*addr = NULL;
*addrlen = 0;
return 0;
}
switch (addr_mid->sa_family) {
case NSOS_MID_AF_INET: {
const struct nsos_mid_sockaddr_in *addr_in_mid =
(const struct nsos_mid_sockaddr_in *)addr_mid;
struct sockaddr_in *addr_in = (struct sockaddr_in *)*addr;
addr_in->sin_family = AF_INET;
addr_in->sin_port = addr_in_mid->sin_port;
addr_in->sin_addr.s_addr = addr_in_mid->sin_addr;
*addrlen = sizeof(*addr_in);
return 0;
}
}
return -NSOS_MID_EINVAL;
}
static int sockaddr_to_nsos_mid(const struct sockaddr *addr, socklen_t addrlen,
struct nsos_mid_sockaddr *addr_mid, size_t *addrlen_mid)
{
if (!addr || addrlen == 0) {
*addrlen_mid = 0;
return 0;
}
switch (addr->sa_family) {
case AF_INET: {
struct nsos_mid_sockaddr_in *addr_in_mid =
(struct nsos_mid_sockaddr_in *)addr_mid;
const struct sockaddr_in *addr_in = (const struct sockaddr_in *)addr;
if (addr_in_mid) {
addr_in_mid->sin_family = NSOS_MID_AF_INET;
addr_in_mid->sin_port = addr_in->sin_port;
addr_in_mid->sin_addr = addr_in->sin_addr.s_addr;
}
if (addrlen_mid) {
*addrlen_mid = sizeof(*addr_in);
}
return 0;
}
}
nsi_print_warning("%s: socket family %d not supported\n", __func__, addr->sa_family);
return -NSOS_MID_EINVAL;
}
int nsos_adapt_bind(int fd, const struct nsos_mid_sockaddr *addr_mid, size_t addrlen_mid)
{
struct sockaddr_storage addr_storage;
struct sockaddr *addr = (struct sockaddr *)&addr_storage;
socklen_t addrlen;
int ret;
ret = sockaddr_from_nsos_mid(&addr, &addrlen, addr_mid, addrlen_mid);
if (ret < 0) {
return ret;
}
ret = bind(fd, addr, addrlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
return ret;
}
int nsos_adapt_connect(int fd, const struct nsos_mid_sockaddr *addr_mid, size_t addrlen_mid)
{
struct sockaddr_storage addr_storage;
struct sockaddr *addr = (struct sockaddr *)&addr_storage;
socklen_t addrlen;
int ret;
ret = sockaddr_from_nsos_mid(&addr, &addrlen, addr_mid, addrlen_mid);
if (ret < 0) {
return ret;
}
ret = connect(fd, addr, addrlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
return ret;
}
int nsos_adapt_listen(int fd, int backlog)
{
int ret;
ret = listen(fd, backlog);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
return ret;
}
int nsos_adapt_accept(int fd, struct nsos_mid_sockaddr *addr_mid, size_t *addrlen_mid)
{
struct sockaddr_storage addr_storage;
struct sockaddr *addr = (struct sockaddr *)&addr_storage;
socklen_t addrlen = sizeof(addr_storage);
int ret;
int err;
ret = accept(fd, addr, &addrlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
err = sockaddr_to_nsos_mid(addr, addrlen, addr_mid, addrlen_mid);
if (err) {
return err;
}
return ret;
}
int nsos_adapt_sendto(int fd, const void *buf, size_t len, int flags,
const struct nsos_mid_sockaddr *addr_mid, size_t addrlen_mid)
{
struct sockaddr_storage addr_storage;
struct sockaddr *addr = (struct sockaddr *)&addr_storage;
socklen_t addrlen;
int ret;
ret = sockaddr_from_nsos_mid(&addr, &addrlen, addr_mid, addrlen_mid);
if (ret < 0) {
return ret;
}
ret = sendto(fd, buf, len,
socket_flags_from_nsos_mid(flags) | MSG_NOSIGNAL,
addr, addrlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
return ret;
}
int nsos_adapt_recvfrom(int fd, void *buf, size_t len, int flags,
struct nsos_mid_sockaddr *addr_mid, size_t *addrlen_mid)
{
struct sockaddr_storage addr_storage;
struct sockaddr *addr = (struct sockaddr *)&addr_storage;
socklen_t addrlen = sizeof(addr_storage);
int ret;
int err;
ret = recvfrom(fd, buf, len, socket_flags_from_nsos_mid(flags),
addr, &addrlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
err = sockaddr_to_nsos_mid(addr, addrlen, addr_mid, addrlen_mid);
if (err) {
return err;
}
return ret;
}

125
drivers/net/nsos_errno.c Normal file
View file

@ -0,0 +1,125 @@
/**
* Copyright (c) 2023-2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nsos_errno.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif
struct nsos_mid_errno_map {
/** Zephyr/host error code */
int err;
/** NSOS middleground error code */
int mid_err;
};
#define ERR(_name) \
{ _name, NSOS_MID_ ## _name }
static const struct nsos_mid_errno_map map[] = {
ERR(EPERM),
ERR(ENOENT),
ERR(ESRCH),
ERR(EINTR),
ERR(EIO),
ERR(ENXIO),
ERR(E2BIG),
ERR(ENOEXEC),
ERR(EBADF),
ERR(ECHILD),
ERR(EAGAIN),
ERR(ENOMEM),
ERR(EACCES),
ERR(EFAULT),
ERR(ENOTBLK),
ERR(EBUSY),
ERR(EEXIST),
ERR(EXDEV),
ERR(ENODEV),
ERR(ENOTDIR),
ERR(EISDIR),
ERR(EINVAL),
ERR(ENFILE),
ERR(EMFILE),
ERR(ENOTTY),
ERR(ETXTBSY),
ERR(EFBIG),
ERR(ENOSPC),
ERR(ESPIPE),
ERR(EROFS),
ERR(EMLINK),
ERR(EPIPE),
ERR(EDOM),
ERR(ERANGE),
ERR(ENOMSG),
ERR(EDEADLK),
ERR(ENOLCK),
ERR(ENOSTR),
ERR(ENODATA),
ERR(ETIME),
ERR(ENOSR),
ERR(EPROTO),
ERR(EBADMSG),
ERR(ENOSYS),
ERR(ENOTEMPTY),
ERR(ENAMETOOLONG),
ERR(ELOOP),
ERR(EOPNOTSUPP),
ERR(EPFNOSUPPORT),
ERR(ECONNRESET),
ERR(ENOBUFS),
ERR(EAFNOSUPPORT),
ERR(EPROTOTYPE),
ERR(ENOTSOCK),
ERR(ENOPROTOOPT),
ERR(ESHUTDOWN),
ERR(ECONNREFUSED),
ERR(EADDRINUSE),
ERR(ECONNABORTED),
ERR(ENETUNREACH),
ERR(ENETDOWN),
ERR(ETIMEDOUT),
ERR(EHOSTDOWN),
ERR(EHOSTUNREACH),
ERR(EINPROGRESS),
ERR(EALREADY),
ERR(EDESTADDRREQ),
ERR(EMSGSIZE),
ERR(EPROTONOSUPPORT),
ERR(ESOCKTNOSUPPORT),
ERR(EADDRNOTAVAIL),
ERR(ENETRESET),
ERR(EISCONN),
ERR(ENOTCONN),
ERR(ETOOMANYREFS),
ERR(ENOTSUP),
ERR(EILSEQ),
ERR(EOVERFLOW),
ERR(ECANCELED),
};
int errno_to_nsos_mid(int err)
{
for (int i = 0; i < ARRAY_SIZE(map); i++) {
if (map[i].err == err) {
return map[i].mid_err;
}
}
return err;
}
int errno_from_nsos_mid(int err)
{
for (int i = 0; i < ARRAY_SIZE(map); i++) {
if (map[i].mid_err == err) {
return map[i].err;
}
}
return err;
}

95
drivers/net/nsos_errno.h Normal file
View file

@ -0,0 +1,95 @@
/**
* Copyright (c) 2023-2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __DRIVERS_NET_NSOS_ERRNO_H__
#define __DRIVERS_NET_NSOS_ERRNO_H__
#include <errno.h>
#define NSOS_MID_EPERM 1 /**< Not owner */
#define NSOS_MID_ENOENT 2 /**< No such file or directory */
#define NSOS_MID_ESRCH 3 /**< No such context */
#define NSOS_MID_EINTR 4 /**< Interrupted system call */
#define NSOS_MID_EIO 5 /**< I/O error */
#define NSOS_MID_ENXIO 6 /**< No such device or address */
#define NSOS_MID_E2BIG 7 /**< Arg list too long */
#define NSOS_MID_ENOEXEC 8 /**< Exec format error */
#define NSOS_MID_EBADF 9 /**< Bad file number */
#define NSOS_MID_ECHILD 10 /**< No children */
#define NSOS_MID_EAGAIN 11 /**< No more contexts */
#define NSOS_MID_ENOMEM 12 /**< Not enough core */
#define NSOS_MID_EACCES 13 /**< Permission denied */
#define NSOS_MID_EFAULT 14 /**< Bad address */
#define NSOS_MID_ENOTBLK 15 /**< Block device required */
#define NSOS_MID_EBUSY 16 /**< Mount device busy */
#define NSOS_MID_EEXIST 17 /**< File exists */
#define NSOS_MID_EXDEV 18 /**< Cross-device link */
#define NSOS_MID_ENODEV 19 /**< No such device */
#define NSOS_MID_ENOTDIR 20 /**< Not a directory */
#define NSOS_MID_EISDIR 21 /**< Is a directory */
#define NSOS_MID_EINVAL 22 /**< Invalid argument */
#define NSOS_MID_ENFILE 23 /**< File table overflow */
#define NSOS_MID_EMFILE 24 /**< Too many open files */
#define NSOS_MID_ENOTTY 25 /**< Not a typewriter */
#define NSOS_MID_ETXTBSY 26 /**< Text file busy */
#define NSOS_MID_EFBIG 27 /**< File too large */
#define NSOS_MID_ENOSPC 28 /**< No space left on device */
#define NSOS_MID_ESPIPE 29 /**< Illegal seek */
#define NSOS_MID_EROFS 30 /**< Read-only file system */
#define NSOS_MID_EMLINK 31 /**< Too many links */
#define NSOS_MID_EPIPE 32 /**< Broken pipe */
#define NSOS_MID_EDOM 33 /**< Argument too large */
#define NSOS_MID_ERANGE 34 /**< Result too large */
#define NSOS_MID_ENOMSG 35 /**< Unexpected message type */
#define NSOS_MID_EDEADLK 45 /**< Resource deadlock avoided */
#define NSOS_MID_ENOLCK 46 /**< No locks available */
#define NSOS_MID_ENOSTR 60 /**< STREAMS device required */
#define NSOS_MID_ENODATA 61 /**< Missing expected message data */
#define NSOS_MID_ETIME 62 /**< STREAMS timeout occurred */
#define NSOS_MID_ENOSR 63 /**< Insufficient memory */
#define NSOS_MID_EPROTO 71 /**< Generic STREAMS error */
#define NSOS_MID_EBADMSG 77 /**< Invalid STREAMS message */
#define NSOS_MID_ENOSYS 88 /**< Function not implemented */
#define NSOS_MID_ENOTEMPTY 90 /**< Directory not empty */
#define NSOS_MID_ENAMETOOLONG 91 /**< File name too long */
#define NSOS_MID_ELOOP 92 /**< Too many levels of symbolic links */
#define NSOS_MID_EOPNOTSUPP 95 /**< Operation not supported on socket */
#define NSOS_MID_EPFNOSUPPORT 96 /**< Protocol family not supported */
#define NSOS_MID_ECONNRESET 104 /**< Connection reset by peer */
#define NSOS_MID_ENOBUFS 105 /**< No buffer space available */
#define NSOS_MID_EAFNOSUPPORT 106 /**< Addr family not supported */
#define NSOS_MID_EPROTOTYPE 107 /**< Protocol wrong type for socket */
#define NSOS_MID_ENOTSOCK 108 /**< Socket operation on non-socket */
#define NSOS_MID_ENOPROTOOPT 109 /**< Protocol not available */
#define NSOS_MID_ESHUTDOWN 110 /**< Can't send after socket shutdown */
#define NSOS_MID_ECONNREFUSED 111 /**< Connection refused */
#define NSOS_MID_EADDRINUSE 112 /**< Address already in use */
#define NSOS_MID_ECONNABORTED 113 /**< Software caused connection abort */
#define NSOS_MID_ENETUNREACH 114 /**< Network is unreachable */
#define NSOS_MID_ENETDOWN 115 /**< Network is down */
#define NSOS_MID_ETIMEDOUT 116 /**< Connection timed out */
#define NSOS_MID_EHOSTDOWN 117 /**< Host is down */
#define NSOS_MID_EHOSTUNREACH 118 /**< No route to host */
#define NSOS_MID_EINPROGRESS 119 /**< Operation now in progress */
#define NSOS_MID_EALREADY 120 /**< Operation already in progress */
#define NSOS_MID_EDESTADDRREQ 121 /**< Destination address required */
#define NSOS_MID_EMSGSIZE 122 /**< Message size */
#define NSOS_MID_EPROTONOSUPPORT 123 /**< Protocol not supported */
#define NSOS_MID_ESOCKTNOSUPPORT 124 /**< Socket type not supported */
#define NSOS_MID_EADDRNOTAVAIL 125 /**< Can't assign requested address */
#define NSOS_MID_ENETRESET 126 /**< Network dropped connection on reset */
#define NSOS_MID_EISCONN 127 /**< Socket is already connected */
#define NSOS_MID_ENOTCONN 128 /**< Socket is not connected */
#define NSOS_MID_ETOOMANYREFS 129 /**< Too many references: can't splice */
#define NSOS_MID_ENOTSUP 134 /**< Unsupported value */
#define NSOS_MID_EILSEQ 138 /**< Illegal byte sequence */
#define NSOS_MID_EOVERFLOW 139 /**< Value overflow */
#define NSOS_MID_ECANCELED 140 /**< Operation canceled */
int errno_to_nsos_mid(int err);
int errno_from_nsos_mid(int err);
#endif /* __DRIVERS_NET_NSOS_ERRNO_H__ */

532
drivers/net/nsos_sockets.c Normal file
View file

@ -0,0 +1,532 @@
/**
* Copyright (c) 2023-2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* Zephyr (top) side of NSOS (Native Simulator Offloaded Sockets).
*/
#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/offloaded_netdev.h>
#include <zephyr/net/socket_offload.h>
#include <zephyr/sys/fdtable.h>
#include "sockets_internal.h"
#include "nsos.h"
#include "nsos_errno.h"
#include "nsi_host_trampolines.h"
/* Increment by 1 to make sure we do not store the value of 0, which has
* a special meaning in the fdtable subsys.
*/
#define FD_TO_OBJ(fd) ((void *)(intptr_t)((fd) + 1))
#define OBJ_TO_FD(obj) (((int)(intptr_t)(obj)) - 1)
static int socket_family_to_nsos_mid(int family, int *family_mid)
{
switch (family) {
case AF_UNSPEC:
*family_mid = NSOS_MID_AF_UNSPEC;
break;
case AF_INET:
*family_mid = NSOS_MID_AF_INET;
break;
default:
return -NSOS_MID_EAFNOSUPPORT;
}
return 0;
}
static int socket_proto_to_nsos_mid(int proto, int *proto_mid)
{
switch (proto) {
case IPPROTO_IP:
*proto_mid = NSOS_MID_IPPROTO_IP;
break;
case IPPROTO_ICMP:
*proto_mid = NSOS_MID_IPPROTO_ICMP;
break;
case IPPROTO_IGMP:
*proto_mid = NSOS_MID_IPPROTO_IGMP;
break;
case IPPROTO_IPIP:
*proto_mid = NSOS_MID_IPPROTO_IPIP;
break;
case IPPROTO_TCP:
*proto_mid = NSOS_MID_IPPROTO_TCP;
break;
case IPPROTO_UDP:
*proto_mid = NSOS_MID_IPPROTO_UDP;
break;
case IPPROTO_IPV6:
*proto_mid = NSOS_MID_IPPROTO_IPV6;
break;
case IPPROTO_RAW:
*proto_mid = NSOS_MID_IPPROTO_RAW;
break;
default:
return -NSOS_MID_EPROTONOSUPPORT;
}
return 0;
}
static int socket_type_to_nsos_mid(int type, int *type_mid)
{
switch (type) {
case SOCK_STREAM:
*type_mid = NSOS_MID_SOCK_STREAM;
break;
case SOCK_DGRAM:
*type_mid = NSOS_MID_SOCK_DGRAM;
break;
case SOCK_RAW:
*type_mid = NSOS_MID_SOCK_RAW;
break;
default:
return -NSOS_MID_ESOCKTNOSUPPORT;
}
return 0;
}
static int socket_flags_to_nsos_mid(int flags)
{
int flags_mid = 0;
nsos_socket_flag_convert(&flags, ZSOCK_MSG_PEEK,
&flags_mid, NSOS_MID_MSG_PEEK);
nsos_socket_flag_convert(&flags, ZSOCK_MSG_TRUNC,
&flags_mid, NSOS_MID_MSG_TRUNC);
nsos_socket_flag_convert(&flags, ZSOCK_MSG_DONTWAIT,
&flags_mid, NSOS_MID_MSG_DONTWAIT);
nsos_socket_flag_convert(&flags, ZSOCK_MSG_WAITALL,
&flags_mid, NSOS_MID_MSG_WAITALL);
if (flags != 0) {
return -NSOS_MID_EINVAL;
}
return flags_mid;
}
static const struct socket_op_vtable nsos_socket_fd_op_vtable;
static int nsos_socket_create(int family, int type, int proto)
{
int fd;
int sock;
int family_mid;
int type_mid;
int proto_mid;
int err;
err = socket_family_to_nsos_mid(family, &family_mid);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
err = socket_type_to_nsos_mid(type, &type_mid);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
err = socket_proto_to_nsos_mid(proto, &proto_mid);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
fd = z_reserve_fd();
if (fd < 0) {
return -1;
}
sock = nsos_adapt_socket(family_mid, type_mid, proto_mid);
if (sock < 0) {
errno = errno_from_nsos_mid(-sock);
goto free_fd;
}
z_finalize_fd(fd, FD_TO_OBJ(sock), &nsos_socket_fd_op_vtable.fd_vtable);
return fd;
free_fd:
z_free_fd(fd);
return -1;
}
static int nsos_adapt_get_zephyr_errno(void)
{
return errno_from_nsos_mid(nsos_adapt_get_errno());
}
static ssize_t nsos_read(void *obj, void *buf, size_t sz)
{
int ret;
ret = nsi_host_read(OBJ_TO_FD(obj), buf, sz);
if (ret < 0) {
errno = nsos_adapt_get_zephyr_errno();
}
return ret;
}
static ssize_t nsos_write(void *obj, const void *buf, size_t sz)
{
int ret;
ret = nsi_host_write(OBJ_TO_FD(obj), buf, sz);
if (ret < 0) {
errno = nsos_adapt_get_zephyr_errno();
}
return ret;
}
static int nsos_close(void *obj)
{
int ret;
ret = nsi_host_close(OBJ_TO_FD(obj));
if (ret < 0) {
errno = nsos_adapt_get_zephyr_errno();
}
return ret;
}
static int nsos_ioctl(void *obj, unsigned int request, va_list args)
{
switch (request) {
case ZFD_IOCTL_POLL_PREPARE:
return -EXDEV;
case ZFD_IOCTL_POLL_UPDATE:
return -EOPNOTSUPP;
case ZFD_IOCTL_POLL_OFFLOAD:
return -EOPNOTSUPP;
}
return -EINVAL;
}
static int sockaddr_to_nsos_mid(const struct sockaddr *addr, socklen_t *addrlen,
struct nsos_mid_sockaddr **addr_mid, size_t *addrlen_mid)
{
if (!addr || !addrlen) {
*addr_mid = NULL;
*addrlen_mid = 0;
return 0;
}
switch (addr->sa_family) {
case AF_INET: {
const struct sockaddr_in *addr_in =
(const struct sockaddr_in *)addr;
struct nsos_mid_sockaddr_in *addr_in_mid =
(struct nsos_mid_sockaddr_in *)*addr_mid;
if (*addrlen < sizeof(*addr_in)) {
return -NSOS_MID_EINVAL;
}
addr_in_mid->sin_family = NSOS_MID_AF_INET;
addr_in_mid->sin_port = addr_in->sin_port;
addr_in_mid->sin_addr = addr_in->sin_addr.s_addr;
*addrlen_mid = sizeof(*addr_in_mid);
return 0;
}
}
return -NSOS_MID_EINVAL;
}
static int sockaddr_from_nsos_mid(struct sockaddr *addr, socklen_t *addrlen,
const struct nsos_mid_sockaddr *addr_mid, size_t addrlen_mid)
{
if (!addr || !addrlen) {
return 0;
}
switch (addr_mid->sa_family) {
case NSOS_MID_AF_INET: {
const struct nsos_mid_sockaddr_in *addr_in_mid =
(const struct nsos_mid_sockaddr_in *)addr_mid;
struct sockaddr_in addr_in;
addr_in.sin_family = AF_INET;
addr_in.sin_port = addr_in_mid->sin_port;
addr_in.sin_addr.s_addr = addr_in_mid->sin_addr;
memcpy(addr, &addr_in, MIN(*addrlen, sizeof(addr_in)));
*addrlen = sizeof(addr_in);
return 0;
}
}
return -NSOS_MID_EINVAL;
}
static int nsos_bind(void *obj, const struct sockaddr *addr, socklen_t addrlen)
{
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
size_t addrlen_mid;
int ret;
ret = sockaddr_to_nsos_mid(addr, &addrlen, &addr_mid, &addrlen_mid);
if (ret < 0) {
goto return_ret;
}
ret = nsos_adapt_bind(OBJ_TO_FD(obj), addr_mid, addrlen_mid);
return_ret:
if (ret < 0) {
errno = errno_from_nsos_mid(-ret);
return -1;
}
return ret;
}
static int nsos_connect(void *obj, const struct sockaddr *addr, socklen_t addrlen)
{
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
size_t addrlen_mid;
int ret;
ret = sockaddr_to_nsos_mid(addr, &addrlen, &addr_mid, &addrlen_mid);
if (ret < 0) {
goto return_ret;
}
ret = nsos_adapt_connect(OBJ_TO_FD(obj), addr_mid, addrlen_mid);
return_ret:
if (ret < 0) {
errno = errno_from_nsos_mid(-ret);
return -1;
}
return ret;
}
static int nsos_listen(void *obj, int backlog)
{
int ret;
ret = nsos_adapt_listen(OBJ_TO_FD(obj), backlog);
if (ret < 0) {
errno = errno_from_nsos_mid(-ret);
return -1;
}
return ret;
}
static int nsos_accept(void *obj, struct sockaddr *addr, socklen_t *addrlen)
{
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
size_t addrlen_mid = sizeof(addr_storage_mid);
int ret;
ret = nsos_adapt_accept(OBJ_TO_FD(obj), addr_mid, &addrlen_mid);
if (ret < 0) {
errno = errno_from_nsos_mid(-ret);
return -1;
}
adapt_fd = ret;
ret = sockaddr_from_nsos_mid(addr, addrlen, addr_mid, addrlen_mid);
if (ret < 0) {
errno = errno_from_nsos_mid(-ret);
goto close_adapt_fd;
}
zephyr_fd = z_reserve_fd();
if (zephyr_fd < 0) {
goto close_adapt_fd;
}
z_finalize_fd(zephyr_fd, FD_TO_OBJ(adapt_fd), &nsos_socket_fd_op_vtable.fd_vtable);
return zephyr_fd;
close_adapt_fd:
nsi_host_close(adapt_fd);
return -1;
}
static ssize_t nsos_sendto(void *obj, const void *buf, size_t len, int flags,
const struct sockaddr *addr, socklen_t addrlen)
{
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
size_t addrlen_mid = sizeof(addr_storage_mid);
int flags_mid;
int ret;
ret = socket_flags_to_nsos_mid(flags);
if (ret < 0) {
goto return_ret;
}
flags_mid = ret;
ret = sockaddr_to_nsos_mid(addr, &addrlen, &addr_mid, &addrlen_mid);
if (ret < 0) {
goto return_ret;
}
ret = nsos_adapt_sendto(OBJ_TO_FD(obj), buf, len, flags_mid,
addr_mid, addrlen_mid);
return_ret:
if (ret < 0) {
errno = errno_from_nsos_mid(-ret);
return -1;
}
return ret;
}
static ssize_t nsos_sendmsg(void *obj, const struct msghdr *msg, int flags)
{
errno = ENOTSUP;
return -1;
}
static ssize_t nsos_recvfrom(void *obj, void *buf, size_t len, int flags,
struct sockaddr *addr, socklen_t *addrlen)
{
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
size_t addrlen_mid = sizeof(addr_storage_mid);
int flags_mid;
int ret;
ret = socket_flags_to_nsos_mid(flags);
if (ret < 0) {
goto return_ret;
}
flags_mid = ret;
ret = nsos_adapt_recvfrom(OBJ_TO_FD(obj), buf, len, flags_mid,
addr_mid, &addrlen_mid);
if (ret < 0) {
goto return_ret;
}
sockaddr_from_nsos_mid(addr, addrlen, addr_mid, addrlen_mid);
return_ret:
if (ret < 0) {
errno = errno_from_nsos_mid(-ret);
return -1;
}
return ret;
}
static ssize_t nsos_recvmsg(void *obj, struct msghdr *msg, int flags)
{
errno = ENOTSUP;
return -1;
}
static const struct socket_op_vtable nsos_socket_fd_op_vtable = {
.fd_vtable = {
.read = nsos_read,
.write = nsos_write,
.close = nsos_close,
.ioctl = nsos_ioctl,
},
.bind = nsos_bind,
.connect = nsos_connect,
.listen = nsos_listen,
.accept = nsos_accept,
.sendto = nsos_sendto,
.sendmsg = nsos_sendmsg,
.recvfrom = nsos_recvfrom,
.recvmsg = nsos_recvmsg,
};
static bool nsos_is_supported(int family, int type, int proto)
{
int dummy;
int err;
err = socket_family_to_nsos_mid(family, &dummy);
if (err) {
return false;
}
err = socket_type_to_nsos_mid(type, &dummy);
if (err) {
return false;
}
err = socket_proto_to_nsos_mid(proto, &dummy);
if (err) {
return false;
}
return true;
}
NET_SOCKET_OFFLOAD_REGISTER(nsos, CONFIG_NET_SOCKETS_OFFLOAD_PRIORITY, AF_UNSPEC,
nsos_is_supported, nsos_socket_create);
static int nsos_socket_offload_init(const struct device *arg)
{
ARG_UNUSED(arg);
return 0;
}
static void nsos_iface_api_init(struct net_if *iface)
{
iface->if_dev->socket_offload = nsos_socket_create;
}
static int nsos_iface_enable(const struct net_if *iface, bool enabled)
{
ARG_UNUSED(iface);
ARG_UNUSED(enabled);
return 0;
}
static struct offloaded_if_api nsos_iface_offload_api = {
.iface_api.init = nsos_iface_api_init,
.enable = nsos_iface_enable,
};
NET_DEVICE_OFFLOAD_INIT(nsos_socket, "nsos_socket",
nsos_socket_offload_init,
NULL,
NULL, NULL,
0, &nsos_iface_offload_api, NET_ETH_MTU);