[PATCH] tidbits: net-udp: add server and client mode

Hannes Diethelm <[email protected]>
Newsgroups dev.linux.lists.xenomai
Message-ID <[email protected]>
Additionally, fix memory leaks

Signed-off-by: Hannes Diethelm <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.