[PATCH] tidbits: net-udp: use MSG_STABLE for server mode

Hannes Diethelm <[email protected]> Sun, 26 Jul 2026 20:35:11 +0200
Newsgroups dev.linux.lists.xenomai
Message-ID <[email protected]>
MSG_STABLE is now supported, so it is used to check if a client is
already in ARP cache and permanent. If not, use evl_net_solicit()
and try again.

Signed-off-by: Hannes Diethelm <[email protected]>
---
 tidbits/oob-net-udp.c | 72 +++++++++++++++++++------------------------
 1 file changed, 32 insertions(+), 40 deletions(-)

diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c
index 658b463..931c4dc 100644
--- a/tidbits/oob-net-udp.c
+++ b/tidbits/oob-net-udp.c
@@ -238,9 +238,6 @@ static void server(int s, const char *text, int mcount,
 	ssize_t ret;
 	char *tbuf;
 	char rbuf[16384];
-	in_addr_t addr_list[SERVER_ADDR_LIST_SIZE] = {};
-	size_t addr_list_fill = 0;
-	bool solicit_done;
 
 	tlen = (strlen(text) + 1) * mcount;
 	tbuf = malloc(tlen);
@@ -281,39 +278,6 @@ static void server(int s, const char *text, int mcount,
 			evl_printf(" (TRUNCATED)");
 		evl_printf(": %.*s\n", (int)ret, rbuf);
 
-		/*
-		 * We need to call evl_net_solicit for each new client
-		 * once before sending data. This demotes the caller to the
-		 * in-band stage for the first response.
-		 * If this is not done and the ARP address is not
-		 * yet in cache or garbage-collected, oob_sendmsg
-		 * will return EINPROGRESS on start or during runtime.
-		 */
-		solicit_done = false;
-		for (size_t i = 0; i < addr_list_fill && !solicit_done; i++) {
-			if (addr_list[i] == _addr.sin_addr.s_addr) {
-				solicit_done = true;
-			}
-		}
-		if (!solicit_done) {
-			if (verbosity) {
-				char ip_str[INET_ADDRSTRLEN+1];
-				inet_ntop(AF_INET, &(_addr.sin_addr), ip_str, sizeof(ip_str));
-				evl_printf("== client %s first seen: evl_net_solicit\n", ip_str);
-			}
-			ret = evl_net_solicit(s, (const struct sockaddr *)&_addr,
-					EVL_NEIGH_PERMANENT);
-			if (ret)
-				error(1, -ret, "evl_net_solicit()");
-
-			if (addr_list_fill < SERVER_ADDR_LIST_SIZE) {
-				addr_list[addr_list_fill] = _addr.sin_addr.s_addr;
-				addr_list_fill++;
-			} else {
-				error(1, EPERM, "address list full");
-			}
-		}
-
 		iov.iov_base = tbuf;
 		iov.iov_len = tlen;
 		msghdr.msg_iov = &iov;
@@ -323,9 +287,37 @@ static void server(int s, const char *text, int mcount,
 		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");
+		ret = oob_sendmsg(s, &msghdr, NULL, MSG_STEADY);
+		if (ret < 0) {
+			if (errno == EADDRNOTAVAIL) {
+				/*
+				 * oob_sendmsg with MSG_STEADY returns EADDRNOTAVAIL if
+				 * the MAC address is not yet in ARP cache or not permanent.
+				 * We need to call evl_net_solicit in this case.
+				 * This demotes the caller to the in-band stage for the first response.
+				 * To probe if the MAC address is in the ARP cache without sending
+				 * any data, oob_sendmsg() with msghdr.msg_iov = NULL
+				 * and msghdr.msg_iovlen = 0 can be used.
+				 */
+				if (verbosity) {
+					char ip_str[INET_ADDRSTRLEN + 1];
+					inet_ntop(AF_INET, &(_addr.sin_addr), ip_str, sizeof(ip_str));
+					evl_printf("== client %s not in ARP cache: evl_net_solicit\n",
+						ip_str);
+				}
+				ret = evl_net_solicit(s, (const struct sockaddr *)&_addr,
+					EVL_NEIGH_PERMANENT);
+				if (ret)
+					error(1, -ret, "evl_net_solicit()");
+
+				/* oob_sendmsg again, now it should succeed */
+				ret = oob_sendmsg(s, &msghdr, NULL, MSG_STEADY);
+				if (ret < 0)
+					error(1, errno, "oob_sendmsg() failed");
+			} else {
+				error(1, errno, "oob_sendmsg() failed");
+			}
+		}
 		if (verbosity > 1)
 			print_addr("sent to", &_addr);
 	}
@@ -350,7 +342,7 @@ int main(int argc, char *argv[])
 	struct sockaddr_in addr;
 	const char *ip = NULL;
 	ssize_t ret;
-	useconds_t delay=1000000;
+	useconds_t delay = 1000000;
 
 	while ((c = getopt(argc, argv, "a:m:n:i:w:I:p:dsTRCSb")) != EOF) {
 		switch (c) {
-- 
2.47.3