drivers: nsos: initial support for getsockopt() and setsockopt()

Add initial support for getsockopt() and setsockopt() on SOL_SOCKET level.

Signed-off-by: Marcin Niestroj <m.niestroj@emb.dev>
This commit is contained in:
Marcin Niestroj 2024-05-03 10:13:57 +02:00 committed by Carles Cufí
parent 29f81872ce
commit a3f2b5f4b3
4 changed files with 556 additions and 0 deletions

View file

@ -126,6 +126,10 @@ int nsos_adapt_sendto(int fd, const void *buf, size_t len, int flags,
int nsos_adapt_sendmsg(int fd, const struct nsos_mid_msghdr *msg_mid, int flags);
int nsos_adapt_recvfrom(int fd, void *buf, size_t len, int flags,
struct nsos_mid_sockaddr *addr, size_t *addrlen);
int nsos_adapt_getsockopt(int fd, int level, int optname,
void *optval, size_t *optlen);
int nsos_adapt_setsockopt(int fd, int level, int optname,
const void *optval, size_t optlen);
void nsos_adapt_poll_add(struct nsos_mid_pollfd *pollfd);
void nsos_adapt_poll_remove(struct nsos_mid_pollfd *pollfd);

View file

@ -10,6 +10,8 @@
* Linux (bottom) side of NSOS (Native Simulator Offloaded Sockets).
*/
#define _DEFAULT_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
@ -25,6 +27,7 @@
#include "nsos_errno.h"
#include "nsos_fcntl.h"
#include "nsos_netdb.h"
#include "nsos_socket.h"
#include "board_soc.h"
#include "irq_ctrl.h"
@ -511,6 +514,167 @@ int nsos_adapt_recvfrom(int fd, void *buf, size_t len, int flags,
return ret;
}
static int nsos_adapt_getsockopt_int(int fd, int level, int optname,
void *optval, size_t *nsos_mid_optlen)
{
socklen_t optlen = *nsos_mid_optlen;
int ret;
ret = getsockopt(fd, level, optname, optval, &optlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
*nsos_mid_optlen = optlen;
return 0;
}
int nsos_adapt_getsockopt(int fd, int nsos_mid_level, int nsos_mid_optname,
void *nsos_mid_optval, size_t *nsos_mid_optlen)
{
switch (nsos_mid_level) {
case NSOS_MID_SOL_SOCKET:
switch (nsos_mid_optname) {
case NSOS_MID_SO_ERROR: {
int err;
socklen_t optlen = sizeof(err);
int ret;
ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &optlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
*(int *)nsos_mid_optval = errno_to_nsos_mid(err);
return 0;
}
case NSOS_MID_SO_TYPE: {
int type;
socklen_t optlen = sizeof(type);
int ret;
int err;
ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &optlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
err = socket_type_to_nsos_mid(type, nsos_mid_optval);
if (err) {
return err;
}
return 0;
}
case NSOS_MID_SO_PROTOCOL: {
int proto;
socklen_t optlen = sizeof(proto);
int ret;
int err;
ret = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &proto, &optlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
err = socket_proto_to_nsos_mid(proto, nsos_mid_optval);
if (err) {
return err;
}
return 0;
}
case NSOS_MID_SO_DOMAIN: {
int family;
socklen_t optlen = sizeof(family);
int ret;
int err;
ret = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &family, &optlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
err = socket_family_to_nsos_mid(family, nsos_mid_optval);
if (err) {
return err;
}
return 0;
}
case NSOS_MID_SO_RCVBUF:
return nsos_adapt_getsockopt_int(fd, SOL_SOCKET, SO_RCVBUF,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_SNDBUF:
return nsos_adapt_getsockopt_int(fd, SOL_SOCKET, SO_SNDBUF,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_REUSEADDR:
return nsos_adapt_getsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_REUSEPORT:
return nsos_adapt_getsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_LINGER:
return nsos_adapt_getsockopt_int(fd, SOL_SOCKET, SO_LINGER,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_KEEPALIVE:
return nsos_adapt_getsockopt_int(fd, SOL_SOCKET, SO_KEEPALIVE,
nsos_mid_optval, nsos_mid_optlen);
}
}
return -NSOS_MID_EOPNOTSUPP;
}
static int nsos_adapt_setsockopt_int(int fd, int level, int optname,
const void *optval, size_t optlen)
{
int ret;
ret = setsockopt(fd, level, optname, optval, optlen);
if (ret < 0) {
return -errno_to_nsos_mid(errno);
}
return 0;
}
int nsos_adapt_setsockopt(int fd, int nsos_mid_level, int nsos_mid_optname,
const void *nsos_mid_optval, size_t nsos_mid_optlen)
{
switch (nsos_mid_level) {
case NSOS_MID_SOL_SOCKET:
switch (nsos_mid_optname) {
case NSOS_MID_SO_PRIORITY:
return nsos_adapt_setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_RCVBUF:
return nsos_adapt_setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_SNDBUF:
return nsos_adapt_setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_REUSEADDR:
return nsos_adapt_setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_REUSEPORT:
return nsos_adapt_setsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_LINGER:
return nsos_adapt_setsockopt_int(fd, SOL_SOCKET, SO_LINGER,
nsos_mid_optval, nsos_mid_optlen);
case NSOS_MID_SO_KEEPALIVE:
return nsos_adapt_setsockopt_int(fd, SOL_SOCKET, SO_KEEPALIVE,
nsos_mid_optval, nsos_mid_optlen);
}
}
return -NSOS_MID_EOPNOTSUPP;
}
#define MAP_POLL_EPOLL(_event_from, _event_to) \
if (events_from & (_event_from)) { \
events_from &= ~(_event_from); \

83
drivers/net/nsos_socket.h Normal file
View file

@ -0,0 +1,83 @@
/**
* Copyright (c) 2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __DRIVERS_NET_NSOS_SOCKET_H__
#define __DRIVERS_NET_NSOS_SOCKET_H__
/**
* @name Socket level options (NSOS_MID_SOL_SOCKET)
* @{
*/
/** Socket-level option */
#define NSOS_MID_SOL_SOCKET 1
/* Socket options for NSOS_MID_SOL_SOCKET level */
/** Recording debugging information (ignored, for compatibility) */
#define NSOS_MID_SO_DEBUG 1
/** address reuse */
#define NSOS_MID_SO_REUSEADDR 2
/** Type of the socket */
#define NSOS_MID_SO_TYPE 3
/** Async error */
#define NSOS_MID_SO_ERROR 4
/** Bypass normal routing and send directly to host (ignored, for compatibility) */
#define NSOS_MID_SO_DONTROUTE 5
/** Transmission of broadcast messages is supported (ignored, for compatibility) */
#define NSOS_MID_SO_BROADCAST 6
/** Size of socket send buffer */
#define NSOS_MID_SO_SNDBUF 7
/** Size of socket recv buffer */
#define NSOS_MID_SO_RCVBUF 8
/** Enable sending keep-alive messages on connections */
#define NSOS_MID_SO_KEEPALIVE 9
/** Place out-of-band data into receive stream (ignored, for compatibility) */
#define NSOS_MID_SO_OOBINLINE 10
/** Socket priority */
#define NSOS_MID_SO_PRIORITY 12
/** Socket lingers on close (ignored, for compatibility) */
#define NSOS_MID_SO_LINGER 13
/** Allow multiple sockets to reuse a single port */
#define NSOS_MID_SO_REUSEPORT 15
/** Receive low watermark (ignored, for compatibility) */
#define NSOS_MID_SO_RCVLOWAT 18
/** Send low watermark (ignored, for compatibility) */
#define NSOS_MID_SO_SNDLOWAT 19
/**
* Receive timeout
* Applies to receive functions like recv(), but not to connect()
*/
#define NSOS_MID_SO_RCVTIMEO 20
/** Send timeout */
#define NSOS_MID_SO_SNDTIMEO 21
/** Bind a socket to an interface */
#define NSOS_MID_SO_BINDTODEVICE 25
/** Socket accepts incoming connections (ignored, for compatibility) */
#define NSOS_MID_SO_ACCEPTCONN 30
/** Timestamp TX packets */
#define NSOS_MID_SO_TIMESTAMPING 37
/** Protocol used with the socket */
#define NSOS_MID_SO_PROTOCOL 38
/** Domain used with SOCKET */
#define NSOS_MID_SO_DOMAIN 39
/** Enable SOCKS5 for Socket */
#define NSOS_MID_SO_SOCKS5 60
/** Socket TX time (when the data should be sent) */
#define NSOS_MID_SO_TXTIME 61
/** @} */
#endif /* __DRIVERS_NET_NSOS_SOCKET_H__ */

View file

@ -28,6 +28,7 @@
#include "nsos_errno.h"
#include "nsos_fcntl.h"
#include "nsos_netdb.h"
#include "nsos_socket.h"
#include "nsi_host_trampolines.h"
@ -795,6 +796,308 @@ static ssize_t nsos_recvmsg(void *obj, struct msghdr *msg, int flags)
return -1;
}
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:
return -NSOS_MID_ESOCKTNOSUPPORT;
}
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:
return -NSOS_MID_EPROTONOSUPPORT;
}
return 0;
}
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;
case NSOS_MID_AF_INET6:
*family = AF_INET6;
break;
default:
return -NSOS_MID_EAFNOSUPPORT;
}
return 0;
}
static int nsos_getsockopt_int(struct nsos_socket *sock, int nsos_mid_level, int nsos_mid_optname,
void *optval, socklen_t *optlen)
{
size_t nsos_mid_optlen = sizeof(int);
int err;
if (*optlen != sizeof(int)) {
errno = EINVAL;
return -1;
}
err = nsos_adapt_getsockopt(sock->pollfd.fd, NSOS_MID_SOL_SOCKET,
NSOS_MID_SO_KEEPALIVE, optval, &nsos_mid_optlen);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
*optlen = nsos_mid_optlen;
return 0;
}
static int nsos_getsockopt(void *obj, int level, int optname,
void *optval, socklen_t *optlen)
{
struct nsos_socket *sock = obj;
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_ERROR: {
int nsos_mid_err;
int err;
if (*optlen != sizeof(int)) {
errno = EINVAL;
return -1;
}
err = nsos_adapt_getsockopt(sock->pollfd.fd, NSOS_MID_SOL_SOCKET,
NSOS_MID_SO_ERROR, &nsos_mid_err, NULL);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
*(int *)optval = errno_from_nsos_mid(nsos_mid_err);
return 0;
}
case SO_TYPE: {
int nsos_mid_type;
int err;
if (*optlen != sizeof(nsos_mid_type)) {
errno = EINVAL;
return -1;
}
err = nsos_adapt_getsockopt(sock->pollfd.fd, NSOS_MID_SOL_SOCKET,
NSOS_MID_SO_TYPE, &nsos_mid_type, NULL);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
err = socket_type_from_nsos_mid(nsos_mid_type, optval);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
return 0;
}
case SO_PROTOCOL: {
int nsos_mid_proto;
int err;
if (*optlen != sizeof(nsos_mid_proto)) {
errno = EINVAL;
return -1;
}
err = nsos_adapt_getsockopt(sock->pollfd.fd, NSOS_MID_SOL_SOCKET,
NSOS_MID_SO_PROTOCOL, &nsos_mid_proto, NULL);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
err = socket_proto_from_nsos_mid(nsos_mid_proto, optval);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
return 0;
}
case SO_DOMAIN: {
int nsos_mid_family;
int err;
if (*optlen != sizeof(nsos_mid_family)) {
errno = EINVAL;
return -1;
}
err = nsos_adapt_getsockopt(sock->pollfd.fd, NSOS_MID_SOL_SOCKET,
NSOS_MID_SO_DOMAIN, &nsos_mid_family, NULL);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
err = socket_family_from_nsos_mid(nsos_mid_family, optval);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
return 0;
}
case SO_RCVBUF:
return nsos_getsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_RCVBUF,
optval, optlen);
case SO_SNDBUF:
return nsos_getsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_SNDBUF,
optval, optlen);
case SO_REUSEADDR:
return nsos_getsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_REUSEADDR,
optval, optlen);
case SO_REUSEPORT:
return nsos_getsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_REUSEPORT,
optval, optlen);
case SO_KEEPALIVE:
return nsos_getsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_KEEPALIVE,
optval, optlen);
}
}
errno = EOPNOTSUPP;
return -1;
}
static int nsos_setsockopt_int(struct nsos_socket *sock, int nsos_mid_level, int nsos_mid_optname,
const void *optval, socklen_t optlen)
{
int err;
if (optlen != sizeof(int)) {
errno = EINVAL;
return -1;
}
err = nsos_adapt_setsockopt(sock->pollfd.fd, nsos_mid_level, nsos_mid_optname,
optval, optlen);
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
return 0;
}
static int nsos_setsockopt(void *obj, int level, int optname,
const void *optval, socklen_t optlen)
{
struct nsos_socket *sock = obj;
switch (level) {
case SOL_SOCKET:
switch (optname) {
case SO_PRIORITY: {
int nsos_mid_priority;
int err;
if (optlen != sizeof(uint8_t)) {
errno = EINVAL;
return -1;
}
nsos_mid_priority = *(uint8_t *)optval;
err = nsos_adapt_setsockopt(sock->pollfd.fd, NSOS_MID_SOL_SOCKET,
NSOS_MID_SO_PRIORITY, &nsos_mid_priority,
sizeof(nsos_mid_priority));
if (err) {
errno = errno_from_nsos_mid(-err);
return -1;
}
return 0;
}
case SO_RCVBUF:
return nsos_setsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_RCVBUF,
optval, optlen);
case SO_SNDBUF:
return nsos_setsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_SNDBUF,
optval, optlen);
case SO_REUSEADDR:
return nsos_setsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_REUSEADDR,
optval, optlen);
case SO_REUSEPORT:
return nsos_setsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_REUSEPORT,
optval, optlen);
case SO_LINGER:
return nsos_setsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_LINGER,
optval, optlen);
case SO_KEEPALIVE:
return nsos_setsockopt_int(sock,
NSOS_MID_SOL_SOCKET, NSOS_MID_SO_KEEPALIVE,
optval, optlen);
}
}
errno = EOPNOTSUPP;
return -1;
}
static const struct socket_op_vtable nsos_socket_fd_op_vtable = {
.fd_vtable = {
.read = nsos_read,
@ -810,6 +1113,8 @@ static const struct socket_op_vtable nsos_socket_fd_op_vtable = {
.sendmsg = nsos_sendmsg,
.recvfrom = nsos_recvfrom,
.recvmsg = nsos_recvmsg,
.getsockopt = nsos_getsockopt,
.setsockopt = nsos_setsockopt,
};
static bool nsos_is_supported(int family, int type, int proto)