Skip to content

Instantly share code, notes, and snippets.

@mihalyr
Created October 28, 2020 11:17
Show Gist options
  • Save mihalyr/6d143e4c1ac4c5258217cb934e135dd4 to your computer and use it in GitHub Desktop.
Save mihalyr/6d143e4c1ac4c5258217cb934e135dd4 to your computer and use it in GitHub Desktop.
Multicast testing

IPv4 and IPv6 multicast testing script. Based on Multicast: From Theory to Practice in Linux Journal

Run on two separate machines with the same group/port as arguments to test multicast.

On Fedora if multicast is not enabled in the firewall this rule might help:

sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -s 192.168.0.0/24 -m addrtype --dst-type MULTICAST -j ACCEPT
sudo firewall-cmd --direct --add-rule ipv6 filter INPUT 0 -s fe80::/64 -m addrtype --dst-type MULTICAST -j ACCEPT

Usage:

Usage: ./mcast_test mcast_group port [ttl]

IPv4 example:

Machine 1 (eli):

./mcast_test 230.0.0.1 37652
eli: Received message from 192.168.0.201.
	Hi, I'm raspi. Merry Christmas!	(TTL==1)
eli: Received message from 192.168.0.201.
	Hi, I'm raspi. Merry Christmas!	(TTL==1)

Machine 2 (raspi):

./mcast_test 230.0.0.1 37652
raspi: Received message from 192.168.0.193.
	Hi, I'm eli. Merry Christmas!	(TTL==1)
raspi: Received message from 192.168.0.193.
	Hi, I'm eli. Merry Christmas!	(TTL==1)

IPv6 example:

Machine 1 (eli):

./mcast6_test ff05::1 33333
eli: Received message from fe80::xxxx:xxxx:xxxx:c920.
	Hi, I'm raspi. Merry Christmas!	(TTL==1)
eli: Received message from fe80::xxxx:xxxx:xxxx:c920.
	Hi, I'm raspi. Merry Christmas!	(TTL==1)

Machine 2 (raspi):

./mcast6_test ff05::1 33333
raspi: Received message from fe80::xxxx:xxxx:xxxx:6eed.
	Hi, I'm eli. Merry Christmas!	(TTL==1)
raspi: Received message from fe80::xxxx:xxxx:xxxx:6eed.
	Hi, I'm eli. Merry Christmas!	(TTL==1)
.SUFFIXES:
.SUFFIXES: .c
.PHONY: all clean
SHELL = /bin/sh
TARGETS = mcast_test mcast6_test
all: $(TARGETS)
clean:
rm -f $(TARGETS)
/*
* 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. */
}
/*
* IPv4 Multicast testing utility.
* Source: Multicast: From Theory to Practice in Linux Journal
* 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.
*
*/
#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_char 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. */
u_char ttl;
struct sockaddr_in mcast_group;
struct ip_mreq mreq;
struct utsname name;
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.sin_family = AF_INET;
mcast_group.sin_port = htons((unsigned short int)strtol(argv[2], NULL, 0));
mcast_group.sin_addr.s_addr = inet_addr(argv[1]);
if ((send_s=socket(AF_INET, 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;
}
if (setsockopt(send_s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
perror ("ttl setsockopt");
exit(1);
}
/* Disable Loop-back */
if (setsockopt(send_s, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(no)) < 0) {
perror ("loop setsockopt");
exit(1);
}
if ((recv_s=socket(AF_INET, 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.imr_multiaddr = mcast_group.sin_addr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(recv_s, IPPROTO_IP, IP_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_in 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);
}
message[n] = 0; /* null-terminate string */
printf("%s: Received message from %s.\n", name.nodename, inet_ntoa(from.sin_addr));
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. */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment