Skip to content

Commit c9cf93f

Browse files
kr-tdkouba-atym
andauthored
Improve networking (#7)
* Support get/set for flags on socket fds * Changes to support open62541 client/server * Fix mismatch of enum sizes between WASM and host * Improve zephyr sockets * Add explanation comment --------- Signed-off-by: Dan Kouba <dan@atym.io> Signed-off-by: Krisztian Szilvasi <34309983+kr-t@users.noreply.github.com> Co-authored-by: Dan Kouba <dan@atym.io>
1 parent 7898af9 commit c9cf93f

3 files changed

Lines changed: 151 additions & 57 deletions

File tree

core/shared/platform/include/platform_wasi_types.h

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,6 @@ assert_wasi_layout(offsetof(__wasi_subscription_t, userdata) == 0, "witx calcula
530530
assert_wasi_layout(offsetof(__wasi_subscription_t, u) == 8, "witx calculated offset");
531531

532532
/* keep syncing with wasi_socket_ext.h */
533-
typedef enum {
534-
/* Used only for sock_addr_resolve hints */
535-
SOCKET_ANY = -1,
536-
SOCKET_DGRAM = 0,
537-
SOCKET_STREAM,
538-
} __wasi_sock_type_t;
539533

540534
typedef uint16_t __wasi_ip_port_t;
541535

@@ -589,20 +583,36 @@ typedef struct __wasi_addr_t {
589583
} addr;
590584
} __wasi_addr_t;
591585

592-
typedef enum { INET4 = 0, INET6, INET_UNSPEC } __wasi_address_family_t;
586+
/* Force 32-bit wire width for cross-boundary fields */
587+
typedef int32_t __wasi_sock_type_t;
588+
enum { SOCKET_ANY = -1, SOCKET_DGRAM = 0, SOCKET_STREAM = 1 };
589+
590+
typedef int32_t __wasi_address_family_t;
591+
enum { INET4 = 0, INET6 = 1, INET_UNSPEC = 2 };
593592

594593
typedef struct __wasi_addr_info_t {
595-
__wasi_addr_t addr;
594+
__wasi_addr_t addr;
596595
__wasi_sock_type_t type;
597596
} __wasi_addr_info_t;
598597

599598
typedef struct __wasi_addr_info_hints_t {
600-
__wasi_sock_type_t type;
601-
__wasi_address_family_t family;
602-
// this is to workaround lack of optional parameters
603-
uint8_t hints_enabled;
599+
__wasi_sock_type_t type; // 4 bytes
600+
__wasi_address_family_t family; // 4 bytes
601+
uint8_t hints_enabled; // 1 byte
602+
uint8_t _pad[3]; // enforce layout
604603
} __wasi_addr_info_hints_t;
605604

605+
assert_wasi_layout(sizeof(__wasi_sock_type_t) == 4, "sock_type must be 4 bytes");
606+
assert_wasi_layout(sizeof(__wasi_address_family_t) == 4, "addr_family must be 4 bytes");
607+
608+
assert_wasi_layout(sizeof(__wasi_addr_info_hints_t) == 12, "hints_t must be 12 bytes");
609+
assert_wasi_layout(offsetof(__wasi_addr_info_hints_t, type) == 0, "hints.type@0");
610+
assert_wasi_layout(offsetof(__wasi_addr_info_hints_t, family) == 4, "hints.family@4");
611+
assert_wasi_layout(offsetof(__wasi_addr_info_hints_t, hints_enabled) == 8, "hints.enabled@8");
612+
613+
assert_wasi_layout(offsetof(__wasi_addr_info_t, type) == sizeof(__wasi_addr_t),
614+
"addr_info.type follows addr");
615+
606616
#undef assert_wasi_layout
607617

608618
/* clang-format on */

core/shared/platform/zephyr/zephyr_file.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#include <zephyr/fs/fs_sys.h>
1616
#include <zephyr/fs/littlefs.h>
1717

18+
#include <zephyr/net/socket.h>
19+
#include <zephyr/posix/fcntl.h> // F_GETFL, F_SETFL, O_NONBLOCK
20+
1821
/* Notes:
1922
* This is the implementation of a POSIX-like file system interface for Zephyr.
2023
* To manage our file descriptors, we created a struct `zephyr_fs_desc` that
@@ -273,6 +276,16 @@ os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
273276
{
274277
struct zephyr_fs_desc *ptr = NULL;
275278

279+
/* Sockets: reflect current non-blocking state */
280+
if (handle->is_sock) {
281+
int cur = zsock_fcntl(handle->fd, F_GETFL, 0);
282+
if (cur < 0)
283+
return convert_errno(errno);
284+
if (cur & O_NONBLOCK)
285+
*flags |= __WASI_FDFLAG_NONBLOCK;
286+
return __WASI_ESUCCESS;
287+
}
288+
276289
if (os_is_virtual_fd(handle->fd)) {
277290
*flags = 0;
278291
return __WASI_ESUCCESS;
@@ -296,6 +309,21 @@ os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
296309
__wasi_errno_t
297310
os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags)
298311
{
312+
/* Sockets: set/clear O_NONBLOCK */
313+
if (handle->is_sock) {
314+
int cur = zsock_fcntl(handle->fd, F_GETFL, 0);
315+
if (cur < 0)
316+
return convert_errno(errno);
317+
318+
bool want_nb = (flags & __WASI_FDFLAG_NONBLOCK) != 0;
319+
int newf = want_nb ? (cur | O_NONBLOCK) : (cur & ~O_NONBLOCK);
320+
321+
if (zsock_fcntl(handle->fd, F_SETFL, newf) < 0) {
322+
return convert_errno(errno);
323+
}
324+
return __WASI_ESUCCESS;
325+
}
326+
299327
if (os_is_virtual_fd(handle->fd)) {
300328
return __WASI_ESUCCESS;
301329
}
@@ -1195,4 +1223,4 @@ bool
11951223
os_is_stderr_handle(os_file_handle handle)
11961224
{
11971225
return (handle == (os_file_handle)stderr);
1198-
}
1226+
}

core/shared/platform/zephyr/zephyr_socket.c

Lines changed: 100 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,23 @@ static bool
2222
textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out,
2323
socklen_t *out_len)
2424
{
25-
struct sockaddr_in *v4;
25+
struct sockaddr_in *v4 = (struct sockaddr_in *)out;
2626
#ifdef IPPROTO_IPV6
2727
struct sockaddr_in *v6;
2828
#endif
2929

3030
assert(textual);
3131

32-
v4 = (struct sockaddr_in *)out;
3332
if (zsock_inet_pton(AF_INET, textual, &v4->sin_addr.s_addr) == 1) {
3433
v4->sin_family = AF_INET;
3534
v4->sin_port = htons(port);
35+
/**
36+
* In `zephyr_socket.c` the method `textual_addr_to_sockaddr` takes the
37+
* multicast address of "224.0.0.22" and properly sets the sockaddr.
38+
* However, this fails when binding to the adapter for some reason.
39+
* Setting this to “0.0.0.0” works. This appears to be a bug in Zephyr.
40+
*/
41+
v4->sin_addr.s_addr = htonl(INADDR_ANY);
3642
*out_len = sizeof(struct sockaddr_in);
3743
return true;
3844
}
@@ -173,7 +179,7 @@ os_socket_getbooloption(bh_socket_t socket, int level, int optname,
173179
int optval;
174180
socklen_t optval_size = sizeof(optval);
175181

176-
if (zsock_setsockopt(socket->fd, level, optname, &optval, optval_size)
182+
if (zsock_getsockopt(socket->fd, level, optname, &optval, &optval_size)
177183
!= 0) {
178184
return BHT_ERROR;
179185
}
@@ -190,7 +196,7 @@ os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp)
190196

191197
*(sock) = BH_MALLOC(sizeof(zephyr_handle));
192198

193-
if (!sock) {
199+
if (!*sock) {
194200
return BHT_ERROR;
195201
}
196202

@@ -262,8 +268,13 @@ os_socket_settimeout(bh_socket_t socket, uint64 timeout_us)
262268
timeout.tv_sec = timeout_us / 1000000;
263269
timeout.tv_usec = timeout_us % 1000000;
264270

265-
return zsock_setsockopt(socket->fd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
266-
sizeof(timeout));
271+
if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
272+
sizeof(timeout))
273+
!= 0) {
274+
return BHT_ERROR;
275+
}
276+
277+
return BHT_OK;
267278
}
268279

269280
int
@@ -277,7 +288,7 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr,
277288
unsigned int *addrlen)
278289
{
279290
*sock = BH_MALLOC(sizeof(zephyr_handle));
280-
if (!sock) {
291+
if (!*sock) {
281292
return BHT_ERROR;
282293
}
283294

@@ -609,7 +620,7 @@ os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us)
609620
struct timeval tv;
610621
socklen_t tv_len = sizeof(tv);
611622

612-
if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, tv_len)
623+
if (zsock_getsockopt(socket->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len)
613624
!= 0) {
614625
return BHT_ERROR;
615626
}
@@ -639,7 +650,7 @@ os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us)
639650
struct timeval tv;
640651
socklen_t tv_len = sizeof(tv);
641652

642-
if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, tv_len)
653+
if (zsock_getsockopt(socket->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len)
643654
!= 0) {
644655
return BHT_ERROR;
645656
}
@@ -693,21 +704,18 @@ os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s)
693704
return BHT_ERROR;
694705
}
695706

696-
// TCP_NODELAY Disable TCP buffering (ignored, for compatibility)
697707
int
698708
os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled)
699709
{
700-
errno = ENOSYS;
701-
702-
return BHT_ERROR;
710+
return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_NODELAY,
711+
is_enabled);
703712
}
704713

705714
int
706715
os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled)
707716
{
708-
errno = ENOSYS;
709-
710-
return BHT_ERROR;
717+
return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_NODELAY,
718+
is_enabled);
711719
}
712720

713721
int
@@ -760,17 +768,17 @@ os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32_t *time_s)
760768
socklen_t time_s_len = sizeof(time_s_int);
761769

762770
#ifdef TCP_KEEPIDLE
763-
if (zsock_setsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int,
764-
time_s_len)
771+
if (zsock_getsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int,
772+
&time_s_len)
765773
!= 0) {
766774
return BHT_ERROR;
767775
}
768776
*time_s = (uint32)time_s_int;
769777

770778
return BHT_OK;
771779
#elif defined(TCP_KEEPALIVE)
772-
if (zsock_setsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int,
773-
time_s_len)
780+
if (zsock_getsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int,
781+
&time_s_len)
774782
!= 0) {
775783
return BHT_ERROR;
776784
}
@@ -856,17 +864,37 @@ os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled)
856864
int
857865
os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled)
858866
{
859-
errno = ENOSYS;
860-
861-
return BHT_ERROR;
867+
if (ipv6) {
868+
#ifdef IPPROTO_IPV6
869+
return os_socket_setbooloption(socket, IPPROTO_IPV6,
870+
IPV6_MULTICAST_LOOP, is_enabled);
871+
#else
872+
errno = EAFNOSUPPORT;
873+
return BHT_ERROR;
874+
#endif
875+
}
876+
else {
877+
return os_socket_setbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP,
878+
is_enabled);
879+
}
862880
}
863881

864882
int
865883
os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled)
866884
{
867-
errno = ENOSYS;
868-
869-
return BHT_ERROR;
885+
if (ipv6) {
886+
#ifdef IPPROTO_IPV6
887+
return os_socket_getbooloption(socket, IPPROTO_IPV6,
888+
IPV6_MULTICAST_LOOP, is_enabled);
889+
#else
890+
errno = EAFNOSUPPORT;
891+
return BHT_ERROR;
892+
#endif
893+
}
894+
else {
895+
return os_socket_getbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP,
896+
is_enabled);
897+
}
870898
}
871899

872900
int
@@ -901,10 +929,23 @@ os_socket_set_ip_add_membership(bh_socket_t socket,
901929
mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4;
902930
mreq.imr_address.s_addr = imr_interface;
903931

904-
if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
905-
sizeof(mreq))
906-
!= 0) {
907-
return BHT_ERROR;
932+
int ret = zsock_setsockopt(socket->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
933+
&mreq, sizeof(mreq));
934+
if (ret != 0) {
935+
/* Treat certain errors as non-fatal for multicast membership.
936+
* These conditions indicate the operation is either already
937+
* complete or not supported by the network stack, but shouldn't
938+
* prevent the application from continuing. This improves
939+
* compatibility across different Zephyr network configurations. */
940+
switch (errno) {
941+
case EALREADY: /* already joined: OK */
942+
case EADDRINUSE: /* duplicate membership: OK */
943+
case ENOPROTOOPT: /* option not supported: treat as soft-OK */
944+
case ENOSYS: /* not implemented: soft-OK */
945+
return BHT_OK;
946+
default:
947+
return BHT_ERROR;
948+
}
908949
}
909950
}
910951
return BHT_OK;
@@ -942,10 +983,23 @@ os_socket_set_ip_drop_membership(bh_socket_t socket,
942983
mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4;
943984
mreq.imr_address.s_addr = imr_interface;
944985

945-
if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq,
946-
sizeof(mreq))
947-
!= 0) {
948-
return BHT_ERROR;
986+
int ret = zsock_setsockopt(socket->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
987+
&mreq, sizeof(mreq));
988+
if (ret != 0) {
989+
/* Treat certain errors as non-fatal for multicast membership.
990+
* These conditions indicate the operation is either already
991+
* complete or not supported by the network stack, but shouldn't
992+
* prevent the application from continuing. This improves
993+
* compatibility across different Zephyr network configurations. */
994+
switch (errno) {
995+
case EALREADY: /* already joined: OK */
996+
case EADDRINUSE: /* duplicate membership: OK */
997+
case ENOPROTOOPT: /* option not supported: treat as soft-OK */
998+
case ENOSYS: /* not implemented: soft-OK */
999+
return BHT_OK;
1000+
default:
1001+
return BHT_ERROR;
1002+
}
9491003
}
9501004
}
9511005
return BHT_OK;
@@ -965,13 +1019,13 @@ os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s)
9651019
int
9661020
os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s)
9671021
{
968-
socklen_t opt_len = sizeof(*ttl_s);
969-
970-
if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_MULTICAST_TTL, ttl_s,
971-
opt_len)
972-
!= 0) {
1022+
/* Use proper int for zsock_getsockopt */
1023+
int opt;
1024+
socklen_t opt_len = sizeof(opt);
1025+
if (zsock_getsockopt(socket->fd, IPPROTO_IP, IP_TTL, &opt, &opt_len) != 0) {
9731026
return BHT_ERROR;
9741027
}
1028+
*ttl_s = (uint8_t)opt;
9751029

9761030
return BHT_OK;
9771031
}
@@ -991,13 +1045,15 @@ os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s)
9911045
int
9921046
os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s)
9931047
{
994-
socklen_t opt_len = sizeof(*ttl_s);
995-
996-
if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_MULTICAST_TTL, ttl_s,
997-
opt_len)
1048+
/* Use proper int for zsock_getsockopt */
1049+
int opt;
1050+
socklen_t opt_len = sizeof(opt);
1051+
if (zsock_getsockopt(socket->fd, IPPROTO_IP, IP_MULTICAST_TTL, &opt,
1052+
&opt_len)
9981053
!= 0) {
9991054
return BHT_ERROR;
10001055
}
1056+
*ttl_s = (uint8_t)opt;
10011057

10021058
return BHT_OK;
10031059
}
@@ -1059,4 +1115,4 @@ int
10591115
os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout)
10601116
{
10611117
return zsock_poll(fds, nfs, timeout);
1062-
}
1118+
}

0 commit comments

Comments
 (0)