* [PATCH] tidbits: net-udp: add server and client mode
@ 2026-05-23 20:10 Hannes Diethelm
2026-05-25 16:51 ` Philippe Gerum
0 siblings, 1 reply; 10+ messages in thread
From: Hannes Diethelm @ 2026-05-23 20:10 UTC (permalink / raw)
To: xenomai, rpm; +Cc: Hannes Diethelm
Additionally, fix memory leaks
Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
---
tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 240 insertions(+), 18 deletions(-)
diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c
index 5673644..3cd7cb2 100644
--- a/tidbits/oob-net-udp.c
+++ b/tidbits/oob-net-udp.c
@@ -2,9 +2,23 @@
* SPDX-License-Identifier: MIT
*
* This tidbit demonstrates out-of-band networking, by
- * sending[-S]/receiving[-R] UDP packets to/from a particular IP
+ * transmitting[-T]/receiving[-R] UDP packets to/from a particular IP
* address[-a] and port[-p].
*
+ * The server mode [-S] receives a packet from on a given
+ * port and sends a response to the sender's ip/port.
+ *
+ * The client mode [-S] sends a packet to a port/ip
+ * and receives the response from a server. The round
+ * trip time is measured.
+ *
+ * The client and the transmitter wait [-w] before sending the
+ * next packet to not flood the network.
+ *
+ * For the other side, the same code can be used.
+ * For transmitting / client on a non-oob port: socat - UDP-LISTEN:<port>
+ * For receiving / server on a non-oob port: socat - UDP:<Remote-IP-address>:<port>
+ *
* See https://v4.xenomai.org/core/net/ for details.
*/
@@ -13,6 +27,7 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <unistd.h>
#include <memory.h>
#include <getopt.h>
@@ -30,12 +45,21 @@ static int verbosity = 1;
static void usage(void)
{
fprintf(stderr, "oob-net-udp -a <IP-address> [-p <port>]"
- "[-m <text>][-n <msgcount>][-I <iterations>][-i <interface>]"
- "[-d][-s][-R|-S][-b]\n");
+ "[-m <text>][-n <msgcount>][-I <iterations>][-i <interface>][-w <wait_time_us>]"
+ "[-d][-s][-T|-R|-C|-S][-b]\n");
+}
+
+static void print_addr(char* text, struct sockaddr_in *addr){
+ char ip_str[INET_ADDRSTRLEN+1];
+ inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str));
+ evl_printf("%s--------\n", text);
+ evl_printf("IP-Address: %s\n", ip_str);
+ evl_printf("Port: %d\n", ntohs(addr->sin_port));
+ evl_printf("Family: %d\n", addr->sin_family);
}
static void sender(int s, const char *text, int mcount,
- struct sockaddr_in *addr, int iter)
+ struct sockaddr_in *addr, int iter, useconds_t delay)
{
struct oob_msghdr msghdr;
struct iovec iov;
@@ -66,8 +90,10 @@ static void sender(int s, const char *text, int mcount,
if (ret < 0)
error(1, errno, "oob_sendmsg() failed");
- evl_usleep(1000000);
+ evl_usleep(delay);
}
+
+ free(tbuf);
}
static void receiver(int s, struct sockaddr_in *addr, int iter)
@@ -76,7 +102,7 @@ static void receiver(int s, struct sockaddr_in *addr, int iter)
socklen_t len = sizeof(_addr);
struct oob_msghdr msghdr;
struct iovec iov;
- char tbuf[16384];
+ char rbuf[16384];
ssize_t ret;
int n;
@@ -89,12 +115,90 @@ static void receiver(int s, struct sockaddr_in *addr, int iter)
error(1, errno, "getsockname() failed");
if (verbosity)
- printf("== bound to port %d\n", ntohs(_addr.sin_port));
+ print_addr("bind", addr);
+
+ for (n = 0; !iter || n < iter; n++) {
+ memset(rbuf, 0, sizeof(rbuf));
+ iov.iov_base = rbuf;
+ iov.iov_len = sizeof(rbuf);
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+ msghdr.msg_name = &_addr;
+ msghdr.msg_namelen = sizeof(_addr);
+ msghdr.msg_flags = 0;
+ ret = oob_recvmsg(s, &msghdr, NULL, 0);
+ if (ret < 0)
+ error(1, errno, "oob_recvmsg() failed");
+ evl_printf("= %zd bytes received", ret);
+ if (msghdr.msg_flags & MSG_TRUNC)
+ evl_printf(" (TRUNCATED)");
+ evl_printf(": %.*s\n", (int)ret, rbuf);
+ }
+}
+
+static void client(int s, const char *text, int mcount,
+ struct sockaddr_in *addr, int iter, useconds_t delay)
+{
+ struct sockaddr_in _addr;
+ socklen_t len = sizeof(_addr);
+ struct oob_msghdr msghdr;
+ struct iovec iov;
+ int n, tlen;
+ ssize_t ret;
+ char *tbuf;
+ char rbuf[16384];
+ struct timespec ts_tx;
+ struct timespec ts_rx;
+ double rtt_us = 0.0;
+
+ tlen = (strlen(text) + 1) * mcount;
+ tbuf = malloc(tlen);
+ if (!tbuf)
+ error(1, ENOMEM, "cannot create message");
+
+ *tbuf = '\0';
+ for (n = 0; n < mcount; n++)
+ strcat(tbuf, text); /* yep, lazy.. */
+
+ ret = connect(s, (struct sockaddr *)addr, sizeof(*addr));
+ if (ret < 0)
+ error(1, errno, "connect() failed");
+
+ if (verbosity)
+ print_addr("send address", addr);
+
+ ret = getsockname(s, (struct sockaddr *)&_addr, &len);
+ if (ret < 0)
+ error(1, errno, "getsockname() failed");
+
+ if (verbosity)
+ print_addr("receive address", &_addr);
for (n = 0; !iter || n < iter; n++) {
- memset(tbuf, 0, sizeof(tbuf));
+ evl_read_clock(EVL_CLOCK_MONOTONIC, &ts_tx);
+
iov.iov_base = tbuf;
- iov.iov_len = sizeof(tbuf);
+ iov.iov_len = tlen;
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+ msghdr.msg_name = addr;
+ msghdr.msg_namelen = sizeof(*addr);
+ msghdr.msg_flags = 0;
+ ret = oob_sendmsg(s, &msghdr, NULL, 0);
+ if (ret < 0)
+ error(1, errno, "oob_sendmsg() failed");
+ if (verbosity > 1)
+ print_addr("sent to", addr);
+
+ memset(rbuf, 0, sizeof(rbuf));
+ /* recvmsg stores remote address */
+ memset(&_addr, 0, sizeof(_addr));
+ iov.iov_base = rbuf;
+ iov.iov_len = sizeof(rbuf);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = NULL;
@@ -103,26 +207,114 @@ static void receiver(int s, struct sockaddr_in *addr, int iter)
msghdr.msg_namelen = sizeof(_addr);
msghdr.msg_flags = 0;
ret = oob_recvmsg(s, &msghdr, NULL, 0);
+
+ evl_read_clock(EVL_CLOCK_MONOTONIC, &ts_rx);
+ rtt_us = (ts_rx.tv_sec - ts_tx.tv_sec) * 1000000.0 + (ts_rx.tv_nsec - ts_tx.tv_nsec) / 1000.0;
+
if (ret < 0)
error(1, errno, "oob_recvmsg() failed");
+ if (verbosity > 1)
+ print_addr("received from", &_addr);
+ evl_printf("= %zd bytes received rtt=%.1fus", ret, rtt_us);
+ if (msghdr.msg_flags & MSG_TRUNC)
+ evl_printf(" (TRUNCATED)");
+ evl_printf(": %.*s\n", (int)ret, rbuf);
+
+ evl_usleep(delay);
+ }
+
+ free(tbuf);
+}
+
+static void server(int s, const char *text, int mcount,
+ struct sockaddr_in *addr, int iter)
+{
+ struct sockaddr_in _addr;
+ struct oob_msghdr msghdr;
+ struct iovec iov;
+ int n, tlen;
+ ssize_t ret;
+ char *tbuf;
+ char rbuf[16384];
+
+ tlen = (strlen(text) + 1) * mcount;
+ tbuf = malloc(tlen);
+ if (!tbuf)
+ error(1, ENOMEM, "cannot create message");
+
+ *tbuf = '\0';
+ for (n = 0; n < mcount; n++)
+ strcat(tbuf, text); /* yep, lazy.. */
+
+ ret = bind(s, (struct sockaddr *)addr, sizeof(*addr));
+ if (ret < 0)
+ error(1, errno, "bind() failed");
+
+ if (verbosity)
+ print_addr("bind", addr);
+
+ for (n = 0; !iter || n < iter; n++) {
+ memset(rbuf, 0, sizeof(rbuf));
+ /* recvmsg stores remote address, we respond to this address */
+ memset(&_addr, 0, sizeof(_addr));
+ iov.iov_base = rbuf;
+ iov.iov_len = sizeof(rbuf);
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+ msghdr.msg_name = &_addr;
+ msghdr.msg_namelen = sizeof(_addr);
+ msghdr.msg_flags = 0;
+ ret = oob_recvmsg(s, &msghdr, NULL, 0);
+ if (ret < 0)
+ error(1, errno, "oob_recvmsg() failed");
+ if (verbosity > 1)
+ print_addr("received from", &_addr);
evl_printf("= %zd bytes received", ret);
if (msghdr.msg_flags & MSG_TRUNC)
evl_printf(" (TRUNCATED)");
- evl_printf(": %.*s\n", (int)ret, tbuf);
+ evl_printf(": %.*s\n", (int)ret, rbuf);
+
+ iov.iov_base = tbuf;
+ iov.iov_len = tlen;
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+ msghdr.msg_name = &_addr;
+ msghdr.msg_namelen = sizeof(_addr);
+ msghdr.msg_flags = 0;
+ ret = oob_sendmsg(s, &msghdr, NULL, 0);
+ if (ret < 0)
+ error(1, errno, "oob_sendmsg() failed");
+ if (verbosity > 1)
+ print_addr("sent to", &_addr);
}
+
+ free(tbuf);
}
+typedef enum {
+ TRANSMIT,
+ RECEIVE,
+ SERVER,
+ CLIENT
+} udp_mode_t;
+
int main(int argc, char *argv[])
{
int tfd, s, c, mcount = 1, iter = 0, port = 42042, on = 1;
- const char *text = "Mellow sword!", *iface = NULL;
- bool send = false, bcast = false;
+ const char *text = "Mellow sword!\n", *iface = NULL;
+ bool bcast = false;
+ udp_mode_t mode = RECEIVE;
struct sched_param param;
struct sockaddr_in addr;
const char *ip = NULL;
ssize_t ret;
+ useconds_t delay=1000000;
- while ((c = getopt(argc, argv, "a:m:n:i:I:p:dsRSb")) != EOF) {
+ while ((c = getopt(argc, argv, "a:m:n:i:w:I:p:dsTRCSb")) != EOF) {
switch (c) {
case 'a':
ip = optarg;
@@ -142,6 +334,9 @@ int main(int argc, char *argv[])
case 'i':
iface = optarg;
break;
+ case 'w':
+ delay = atoi(optarg);
+ break;
case 'I':
iter = atoi(optarg);
break;
@@ -151,11 +346,17 @@ int main(int argc, char *argv[])
case 'b':
bcast = true; /* Force mode, e.g. for directed broadcast */
break;
+ case 'T':
+ mode = TRANSMIT;
+ break;
case 'R':
- send = false;
+ mode = RECEIVE;
+ break;
+ case 'C':
+ mode = CLIENT;
break;
case 'S':
- send = true;
+ mode = SERVER;
break;
default:
usage();
@@ -203,7 +404,7 @@ int main(int argc, char *argv[])
printf("== bound to %s\n", iface);
}
- if (send) {
+ if (mode == TRANSMIT) {
if (bcast) {
ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
if (ret)
@@ -224,12 +425,33 @@ int main(int argc, char *argv[])
if (verbosity)
printf("== sender mode (=> %s:%d)\n", bcast ? "[broadcast]" : ip, port);
- sender(s, text, mcount, &addr, iter);
- } else {
+ sender(s, text, mcount, &addr, iter, delay);
+ } else if (mode == RECEIVE) {
if (verbosity)
printf("== receiver mode (<= %s:%d)\n", ip, port);
receiver(s, &addr, iter);
+ } else if (mode == CLIENT) {
+ if (verbosity)
+ printf("== client mode (<= %s:%d)\n", ip, port);
+
+ /*
+ * Guarantee a mere oob path from the first packet
+ * onward by pre-caching the route and link-layer
+ * address via an explicit neighbour solicitation
+ * before we start sending data.
+ */
+ ret = evl_net_solicit(s, (const struct sockaddr *)&addr,
+ EVL_NEIGH_PERMANENT);
+
+ client(s, text, mcount, &addr, iter, delay);
+ } else if (mode == SERVER) {
+ if (verbosity)
+ printf("== server mode (<= %s:%d)\n", ip, port);
+
+ server(s, text, mcount, &addr, iter);
+ } else {
+ error(1, 0, "Mode not implemented");
}
return 0;
--
2.47.3
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-23 20:10 [PATCH] tidbits: net-udp: add server and client mode Hannes Diethelm
@ 2026-05-25 16:51 ` Philippe Gerum
2026-05-25 21:02 ` Hannes Diethelm
0 siblings, 1 reply; 10+ messages in thread
From: Philippe Gerum @ 2026-05-25 16:51 UTC (permalink / raw)
To: Hannes Diethelm; +Cc: xenomai
Hannes Diethelm <hannes.diethelm@gmail.com> writes:
> Additionally, fix memory leaks
>
> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
> ---
> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 240 insertions(+), 18 deletions(-)
>
Merged, thanks. Would you mind sending a patch to update the related doc
on the website at [1], regarding the new -S mode? The pages can be
downloaded from [2].
[1] https://v4.xenomai.org/core/net/udp-demo/index.html
[2] https://gitlab.com/Xenomai/xenomai4/website.git
--
Philippe.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-25 16:51 ` Philippe Gerum
@ 2026-05-25 21:02 ` Hannes Diethelm
2026-05-27 15:12 ` Hannes Diethelm
2026-05-27 18:19 ` Philippe Gerum
0 siblings, 2 replies; 10+ messages in thread
From: Hannes Diethelm @ 2026-05-25 21:02 UTC (permalink / raw)
To: Philippe Gerum; +Cc: xenomai
[-- Attachment #1: Type: text/plain, Size: 1886 bytes --]
Am 25.05.26 um 18:51 schrieb Philippe Gerum:
> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>
>> Additionally, fix memory leaks
>>
>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>> ---
>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>
>
> Merged, thanks. Would you mind sending a patch to update the related doc
> on the website at [1], regarding the new -S mode? The pages can be
> downloaded from [2].
>
> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>
Sure, done. Is there any way to preview the website? I just used a markdown
preview, I hope it did the job and I did not introduce format issues.
By the way:
I had sometimes issues in my VM. It might just be that I am using two
interfaces on the same subnet and disabled the non-oob one after
enabling the oob mode on the other one or there might also be an issue
somewhere in the evl code. I am not yet able to reproduce it clearly.
Sometimes it is all fine, sometimes not. Until now, it did not happen on
the real PC or in loopback mode. But I also don't have a good setup with two
PC's to really test this (yet). I will follow up when I have something reproducible.
If it went wrong, the following happened. After a reboot, all was fine again:
In client mode, the sender address was from the wrong interface:
192.168.255.245 instead of 192.168.122.155
In server mode, the sender address was just 0.0.0.0.
In both cases, there is no answer from the other side due to the response was sent
to the wrong IP address.
I think the issue is not in my code, due to when I use the standard libc functions
in otherwise the same code, the issue disappeared. Attached the code I was using
for tests and also on the host to test server/client.
Regards
Hannes
[-- Attachment #2: net-udp.c --]
[-- Type: text/x-csrc, Size: 10095 bytes --]
/*
* SPDX-License-Identifier: MIT
*
* This tidbit demonstrates out-of-band networking, by
* transmitting[-T]/receiving[-R] UDP packets to/from a particular IP
* address[-a] and port[-p].
*
* The server mode [-S] receives a packet from on a given
* port and sends a response to the sender's ip/port.
*
* The client mode [-C] sends a packet to a port/ip
* and receives the response from a server. The round
* trip time is measured.
*
* The client and the transmitter wait [-w] before sending the
* next packet to not flood the network.
*
* For the other side, the same code can be used.
* For transmitting / client on a non-oob port: socat - UDP-LISTEN:<port>
* For receiving / server on a non-oob port: socat - UDP:<Remote-IP-address>:<port>
*
* See https://v4.xenomai.org/core/net/ for details.
*/
#include <pthread.h>
#include <error.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <memory.h>
#include <getopt.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
static int verbosity = 1;
static void usage(void)
{
fprintf(stderr, "oob-net-udp -a <IP-address> [-p <port>]"
"[-m <text>][-n <msgcount>][-I <iterations>][-i <interface>][-w <wait_time_us>]"
"[-d][-s][-T|-R|-C|-S][-b]\n");
}
static void print_addr(char* text, struct sockaddr_in *addr){
char ip_str[INET_ADDRSTRLEN+1];
inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str));
printf("%s--------\n", text);
printf("IP-Address: %s\n", ip_str);
printf("Port: %d\n", ntohs(addr->sin_port));
printf("Family: %d\n", addr->sin_family);
}
static void sender(int s, const char *text, int mcount,
struct sockaddr_in *addr, int iter, useconds_t delay)
{
struct msghdr msghdr;
struct iovec iov;
int n, tlen;
ssize_t ret;
char *tbuf;
tlen = (strlen(text) + 1) * mcount;
tbuf = malloc(tlen);
if (!tbuf)
error(1, ENOMEM, "cannot create message");
*tbuf = '\0';
for (n = 0; n < mcount; n++)
strcat(tbuf, text); /* yep, lazy.. */
for (n = 0; !iter || n < iter; n++) {
iov.iov_base = tbuf;
iov.iov_len = tlen;
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = NULL;
msghdr.msg_controllen = 0;
msghdr.msg_name = addr;
msghdr.msg_namelen = sizeof(*addr);
msghdr.msg_flags = 0;
ret = sendmsg(s, &msghdr, 0);
if (ret < 0)
error(1, errno, "sendmsg() failed");
usleep(delay);
}
free(tbuf);
}
static void receiver(int s, struct sockaddr_in *addr, int iter)
{
struct sockaddr_in _addr;
socklen_t len = sizeof(_addr);
struct msghdr msghdr;
struct iovec iov;
char rbuf[16384];
ssize_t ret;
int n;
ret = bind(s, (struct sockaddr *)addr, sizeof(*addr));
if (ret < 0)
error(1, errno, "bind() failed");
ret = getsockname(s, (struct sockaddr *)&_addr, &len);
if (ret < 0)
error(1, errno, "getsockname() failed");
if (verbosity)
print_addr("bind", addr);
for (n = 0; !iter || n < iter; n++) {
memset(rbuf, 0, sizeof(rbuf));
iov.iov_base = rbuf;
iov.iov_len = sizeof(rbuf);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = NULL;
msghdr.msg_controllen = 0;
msghdr.msg_name = &_addr;
msghdr.msg_namelen = sizeof(_addr);
msghdr.msg_flags = 0;
ret = recvmsg(s, &msghdr, 0);
if (ret < 0)
error(1, errno, "recvmsg() failed");
printf("= %zd bytes received", ret);
if (msghdr.msg_flags & MSG_TRUNC)
printf(" (TRUNCATED)");
printf(": %.*s\n", (int)ret, rbuf);
}
}
static void client(int s, const char *text, int mcount,
struct sockaddr_in *addr, int iter, useconds_t delay)
{
struct sockaddr_in _addr;
socklen_t len = sizeof(_addr);
struct msghdr msghdr;
struct iovec iov;
int n, tlen;
ssize_t ret;
char *tbuf;
char rbuf[16384];
struct timespec ts_tx;
struct timespec ts_rx;
double rtt_us = 0.0;
tlen = (strlen(text) + 1) * mcount;
tbuf = malloc(tlen);
if (!tbuf)
error(1, ENOMEM, "cannot create message");
*tbuf = '\0';
for (n = 0; n < mcount; n++)
strcat(tbuf, text); /* yep, lazy.. */
ret = connect(s, (struct sockaddr *)addr, sizeof(*addr));
if (ret < 0)
error(1, errno, "connect() failed");
if (verbosity)
print_addr("send address", addr);
ret = getsockname(s, (struct sockaddr *)&_addr, &len);
if (ret < 0)
error(1, errno, "getsockname() failed");
if (verbosity)
print_addr("receive address", &_addr);
for (n = 0; !iter || n < iter; n++) {
clock_gettime(CLOCK_MONOTONIC, &ts_tx);
iov.iov_base = tbuf;
iov.iov_len = tlen;
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = NULL;
msghdr.msg_controllen = 0;
msghdr.msg_name = addr;
msghdr.msg_namelen = sizeof(*addr);
msghdr.msg_flags = 0;
ret = sendmsg(s, &msghdr, 0);
if (ret < 0)
error(1, errno, "sendmsg() failed");
if (verbosity > 1)
print_addr("sent to", addr);
memset(rbuf, 0, sizeof(rbuf));
/* recvmsg stores remote address */
memset(&_addr, 0, sizeof(_addr));
iov.iov_base = rbuf;
iov.iov_len = sizeof(rbuf);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = NULL;
msghdr.msg_controllen = 0;
msghdr.msg_name = &_addr;
msghdr.msg_namelen = sizeof(_addr);
msghdr.msg_flags = 0;
ret = recvmsg(s, &msghdr, 0);
clock_gettime(CLOCK_MONOTONIC, &ts_rx);
rtt_us = (ts_rx.tv_sec - ts_tx.tv_sec) * 1000000.0 + (ts_rx.tv_nsec - ts_tx.tv_nsec) / 1000.0;
if (ret < 0)
error(1, errno, "recvmsg() failed");
if (verbosity > 1)
print_addr("received from", &_addr);
printf("= %zd bytes received rtt=%.1fus", ret, rtt_us);
if (msghdr.msg_flags & MSG_TRUNC)
printf(" (TRUNCATED)");
printf(": %.*s\n", (int)ret, rbuf);
usleep(delay);
}
free(tbuf);
}
static void server(int s, const char *text, int mcount,
struct sockaddr_in *addr, int iter)
{
struct sockaddr_in _addr;
struct msghdr msghdr;
struct iovec iov;
int n, tlen;
ssize_t ret;
char *tbuf;
char rbuf[16384];
tlen = (strlen(text) + 1) * mcount;
tbuf = malloc(tlen);
if (!tbuf)
error(1, ENOMEM, "cannot create message");
*tbuf = '\0';
for (n = 0; n < mcount; n++)
strcat(tbuf, text); /* yep, lazy.. */
ret = bind(s, (struct sockaddr *)addr, sizeof(*addr));
if (ret < 0)
error(1, errno, "bind() failed");
if (verbosity)
print_addr("bind", addr);
for (n = 0; !iter || n < iter; n++) {
memset(rbuf, 0, sizeof(rbuf));
/* recvmsg stores remote address, we respond to this address */
memset(&_addr, 0, sizeof(_addr));
iov.iov_base = rbuf;
iov.iov_len = sizeof(rbuf);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = NULL;
msghdr.msg_controllen = 0;
msghdr.msg_name = &_addr;
msghdr.msg_namelen = sizeof(_addr);
msghdr.msg_flags = 0;
ret = recvmsg(s, &msghdr, 0);
if (ret < 0)
error(1, errno, "recvmsg() failed");
if (verbosity > 1)
print_addr("received from", &_addr);
printf("= %zd bytes received", ret);
if (msghdr.msg_flags & MSG_TRUNC)
printf(" (TRUNCATED)");
printf(": %.*s\n", (int)ret, rbuf);
iov.iov_base = tbuf;
iov.iov_len = tlen;
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = NULL;
msghdr.msg_controllen = 0;
msghdr.msg_name = &_addr;
msghdr.msg_namelen = sizeof(_addr);
msghdr.msg_flags = 0;
ret = sendmsg(s, &msghdr, 0);
if (ret < 0)
error(1, errno, "sendmsg() failed");
if (verbosity > 1)
print_addr("sent to", &_addr);
}
free(tbuf);
}
typedef enum {
TRANSMIT,
RECEIVE,
SERVER,
CLIENT
} udp_mode_t;
int main(int argc, char *argv[])
{
int s, c, mcount = 1, iter = 0, port = 42042, on = 1;
const char *text = "Mellow sword!\n", *iface = NULL;
bool bcast = false;
udp_mode_t mode = RECEIVE;
struct sockaddr_in addr;
const char *ip = NULL;
ssize_t ret;
useconds_t delay=1000000;
while ((c = getopt(argc, argv, "a:m:n:i:w:I:p:dsTRCSb")) != EOF) {
switch (c) {
case 'a':
ip = optarg;
break;
case 'd':
verbosity = 2;
break;
case 's':
verbosity = 0;
break;
case 'm':
text = optarg;
break;
case 'n':
mcount = atoi(optarg);
break;
case 'i':
iface = optarg;
break;
case 'w':
delay = atoi(optarg);
break;
case 'I':
iter = atoi(optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 'b':
bcast = true; /* Force mode, e.g. for directed broadcast */
break;
case 'T':
mode = TRANSMIT;
break;
case 'R':
mode = RECEIVE;
break;
case 'C':
mode = CLIENT;
break;
case 'S':
mode = SERVER;
break;
default:
usage();
exit(1);
}
}
if (ip == NULL) {
usage();
exit(2);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (!strcmp(ip, "broadcast")) {
addr.sin_addr.s_addr = INADDR_BROADCAST;
bcast = true;
} else if (!inet_pton(AF_INET, ip, &addr.sin_addr)) {
error(1, EINVAL, "invalid IP address");
} else if (addr.sin_addr.s_addr == INADDR_BROADCAST) {
bcast = true;
}
/*
* Get an UDP socket with out-of-band capabilities.
*/
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
error(1, errno, "cannot create out-of-band UDP socket");
if (iface) {
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface)))
error(1, errno, "setsockopt(SO_BINDTODEVICE)");
if (verbosity)
printf("== bound to %s\n", iface);
}
if (mode == TRANSMIT) {
if (bcast) {
ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
if (ret)
error(1, errno, "cannot enable broadcast for UDP socket");
}
if (verbosity)
printf("== sender mode (=> %s:%d)\n", bcast ? "[broadcast]" : ip, port);
sender(s, text, mcount, &addr, iter, delay);
} else if (mode == RECEIVE) {
if (verbosity)
printf("== receiver mode (<= %s:%d)\n", ip, port);
receiver(s, &addr, iter);
} else if (mode == CLIENT) {
if (verbosity)
printf("== client mode (<= %s:%d)\n", ip, port);
client(s, text, mcount, &addr, iter, delay);
} else if (mode == SERVER) {
if (verbosity)
printf("== server mode (<= %s:%d)\n", ip, port);
server(s, text, mcount, &addr, iter);
} else {
error(1, 0, "Mode not implemented");
}
return 0;
}
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-25 21:02 ` Hannes Diethelm
@ 2026-05-27 15:12 ` Hannes Diethelm
2026-05-27 19:04 ` Philippe Gerum
2026-05-27 18:19 ` Philippe Gerum
1 sibling, 1 reply; 10+ messages in thread
From: Hannes Diethelm @ 2026-05-27 15:12 UTC (permalink / raw)
To: Philippe Gerum; +Cc: xenomai
Am 25.05.26 um 23:02 schrieb Hannes Diethelm:
> Am 25.05.26 um 18:51 schrieb Philippe Gerum:
>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>
>>> Additionally, fix memory leaks
>>>
>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>>> ---
>>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>>
>>
>> Merged, thanks. Would you mind sending a patch to update the related doc
>> on the website at [1], regarding the new -S mode? The pages can be
>> downloaded from [2].
>>
>> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
>> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>>
>
> Sure, done. Is there any way to preview the website? I just used a markdown
> preview, I hope it did the job and I did not introduce format issues.
>
> By the way:
> I had sometimes issues in my VM. It might just be that I am using two
> interfaces on the same subnet and disabled the non-oob one after
> enabling the oob mode on the other one or there might also be an issue
> somewhere in the evl code. I am not yet able to reproduce it clearly.
> Sometimes it is all fine, sometimes not. Until now, it did not happen on
> the real PC or in loopback mode. But I also don't have a good setup with
> two
> PC's to really test this (yet). I will follow up when I have something
> reproducible.
>
> If it went wrong, the following happened. After a reboot, all was fine
> again:
> In client mode, the sender address was from the wrong interface:
> 192.168.255.245 instead of 192.168.122.155
>
> In server mode, the sender address was just 0.0.0.0.
>
> In both cases, there is no answer from the other side due to the
> response was sent
> to the wrong IP address.
>
> I think the issue is not in my code, due to when I use the standard libc
> functions
> in otherwise the same code, the issue disappeared. Attached the code I
> was using
> for tests and also on the host to test server/client.
>
> Regards
> Hannes
So, I was able to create something reproducible. I was a bit confused first
due to sometimes it worked, sometimes not. But this was my fault, not
rebooting before the test and not exactly using the same commands in the same order.
All command blocks below where performed after a reboot.
I have two network interfaces:
enp1s0: virtio
enp7s0: e1000e
After boot:
enp1s0: 192.168.122.246 nm 255.255.255.0
enp7s0: No address
First Scenario-------------------
The client works fine:
dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
ifconfig enp1s0 down
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -C -p 5201 -m "Client"
The server sometimes fails:
dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
ifconfig enp1s0 down
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 0.0.0.0 -S -p 5201 -m "Server"
There is sometimes the error:
oob-net-udp: oob_sendmsg() failed: Operation now in progress
This goes away after a few tries and then it works afterwards.
If I bind to the interface address instead of INADDR_ANY, same issue:
./oob-net-udp -d -i enp7s0 -a 192.168.122.155 -S -p 5201 -m "Server"
Second Scenario-------------------
Here, I do not set an IP address for enp7s0. This was a mistake on my side. Disregard,
more careful testing showed the exact same behavior for posix / vanilla kernel. But it is
important for the third scenario.
ifconfig enp1s0 down
ifconfig enp7s0 up
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501
Here, the sender address shown in wireshark is 192.168.122.246. This is the address from enp1s0
that is disabled. Exactly the same for posix / debian kernel.
ifconfig:
enp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::5054:ff:fe5a:79c5 prefixlen 64 scopeid 0x20<link>
ether 52:54:00:5a:79:c5 txqueuelen 1000 (Ethernet)
RX packets 15 bytes 960 (960.0 B)
RX errors 28 dropped 0 overruns 0 frame 28
TX packets 30 bytes 3332 (3.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 22 memory 0xfdc40000-fdc60000
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Lokale Schleife)
RX packets 28 bytes 2672 (2.6 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 28 bytes 2672 (2.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Third scenario------------------
Setting an IP address after evl net -ei and the first package sent doesn't work. It looks like
the info is copied once and not updated later, also not when disabled and enabled
again.
The following does not work:
ifconfig enp1s0 down
ifconfig enp7s0 up 192.168.122.100
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.100
evl net -di enp7s0
ifconfig enp7s0 192.168.122.110
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Sender address is still 192.168.122.100, should be 192.168.122.110
This works:
ifconfig enp1s0 down
ifconfig enp7s0 up
evl net -ei enp7s0
ifconfig enp7s0 192.168.122.155
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, the sender address is 192.168.122.155
This also doesn't work and created the confusion:
ifconfig enp1s0 down
ifconfig enp7s0 up
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
ifconfig enp7s0 192.168.122.155 #Note, setting the IP address is after the first package sent
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
evl net -di enp7s0
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
evl net -di enp7s0
ifconfig enp7s0 192.168.122.155
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
Can you reproduce this? It is of now importance for what I am using EVL for. I just discovered this while testing the server / client mode.
So no urgency from my side.
Regards
Hannes
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-25 21:02 ` Hannes Diethelm
2026-05-27 15:12 ` Hannes Diethelm
@ 2026-05-27 18:19 ` Philippe Gerum
1 sibling, 0 replies; 10+ messages in thread
From: Philippe Gerum @ 2026-05-27 18:19 UTC (permalink / raw)
To: Hannes Diethelm; +Cc: xenomai
Hannes Diethelm <hannes.diethelm@gmail.com> writes:
> Am 25.05.26 um 18:51 schrieb Philippe Gerum:
>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>
>>> Additionally, fix memory leaks
>>>
>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>>> ---
>>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>>
>> Merged, thanks. Would you mind sending a patch to update the related
>> doc
>> on the website at [1], regarding the new -S mode? The pages can be
>> downloaded from [2].
>> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
>> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>>
>
> Sure, done. Is there any way to preview the website? I just used a markdown
> preview, I hope it did the job and I did not introduce format issues.
>
Sure, I'm using a plain 'hugo' server for that [1].
$ git clone https://gitlab.com/Xenomai/xenomai4/website.git
$ hugo server
Then you can connect to localhost:1313/ to view the website
locally. The server immediately auto-refreshes the view on updating a
markdown file, very handy.
[1] On fedora, from the 'hugo' package:
hugo.x86_64 The world’s fastest framework for building websites
--
Philippe.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-27 15:12 ` Hannes Diethelm
@ 2026-05-27 19:04 ` Philippe Gerum
2026-05-27 20:03 ` Hannes Diethelm
2026-05-29 10:23 ` Philippe Gerum
0 siblings, 2 replies; 10+ messages in thread
From: Philippe Gerum @ 2026-05-27 19:04 UTC (permalink / raw)
To: Hannes Diethelm; +Cc: xenomai
Hannes Diethelm <hannes.diethelm@gmail.com> writes:
> Am 25.05.26 um 23:02 schrieb Hannes Diethelm:
>> Am 25.05.26 um 18:51 schrieb Philippe Gerum:
>>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>>
>>>> Additionally, fix memory leaks
>>>>
>>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>>>> ---
>>>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>>>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>>>
>>>
>>> Merged, thanks. Would you mind sending a patch to update the related doc
>>> on the website at [1], regarding the new -S mode? The pages can be
>>> downloaded from [2].
>>>
>>> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
>>> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>>>
>> Sure, done. Is there any way to preview the website? I just used a
>> markdown
>> preview, I hope it did the job and I did not introduce format issues.
>> By the way:
>> I had sometimes issues in my VM. It might just be that I am using two
>> interfaces on the same subnet and disabled the non-oob one after
>> enabling the oob mode on the other one or there might also be an issue
>> somewhere in the evl code. I am not yet able to reproduce it clearly.
>> Sometimes it is all fine, sometimes not. Until now, it did not happen on
>> the real PC or in loopback mode. But I also don't have a good setup
>> with two
>> PC's to really test this (yet). I will follow up when I have
>> something reproducible.
>> If it went wrong, the following happened. After a reboot, all was
>> fine again:
>> In client mode, the sender address was from the wrong interface:
>> 192.168.255.245 instead of 192.168.122.155
>> In server mode, the sender address was just 0.0.0.0.
>> In both cases, there is no answer from the other side due to the
>> response was sent
>> to the wrong IP address.
>> I think the issue is not in my code, due to when I use the standard
>> libc functions
>> in otherwise the same code, the issue disappeared. Attached the code
>> I was using
>> for tests and also on the host to test server/client.
>> Regards
>> Hannes
>
> So, I was able to create something reproducible. I was a bit confused first
> due to sometimes it worked, sometimes not. But this was my fault, not
> rebooting before the test and not exactly using the same commands in the same order.
>
> All command blocks below where performed after a reboot.
>
> I have two network interfaces:
> enp1s0: virtio
> enp7s0: e1000e
>
> After boot:
> enp1s0: 192.168.122.246 nm 255.255.255.0
> enp7s0: No address
>
> First Scenario-------------------
>
> The client works fine:
> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
> ifconfig enp1s0 down
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -C -p 5201 -m "Client"
>
> The server sometimes fails:
> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
> ifconfig enp1s0 down
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 0.0.0.0 -S -p 5201 -m "Server"
>
> There is sometimes the error:
> oob-net-udp: oob_sendmsg() failed: Operation now in progress
> This goes away after a few tries and then it works afterwards.
>
> If I bind to the interface address instead of INADDR_ANY, same issue:
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.155 -S -p 5201 -m "Server"
>
> Second Scenario-------------------
>
> Here, I do not set an IP address for enp7s0. This was a mistake on my side. Disregard,
> more careful testing showed the exact same behavior for posix / vanilla kernel. But it is
> important for the third scenario.
>
> ifconfig enp1s0 down
> ifconfig enp7s0 up
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501
>
> Here, the sender address shown in wireshark is 192.168.122.246. This is the address from enp1s0
> that is disabled. Exactly the same for posix / debian kernel.
>
> ifconfig:
> enp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
> inet6 fe80::5054:ff:fe5a:79c5 prefixlen 64 scopeid 0x20<link>
> ether 52:54:00:5a:79:c5 txqueuelen 1000 (Ethernet)
> RX packets 15 bytes 960 (960.0 B)
> RX errors 28 dropped 0 overruns 0 frame 28
> TX packets 30 bytes 3332 (3.2 KiB)
> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
> device interrupt 22 memory 0xfdc40000-fdc60000
>
> lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
> inet 127.0.0.1 netmask 255.0.0.0
> inet6 ::1 prefixlen 128 scopeid 0x10<host>
> loop txqueuelen 1000 (Lokale Schleife)
> RX packets 28 bytes 2672 (2.6 KiB)
> RX errors 0 dropped 0 overruns 0 frame 0
> TX packets 28 bytes 2672 (2.6 KiB)
> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>
> Third scenario------------------
>
> Setting an IP address after evl net -ei and the first package sent doesn't work. It looks like
> the info is copied once and not updated later, also not when disabled and enabled
> again.
>
> The following does not work:
> ifconfig enp1s0 down
> ifconfig enp7s0 up 192.168.122.100
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.100
> evl net -di enp7s0
> ifconfig enp7s0 192.168.122.110
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Sender address is still 192.168.122.100, should be 192.168.122.110
>
> This works:
> ifconfig enp1s0 down
> ifconfig enp7s0 up
> evl net -ei enp7s0
> ifconfig enp7s0 192.168.122.155
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, the sender address is 192.168.122.155
>
> This also doesn't work and created the confusion:
> ifconfig enp1s0 down
> ifconfig enp7s0 up
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
> ifconfig enp7s0 192.168.122.155 #Note, setting the IP address is after the first package sent
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
> evl net -di enp7s0
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
> evl net -di enp7s0
> ifconfig enp7s0 192.168.122.155
> evl net -ei enp7s0
> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>
> Can you reproduce this? It is of now importance for what I am using EVL for. I just discovered this while testing the server / client mode.
> So no urgency from my side.
>
I did not try to reproduce it yet, but reading this description, I would
most certainly see the same outcome. I believe this is all related to
the so-called "oob front caches" for ARP and IPv4 routing decisions EVL
maintains [1].
In light of that doc, what is most likely happening is:
- the EINPROGRESS status is EVL telling the caller that it could not
resolve either the dest ip using its route cache or the MAC address of
the destination in its ARP cache, so it had to relay the packet to the
in-band stage for transmit. IOW, the packet is outgoing, but the
end-to-end real-time guarantee you would have if the NIC driver was
oob-capable is lost for this particular transmit.
As the in-band stack does the proper resolution for that relayed
packet eventually, recording the results into its own neighbour and
routing tables, it also conveniently pass this information to some
dovetail hooks which EVL listens to, and therefore learns from,
feeding its front caches with it. This usually happens quickly after
the in-band transmit happens, but some delay may appear due to the
time required to receive an ARP reply message from a peer for
instance. This is what might make the behavior look slightly flaky at
times from a user perspective.
The way to make this deterministic (therefore without transient
EINPROGRESS error on first transmit) is by using explicit peer
solicitation as discussed in [2].
- the bad sender address of the 3rd scenario may be a variant of this
bug, with the added trick that it should only happen with
unconnected/unbound sockets, in which case the source address is
retrieved from the routing record matching the destination address. In
this case, the routing record found in the EVL front cache is obsolete
since the transmitting device changed its (prefix) address. So it
looks like some flushing of those obsolete records is missing on
changing the address with an active oob port..
If I'm right, you should be able to work around this issue by flushing
the EVL route cache after the netdev update and before transmitting,
as follows:
# echo 1 > /sys/class/evl/net/ipv4_routes
Obviously, this is not that nice, and the netstack should behave and
do this automatically. Will fix.
[1] https://v4.xenomai.org/core/net/index.html#evl-output-routing
[2] https://v4.xenomai.org/core/net/index.html#evl-peer-solicitation
--
Philippe.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-27 19:04 ` Philippe Gerum
@ 2026-05-27 20:03 ` Hannes Diethelm
2026-05-29 10:23 ` Philippe Gerum
1 sibling, 0 replies; 10+ messages in thread
From: Hannes Diethelm @ 2026-05-27 20:03 UTC (permalink / raw)
To: Philippe Gerum; +Cc: xenomai
Am 27.05.26 um 21:04 schrieb Philippe Gerum:
> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>
>> Am 25.05.26 um 23:02 schrieb Hannes Diethelm:
>>> Am 25.05.26 um 18:51 schrieb Philippe Gerum:
>>>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>>>
>>>>> Additionally, fix memory leaks
>>>>>
>>>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>>>>> ---
>>>>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>>>>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>>>>
>>>>
>>>> Merged, thanks. Would you mind sending a patch to update the related doc
>>>> on the website at [1], regarding the new -S mode? The pages can be
>>>> downloaded from [2].
>>>>
>>>> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
>>>> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>>>>
>>> Sure, done. Is there any way to preview the website? I just used a
>>> markdown
>>> preview, I hope it did the job and I did not introduce format issues.
>>> By the way:
>>> I had sometimes issues in my VM. It might just be that I am using two
>>> interfaces on the same subnet and disabled the non-oob one after
>>> enabling the oob mode on the other one or there might also be an issue
>>> somewhere in the evl code. I am not yet able to reproduce it clearly.
>>> Sometimes it is all fine, sometimes not. Until now, it did not happen on
>>> the real PC or in loopback mode. But I also don't have a good setup
>>> with two
>>> PC's to really test this (yet). I will follow up when I have
>>> something reproducible.
>>> If it went wrong, the following happened. After a reboot, all was
>>> fine again:
>>> In client mode, the sender address was from the wrong interface:
>>> 192.168.255.245 instead of 192.168.122.155
>>> In server mode, the sender address was just 0.0.0.0.
>>> In both cases, there is no answer from the other side due to the
>>> response was sent
>>> to the wrong IP address.
>>> I think the issue is not in my code, due to when I use the standard
>>> libc functions
>>> in otherwise the same code, the issue disappeared. Attached the code
>>> I was using
>>> for tests and also on the host to test server/client.
>>> Regards
>>> Hannes
>>
>> So, I was able to create something reproducible. I was a bit confused first
>> due to sometimes it worked, sometimes not. But this was my fault, not
>> rebooting before the test and not exactly using the same commands in the same order.
>>
>> All command blocks below where performed after a reboot.
>>
>> I have two network interfaces:
>> enp1s0: virtio
>> enp7s0: e1000e
>>
>> After boot:
>> enp1s0: 192.168.122.246 nm 255.255.255.0
>> enp7s0: No address
>>
>> First Scenario-------------------
>>
>> The client works fine:
>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>> ifconfig enp1s0 down
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -C -p 5201 -m "Client"
>>
>> The server sometimes fails:
>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>> ifconfig enp1s0 down
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 0.0.0.0 -S -p 5201 -m "Server"
>>
>> There is sometimes the error:
>> oob-net-udp: oob_sendmsg() failed: Operation now in progress
>> This goes away after a few tries and then it works afterwards.
>>
>> If I bind to the interface address instead of INADDR_ANY, same issue:
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.155 -S -p 5201 -m "Server"
>>
>> Second Scenario-------------------
>>
>> Here, I do not set an IP address for enp7s0. This was a mistake on my side. Disregard,
>> more careful testing showed the exact same behavior for posix / vanilla kernel. But it is
>> important for the third scenario.
>>
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501
>>
>> Here, the sender address shown in wireshark is 192.168.122.246. This is the address from enp1s0
>> that is disabled. Exactly the same for posix / debian kernel.
>>
>> ifconfig:
>> enp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
>> inet6 fe80::5054:ff:fe5a:79c5 prefixlen 64 scopeid 0x20<link>
>> ether 52:54:00:5a:79:c5 txqueuelen 1000 (Ethernet)
>> RX packets 15 bytes 960 (960.0 B)
>> RX errors 28 dropped 0 overruns 0 frame 28
>> TX packets 30 bytes 3332 (3.2 KiB)
>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>> device interrupt 22 memory 0xfdc40000-fdc60000
>>
>> lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
>> inet 127.0.0.1 netmask 255.0.0.0
>> inet6 ::1 prefixlen 128 scopeid 0x10<host>
>> loop txqueuelen 1000 (Lokale Schleife)
>> RX packets 28 bytes 2672 (2.6 KiB)
>> RX errors 0 dropped 0 overruns 0 frame 0
>> TX packets 28 bytes 2672 (2.6 KiB)
>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>>
>> Third scenario------------------
>>
>> Setting an IP address after evl net -ei and the first package sent doesn't work. It looks like
>> the info is copied once and not updated later, also not when disabled and enabled
>> again.
>>
>> The following does not work:
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up 192.168.122.100
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.100
>> evl net -di enp7s0
>> ifconfig enp7s0 192.168.122.110
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Sender address is still 192.168.122.100, should be 192.168.122.110
>>
>> This works:
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up
>> evl net -ei enp7s0
>> ifconfig enp7s0 192.168.122.155
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, the sender address is 192.168.122.155
>>
>> This also doesn't work and created the confusion:
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>> ifconfig enp7s0 192.168.122.155 #Note, setting the IP address is after the first package sent
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>> evl net -di enp7s0
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>> evl net -di enp7s0
>> ifconfig enp7s0 192.168.122.155
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>
>> Can you reproduce this? It is of now importance for what I am using EVL for. I just discovered this while testing the server / client mode.
>> So no urgency from my side.
>>
>
> I did not try to reproduce it yet, but reading this description, I would
> most certainly see the same outcome. I believe this is all related to
> the so-called "oob front caches" for ARP and IPv4 routing decisions EVL
> maintains [1].
>
> In light of that doc, what is most likely happening is:
>
> - the EINPROGRESS status is EVL telling the caller that it could not
> resolve either the dest ip using its route cache or the MAC address of
> the destination in its ARP cache, so it had to relay the packet to the
> in-band stage for transmit. IOW, the packet is outgoing, but the
> end-to-end real-time guarantee you would have if the NIC driver was
> oob-capable is lost for this particular transmit.
>
> As the in-band stack does the proper resolution for that relayed
> packet eventually, recording the results into its own neighbour and
> routing tables, it also conveniently pass this information to some
> dovetail hooks which EVL listens to, and therefore learns from,
> feeding its front caches with it. This usually happens quickly after
> the in-band transmit happens, but some delay may appear due to the
> time required to receive an ARP reply message from a peer for
> instance. This is what might make the behavior look slightly flaky at
> times from a user perspective.
>
> The way to make this deterministic (therefore without transient
> EINPROGRESS error on first transmit) is by using explicit peer
> solicitation as discussed in [2].
>
Right, the server mode does not call evl_net_solicit(). I somehow expected duet to the remote packages is
received before, this information could be used directly, so no evl_net_solicit() is needed.
But by using the posix network implementation, I now see also an ARP request going out. So an evl_net_solicit()
is needed for each IP where a package is sent to for the first time. I will add this to the demo and send a patch.
Options:
1. Just be lazy and always call evl_net_solicit() -> If evl_net_solicit() does not takes to long if the ARP is already there, why not.
2. Internally manage a list of IP's where evl_net_solicit() was already performed, should be easy.
3. Is there a way to ask EVL if the ARP already done?
What do you think?
> - the bad sender address of the 3rd scenario may be a variant of this
> bug, with the added trick that it should only happen with
> unconnected/unbound sockets, in which case the source address is
> retrieved from the routing record matching the destination address. In
> this case, the routing record found in the EVL front cache is obsolete
> since the transmitting device changed its (prefix) address. So it
> looks like some flushing of those obsolete records is missing on
> changing the address with an active oob port..
>
> If I'm right, you should be able to work around this issue by flushing
> the EVL route cache after the netdev update and before transmitting,
> as follows:
> # echo 1 > /sys/class/evl/net/ipv4_routes
> Obviously, this is not that nice, and the netstack should behave and
> do this automatically. Will fix.
Thanks, this works. I looked trough [1] but it was not clear to me that this applies also to the sender IP.
ifconfig enp1s0 down
ifconfig enp7s0 up 192.168.122.100
evl net -ei enp7s0
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.100
ifconfig enp7s0 192.168.122.110
echo 1 > /sys/class/evl/net/ipv4_flush_routes
./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.110
Notes:
[3] /sys/class/evl/net/arp is now probably /sys/class/evl/net/ipv4_flush_arp
[4] /sys/class/evl/net/ipv4_routes is now probably /sys/class/evl/net/ipv4_flush_routes
[3] https://v4.xenomai.org/core/net/index.html#evl-arp-cache
[4] https://v4.xenomai.org/core/net/index.html#evl-route-cache
>
> [1] https://v4.xenomai.org/core/net/index.html#evl-output-routing
> [2] https://v4.xenomai.org/core/net/index.html#evl-peer-solicitation
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-27 19:04 ` Philippe Gerum
2026-05-27 20:03 ` Hannes Diethelm
@ 2026-05-29 10:23 ` Philippe Gerum
2026-05-29 21:53 ` Hannes Diethelm
1 sibling, 1 reply; 10+ messages in thread
From: Philippe Gerum @ 2026-05-29 10:23 UTC (permalink / raw)
To: Hannes Diethelm; +Cc: xenomai
Philippe Gerum <rpm@xenomai.org> writes:
> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>
>> Am 25.05.26 um 23:02 schrieb Hannes Diethelm:
>>> Am 25.05.26 um 18:51 schrieb Philippe Gerum:
>>>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>>>
>>>>> Additionally, fix memory leaks
>>>>>
>>>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>>>>> ---
>>>>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>>>>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>>>>
>>>>
>>>> Merged, thanks. Would you mind sending a patch to update the related doc
>>>> on the website at [1], regarding the new -S mode? The pages can be
>>>> downloaded from [2].
>>>>
>>>> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
>>>> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>>>>
>>> Sure, done. Is there any way to preview the website? I just used a
>>> markdown
>>> preview, I hope it did the job and I did not introduce format issues.
>>> By the way:
>>> I had sometimes issues in my VM. It might just be that I am using two
>>> interfaces on the same subnet and disabled the non-oob one after
>>> enabling the oob mode on the other one or there might also be an issue
>>> somewhere in the evl code. I am not yet able to reproduce it clearly.
>>> Sometimes it is all fine, sometimes not. Until now, it did not happen on
>>> the real PC or in loopback mode. But I also don't have a good setup
>>> with two
>>> PC's to really test this (yet). I will follow up when I have
>>> something reproducible.
>>> If it went wrong, the following happened. After a reboot, all was
>>> fine again:
>>> In client mode, the sender address was from the wrong interface:
>>> 192.168.255.245 instead of 192.168.122.155
>>> In server mode, the sender address was just 0.0.0.0.
>>> In both cases, there is no answer from the other side due to the
>>> response was sent
>>> to the wrong IP address.
>>> I think the issue is not in my code, due to when I use the standard
>>> libc functions
>>> in otherwise the same code, the issue disappeared. Attached the code
>>> I was using
>>> for tests and also on the host to test server/client.
>>> Regards
>>> Hannes
>>
>> So, I was able to create something reproducible. I was a bit confused first
>> due to sometimes it worked, sometimes not. But this was my fault, not
>> rebooting before the test and not exactly using the same commands in the same order.
>>
>> All command blocks below where performed after a reboot.
>>
>> I have two network interfaces:
>> enp1s0: virtio
>> enp7s0: e1000e
>>
>> After boot:
>> enp1s0: 192.168.122.246 nm 255.255.255.0
>> enp7s0: No address
>>
>> First Scenario-------------------
>>
>> The client works fine:
>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>> ifconfig enp1s0 down
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -C -p 5201 -m "Client"
>>
>> The server sometimes fails:
>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>> ifconfig enp1s0 down
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 0.0.0.0 -S -p 5201 -m "Server"
>>
>> There is sometimes the error:
>> oob-net-udp: oob_sendmsg() failed: Operation now in progress
>> This goes away after a few tries and then it works afterwards.
>>
>> If I bind to the interface address instead of INADDR_ANY, same issue:
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.155 -S -p 5201 -m "Server"
>>
>> Second Scenario-------------------
>>
>> Here, I do not set an IP address for enp7s0. This was a mistake on my side. Disregard,
>> more careful testing showed the exact same behavior for posix / vanilla kernel. But it is
>> important for the third scenario.
>>
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501
>>
>> Here, the sender address shown in wireshark is 192.168.122.246. This is the address from enp1s0
>> that is disabled. Exactly the same for posix / debian kernel.
>>
>> ifconfig:
>> enp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
>> inet6 fe80::5054:ff:fe5a:79c5 prefixlen 64 scopeid 0x20<link>
>> ether 52:54:00:5a:79:c5 txqueuelen 1000 (Ethernet)
>> RX packets 15 bytes 960 (960.0 B)
>> RX errors 28 dropped 0 overruns 0 frame 28
>> TX packets 30 bytes 3332 (3.2 KiB)
>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>> device interrupt 22 memory 0xfdc40000-fdc60000
>>
>> lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
>> inet 127.0.0.1 netmask 255.0.0.0
>> inet6 ::1 prefixlen 128 scopeid 0x10<host>
>> loop txqueuelen 1000 (Lokale Schleife)
>> RX packets 28 bytes 2672 (2.6 KiB)
>> RX errors 0 dropped 0 overruns 0 frame 0
>> TX packets 28 bytes 2672 (2.6 KiB)
>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>>
>> Third scenario------------------
>>
>> Setting an IP address after evl net -ei and the first package sent doesn't work. It looks like
>> the info is copied once and not updated later, also not when disabled and enabled
>> again.
>>
>> The following does not work:
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up 192.168.122.100
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.100
>> evl net -di enp7s0
>> ifconfig enp7s0 192.168.122.110
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Sender address is still 192.168.122.100, should be 192.168.122.110
>>
>> This works:
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up
>> evl net -ei enp7s0
>> ifconfig enp7s0 192.168.122.155
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, the sender address is 192.168.122.155
>>
>> This also doesn't work and created the confusion:
>> ifconfig enp1s0 down
>> ifconfig enp7s0 up
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>> ifconfig enp7s0 192.168.122.155 #Note, setting the IP address is after the first package sent
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>> evl net -di enp7s0
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>> evl net -di enp7s0
>> ifconfig enp7s0 192.168.122.155
>> evl net -ei enp7s0
>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>
>> Can you reproduce this? It is of now importance for what I am using EVL for. I just discovered this while testing the server / client mode.
>> So no urgency from my side.
>>
>
> I did not try to reproduce it yet, but reading this description, I would
> most certainly see the same outcome. I believe this is all related to
> the so-called "oob front caches" for ARP and IPv4 routing decisions EVL
> maintains [1].
>
> In light of that doc, what is most likely happening is:
>
> - the EINPROGRESS status is EVL telling the caller that it could not
> resolve either the dest ip using its route cache or the MAC address of
> the destination in its ARP cache, so it had to relay the packet to the
> in-band stage for transmit. IOW, the packet is outgoing, but the
> end-to-end real-time guarantee you would have if the NIC driver was
> oob-capable is lost for this particular transmit.
>
> As the in-band stack does the proper resolution for that relayed
> packet eventually, recording the results into its own neighbour and
> routing tables, it also conveniently pass this information to some
> dovetail hooks which EVL listens to, and therefore learns from,
> feeding its front caches with it. This usually happens quickly after
> the in-band transmit happens, but some delay may appear due to the
> time required to receive an ARP reply message from a peer for
> instance. This is what might make the behavior look slightly flaky at
> times from a user perspective.
>
> The way to make this deterministic (therefore without transient
> EINPROGRESS error on first transmit) is by using explicit peer
> solicitation as discussed in [2].
>
> - the bad sender address of the 3rd scenario may be a variant of this
> bug, with the added trick that it should only happen with
> unconnected/unbound sockets, in which case the source address is
> retrieved from the routing record matching the destination address. In
> this case, the routing record found in the EVL front cache is obsolete
> since the transmitting device changed its (prefix) address. So it
> looks like some flushing of those obsolete records is missing on
> changing the address with an active oob port..
>
> If I'm right, you should be able to work around this issue by flushing
> the EVL route cache after the netdev update and before transmitting,
> as follows:
> # echo 1 > /sys/class/evl/net/ipv4_routes
> Obviously, this is not that nice, and the netstack should behave and
> do this automatically. Will fix.
>
Done. The netstack now automatically purges obsolete records with stale
source addresses from its cache upon removal of an IP address from a
device. Scenario 3 should behave as expected now.
--
Philippe.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-29 10:23 ` Philippe Gerum
@ 2026-05-29 21:53 ` Hannes Diethelm
2026-06-01 8:25 ` Philippe Gerum
0 siblings, 1 reply; 10+ messages in thread
From: Hannes Diethelm @ 2026-05-29 21:53 UTC (permalink / raw)
To: Philippe Gerum; +Cc: xenomai
Am 29.05.26 um 12:23 schrieb Philippe Gerum:
> Philippe Gerum <rpm@xenomai.org> writes:
>
>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>
>>> Am 25.05.26 um 23:02 schrieb Hannes Diethelm:
>>>> Am 25.05.26 um 18:51 schrieb Philippe Gerum:
>>>>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>>>>
>>>>>> Additionally, fix memory leaks
>>>>>>
>>>>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>>>>>> ---
>>>>>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>>>>>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>>>>>
>>>>>
>>>>> Merged, thanks. Would you mind sending a patch to update the related doc
>>>>> on the website at [1], regarding the new -S mode? The pages can be
>>>>> downloaded from [2].
>>>>>
>>>>> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
>>>>> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>>>>>
>>>> Sure, done. Is there any way to preview the website? I just used a
>>>> markdown
>>>> preview, I hope it did the job and I did not introduce format issues.
>>>> By the way:
>>>> I had sometimes issues in my VM. It might just be that I am using two
>>>> interfaces on the same subnet and disabled the non-oob one after
>>>> enabling the oob mode on the other one or there might also be an issue
>>>> somewhere in the evl code. I am not yet able to reproduce it clearly.
>>>> Sometimes it is all fine, sometimes not. Until now, it did not happen on
>>>> the real PC or in loopback mode. But I also don't have a good setup
>>>> with two
>>>> PC's to really test this (yet). I will follow up when I have
>>>> something reproducible.
>>>> If it went wrong, the following happened. After a reboot, all was
>>>> fine again:
>>>> In client mode, the sender address was from the wrong interface:
>>>> 192.168.255.245 instead of 192.168.122.155
>>>> In server mode, the sender address was just 0.0.0.0.
>>>> In both cases, there is no answer from the other side due to the
>>>> response was sent
>>>> to the wrong IP address.
>>>> I think the issue is not in my code, due to when I use the standard
>>>> libc functions
>>>> in otherwise the same code, the issue disappeared. Attached the code
>>>> I was using
>>>> for tests and also on the host to test server/client.
>>>> Regards
>>>> Hannes
>>>
>>> So, I was able to create something reproducible. I was a bit confused first
>>> due to sometimes it worked, sometimes not. But this was my fault, not
>>> rebooting before the test and not exactly using the same commands in the same order.
>>>
>>> All command blocks below where performed after a reboot.
>>>
>>> I have two network interfaces:
>>> enp1s0: virtio
>>> enp7s0: e1000e
>>>
>>> After boot:
>>> enp1s0: 192.168.122.246 nm 255.255.255.0
>>> enp7s0: No address
>>>
>>> First Scenario-------------------
>>>
>>> The client works fine:
>>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>>> ifconfig enp1s0 down
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -C -p 5201 -m "Client"
>>>
>>> The server sometimes fails:
>>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>>> ifconfig enp1s0 down
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 0.0.0.0 -S -p 5201 -m "Server"
>>>
>>> There is sometimes the error:
>>> oob-net-udp: oob_sendmsg() failed: Operation now in progress
>>> This goes away after a few tries and then it works afterwards.
>>>
>>> If I bind to the interface address instead of INADDR_ANY, same issue:
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.155 -S -p 5201 -m "Server"
>>>
>>> Second Scenario-------------------
>>>
>>> Here, I do not set an IP address for enp7s0. This was a mistake on my side. Disregard,
>>> more careful testing showed the exact same behavior for posix / vanilla kernel. But it is
>>> important for the third scenario.
>>>
>>> ifconfig enp1s0 down
>>> ifconfig enp7s0 up
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501
>>>
>>> Here, the sender address shown in wireshark is 192.168.122.246. This is the address from enp1s0
>>> that is disabled. Exactly the same for posix / debian kernel.
>>>
>>> ifconfig:
>>> enp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
>>> inet6 fe80::5054:ff:fe5a:79c5 prefixlen 64 scopeid 0x20<link>
>>> ether 52:54:00:5a:79:c5 txqueuelen 1000 (Ethernet)
>>> RX packets 15 bytes 960 (960.0 B)
>>> RX errors 28 dropped 0 overruns 0 frame 28
>>> TX packets 30 bytes 3332 (3.2 KiB)
>>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>>> device interrupt 22 memory 0xfdc40000-fdc60000
>>>
>>> lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
>>> inet 127.0.0.1 netmask 255.0.0.0
>>> inet6 ::1 prefixlen 128 scopeid 0x10<host>
>>> loop txqueuelen 1000 (Lokale Schleife)
>>> RX packets 28 bytes 2672 (2.6 KiB)
>>> RX errors 0 dropped 0 overruns 0 frame 0
>>> TX packets 28 bytes 2672 (2.6 KiB)
>>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>>>
>>> Third scenario------------------
>>>
>>> Setting an IP address after evl net -ei and the first package sent doesn't work. It looks like
>>> the info is copied once and not updated later, also not when disabled and enabled
>>> again.
>>>
>>> The following does not work:
>>> ifconfig enp1s0 down
>>> ifconfig enp7s0 up 192.168.122.100
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.100
>>> evl net -di enp7s0
>>> ifconfig enp7s0 192.168.122.110
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Sender address is still 192.168.122.100, should be 192.168.122.110
>>>
>>> This works:
>>> ifconfig enp1s0 down
>>> ifconfig enp7s0 up
>>> evl net -ei enp7s0
>>> ifconfig enp7s0 192.168.122.155
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, the sender address is 192.168.122.155
>>>
>>> This also doesn't work and created the confusion:
>>> ifconfig enp1s0 down
>>> ifconfig enp7s0 up
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>> ifconfig enp7s0 192.168.122.155 #Note, setting the IP address is after the first package sent
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>> evl net -di enp7s0
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>> evl net -di enp7s0
>>> ifconfig enp7s0 192.168.122.155
>>> evl net -ei enp7s0
>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>>
>>> Can you reproduce this? It is of now importance for what I am using EVL for. I just discovered this while testing the server / client mode.
>>> So no urgency from my side.
>>>
>>
>> I did not try to reproduce it yet, but reading this description, I would
>> most certainly see the same outcome. I believe this is all related to
>> the so-called "oob front caches" for ARP and IPv4 routing decisions EVL
>> maintains [1].
>>
>> In light of that doc, what is most likely happening is:
>>
>> - the EINPROGRESS status is EVL telling the caller that it could not
>> resolve either the dest ip using its route cache or the MAC address of
>> the destination in its ARP cache, so it had to relay the packet to the
>> in-band stage for transmit. IOW, the packet is outgoing, but the
>> end-to-end real-time guarantee you would have if the NIC driver was
>> oob-capable is lost for this particular transmit.
>>
>> As the in-band stack does the proper resolution for that relayed
>> packet eventually, recording the results into its own neighbour and
>> routing tables, it also conveniently pass this information to some
>> dovetail hooks which EVL listens to, and therefore learns from,
>> feeding its front caches with it. This usually happens quickly after
>> the in-band transmit happens, but some delay may appear due to the
>> time required to receive an ARP reply message from a peer for
>> instance. This is what might make the behavior look slightly flaky at
>> times from a user perspective.
>>
>> The way to make this deterministic (therefore without transient
>> EINPROGRESS error on first transmit) is by using explicit peer
>> solicitation as discussed in [2].
>>
>> - the bad sender address of the 3rd scenario may be a variant of this
>> bug, with the added trick that it should only happen with
>> unconnected/unbound sockets, in which case the source address is
>> retrieved from the routing record matching the destination address. In
>> this case, the routing record found in the EVL front cache is obsolete
>> since the transmitting device changed its (prefix) address. So it
>> looks like some flushing of those obsolete records is missing on
>> changing the address with an active oob port..
>>
>> If I'm right, you should be able to work around this issue by flushing
>> the EVL route cache after the netdev update and before transmitting,
>> as follows:
>> # echo 1 > /sys/class/evl/net/ipv4_routes
>> Obviously, this is not that nice, and the netstack should behave and
>> do this automatically. Will fix.
>>
>
> Done. The netstack now automatically purges obsolete records with stale
> source addresses from its cache upon removal of an IP address from a
> device. Scenario 3 should behave as expected now.
>
Nice, I quickely tested it, it works well. No flush needed any more.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] tidbits: net-udp: add server and client mode
2026-05-29 21:53 ` Hannes Diethelm
@ 2026-06-01 8:25 ` Philippe Gerum
0 siblings, 0 replies; 10+ messages in thread
From: Philippe Gerum @ 2026-06-01 8:25 UTC (permalink / raw)
To: Hannes Diethelm; +Cc: xenomai
Hannes Diethelm <hannes.diethelm@gmail.com> writes:
> Am 29.05.26 um 12:23 schrieb Philippe Gerum:
>> Philippe Gerum <rpm@xenomai.org> writes:
>>
>>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>>
>>>> Am 25.05.26 um 23:02 schrieb Hannes Diethelm:
>>>>> Am 25.05.26 um 18:51 schrieb Philippe Gerum:
>>>>>> Hannes Diethelm <hannes.diethelm@gmail.com> writes:
>>>>>>
>>>>>>> Additionally, fix memory leaks
>>>>>>>
>>>>>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com>
>>>>>>> ---
>>>>>>> tidbits/oob-net-udp.c | 258 +++++++++++++++++++++++++++++++++++++++---
>>>>>>> 1 file changed, 240 insertions(+), 18 deletions(-)
>>>>>>>
>>>>>>
>>>>>> Merged, thanks. Would you mind sending a patch to update the related doc
>>>>>> on the website at [1], regarding the new -S mode? The pages can be
>>>>>> downloaded from [2].
>>>>>>
>>>>>> [1] https://v4.xenomai.org/core/net/udp-demo/index.html
>>>>>> [2] https://gitlab.com/Xenomai/xenomai4/website.git
>>>>>>
>>>>> Sure, done. Is there any way to preview the website? I just used a
>>>>> markdown
>>>>> preview, I hope it did the job and I did not introduce format issues.
>>>>> By the way:
>>>>> I had sometimes issues in my VM. It might just be that I am using two
>>>>> interfaces on the same subnet and disabled the non-oob one after
>>>>> enabling the oob mode on the other one or there might also be an issue
>>>>> somewhere in the evl code. I am not yet able to reproduce it clearly.
>>>>> Sometimes it is all fine, sometimes not. Until now, it did not happen on
>>>>> the real PC or in loopback mode. But I also don't have a good setup
>>>>> with two
>>>>> PC's to really test this (yet). I will follow up when I have
>>>>> something reproducible.
>>>>> If it went wrong, the following happened. After a reboot, all was
>>>>> fine again:
>>>>> In client mode, the sender address was from the wrong interface:
>>>>> 192.168.255.245 instead of 192.168.122.155
>>>>> In server mode, the sender address was just 0.0.0.0.
>>>>> In both cases, there is no answer from the other side due to the
>>>>> response was sent
>>>>> to the wrong IP address.
>>>>> I think the issue is not in my code, due to when I use the standard
>>>>> libc functions
>>>>> in otherwise the same code, the issue disappeared. Attached the code
>>>>> I was using
>>>>> for tests and also on the host to test server/client.
>>>>> Regards
>>>>> Hannes
>>>>
>>>> So, I was able to create something reproducible. I was a bit confused first
>>>> due to sometimes it worked, sometimes not. But this was my fault, not
>>>> rebooting before the test and not exactly using the same commands in the same order.
>>>>
>>>> All command blocks below where performed after a reboot.
>>>>
>>>> I have two network interfaces:
>>>> enp1s0: virtio
>>>> enp7s0: e1000e
>>>>
>>>> After boot:
>>>> enp1s0: 192.168.122.246 nm 255.255.255.0
>>>> enp7s0: No address
>>>>
>>>> First Scenario-------------------
>>>>
>>>> The client works fine:
>>>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>>>> ifconfig enp1s0 down
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -C -p 5201 -m "Client"
>>>>
>>>> The server sometimes fails:
>>>> dhclient enp7s0 # 192.168.122.155 nm 255.255.255.0
>>>> ifconfig enp1s0 down
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 0.0.0.0 -S -p 5201 -m "Server"
>>>>
>>>> There is sometimes the error:
>>>> oob-net-udp: oob_sendmsg() failed: Operation now in progress
>>>> This goes away after a few tries and then it works afterwards.
>>>>
>>>> If I bind to the interface address instead of INADDR_ANY, same issue:
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.155 -S -p 5201 -m "Server"
>>>>
>>>> Second Scenario-------------------
>>>>
>>>> Here, I do not set an IP address for enp7s0. This was a mistake on my side. Disregard,
>>>> more careful testing showed the exact same behavior for posix / vanilla kernel. But it is
>>>> important for the third scenario.
>>>>
>>>> ifconfig enp1s0 down
>>>> ifconfig enp7s0 up
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501
>>>>
>>>> Here, the sender address shown in wireshark is 192.168.122.246. This is the address from enp1s0
>>>> that is disabled. Exactly the same for posix / debian kernel.
>>>>
>>>> ifconfig:
>>>> enp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
>>>> inet6 fe80::5054:ff:fe5a:79c5 prefixlen 64 scopeid 0x20<link>
>>>> ether 52:54:00:5a:79:c5 txqueuelen 1000 (Ethernet)
>>>> RX packets 15 bytes 960 (960.0 B)
>>>> RX errors 28 dropped 0 overruns 0 frame 28
>>>> TX packets 30 bytes 3332 (3.2 KiB)
>>>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>>>> device interrupt 22 memory 0xfdc40000-fdc60000
>>>>
>>>> lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
>>>> inet 127.0.0.1 netmask 255.0.0.0
>>>> inet6 ::1 prefixlen 128 scopeid 0x10<host>
>>>> loop txqueuelen 1000 (Lokale Schleife)
>>>> RX packets 28 bytes 2672 (2.6 KiB)
>>>> RX errors 0 dropped 0 overruns 0 frame 0
>>>> TX packets 28 bytes 2672 (2.6 KiB)
>>>> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
>>>>
>>>> Third scenario------------------
>>>>
>>>> Setting an IP address after evl net -ei and the first package sent doesn't work. It looks like
>>>> the info is copied once and not updated later, also not when disabled and enabled
>>>> again.
>>>>
>>>> The following does not work:
>>>> ifconfig enp1s0 down
>>>> ifconfig enp7s0 up 192.168.122.100
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, sender address is 192.168.122.100
>>>> evl net -di enp7s0
>>>> ifconfig enp7s0 192.168.122.110
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Sender address is still 192.168.122.100, should be 192.168.122.110
>>>>
>>>> This works:
>>>> ifconfig enp1s0 down
>>>> ifconfig enp7s0 up
>>>> evl net -ei enp7s0
>>>> ifconfig enp7s0 192.168.122.155
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Fine, the sender address is 192.168.122.155
>>>>
>>>> This also doesn't work and created the confusion:
>>>> ifconfig enp1s0 down
>>>> ifconfig enp7s0 up
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>>> ifconfig enp7s0 192.168.122.155 #Note, setting the IP address is after the first package sent
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>>> evl net -di enp7s0
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>>> evl net -di enp7s0
>>>> ifconfig enp7s0 192.168.122.155
>>>> evl net -ei enp7s0
>>>> ./oob-net-udp -d -i enp7s0 -a 192.168.122.1 -T -p 2501 # Same behavior as in second scenario, sender address is 192.168.122.246
>>>>
>>>> Can you reproduce this? It is of now importance for what I am using EVL for. I just discovered this while testing the server / client mode.
>>>> So no urgency from my side.
>>>>
>>>
>>> I did not try to reproduce it yet, but reading this description, I would
>>> most certainly see the same outcome. I believe this is all related to
>>> the so-called "oob front caches" for ARP and IPv4 routing decisions EVL
>>> maintains [1].
>>>
>>> In light of that doc, what is most likely happening is:
>>>
>>> - the EINPROGRESS status is EVL telling the caller that it could not
>>> resolve either the dest ip using its route cache or the MAC address of
>>> the destination in its ARP cache, so it had to relay the packet to the
>>> in-band stage for transmit. IOW, the packet is outgoing, but the
>>> end-to-end real-time guarantee you would have if the NIC driver was
>>> oob-capable is lost for this particular transmit.
>>>
>>> As the in-band stack does the proper resolution for that relayed
>>> packet eventually, recording the results into its own neighbour and
>>> routing tables, it also conveniently pass this information to some
>>> dovetail hooks which EVL listens to, and therefore learns from,
>>> feeding its front caches with it. This usually happens quickly after
>>> the in-band transmit happens, but some delay may appear due to the
>>> time required to receive an ARP reply message from a peer for
>>> instance. This is what might make the behavior look slightly flaky at
>>> times from a user perspective.
>>>
>>> The way to make this deterministic (therefore without transient
>>> EINPROGRESS error on first transmit) is by using explicit peer
>>> solicitation as discussed in [2].
>>>
>>> - the bad sender address of the 3rd scenario may be a variant of this
>>> bug, with the added trick that it should only happen with
>>> unconnected/unbound sockets, in which case the source address is
>>> retrieved from the routing record matching the destination address. In
>>> this case, the routing record found in the EVL front cache is obsolete
>>> since the transmitting device changed its (prefix) address. So it
>>> looks like some flushing of those obsolete records is missing on
>>> changing the address with an active oob port..
>>>
>>> If I'm right, you should be able to work around this issue by flushing
>>> the EVL route cache after the netdev update and before transmitting,
>>> as follows:
>>> # echo 1 > /sys/class/evl/net/ipv4_routes
>>> Obviously, this is not that nice, and the netstack should behave and
>>> do this automatically. Will fix.
>>>
>> Done. The netstack now automatically purges obsolete records with
>> stale
>> source addresses from its cache upon removal of an IP address from a
>> device. Scenario 3 should behave as expected now.
>>
>
> Nice, I quickely tested it, it works well. No flush needed any more.
Good, merged upstream. Thanks for the feedback.
--
Philippe.
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-01 8:25 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-23 20:10 [PATCH] tidbits: net-udp: add server and client mode Hannes Diethelm
2026-05-25 16:51 ` Philippe Gerum
2026-05-25 21:02 ` Hannes Diethelm
2026-05-27 15:12 ` Hannes Diethelm
2026-05-27 19:04 ` Philippe Gerum
2026-05-27 20:03 ` Hannes Diethelm
2026-05-29 10:23 ` Philippe Gerum
2026-05-29 21:53 ` Hannes Diethelm
2026-06-01 8:25 ` Philippe Gerum
2026-05-27 18:19 ` Philippe Gerum
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.