All of lore.kernel.org
 help / color / mirror / Atom feed
* [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

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.