Merge pull request #21496 from Ansuel/add-mtools
authorJosef Schlehofer <pepe.schlehofer@gmail.com>
Fri, 7 Jul 2023 12:05:48 +0000 (14:05 +0200)
committerGitHub <noreply@github.com>
Fri, 7 Jul 2023 12:05:48 +0000 (14:05 +0200)
net-mtools: Add new package to debug multicast setups

net/net-mtools/Makefile [new file with mode: 0644]
net/net-mtools/patches/001-mreceive-refactor-multicast-joining-to-separate-func.patch [new file with mode: 0644]
net/net-mtools/patches/002-mreceive-join-IGMP-group-by-interface.patch [new file with mode: 0644]
net/net-mtools/patches/003-mreceive-support-IPv6.patch [new file with mode: 0644]
net/net-mtools/patches/004-msend-support-IPv6.patch [new file with mode: 0644]
net/net-mtools/patches/005-mreceive-msend-add-new-I-option-to-the-help-text.patch [new file with mode: 0644]
net/net-mtools/patches/006-msend-send-a-limited-number-of-test-packets.patch [new file with mode: 0644]
net/net-mtools/patches/100-mreceive-msend-fix-wrong-version-in-v-output.patch [new file with mode: 0644]
net/net-mtools/test.sh [new file with mode: 0755]

diff --git a/net/net-mtools/Makefile b/net/net-mtools/Makefile
new file mode 100644 (file)
index 0000000..c195326
--- /dev/null
@@ -0,0 +1,40 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=net-mtools
+PKG_VERSION:=2.3
+PKG_RELEASE:=1
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL=https://github.com/troglobit/mtools
+PKG_SOURCE_VERSION:=db665a4303c38cee908eba4dac50873c3f1d899c
+PKG_MIRROR_HASH:=687e3743e46c8ddd23f03168b4021ed08b1a858b2a6743db3b62cb3d4c3592a0
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/net-mtools
+  SECTION:=net
+  CATEGORY:=Network
+  TITLE:=Debug multicast setups with mtools (msend and mreceive)
+  URL:=https://github.com/troglobit/mtools
+endef
+
+define Package/net-mtools/description
+  The tools msend and mreceive can be particulary useful
+  when debugging multicast setups.
+
+  msend continuously sends UDP packets to the multicast
+  group specified by the -g and -p options.
+
+  mreceive joins a multicast group specified by the -g and
+  -p options, then receives and displays the multicast
+  packets sent to this group:port combination by the msend
+  command.
+endef
+
+define Package/net-mtools/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(CP) $(PKG_BUILD_DIR)/msend $(1)/usr/sbin/
+       $(CP) $(PKG_BUILD_DIR)/mreceive $(1)/usr/sbin/
+endef
+
+$(eval $(call BuildPackage,net-mtools))
diff --git a/net/net-mtools/patches/001-mreceive-refactor-multicast-joining-to-separate-func.patch b/net/net-mtools/patches/001-mreceive-refactor-multicast-joining-to-separate-func.patch
new file mode 100644 (file)
index 0000000..db96dae
--- /dev/null
@@ -0,0 +1,66 @@
+From 0cfc04eac370ee33118e17a298d4739c94cacc73 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Tue, 19 Apr 2022 12:28:03 +0300
+Subject: [PATCH 1/6] mreceive: refactor multicast joining to separate function
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+---
+ mreceive.c | 33 ++++++++++++++++++---------------
+ 1 file changed, 18 insertions(+), 15 deletions(-)
+
+--- a/mreceive.c
++++ b/mreceive.c
+@@ -61,12 +61,27 @@ Usage: mreceive [-g GROUP] [-p PORT] [-i
+   -h           Print the command usage.\n\n", VERSION);
+ }
++static void igmp_join(int s, in_addr_t multiaddr, in_addr_t interface)
++{
++      struct ip_mreq mreq;
++      int ret;
++
++      mreq.imr_multiaddr.s_addr = multiaddr;
++      mreq.imr_interface.s_addr = interface;
++
++      ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
++                       (char *)&mreq, sizeof(mreq));
++      if (ret == SOCKET_ERROR) {
++              printf("setsockopt() IP_ADD_MEMBERSHIP failed.\n");
++              exit(1);
++      }
++}
++
+ int main(int argc, char *argv[])
+ {
+       struct sockaddr_in stLocal, stFrom;
+       unsigned char achIn[BUFSIZE];
+       int s, i;
+-      struct ip_mreq stMreq;
+       int iTmp, iRet;
+       int ipnum = 0;
+       int ii;
+@@ -153,22 +168,10 @@ int main(int argc, char *argv[])
+       /* join the multicast group. */
+       if (!ipnum) {           /* single interface */
+-              stMreq.imr_multiaddr.s_addr = inet_addr(TEST_ADDR);
+-              stMreq.imr_interface.s_addr = INADDR_ANY;
+-              iRet = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&stMreq, sizeof(stMreq));
+-              if (iRet == SOCKET_ERROR) {
+-                      printf("setsockopt() IP_ADD_MEMBERSHIP failed.\n");
+-                      exit(1);
+-              }
++              igmp_join(s, inet_addr(TEST_ADDR), INADDR_ANY);
+       } else {
+               for (i = 0; i < ipnum; i++) {
+-                      stMreq.imr_multiaddr.s_addr = inet_addr(TEST_ADDR);
+-                      stMreq.imr_interface.s_addr = IP[i];
+-                      iRet = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&stMreq, sizeof(stMreq));
+-                      if (iRet == SOCKET_ERROR) {
+-                              printf("setsockopt() IP_ADD_MEMBERSHIP failed.\n");
+-                              exit(1);
+-                      }
++                      igmp_join(s, inet_addr(TEST_ADDR), IP[i]);
+               }
+       }
diff --git a/net/net-mtools/patches/002-mreceive-join-IGMP-group-by-interface.patch b/net/net-mtools/patches/002-mreceive-join-IGMP-group-by-interface.patch
new file mode 100644 (file)
index 0000000..5959062
--- /dev/null
@@ -0,0 +1,113 @@
+From 65af96e0907ba9367aab9c1534b11c7f674c1e6a Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Tue, 19 Apr 2022 13:29:07 +0300
+Subject: [PATCH 2/6] mreceive: join IGMP group by interface
+
+mreceive uses the old-style struct ip_mreq for IP_ADD_MEMBERSHIP, which
+takes the source address of the interface wishing to join.
+
+Since the IPV6_ADD_MEMBERSHIP variant only takes a struct ipv6_mreq
+which contains the ifindex and not the source address, we need to add
+support for that.
+
+In preparation for IPv6 support, add logic to join an IGMP group either
+by source address or by interface name, whichever is specified.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+---
+ mreceive.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 46 insertions(+), 5 deletions(-)
+
+--- a/mreceive.c
++++ b/mreceive.c
+@@ -23,6 +23,7 @@
+ #include <string.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>
++#include <net/if.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <sys/time.h>
+@@ -61,7 +62,7 @@ Usage: mreceive [-g GROUP] [-p PORT] [-i
+   -h           Print the command usage.\n\n", VERSION);
+ }
+-static void igmp_join(int s, in_addr_t multiaddr, in_addr_t interface)
++static void igmp_join_by_saddr(int s, in_addr_t multiaddr, in_addr_t interface)
+ {
+       struct ip_mreq mreq;
+       int ret;
+@@ -77,10 +78,34 @@ static void igmp_join(int s, in_addr_t m
+       }
+ }
++static void igmp_join_by_if_name(int s, in_addr_t multicast,
++                               const char *if_name)
++{
++      struct ip_mreqn mreq = {};
++      int if_index;
++      int ret;
++
++      if_index = if_nametoindex(if_name);
++      if (!if_index) {
++              perror("if_nametoindex");
++              exit(1);
++      }
++
++      mreq.imr_multiaddr.s_addr = multicast;
++      mreq.imr_ifindex = if_index;
++
++      ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
++      if (ret) {
++              perror("setsockopt() IP_ADD_MEMBERSHIP");
++              exit(1);
++      }
++}
++
+ int main(int argc, char *argv[])
+ {
+       struct sockaddr_in stLocal, stFrom;
+       unsigned char achIn[BUFSIZE];
++      const char *if_name;
+       int s, i;
+       int iTmp, iRet;
+       int ipnum = 0;
+@@ -131,6 +156,17 @@ int main(int argc, char *argv[])
+                               ii++;
+                               ipnum++;
+                       }
++              } else if (strcmp(argv[ii], "-I") == 0) {
++                      ii++;
++                      if (ii < argc) {
++                              if (if_name) {
++                                      printf("Single interface expected\n");
++                                      exit(1);
++                              }
++
++                              if_name = argv[ii];
++                              ii++;
++                      }
+               } else if (strcmp(argv[ii], "-n") == 0) {
+                       ii++;
+                       NUM = 1;
+@@ -167,11 +203,16 @@ int main(int argc, char *argv[])
+       }
+       /* join the multicast group. */
+-      if (!ipnum) {           /* single interface */
+-              igmp_join(s, inet_addr(TEST_ADDR), INADDR_ANY);
++      if (if_name) {
++              igmp_join_by_if_name(s, inet_addr(TEST_ADDR), if_name);
+       } else {
+-              for (i = 0; i < ipnum; i++) {
+-                      igmp_join(s, inet_addr(TEST_ADDR), IP[i]);
++              if (!ipnum) {           /* single interface */
++                      igmp_join_by_saddr(s, inet_addr(TEST_ADDR), INADDR_ANY);
++              } else {
++                      for (i = 0; i < ipnum; i++) {
++                              igmp_join_by_saddr(s, inet_addr(TEST_ADDR),
++                                                 IP[i]);
++                      }
+               }
+       }
diff --git a/net/net-mtools/patches/003-mreceive-support-IPv6.patch b/net/net-mtools/patches/003-mreceive-support-IPv6.patch
new file mode 100644 (file)
index 0000000..cfd1ab2
--- /dev/null
@@ -0,0 +1,578 @@
+From cc7f68045e5f3cfc6c932996af784ab319951426 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Tue, 19 Apr 2022 13:29:20 +0300
+Subject: [PATCH 3/6] mreceive: support IPv6
+
+Extend the mreceive program with a generalization of sockets,
+addresses and socket options that covers both IPv4 and IPv6.
+
+Most of the lower-level implementation is moved to common.c and exported
+through common.h such that it can be reused by msend at a later time.
+
+The makefile rule to link object files into executables is updated to
+look at all specified objects rather than just the first, by using $^
+instead of $<. Otherwise, common.o would be ignored when linking
+mreceive.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+---
+ Makefile   |   8 +-
+ common.c   | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ common.h   |  36 ++++++++
+ mreceive.c | 142 ++++++++++-------------------
+ 4 files changed, 349 insertions(+), 98 deletions(-)
+ create mode 100644 common.c
+ create mode 100644 common.h
+
+--- a/Makefile
++++ b/Makefile
+@@ -20,8 +20,8 @@ mandir      = $(prefix)/share/man/man8
+ # ttcp is currently not part of the distribution because its not tested
+ # yet.  Please test and let me know at GitHub so I can include it! :)
+ EXEC       := msend mreceive
+-OBJS       := $(EXEC:=.o)
+-DEPS       := $(EXEC:=.d)
++OBJS       := msend.o mreceive.o common.o
++DEPS       := msend.d mreceive.d common.d
+ MANS        = $(addsuffix .8,$(EXEC))
+ DISTFILES   = README.md LICENSE.md
+@@ -33,10 +33,10 @@ all: $(EXEC)
+ .o:
+       @printf "  LINK    $@\n"
+-      @$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $< $(LDLIBS$(LDLIBS-$(@)))
++      @$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@)))
+ msend:    msend.o
+-mreceive: mreceive.o
++mreceive: mreceive.o common.o
+ ttcp:     ttcp.o
+ install: $(EXEC)
+--- /dev/null
++++ b/common.c
+@@ -0,0 +1,261 @@
++/*
++ * common.c -- Common functions for mreceive.c and msend.c
++ */
++#include <arpa/inet.h>
++#include <net/if.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++
++#include "common.h"
++
++int ip_address_parse(const char *string, struct ip_address *ip)
++{
++      int ret;
++
++      ret = inet_pton(AF_INET6, string, &ip->addr6);
++      if (ret > 0) {
++              ip->family = AF_INET6;
++      } else {
++              ret = inet_pton(AF_INET, string, &ip->addr);
++              if (ret > 0) {
++                      ip->family = AF_INET;
++              } else {
++                      fprintf(stderr, "IP address %s not in known format\n",
++                              string);
++                      return -1;
++              }
++      }
++
++      return 0;
++}
++
++int socket_create(struct sock *s, int family, int port)
++{
++      struct sockaddr *serv_addr;
++      int sockopt = 1;
++      int fd, ret;
++
++      memset(s, 0, sizeof(*s));
++
++      if (family == AF_INET) {
++              serv_addr = (struct sockaddr *)&s->udp4;
++              s->udp4.sin_addr.s_addr = htonl(INADDR_ANY);
++              s->udp4.sin_port = htons(port);
++              s->udp4.sin_family = AF_INET;
++              s->addr_size = sizeof(struct sockaddr_in);
++      } else {
++              serv_addr = (struct sockaddr *)&s->udp6;
++              s->udp6.sin6_addr = in6addr_any;
++              s->udp6.sin6_port = htons(port);
++              s->udp6.sin6_family = AF_INET6;
++              s->addr_size = sizeof(struct sockaddr_in6);
++      }
++
++      fd = socket(family, SOCK_DGRAM, 0);
++      if (fd < 0) {
++              perror("socket");
++              return fd;
++      }
++
++      /* avoid EADDRINUSE error on bind() */
++      ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int));
++      if (ret) {
++              perror("setsockopt() SO_REUSEADDR");
++              close(fd);
++              return ret;
++      }
++
++      ret = bind(fd, serv_addr, s->addr_size);
++      if (ret) {
++              perror("bind");
++              close(fd);
++              return ret;
++      }
++
++      s->fd = fd;
++
++      return 0;
++}
++
++static int igmp_join_by_saddr(struct sock *s, const struct ip_address *mc,
++                            struct ip_address *saddr)
++{
++      struct ip_mreq mreq = {};
++      int fd = s->fd;
++      int off = 0;
++      int ret;
++
++      memcpy(&mreq.imr_multiaddr, &mc->addr, sizeof(struct in_addr));
++      memcpy(&mreq.imr_interface.s_addr, &saddr->addr,
++             sizeof(struct in_addr));
++
++      ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
++      if (ret) {
++              perror("setsockopt() IP_ADD_MEMBERSHIP");
++              return -1;
++      }
++
++      ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &off, sizeof(int));
++      if (ret) {
++              perror("setsockopt() IP_MULTICAST_LOOP");
++              return -1;
++      }
++
++      return 0;
++}
++
++static int igmp_join_by_if_name(struct sock *s, const struct ip_address *mc,
++                              const char *if_name)
++{
++      struct ip_mreqn mreq = {};
++      int fd = s->fd;
++      int if_index;
++      int off = 0;
++      int ret;
++
++      if_index = if_nametoindex(if_name);
++      if (!if_index) {
++              perror("if_nametoindex");
++              return -1;
++      }
++
++      memcpy(&mreq.imr_multiaddr, &mc->addr, sizeof(struct in_addr));
++      mreq.imr_ifindex = if_index;
++
++      ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
++      if (ret) {
++              perror("setsockopt() IP_ADD_MEMBERSHIP");
++              return -1;
++      }
++
++      ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &off, sizeof(int));
++      if (ret) {
++              perror("setsockopt() IP_MULTICAST_LOOP");
++              return -1;
++      }
++
++      return 0;
++}
++
++static int mld_join(struct sock *s, const struct ip_address *mc,
++                  const char *if_name)
++{
++      struct ipv6_mreq mreq = {};
++      int if_index, off = 0;
++      int fd = s->fd;
++      int ret;
++
++      if_index = if_nametoindex(if_name);
++      if (!if_index) {
++              perror("if_nametoindex");
++              return -1;
++      }
++
++      memcpy(&mreq.ipv6mr_multiaddr, &mc->addr6, sizeof(struct in6_addr));
++      mreq.ipv6mr_interface = if_index;
++      ret = setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
++                       sizeof(mreq));
++      if (ret) {
++              perror("setsockopt IPV6_ADD_MEMBERSHIP");
++              return -1;
++      }
++
++      ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off,
++                       sizeof(int));
++      if (ret) {
++              perror("setsockopt IPV6_MULTICAST_LOOP");
++              return -1;
++      }
++
++      return 0;
++}
++
++int mc_join(struct sock *s, const struct ip_address *mc, const char *if_name,
++          int num_saddrs, struct ip_address *saddrs)
++{
++      int i, ret;
++
++      if (if_name) {
++              switch (mc->family) {
++              case AF_INET:
++                      return igmp_join_by_if_name(s, mc, if_name);
++              case AF_INET6:
++                      return mld_join(s, mc, if_name);
++              default:
++                      return -1;
++              }
++      }
++
++      if (!num_saddrs) {              /* single interface */
++              struct ip_address saddr = {
++                      .family = AF_INET,
++                      .addr.s_addr = INADDR_ANY,
++              };
++
++              return igmp_join_by_saddr(s, mc, &saddr);
++      }
++
++      for (i = 0; i < num_saddrs; i++) {
++              ret = igmp_join_by_saddr(s, mc, &saddrs[i]);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int igmp_set_ttl(int fd, int ttl)
++{
++      int ret;
++
++      ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(int));
++      if (ret)
++              perror("setsockopt() IP_MULTICAST_TTL");
++
++      return ret;
++}
++
++static int mld_set_hop_limit(int fd, int limit)
++{
++      int ret;
++
++      ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &limit,
++                       sizeof(int));
++      if (ret)
++              perror("setsockopt() IPV6_MULTICAST_HOPS");
++
++      return ret;
++}
++
++int mc_set_hop_limit(struct sock *s, int limit)
++{
++      switch (s->addr_size) {
++      case sizeof(struct sockaddr_in):
++              return igmp_set_ttl(s->fd, limit);
++      case sizeof(struct sockaddr_in6):
++              return mld_set_hop_limit(s->fd, limit);
++      default:
++              return -1;
++      }
++}
++
++int mc_recv(struct sock *s, void *buf, size_t len, struct sock *from)
++{
++      from->addr_size = sizeof(struct sockaddr_in6);
++
++      return recvfrom(s->fd, buf, len, 0, (struct sockaddr *)&(from->udp6),
++                      &from->addr_size);
++}
++
++int socket_get_port(const struct sock *s)
++{
++      switch (s->addr_size) {
++      case sizeof(struct sockaddr_in):
++              return ntohs(s->udp4.sin_port);
++      case sizeof(struct sockaddr_in6):
++              return ntohs(s->udp6.sin6_port);
++      default:
++              return 0;
++      }
++}
+--- /dev/null
++++ b/common.h
+@@ -0,0 +1,36 @@
++/*
++ * common.h -- Common header for mreceive.c and msend.c
++ */
++#ifndef _COMMON_H
++#define _COMMON_H
++
++#include <netinet/in.h>
++#include <netinet/ip.h>
++#include <netinet/ip6.h>
++
++struct ip_address {
++      int family;
++      union {
++              struct in_addr addr;
++              struct in6_addr addr6;
++      };
++};
++
++struct sock {
++      socklen_t addr_size;
++      union {
++              struct sockaddr_in udp4;
++              struct sockaddr_in6 udp6;
++      };
++      int fd;
++};
++
++int ip_address_parse(const char *string, struct ip_address *ip);
++int socket_create(struct sock *s, int family, int port);
++int mc_join(struct sock *s, const struct ip_address *mc, const char *if_name,
++          int num_saddrs, struct ip_address *saddrs);
++int mc_set_hop_limit(struct sock *s, int limit);
++int mc_recv(struct sock *s, void *buf, size_t len, struct sock *from);
++int socket_get_port(const struct sock *s);
++
++#endif
+--- a/mreceive.c
++++ b/mreceive.c
+@@ -28,6 +28,8 @@
+ #include <arpa/inet.h>
+ #include <sys/time.h>
++#include "common.h"
++
+ #define TRUE 1
+ #define FALSE 0
+ #ifndef INVALID_SOCKET
+@@ -43,7 +45,7 @@
+ char *TEST_ADDR = "224.1.1.1";
+ int TEST_PORT = 4444;
+-unsigned long IP[MAXIP];
++struct ip_address IP[MAXIP];
+ int NUM = 0;
+ void printHelp(void)
+@@ -62,52 +64,12 @@ Usage: mreceive [-g GROUP] [-p PORT] [-i
+   -h           Print the command usage.\n\n", VERSION);
+ }
+-static void igmp_join_by_saddr(int s, in_addr_t multiaddr, in_addr_t interface)
+-{
+-      struct ip_mreq mreq;
+-      int ret;
+-
+-      mreq.imr_multiaddr.s_addr = multiaddr;
+-      mreq.imr_interface.s_addr = interface;
+-
+-      ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+-                       (char *)&mreq, sizeof(mreq));
+-      if (ret == SOCKET_ERROR) {
+-              printf("setsockopt() IP_ADD_MEMBERSHIP failed.\n");
+-              exit(1);
+-      }
+-}
+-
+-static void igmp_join_by_if_name(int s, in_addr_t multicast,
+-                               const char *if_name)
+-{
+-      struct ip_mreqn mreq = {};
+-      int if_index;
+-      int ret;
+-
+-      if_index = if_nametoindex(if_name);
+-      if (!if_index) {
+-              perror("if_nametoindex");
+-              exit(1);
+-      }
+-
+-      mreq.imr_multiaddr.s_addr = multicast;
+-      mreq.imr_ifindex = if_index;
+-
+-      ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+-      if (ret) {
+-              perror("setsockopt() IP_ADD_MEMBERSHIP");
+-              exit(1);
+-      }
+-}
+-
+ int main(int argc, char *argv[])
+ {
+-      struct sockaddr_in stLocal, stFrom;
+       unsigned char achIn[BUFSIZE];
+-      const char *if_name;
+-      int s, i;
+-      int iTmp, iRet;
++      const char *if_name = NULL;
++      struct ip_address mc;
++      struct sock s, from;
+       int ipnum = 0;
+       int ii;
+       unsigned int numreceived;
+@@ -116,6 +78,8 @@ int main(int argc, char *argv[])
+       int starttime;
+       int curtime;
+       struct timeval tv;
++      int ret;
++      int i;
+ /*
+   if( argc < 2 ) {
+@@ -152,7 +116,10 @@ int main(int argc, char *argv[])
+               } else if (strcmp(argv[ii], "-i") == 0) {
+                       ii++;
+                       if ((ii < argc) && !(strchr(argv[ii], '-'))) {
+-                              IP[ipnum] = inet_addr(argv[ii]);
++                              ret = ip_address_parse(argv[ii], &IP[ipnum]);
++                              if (ret)
++                                      exit(1);
++
+                               ii++;
+                               ipnum++;
+                       }
+@@ -177,73 +144,59 @@ int main(int argc, char *argv[])
+               }
+       }
+-      /* get a datagram socket */
+-      s = socket(AF_INET, SOCK_DGRAM, 0);
+-      if (s == INVALID_SOCKET) {
+-              printf("socket() failed.\n");
++      ret = ip_address_parse(TEST_ADDR, &mc);
++      if (ret)
+               exit(1);
+-      }
+-      /* avoid EADDRINUSE error on bind() */
+-      iTmp = TRUE;
+-      iRet = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&iTmp, sizeof(iTmp));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("setsockopt() SO_REUSEADDR failed.\n");
++      if (mc.family == AF_INET6 && ipnum) {
++              printf("Joining IPv6 groups by source address not supported, use -I\n");
+               exit(1);
+       }
+-      /* name the socket */
+-      stLocal.sin_family = AF_INET;
+-      stLocal.sin_addr.s_addr = htonl(INADDR_ANY);
+-      stLocal.sin_port = htons(TEST_PORT);
+-      iRet = bind(s, (struct sockaddr *)&stLocal, sizeof(stLocal));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("bind() failed.\n");
++      if (mc.family == AF_INET6 && !if_name) {
++              printf("-I is mandatory with IPv6\n");
+               exit(1);
+       }
+-      /* join the multicast group. */
+-      if (if_name) {
+-              igmp_join_by_if_name(s, inet_addr(TEST_ADDR), if_name);
+-      } else {
+-              if (!ipnum) {           /* single interface */
+-                      igmp_join_by_saddr(s, inet_addr(TEST_ADDR), INADDR_ANY);
+-              } else {
+-                      for (i = 0; i < ipnum; i++) {
+-                              igmp_join_by_saddr(s, inet_addr(TEST_ADDR),
+-                                                 IP[i]);
+-                      }
+-              }
+-      }
++      /* get a datagram socket */
++      ret = socket_create(&s, mc.family, TEST_PORT);
++      if (ret)
++              exit(1);
+-      /* set TTL to traverse up to multiple routers */
+-      iTmp = TTL_VALUE;
+-      iRet = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&iTmp, sizeof(iTmp));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("setsockopt() IP_MULTICAST_TTL failed.\n");
++      /* join the multicast group. */
++      ret = mc_join(&s, &mc, if_name, ipnum, IP);
++      if (ret)
+               exit(1);
+-      }
+-      /* disable loopback */
+-      /* iTmp = TRUE; */
+-      iTmp = FALSE;
+-      iRet = setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&iTmp, sizeof(iTmp));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("setsockopt() IP_MULTICAST_LOOP failed.\n");
++      /* set TTL to traverse up to multiple routers */
++      ret = mc_set_hop_limit(&s, TTL_VALUE);
++      if (ret)
+               exit(1);
+-      }
+       printf("Now receiving from multicast group: %s\n", TEST_ADDR);
+       for (i = 0;; i++) {
+-              socklen_t addr_size = sizeof(struct sockaddr_in);
++              char from_buf[INET6_ADDRSTRLEN];
+               static int iCounter = 1;
++              const char *addr_str;
+               /* receive from the multicast address */
+-              iRet = recvfrom(s, achIn, BUFSIZE, 0, (struct sockaddr *)&stFrom, &addr_size);
+-              if (iRet < 0) {
+-                      printf("recvfrom() failed.\n");
++              ret = mc_recv(&s, achIn, BUFSIZE, &from);
++              if (ret < 0) {
++                      perror("recvfrom");
++                      exit(1);
++              }
++
++              if (mc.family == AF_INET) {
++                      addr_str = inet_ntop(AF_INET, &from.udp4.sin_addr,
++                                           from_buf, INET6_ADDRSTRLEN);
++              } else {
++                      addr_str = inet_ntop(AF_INET6, &from.udp6.sin6_addr,
++                                           from_buf, INET6_ADDRSTRLEN);
++              }
++              if (!addr_str) {
++                      perror("inet_ntop");
+                       exit(1);
+               }
+@@ -256,7 +209,8 @@ int main(int argc, char *argv[])
+                       numreceived =
+                           (unsigned int)achIn[0] + ((unsigned int)(achIn[1]) << 8) + ((unsigned int)(achIn[2]) << 16) +
+                           ((unsigned int)(achIn[3]) >> 24);
+-                      fprintf(stdout, "%5d\t%s:%5d\t%d.%03d\t%5d\n", iCounter, inet_ntoa(stFrom.sin_addr), ntohs(stFrom.sin_port),
++                      fprintf(stdout, "%5d\t%s:%5d\t%d.%03d\t%5d\n", iCounter,
++                              from_buf, socket_get_port(&from),
+                               curtime / 1000000, (curtime % 1000000) / 1000, numreceived);
+                       fflush(stdout);
+                       rcvCountNew = numreceived;
+@@ -276,7 +230,7 @@ int main(int argc, char *argv[])
+                       rcvCountOld = rcvCountNew;
+               } else {
+                       printf("Receive msg %d from %s:%d: %s\n",
+-                             iCounter, inet_ntoa(stFrom.sin_addr), ntohs(stFrom.sin_port), achIn);
++                             iCounter, from_buf, socket_get_port(&from), achIn);
+               }
+               iCounter++;
+       }
diff --git a/net/net-mtools/patches/004-msend-support-IPv6.patch b/net/net-mtools/patches/004-msend-support-IPv6.patch
new file mode 100644 (file)
index 0000000..84d5175
--- /dev/null
@@ -0,0 +1,401 @@
+From 9aa908fc2dd84cfed151fa260b39465978079274 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Tue, 19 Apr 2022 19:28:59 +0300
+Subject: [PATCH 4/6] msend: support IPv6
+
+Finish the conversion by updating msend to use the common procedures
+that support IPv6.
+
+I've only tested this with a link-local source address.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+---
+ Makefile   |   2 +-
+ common.c   |  62 +++++++++++++++++++++----
+ common.h   |   5 +-
+ mreceive.c |   2 +-
+ msend.c    | 131 +++++++++++++++++++++++++++++------------------------
+ 5 files changed, 132 insertions(+), 70 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -35,7 +35,7 @@ all: $(EXEC)
+       @printf "  LINK    $@\n"
+       @$(CC) $(CFLAGS) $(LDFLAGS) -Wl,-Map,$@.map -o $@ $^ $(LDLIBS$(LDLIBS-$(@)))
+-msend:    msend.o
++msend:    msend.o common.o
+ mreceive: mreceive.o common.o
+ ttcp:     ttcp.o
+--- a/common.c
++++ b/common.c
+@@ -30,7 +30,8 @@ int ip_address_parse(const char *string,
+       return 0;
+ }
+-int socket_create(struct sock *s, int family, int port)
++int socket_create(struct sock *s, int family, int port,
++                struct ip_address *saddr, const char *if_name)
+ {
+       struct sockaddr *serv_addr;
+       int sockopt = 1;
+@@ -40,13 +41,16 @@ int socket_create(struct sock *s, int fa
+       if (family == AF_INET) {
+               serv_addr = (struct sockaddr *)&s->udp4;
+-              s->udp4.sin_addr.s_addr = htonl(INADDR_ANY);
++              s->udp4.sin_addr = saddr ? saddr->addr :
++                                 (struct in_addr) {
++                                      .s_addr = htonl(INADDR_ANY),
++                                 };
+               s->udp4.sin_port = htons(port);
+               s->udp4.sin_family = AF_INET;
+               s->addr_size = sizeof(struct sockaddr_in);
+       } else {
+               serv_addr = (struct sockaddr *)&s->udp6;
+-              s->udp6.sin6_addr = in6addr_any;
++              s->udp6.sin6_addr = saddr ? saddr->addr6 : in6addr_any;
+               s->udp6.sin6_port = htons(port);
+               s->udp6.sin6_family = AF_INET6;
+               s->addr_size = sizeof(struct sockaddr_in6);
+@@ -66,11 +70,22 @@ int socket_create(struct sock *s, int fa
+               return ret;
+       }
+-      ret = bind(fd, serv_addr, s->addr_size);
+-      if (ret) {
+-              perror("bind");
+-              close(fd);
+-              return ret;
++      if (if_name) {
++              /* Bind to device, required for IPv6 link-local addresses */
++              ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, if_name,
++                               IFNAMSIZ - 1);
++              if (ret) {
++                      perror("setsockopt() SO_BINDTODEVICE");
++                      close(fd);
++                      return ret;
++              }
++      } else {
++              ret = bind(fd, serv_addr, s->addr_size);
++              if (ret) {
++                      perror("bind");
++                      close(fd);
++                      return ret;
++              }
+       }
+       s->fd = fd;
+@@ -248,6 +263,12 @@ int mc_recv(struct sock *s, void *buf, s
+                       &from->addr_size);
+ }
++int mc_send(struct sock *s, struct sock *to, void *buf, size_t len)
++{
++      return sendto(s->fd, buf, len, 0, (struct sockaddr *)&(to->udp4),
++                    s->addr_size);
++}
++
+ int socket_get_port(const struct sock *s)
+ {
+       switch (s->addr_size) {
+@@ -259,3 +280,28 @@ int socket_get_port(const struct sock *s
+               return 0;
+       }
+ }
++
++int socket_set_loopback(struct sock *s, int loop)
++{
++      int fd = s->fd;
++      int ret;
++
++      switch (s->addr_size) {
++      case sizeof(struct sockaddr_in):
++              ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
++                               sizeof(int));
++              if (ret)
++                      perror("setsockopt IP_MULTICAST_LOOP");
++              break;
++      case sizeof(struct sockaddr_in6):
++              ret = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop,
++                               sizeof(int));
++              if (ret)
++                      perror("setsockopt IPV6_MULTICAST_LOOP");
++              break;
++      default:
++              return 0;
++      }
++
++      return ret;
++}
+--- a/common.h
++++ b/common.h
+@@ -26,11 +26,14 @@ struct sock {
+ };
+ int ip_address_parse(const char *string, struct ip_address *ip);
+-int socket_create(struct sock *s, int family, int port);
++int socket_create(struct sock *s, int family, int port,
++                struct ip_address *saddr, const char *if_name);
+ int mc_join(struct sock *s, const struct ip_address *mc, const char *if_name,
+           int num_saddrs, struct ip_address *saddrs);
+ int mc_set_hop_limit(struct sock *s, int limit);
+ int mc_recv(struct sock *s, void *buf, size_t len, struct sock *from);
++int mc_send(struct sock *s, struct sock *to, void *buf, size_t len);
+ int socket_get_port(const struct sock *s);
++int socket_set_loopback(struct sock *s, int loop);
+ #endif
+--- a/mreceive.c
++++ b/mreceive.c
+@@ -159,7 +159,7 @@ int main(int argc, char *argv[])
+       }
+       /* get a datagram socket */
+-      ret = socket_create(&s, mc.family, TEST_PORT);
++      ret = socket_create(&s, mc.family, TEST_PORT, NULL, NULL);
+       if (ret)
+               exit(1);
+--- a/msend.c
++++ b/msend.c
+@@ -30,6 +30,8 @@
+ #include <signal.h>
+ #include <sys/time.h>
++#include "common.h"
++
+ #define TRUE 1
+ #define FALSE 0
+ #ifndef INVALID_SOCKET
+@@ -45,18 +47,16 @@ char *TEST_ADDR = "224.1.1.1";
+ int TEST_PORT = 4444;
+ int TTL_VALUE = 1;
+ int SLEEP_TIME = 1000;
+-unsigned long IP = INADDR_ANY;
+ int NUM = 0;
+ int join_flag = 0;            /* not join */
+ typedef struct timerhandler_s {
+-      int s;
++      struct sock *s;
++      struct sock *to;
+       char *achOut;
+       int len;
+       int n;
+-      struct sockaddr *stTo;
+-      int addr_size;
+ } timerhandler_t;
+ timerhandler_t handler_par;
+ void timerhandler();
+@@ -87,16 +87,15 @@ Usage:  msend [-g GROUP] [-p PORT] [-joi
+ int main(int argc, char *argv[])
+ {
+-      struct sockaddr_in stLocal, stTo;
++      struct ip_address *saddr = NULL, mc;
++      struct sock s = {}, to = {};
++      const char *if_name = NULL;
+       char achOut[BUFSIZE] = "";
+-      int s, i;
+-      struct ip_mreq stMreq;
+-      int iTmp, iRet;
+       int ii = 1;
+-      int addr_size = sizeof(struct sockaddr_in);
+       struct itimerval times;
+       sigset_t sigset;
+       struct sigaction act;
++      int ret, i;
+       if ((argc == 2) && (strcmp(argv[ii], "-v") == 0)) {
+               printf("msend version 2.2\n");
+@@ -126,7 +125,32 @@ int main(int argc, char *argv[])
+               } else if (strcmp(argv[ii], "-i") == 0) {
+                       ii++;
+                       if ((ii < argc) && !(strchr(argv[ii], '-'))) {
+-                              IP = inet_addr(argv[ii]);
++                              if (saddr) {
++                                      printf("Single source address allowed\n");
++                                      exit(1);
++                              }
++
++                              saddr = calloc(1, sizeof(*saddr));
++                              if (!saddr) {
++                                      printf("Low memory\n");
++                                      exit(1);
++                              }
++
++                              ret = ip_address_parse(argv[ii], saddr);
++                              if (ret)
++                                      exit(1);
++
++                              ii++;
++                      }
++              } else if (strcmp(argv[ii], "-I") == 0) {
++                      ii++;
++                      if (ii < argc) {
++                              if (if_name) {
++                                      printf("Single interface expected\n");
++                                      exit(1);
++                              }
++
++                              if_name = argv[ii];
+                               ii++;
+                       }
+               } else if (strcmp(argv[ii], "-t") == 0) {
+@@ -158,62 +182,50 @@ int main(int argc, char *argv[])
+               }
+       }
+-      /* get a datagram socket */
+-      s = socket(AF_INET, SOCK_DGRAM, 0);
+-      if (s == INVALID_SOCKET) {
+-              printf("socket() failed.\n");
++      ret = ip_address_parse(TEST_ADDR, &mc);
++      if (ret)
+               exit(1);
+-      }
+-      /* avoid EADDRINUSE error on bind() */
+-      iTmp = TRUE;
+-      iRet = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&iTmp, sizeof(iTmp));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("setsockopt() SO_REUSEADDR failed.\n");
++      if (join_flag && mc.family == AF_INET6 && !if_name) {
++              printf("-I is mandatory when joining IPv6 group\n");
+               exit(1);
+       }
+-      /* name the socket */
+-      stLocal.sin_family = AF_INET;
+-      stLocal.sin_addr.s_addr = IP;
+-      stLocal.sin_port = htons(TEST_PORT);
+-      iRet = bind(s, (struct sockaddr *)&stLocal, sizeof(stLocal));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("bind() failed.\n");
++      /* get a datagram socket */
++      ret = socket_create(&s, mc.family, TEST_PORT, saddr, if_name);
++      if (ret)
+               exit(1);
+-      }
+       /* join the multicast group. */
+-      stMreq.imr_multiaddr.s_addr = inet_addr(TEST_ADDR);
+-      stMreq.imr_interface.s_addr = IP;
+       if (join_flag == 1) {
+-              iRet = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&stMreq, sizeof(stMreq));
+-              if (iRet == SOCKET_ERROR) {
+-                      printf("setsockopt() IP_ADD_MEMBERSHIP failed.\n");
++              ret = mc_join(&s, &mc, if_name, 0, NULL);
++              if (ret)
+                       exit(1);
+-              }
+       }
+       /* set TTL to traverse up to multiple routers */
+-      iTmp = TTL_VALUE;
+-      iRet = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&iTmp, sizeof(iTmp));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("setsockopt() IP_MULTICAST_TTL failed.\n");
++      ret = mc_set_hop_limit(&s, TTL_VALUE);
++      if (ret)
+               exit(1);
+-      }
+       /* enable loopback */
+-      iTmp = TRUE;
+-      iRet = setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&iTmp, sizeof(iTmp));
+-      if (iRet == SOCKET_ERROR) {
+-              printf("setsockopt() IP_MULTICAST_LOOP failed.\n");
++      ret = socket_set_loopback(&s, 1);
++      if (ret)
+               exit(1);
+-      }
+       /* assign our destination address */
+-      stTo.sin_family = AF_INET;
+-      stTo.sin_addr.s_addr = inet_addr(TEST_ADDR);
+-      stTo.sin_port = htons(TEST_PORT);
++      if (mc.family == AF_INET) {
++              to.udp4.sin_addr = mc.addr;
++              to.udp4.sin_port = htons(TEST_PORT);
++              to.udp4.sin_family = AF_INET;
++              to.addr_size = sizeof(struct sockaddr_in);
++      } else {
++              to.udp6.sin6_addr = mc.addr6;
++              to.udp6.sin6_port = htons(TEST_PORT);
++              to.udp6.sin6_family = AF_INET6;
++              to.addr_size = sizeof(struct sockaddr_in6);
++      }
++
+       printf("Now sending to multicast group: %s\n", TEST_ADDR);
+       SLEEP_TIME *= 1000;     /* convert to microsecond */
+@@ -237,12 +249,11 @@ int main(int argc, char *argv[])
+               times.it_interval.tv_usec = (long)(SLEEP_TIME % 1000000);
+               setitimer(ITIMER_REAL, &times, NULL);
+-              handler_par.s = s;
++              handler_par.s = &s;
++              handler_par.to = &to;
+               handler_par.achOut = achOut;
+               handler_par.len = strlen(achOut) + 1;
+               handler_par.n = 0;
+-              handler_par.stTo = (struct sockaddr *)&stTo;
+-              handler_par.addr_size = addr_size;
+               /* now wait for the alarms */
+               sigemptyset(&sigset);
+@@ -252,8 +263,6 @@ int main(int argc, char *argv[])
+               return 0;
+       } else {
+               for (i = 0; i < 10; i++) {
+-                      int addr_size = sizeof(struct sockaddr_in);
+-
+                       if (NUM) {
+                               achOut[3] = (unsigned char)(i >> 24);
+                               achOut[2] = (unsigned char)(i >> 16);
+@@ -264,9 +273,10 @@ int main(int argc, char *argv[])
+                               printf("Send out msg %d to %s:%d: %s\n", i, TEST_ADDR, TEST_PORT, achOut);
+                       }
+-                      iRet = sendto(s, achOut, (NUM ? 4 : strlen(achOut) + 1), 0, (struct sockaddr *)&stTo, addr_size);
+-                      if (iRet < 0) {
+-                              printf("sendto() failed.\n");
++                      ret = mc_send(&s, &to, achOut,
++                                    NUM ? 4 : strlen(achOut) + 1);
++                      if (ret < 0) {
++                              perror("sendto");
+                               exit(1);
+                       }
+               }               /* end for(;;) */
+@@ -277,8 +287,8 @@ int main(int argc, char *argv[])
+ void timerhandler(void)
+ {
+-      int iRet;
+       static int iCounter = 1;
++      int ret;
+       if (NUM) {
+               handler_par.achOut = (char *)(&iCounter);
+@@ -287,11 +297,14 @@ void timerhandler(void)
+       } else {
+               printf("Sending msg %d, TTL %d, to %s:%d: %s\n", iCounter, TTL_VALUE, TEST_ADDR, TEST_PORT, handler_par.achOut);
+       }
+-      iRet = sendto(handler_par.s, handler_par.achOut, handler_par.len, handler_par.n, handler_par.stTo, handler_par.addr_size);
+-      if (iRet < 0) {
+-              printf("sendto() failed.\n");
++
++      ret = mc_send(handler_par.s, handler_par.to, handler_par.achOut,
++                    handler_par.len);
++      if (ret < 0) {
++              perror("sendto");
+               exit(1);
+       }
++
+       iCounter++;
+       return;
+ }
diff --git a/net/net-mtools/patches/005-mreceive-msend-add-new-I-option-to-the-help-text.patch b/net/net-mtools/patches/005-mreceive-msend-add-new-I-option-to-the-help-text.patch
new file mode 100644 (file)
index 0000000..73af1d1
--- /dev/null
@@ -0,0 +1,33 @@
+From bf95bdeccab98cec77dc1b10bce0b215754e4e46 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Thu, 21 Apr 2022 16:45:08 +0300
+Subject: [PATCH 5/6] mreceive: msend: add new -I option to the help text
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+---
+ mreceive.c | 2 ++
+ msend.c    | 2 ++
+ 2 files changed, 4 insertions(+)
+
+--- a/mreceive.c
++++ b/mreceive.c
+@@ -58,6 +58,8 @@ Usage: mreceive [-g GROUP] [-p PORT] [-i
+   -p PORT      UDP port number used in the multicast packets.  Default: 4444\n\
+   -i ADDRESS   IP addresses of one or more interfaces to listen for the given\n\
+                multicast group.  Default: the system default interface.\n\
++  -I interface The interface on which to receive. Can be specified as an\n\
++               alternative to -i.\n\
+   -n           Interpret the contents of the message as a number instead of\n\
+                a string of characters.  Use this with `msend -n`\n\
+   -v           Print version information.\n\
+--- a/msend.c
++++ b/msend.c
+@@ -72,6 +72,8 @@ Usage:  msend [-g GROUP] [-p PORT] [-joi
+   -p PORT      UDP port number used in the multicast packets.  Default: 4444\n\
+   -i ADDRESS   IP address of the interface to use to send the packets.\n\
+                The default is to use the system default interface.\n\
++  -I interface The interface on which to send. Can be specified as an\n\
++               alternative to -i.\n\
+   -join        Multicast sender will join the multicast group.\n\
+                By default a sender never joins the group.\n\
+   -P PERIOD    Interval in milliseconds between packets.  Default 1000 msec\n\
diff --git a/net/net-mtools/patches/006-msend-send-a-limited-number-of-test-packets.patch b/net/net-mtools/patches/006-msend-send-a-limited-number-of-test-packets.patch
new file mode 100644 (file)
index 0000000..8e8db73
--- /dev/null
@@ -0,0 +1,81 @@
+From 1013b0a83aef868e6cd33b2f467b9f886b41e7bc Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Fri, 22 Apr 2022 12:59:56 +0300
+Subject: [PATCH 6/6] msend: send a limited number of test packets
+
+For easier integration into a selftest framework, limit the amount of
+packets that the program sends via a command-line argument.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+---
+ msend.c | 17 ++++++++++++++---
+ 1 file changed, 14 insertions(+), 3 deletions(-)
+
+--- a/msend.c
++++ b/msend.c
+@@ -56,7 +56,7 @@ typedef struct timerhandler_s {
+       struct sock *to;
+       char *achOut;
+       int len;
+-      int n;
++      int num_pkts;
+ } timerhandler_t;
+ timerhandler_t handler_par;
+ void timerhandler();
+@@ -82,6 +82,7 @@ Usage:  msend [-g GROUP] [-p PORT] [-joi
+                the first router will drop the packets!  Default: 1\n\
+   -text \"text\" Specify a string to use as payload in the packets, also\n\
+                displayed by the mreceive command.  Default: empty\n\
++  -c           Number of packets to send. Default: send indefinitely\n\
+   -n           Encode -text argument as a number instead of a string.\n\
+   -v           Print version information.\n\
+   -h           Print the command usage.\n\n", VERSION);
+@@ -97,6 +98,7 @@ int main(int argc, char *argv[])
+       struct itimerval times;
+       sigset_t sigset;
+       struct sigaction act;
++      int num_pkts = 0;
+       int ret, i;
+       if ((argc == 2) && (strcmp(argv[ii], "-v") == 0)) {
+@@ -171,6 +173,12 @@ int main(int argc, char *argv[])
+                       ii++;
+                       NUM = 1;
+                       ii++;
++              } else if (strcmp(argv[ii], "-c") == 0) {
++                      ii++;
++                      if ((ii < argc) && !(strchr(argv[ii], '-'))) {
++                              num_pkts = atoi(argv[ii]);
++                              ii++;
++                      }
+               } else if (strcmp(argv[ii], "-text") == 0) {
+                       ii++;
+                       if ((ii < argc) && !(strchr(argv[ii], '-'))) {
+@@ -255,7 +263,7 @@ int main(int argc, char *argv[])
+               handler_par.to = &to;
+               handler_par.achOut = achOut;
+               handler_par.len = strlen(achOut) + 1;
+-              handler_par.n = 0;
++              handler_par.num_pkts = num_pkts;
+               /* now wait for the alarms */
+               sigemptyset(&sigset);
+@@ -264,7 +272,7 @@ int main(int argc, char *argv[])
+               }
+               return 0;
+       } else {
+-              for (i = 0; i < 10; i++) {
++              for (i = 0; num_pkts && i < num_pkts; i++) {
+                       if (NUM) {
+                               achOut[3] = (unsigned char)(i >> 24);
+                               achOut[2] = (unsigned char)(i >> 16);
+@@ -307,6 +315,9 @@ void timerhandler(void)
+               exit(1);
+       }
++      if (iCounter == handler_par.num_pkts)
++              exit(1);
++
+       iCounter++;
+       return;
+ }
diff --git a/net/net-mtools/patches/100-mreceive-msend-fix-wrong-version-in-v-output.patch b/net/net-mtools/patches/100-mreceive-msend-fix-wrong-version-in-v-output.patch
new file mode 100644 (file)
index 0000000..2ba8677
--- /dev/null
@@ -0,0 +1,37 @@
+From e0c9115e1ceb6621d6c04ae8bfd423a0452fea9c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 5 Jul 2023 11:03:40 +0200
+Subject: [PATCH] mreceive: msend: fix wrong version in -v output
+
+-v output was never changed to follow VERSION declared in Makefile and
+was still hardcoded. Fix this to improve version output and align to -h
+output.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ mreceive.c | 2 +-
+ msend.c    | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/mreceive.c
++++ b/mreceive.c
+@@ -93,7 +93,7 @@ int main(int argc, char *argv[])
+       ii = 1;
+       if ((argc == 2) && (strcmp(argv[ii], "-v") == 0)) {
+-              printf("mreceive version 2.2\n");
++              printf("mreceive version %s\n", VERSION);
+               return 0;
+       }
+       if ((argc == 2) && (strcmp(argv[ii], "-h") == 0)) {
+--- a/msend.c
++++ b/msend.c
+@@ -102,7 +102,7 @@ int main(int argc, char *argv[])
+       int ret, i;
+       if ((argc == 2) && (strcmp(argv[ii], "-v") == 0)) {
+-              printf("msend version 2.2\n");
++              printf("msend version %s\n", VERSION);
+               return 0;
+       }
+       if ((argc == 2) && (strcmp(argv[ii], "-h") == 0)) {
diff --git a/net/net-mtools/test.sh b/net/net-mtools/test.sh
new file mode 100755 (executable)
index 0000000..c32f768
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+msend -v | grep "$2"
+mreceive -v | grep "$2"