drivers: net: nsos: add DNS offload support

Extend driver to support DNS by offloading getaddrinfo() and freeaddrinfo()
APIs.

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

View file

@ -15,7 +15,9 @@ if(CONFIG_NET_NATIVE_OFFLOADED_SOCKETS)
${ZEPHYR_BASE}/subsys/net/lib/sockets
)
zephyr_library_sources(nsos_errno.c)
zephyr_library_sources(nsos_netdb.c)
zephyr_library_sources(nsos_sockets.c)
target_sources(native_simulator INTERFACE nsos_adapt.c)
target_sources(native_simulator INTERFACE nsos_errno.c)
target_sources(native_simulator INTERFACE nsos_netdb.c)
endif()

View file

@ -75,6 +75,17 @@ struct nsos_mid_pollfd {
short revents;
};
struct nsos_mid_addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
struct nsos_mid_sockaddr *ai_addr;
char *ai_canonname;
struct nsos_mid_addrinfo *ai_next;
};
static inline void nsos_socket_flag_convert(int *flags_a, int flag_a,
int *flags_b, int flag_b)
{
@ -97,4 +108,11 @@ int nsos_adapt_sendto(int fd, const void *buf, size_t len, 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_getaddrinfo(const char *node, const char *service,
const struct nsos_mid_addrinfo *hints,
struct nsos_mid_addrinfo **res,
int *system_errno);
void nsos_adapt_freeaddrinfo(struct nsos_mid_addrinfo *res);
#endif /* __DRIVERS_NET_NSOS_H__ */

View file

@ -11,18 +11,27 @@
*/
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "nsos.h"
#include "nsos_errno.h"
#include "nsos_netdb.h"
#include "nsi_tracing.h"
#include <stdio.h>
#ifndef CONTAINER_OF
#define CONTAINER_OF(ptr, type, field) \
((type *)(((char *)(ptr)) - offsetof(type, field)))
#endif
int nsos_adapt_get_errno(void)
{
return errno_to_nsos_mid(errno);
@ -45,6 +54,23 @@ static int socket_family_from_nsos_mid(int family_mid, int *family)
return 0;
}
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:
nsi_print_warning("%s: socket family %d not supported\n", __func__, family);
return -NSOS_MID_EAFNOSUPPORT;
}
return 0;
}
static int socket_proto_from_nsos_mid(int proto_mid, int *proto)
{
switch (proto_mid) {
@ -80,6 +106,41 @@ static int socket_proto_from_nsos_mid(int proto_mid, int *proto)
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:
nsi_print_warning("%s: socket protocol %d not supported\n", __func__, proto);
return -NSOS_MID_EPROTONOSUPPORT;
}
return 0;
}
static int socket_type_from_nsos_mid(int type_mid, int *type)
{
switch (type_mid) {
@ -100,6 +161,26 @@ static int socket_type_from_nsos_mid(int type_mid, int *type)
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:
nsi_print_warning("%s: socket type %d not supported\n", __func__, type);
return -NSOS_MID_ESOCKTNOSUPPORT;
}
return 0;
}
static int socket_flags_from_nsos_mid(int flags_mid)
{
int flags = 0;
@ -331,3 +412,146 @@ int nsos_adapt_recvfrom(int fd, void *buf, size_t len, int flags,
return ret;
}
struct nsos_addrinfo_wrap {
struct nsos_mid_addrinfo addrinfo_mid;
struct nsos_mid_sockaddr_storage addr_storage;
struct addrinfo *addrinfo;
};
static int addrinfo_to_nsos_mid(struct addrinfo *res,
struct nsos_mid_addrinfo **mid_res)
{
struct nsos_addrinfo_wrap *nsos_res_wraps;
size_t idx_res = 0;
size_t n_res = 0;
int ret;
for (struct addrinfo *res_p = res; res_p; res_p = res_p->ai_next) {
n_res++;
}
if (n_res == 0) {
return 0;
}
nsos_res_wraps = calloc(n_res, sizeof(*nsos_res_wraps));
if (!nsos_res_wraps) {
return -NSOS_MID_ENOMEM;
}
for (struct addrinfo *res_p = res; res_p; res_p = res_p->ai_next, idx_res++) {
struct nsos_addrinfo_wrap *wrap = &nsos_res_wraps[idx_res];
wrap->addrinfo = res_p;
wrap->addrinfo_mid.ai_flags = res_p->ai_flags;
ret = socket_family_to_nsos_mid(res_p->ai_family, &wrap->addrinfo_mid.ai_family);
if (ret < 0) {
goto free_wraps;
}
ret = socket_type_to_nsos_mid(res_p->ai_socktype, &wrap->addrinfo_mid.ai_socktype);
if (ret < 0) {
goto free_wraps;
}
ret = socket_proto_to_nsos_mid(res_p->ai_protocol, &wrap->addrinfo_mid.ai_protocol);
if (ret < 0) {
goto free_wraps;
}
wrap->addrinfo_mid.ai_addr =
(struct nsos_mid_sockaddr *)&wrap->addr_storage;
wrap->addrinfo_mid.ai_addrlen = sizeof(wrap->addr_storage);
ret = sockaddr_to_nsos_mid(res_p->ai_addr, res_p->ai_addrlen,
wrap->addrinfo_mid.ai_addr,
&wrap->addrinfo_mid.ai_addrlen);
if (ret < 0) {
goto free_wraps;
}
wrap->addrinfo_mid.ai_canonname =
res_p->ai_canonname ? strdup(res_p->ai_canonname) : NULL;
wrap->addrinfo_mid.ai_next = &wrap[1].addrinfo_mid;
}
nsos_res_wraps[n_res - 1].addrinfo_mid.ai_next = NULL;
*mid_res = &nsos_res_wraps->addrinfo_mid;
return 0;
free_wraps:
for (struct nsos_mid_addrinfo *res_p = &nsos_res_wraps[0].addrinfo_mid;
res_p;
res_p = res_p->ai_next) {
free(res_p->ai_canonname);
}
free(nsos_res_wraps);
return ret;
}
int nsos_adapt_getaddrinfo(const char *node, const char *service,
const struct nsos_mid_addrinfo *hints_mid,
struct nsos_mid_addrinfo **res_mid,
int *system_errno)
{
struct addrinfo hints;
struct addrinfo *res = NULL;
int ret;
if (hints_mid) {
hints.ai_flags = hints_mid->ai_flags;
ret = socket_family_from_nsos_mid(hints_mid->ai_family, &hints.ai_family);
if (ret < 0) {
*system_errno = -ret;
return NSOS_MID_EAI_SYSTEM;
}
ret = socket_type_from_nsos_mid(hints_mid->ai_socktype, &hints.ai_socktype);
if (ret < 0) {
*system_errno = -ret;
return NSOS_MID_EAI_SYSTEM;
}
ret = socket_proto_from_nsos_mid(hints_mid->ai_protocol, &hints.ai_protocol);
if (ret < 0) {
*system_errno = -ret;
return NSOS_MID_EAI_SYSTEM;
}
}
ret = getaddrinfo(node, service,
hints_mid ? &hints : NULL,
&res);
if (ret < 0) {
return ret;
}
ret = addrinfo_to_nsos_mid(res, res_mid);
if (ret < 0) {
*system_errno = -ret;
return NSOS_MID_EAI_SYSTEM;
}
return ret;
}
void nsos_adapt_freeaddrinfo(struct nsos_mid_addrinfo *res_mid)
{
struct nsos_addrinfo_wrap *wrap =
CONTAINER_OF(res_mid, struct nsos_addrinfo_wrap, addrinfo_mid);
for (struct nsos_mid_addrinfo *res_p = res_mid; res_p; res_p = res_p->ai_next) {
free(res_p->ai_canonname);
}
freeaddrinfo(wrap->addrinfo);
free(wrap);
}

73
drivers/net/nsos_netdb.c Normal file
View file

@ -0,0 +1,73 @@
/**
* Copyright (c) 2023-2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*
* netdb.h related code common to Zephyr (top: nsos_sockets.c) and Linux
* (bottom: nsos_adapt.c).
*
* It is needed by both sides to share the same macro definitions/values
* (prefixed with NSOS_MID_), which is not possible to achieve with two separate
* standard libc libraries, since they use different values for the same
* symbols.
*/
#include "nsos_netdb.h"
#ifdef __ZEPHYR__
#include <zephyr/net/socket.h>
#define ERR(_name) \
{ DNS_ ## _name, NSOS_MID_ ## _name }
#else
#include <netdb.h>
#define ERR(_name) \
{ _name, NSOS_MID_ ## _name }
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif
struct nsos_eai_map {
int err;
int mid_err;
};
static const struct nsos_eai_map map[] = {
ERR(EAI_BADFLAGS),
ERR(EAI_NONAME),
ERR(EAI_AGAIN),
ERR(EAI_FAIL),
ERR(EAI_FAMILY),
ERR(EAI_SOCKTYPE),
ERR(EAI_SERVICE),
ERR(EAI_MEMORY),
ERR(EAI_SYSTEM),
ERR(EAI_OVERFLOW),
};
int eai_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 eai_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;
}

36
drivers/net/nsos_netdb.h Normal file
View file

@ -0,0 +1,36 @@
/**
* Copyright (c) 2023-2024 Marcin Niestroj
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __DRIVERS_NET_NSOS_NETDB_H__
#define __DRIVERS_NET_NSOS_NETDB_H__
enum nsos_resolve_status {
/** Invalid value for `ai_flags' field */
NSOS_MID_EAI_BADFLAGS = -1,
/** NAME or SERVICE is unknown */
NSOS_MID_EAI_NONAME = -2,
/** Temporary failure in name resolution */
NSOS_MID_EAI_AGAIN = -3,
/** Non-recoverable failure in name res */
NSOS_MID_EAI_FAIL = -4,
/** `ai_family' not supported */
NSOS_MID_EAI_FAMILY = -6,
/** `ai_socktype' not supported */
NSOS_MID_EAI_SOCKTYPE = -7,
/** SRV not supported for `ai_socktype' */
NSOS_MID_EAI_SERVICE = -8,
/** Memory allocation failure */
NSOS_MID_EAI_MEMORY = -10,
/** System error returned in `errno' */
NSOS_MID_EAI_SYSTEM = -11,
/** Argument buffer overflow */
NSOS_MID_EAI_OVERFLOW = -12,
};
int eai_to_nsos_mid(int err);
int eai_from_nsos_mid(int err);
#endif /* __DRIVERS_NET_NSOS_NETDB_H__ */

View file

@ -10,6 +10,10 @@
* Zephyr (top) side of NSOS (Native Simulator Offloaded Sockets).
*/
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/offloaded_netdev.h>
@ -19,6 +23,7 @@
#include "sockets_internal.h"
#include "nsos.h"
#include "nsos_errno.h"
#include "nsos_netdb.h"
#include "nsi_host_trampolines.h"
@ -501,6 +506,136 @@ static bool nsos_is_supported(int family, int type, int proto)
NET_SOCKET_OFFLOAD_REGISTER(nsos, CONFIG_NET_SOCKETS_OFFLOAD_PRIORITY, AF_UNSPEC,
nsos_is_supported, nsos_socket_create);
struct zsock_addrinfo_wrap {
struct zsock_addrinfo addrinfo;
struct sockaddr_storage addr_storage;
struct nsos_mid_addrinfo *addrinfo_mid;
};
/*
* (Zephyr)
* zsock_addrinfo_wrap
* -----------------------
* | zsock_addrinfo |
* ----------------------- (trampoline)
* | sockaddr_storage | nsos_addrinfo_wrap
* ----------------------- -----------------------------
* | nsos_mid_addrinfo * | -> | nsos_mid_addrinfo |
* ----------------------- -----------------------------
* | nsos_mid_sockaddr_storage |
* ----------------------------- (Linux host)
* | addrinfo * | -> addrinfo
* -----------------------------
*/
static int addrinfo_from_nsos_mid(struct nsos_mid_addrinfo *nsos_res,
struct zsock_addrinfo **res)
{
struct zsock_addrinfo_wrap *res_wraps;
size_t idx_res = 0;
size_t n_res = 0;
for (struct nsos_mid_addrinfo *res_p = nsos_res; res_p; res_p = res_p->ai_next) {
n_res++;
}
if (n_res == 0) {
return 0;
}
res_wraps = k_calloc(n_res, sizeof(*res_wraps));
if (!res_wraps) {
return -ENOMEM;
}
for (struct nsos_mid_addrinfo *res_p = nsos_res; res_p; res_p = res_p->ai_next, idx_res++) {
struct zsock_addrinfo_wrap *wrap = &res_wraps[idx_res];
wrap->addrinfo_mid = res_p;
wrap->addrinfo.ai_flags = res_p->ai_flags;
wrap->addrinfo.ai_family = res_p->ai_family;
wrap->addrinfo.ai_socktype = res_p->ai_socktype;
wrap->addrinfo.ai_protocol = res_p->ai_protocol;
wrap->addrinfo.ai_addr =
(struct sockaddr *)&wrap->addr_storage;
wrap->addrinfo.ai_addrlen = sizeof(wrap->addr_storage);
sockaddr_from_nsos_mid(wrap->addrinfo.ai_addr, &wrap->addrinfo.ai_addrlen,
res_p->ai_addr, res_p->ai_addrlen);
wrap->addrinfo.ai_canonname =
res_p->ai_canonname ? strdup(res_p->ai_canonname) : NULL;
wrap->addrinfo.ai_next = &wrap[1].addrinfo;
}
res_wraps[n_res - 1].addrinfo.ai_next = NULL;
*res = &res_wraps->addrinfo;
return 0;
}
static int nsos_getaddrinfo(const char *node, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res)
{
struct nsos_mid_addrinfo hints_mid;
struct nsos_mid_addrinfo *res_mid;
int system_errno;
int ret;
if (!res) {
return -EINVAL;
}
if (hints) {
hints_mid.ai_flags = hints->ai_flags;
hints_mid.ai_family = hints->ai_family;
hints_mid.ai_socktype = hints->ai_socktype;
hints_mid.ai_protocol = hints->ai_protocol;
}
ret = nsos_adapt_getaddrinfo(node, service,
hints ? &hints_mid : NULL,
&res_mid,
&system_errno);
if (ret < 0) {
if (ret == NSOS_MID_EAI_SYSTEM) {
errno = errno_from_nsos_mid(system_errno);
}
return eai_from_nsos_mid(ret);
}
ret = addrinfo_from_nsos_mid(res_mid, res);
if (ret < 0) {
errno = -ret;
return EAI_SYSTEM;
}
return ret;
}
static void nsos_freeaddrinfo(struct zsock_addrinfo *res)
{
struct zsock_addrinfo_wrap *wrap =
CONTAINER_OF(res, struct zsock_addrinfo_wrap, addrinfo);
for (struct zsock_addrinfo *res_p = res; res_p; res_p = res_p->ai_next) {
free(res_p->ai_canonname);
}
nsos_adapt_freeaddrinfo(wrap->addrinfo_mid);
k_free(wrap);
}
static const struct socket_dns_offload nsos_dns_ops = {
.getaddrinfo = nsos_getaddrinfo,
.freeaddrinfo = nsos_freeaddrinfo,
};
static int nsos_socket_offload_init(const struct device *arg)
{
ARG_UNUSED(arg);
@ -511,6 +646,8 @@ static int nsos_socket_offload_init(const struct device *arg)
static void nsos_iface_api_init(struct net_if *iface)
{
iface->if_dev->socket_offload = nsos_socket_create;
socket_offload_dns_register(&nsos_dns_ops);
}
static int nsos_iface_enable(const struct net_if *iface, bool enabled)