Add new package to debug multicast setups. This is required to use kselftests script for network testing. net-mtools is used instead of mtools as it does conflicts with another package that is also called mtools. Some additional patch from Vladimir Oltean are added to make the tool works on kernel selftests scripts. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
401 lines
11 KiB
Diff
401 lines
11 KiB
Diff
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;
|
|
}
|