--- /dev/null
+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))
--- /dev/null
+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]);
+ }
+ }
+
--- /dev/null
+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]);
++ }
+ }
+ }
+
--- /dev/null
+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++;
+ }
--- /dev/null
+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, ×, 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;
+ }
--- /dev/null
+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\
--- /dev/null
+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;
+ }
--- /dev/null
+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)) {
--- /dev/null
+#!/bin/sh
+
+msend -v | grep "$2"
+mreceive -v | grep "$2"