diff --git a/drivers/net/CMakeLists.txt b/drivers/net/CMakeLists.txt index 0b431455a2a..d839b9690c3 100644 --- a/drivers/net/CMakeLists.txt +++ b/drivers/net/CMakeLists.txt @@ -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() diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index e0167e80ccb..5ebda70c1d1 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -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 diff --git a/drivers/net/nsos.h b/drivers/net/nsos.h new file mode 100644 index 00000000000..13a6b8f8826 --- /dev/null +++ b/drivers/net/nsos.h @@ -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 +#include + +/* 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__ */ diff --git a/drivers/net/nsos_adapt.c b/drivers/net/nsos_adapt.c new file mode 100644 index 00000000000..37dfe9b2368 --- /dev/null +++ b/drivers/net/nsos_adapt.c @@ -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 +#include +#include +#include +#include + +#include "nsos.h" +#include "nsos_errno.h" + +#include "nsi_tracing.h" + +#include + +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; +} diff --git a/drivers/net/nsos_errno.c b/drivers/net/nsos_errno.c new file mode 100644 index 00000000000..417c1af4f06 --- /dev/null +++ b/drivers/net/nsos_errno.c @@ -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; +} diff --git a/drivers/net/nsos_errno.h b/drivers/net/nsos_errno.h new file mode 100644 index 00000000000..8e9b6214671 --- /dev/null +++ b/drivers/net/nsos_errno.h @@ -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 + +#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__ */ diff --git a/drivers/net/nsos_sockets.c b/drivers/net/nsos_sockets.c new file mode 100644 index 00000000000..4d3ea153ba1 --- /dev/null +++ b/drivers/net/nsos_sockets.c @@ -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 +#include +#include +#include +#include + +#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);