|
/* |
|
* IPv6 Multicast testing utility. |
|
* Adapted from the IPv4 example in https://www.linuxjournal.com/article/3041 |
|
* |
|
* Run on two separate machines with the same group/port as arguments to test multicast. |
|
*/ |
|
/* |
|
* This is free software released under the GPL license. |
|
* See the GNU GPL for details. |
|
* |
|
* (c) Juan-Mariano de Goyeneche. 1998, 1999. |
|
* Modifications: (c) Robert Mihaly. 2020. |
|
* 2020-01-29: Modified original IPv4 code to use IPv6 |
|
* |
|
*/ |
|
#include <stdio.h> /* printf(), snprintf() */ |
|
#include <stdlib.h> /* strtol(), exit() */ |
|
#include <sys/types.h> |
|
#include <sys/socket.h> /* socket(), setsockopt(), bind(), recvfrom(), sendto() */ |
|
#include <errno.h> /* perror() */ |
|
#include <netinet/in.h> /* IPPROTO_IP, sockaddr_in, htons(), htonl() */ |
|
#include <arpa/inet.h> /* inet_addr() */ |
|
#include <unistd.h> /* fork(), sleep() */ |
|
#include <sys/utsname.h> /* uname() */ |
|
#include <string.h> /* memset() */ |
|
|
|
#define MAXLEN 1024 |
|
#define DELAY 2 |
|
#define TTL 1 |
|
|
|
int main(int argc, char* argv[]) |
|
{ |
|
u_int no = 0; |
|
u_int yes = 1; /* Used with SO_REUSEADDR. In Linux both u_int */ |
|
/* and u_char are valid. */ |
|
int send_s, recv_s; /* Sockets for sending and receiving. */ |
|
int ttl; |
|
struct sockaddr_in6 mcast_group; |
|
struct ipv6_mreq mreq; |
|
struct utsname name; |
|
char buf6[INET6_ADDRSTRLEN]; |
|
|
|
if ((argc<3) || (argc>4)) { |
|
fprintf(stderr, "Usage: %s mcast_group port [ttl]\n", argv[0]); |
|
exit(1); |
|
} |
|
|
|
memset(&mcast_group, 0, sizeof(mcast_group)); |
|
mcast_group.sin6_family = AF_INET6; |
|
mcast_group.sin6_port = htons((unsigned short int)strtol(argv[2], NULL, 0)); |
|
inet_pton(AF_INET6, argv[1], &mcast_group.sin6_addr); |
|
|
|
if ((send_s=socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { |
|
perror ("send socket"); |
|
exit(1); |
|
} |
|
|
|
/* If ttl supplied, set it */ |
|
if (argc == 4) { |
|
ttl = strtol(argv[3], NULL, 0); |
|
} else { |
|
ttl = TTL; |
|
} |
|
printf("ttl = %d\n", ttl); |
|
|
|
if (setsockopt(send_s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { |
|
perror ("ttl setsockopt"); |
|
exit(1); |
|
} |
|
|
|
/* Disable Loop-back */ |
|
if (setsockopt(send_s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, sizeof(no)) < 0) { |
|
perror ("loop setsockopt"); |
|
exit(1); |
|
} |
|
|
|
if ((recv_s=socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { |
|
perror ("recv socket"); |
|
exit(1); |
|
} |
|
|
|
if (setsockopt(recv_s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { |
|
perror("reuseaddr setsockopt"); |
|
exit(1); |
|
} |
|
|
|
if (bind(recv_s, (struct sockaddr*)&mcast_group,sizeof(mcast_group)) < 0){ |
|
perror ("bind"); |
|
exit(1); |
|
} |
|
|
|
/* Tell the kernel we want to join that multicast group. */ |
|
mreq.ipv6mr_multiaddr = mcast_group.sin6_addr; |
|
mreq.ipv6mr_interface = INADDR_ANY; |
|
|
|
if (setsockopt(recv_s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { |
|
perror ("add_membership setsockopt"); |
|
exit(1); |
|
} |
|
|
|
if (uname(&name) < 0) { |
|
perror ("uname"); |
|
exit(1); |
|
} |
|
|
|
switch (fork()) { |
|
case -1: /* Error fork()ing */ |
|
perror("fork"); |
|
exit(1); |
|
case 0: { /* Child -> receive. */ |
|
int n; |
|
int len; |
|
struct sockaddr_in6 from; |
|
char message [MAXLEN+1]; |
|
|
|
for (;;) { |
|
len=sizeof(from); |
|
if ((n=recvfrom(recv_s, message, MAXLEN, 0, (struct sockaddr*)&from, &len)) < 0) { |
|
perror ("recv"); |
|
exit(1); |
|
} |
|
inet_ntop(AF_INET6, &from.sin6_addr, buf6, sizeof(buf6)); |
|
message[n] = 0; /* null-terminate string */ |
|
printf("%s: Received message from %s.\n", name.nodename, buf6); |
|
printf("\t%s", message); |
|
} |
|
/* Not reached. */ |
|
} |
|
default: { /* Parent -> send. */ |
|
char message [MAXLEN]; |
|
snprintf (message, sizeof(message), "Hi, I'm %s. " |
|
"Merry Christmas!\t(TTL==%d)\n", name.nodename, ttl); |
|
for (;;) { |
|
if (sendto(send_s, message, strlen(message), 0, (struct sockaddr*)&mcast_group, sizeof(mcast_group)) < strlen(message)) { |
|
perror("sendto"); |
|
exit(1); |
|
} |
|
sleep(DELAY); |
|
} |
|
/* Not reached. */ |
|
} |
|
} |
|
/* Not really reached. */ |
|
} |