xref: /openbsd-src/regress/sys/netinet6/mcast6/mc6recv.c (revision 5a9255e7fde1eff660134511e5a28e2366fbe25a)
1*5a9255e7Sbluhm /*	$OpenBSD: mc6recv.c,v 1.2 2021/07/06 11:50:34 bluhm Exp $	*/
24a94906dSbluhm /*
34a94906dSbluhm  * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
44a94906dSbluhm  *
54a94906dSbluhm  * Permission to use, copy, modify, and distribute this software for any
64a94906dSbluhm  * purpose with or without fee is hereby granted, provided that the above
74a94906dSbluhm  * copyright notice and this permission notice appear in all copies.
84a94906dSbluhm  *
94a94906dSbluhm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104a94906dSbluhm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114a94906dSbluhm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124a94906dSbluhm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134a94906dSbluhm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144a94906dSbluhm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154a94906dSbluhm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164a94906dSbluhm  */
174a94906dSbluhm 
184a94906dSbluhm #include <sys/socket.h>
194a94906dSbluhm #include <sys/wait.h>
204a94906dSbluhm 
214a94906dSbluhm #include <arpa/inet.h>
224a94906dSbluhm #include <net/if.h>
234a94906dSbluhm #include <netinet/in.h>
244a94906dSbluhm 
254a94906dSbluhm #include <err.h>
264a94906dSbluhm #include <limits.h>
274a94906dSbluhm #include <signal.h>
284a94906dSbluhm #include <stdlib.h>
294a94906dSbluhm #include <stdio.h>
304a94906dSbluhm #include <string.h>
314a94906dSbluhm #include <unistd.h>
324a94906dSbluhm 
334a94906dSbluhm void __dead usage(void);
344a94906dSbluhm void sigexit(int);
354a94906dSbluhm 
364a94906dSbluhm void __dead
usage(void)374a94906dSbluhm usage(void)
384a94906dSbluhm {
394a94906dSbluhm 	fprintf(stderr,
404a94906dSbluhm "mc6recv [-f file] [-g group] [-i ifname] [-n timeout] [-p port] [-r timeout]\n"
414a94906dSbluhm "    [mc6send ...]\n"
424a94906dSbluhm "    -f file         print message to log file, default stdout\n"
434a94906dSbluhm "    -g group        multicast group, default 224.0.0.123\n"
444a94906dSbluhm "    -i ifname       multicast interface name\n"
454a94906dSbluhm "    -n timeout      expect not to receive any message until timeout\n"
464a94906dSbluhm "    -p port         destination port number, default 12345\n"
474a94906dSbluhm "    -r timeout      receive timeout in seconds\n"
484a94906dSbluhm "    mc6send ...      after setting up receive, fork and exec send command\n");
494a94906dSbluhm 	exit(2);
504a94906dSbluhm }
514a94906dSbluhm 
524a94906dSbluhm int
main(int argc,char * argv[])534a94906dSbluhm main(int argc, char *argv[])
544a94906dSbluhm {
554a94906dSbluhm 	struct sockaddr_in6 sin6;
564a94906dSbluhm 	struct ipv6_mreq mreq6;
574a94906dSbluhm 	FILE *log;
584a94906dSbluhm 	const char *errstr, *file, *group, *ifname;
594a94906dSbluhm 	char msg[256];
604a94906dSbluhm 	ssize_t n;
614a94906dSbluhm 	int ch, s, norecv, port, status;
624a94906dSbluhm 	unsigned int timeout;
634a94906dSbluhm 	pid_t pid;
644a94906dSbluhm 
654a94906dSbluhm 	log = stdout;
664a94906dSbluhm 	file = NULL;
674a94906dSbluhm 	group = "ff04::123";
684a94906dSbluhm 	ifname = "lo0";
694a94906dSbluhm 	norecv = 0;
704a94906dSbluhm 	port = 12345;
714a94906dSbluhm 	timeout = 0;
724a94906dSbluhm 	while ((ch = getopt(argc, argv, "f:g:i:n:p:r:")) != -1) {
734a94906dSbluhm 		switch (ch) {
744a94906dSbluhm 		case 'f':
754a94906dSbluhm 			file = optarg;
764a94906dSbluhm 			break;
774a94906dSbluhm 		case 'g':
784a94906dSbluhm 			group = optarg;
794a94906dSbluhm 			break;
804a94906dSbluhm 		case 'i':
814a94906dSbluhm 			ifname = optarg;
824a94906dSbluhm 			break;
834a94906dSbluhm 		case 'n':
844a94906dSbluhm 			norecv = 1;
854a94906dSbluhm 			timeout = strtonum(optarg, 1, INT_MAX, &errstr);
864a94906dSbluhm 			if (errstr != NULL)
874a94906dSbluhm 				errx(1, "no timeout is %s: %s", errstr, optarg);
884a94906dSbluhm 			break;
894a94906dSbluhm 		case 'p':
904a94906dSbluhm 			port = strtonum(optarg, 1, 0xffff, &errstr);
914a94906dSbluhm 			if (errstr != NULL)
924a94906dSbluhm 				errx(1, "port is %s: %s", errstr, optarg);
934a94906dSbluhm 			break;
944a94906dSbluhm 		case 'r':
954a94906dSbluhm 			timeout = strtonum(optarg, 1, INT_MAX, &errstr);
964a94906dSbluhm 			if (errstr != NULL)
974a94906dSbluhm 				errx(1, "timeout is %s: %s", errstr, optarg);
984a94906dSbluhm 			break;
994a94906dSbluhm 		default:
1004a94906dSbluhm 			usage();
1014a94906dSbluhm 		}
1024a94906dSbluhm 	}
1034a94906dSbluhm 	argc -= optind;
1044a94906dSbluhm 	argv += optind;
1054a94906dSbluhm 
1064a94906dSbluhm 	if (file != NULL) {
1074a94906dSbluhm 		log = fopen(file, "w");
1084a94906dSbluhm 		if (log == NULL)
1094a94906dSbluhm 			err(1, "fopen %s", file);
1104a94906dSbluhm 	}
1114a94906dSbluhm 
1124a94906dSbluhm 	s = socket(AF_INET6, SOCK_DGRAM, 0);
1134a94906dSbluhm 	if (s == -1)
1144a94906dSbluhm 		err(1, "socket");
1154a94906dSbluhm 	if (inet_pton(AF_INET6, group, &mreq6.ipv6mr_multiaddr) == -1)
1164a94906dSbluhm 		err(1, "inet_pton %s", group);
1174a94906dSbluhm 	mreq6.ipv6mr_interface = if_nametoindex(ifname);
1184a94906dSbluhm 	if (mreq6.ipv6mr_interface == 0)
1194a94906dSbluhm 		err(1, "if_nametoindex %s", ifname);
1204a94906dSbluhm 	if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
1214a94906dSbluhm 	    sizeof(mreq6)) == -1)
1224a94906dSbluhm 		err(1, "setsockopt IPV6_JOIN_GROUP %s %s", group, ifname);
1234a94906dSbluhm 
1244a94906dSbluhm 	memset(&sin6, 0, sizeof(sin6));
1254a94906dSbluhm 	sin6.sin6_len = sizeof(sin6);
1264a94906dSbluhm 	sin6.sin6_family = AF_INET6;
1274a94906dSbluhm 	sin6.sin6_port = htons(port);
1284a94906dSbluhm 	if (inet_pton(AF_INET6, group, &sin6.sin6_addr) == -1)
1294a94906dSbluhm 		err(1, "inet_pton %s", group);
1304a94906dSbluhm 	if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
1314a94906dSbluhm 	    IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr) ||
1324a94906dSbluhm 	    IN6_IS_ADDR_MC_INTFACELOCAL(&sin6.sin6_addr)) {
1334a94906dSbluhm 		sin6.sin6_scope_id = mreq6.ipv6mr_interface;
1344a94906dSbluhm 	}
1354a94906dSbluhm 	if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1364a94906dSbluhm 		err(1, "bind [%s]:%d", group, port);
1374a94906dSbluhm 
1384a94906dSbluhm 	if (argc) {
1394a94906dSbluhm 		pid = fork();
1404a94906dSbluhm 		switch (pid) {
1414a94906dSbluhm 		case -1:
1424a94906dSbluhm 			err(1, "fork");
1434a94906dSbluhm 		case 0:
1444a94906dSbluhm 			execvp(argv[0], argv);
1454a94906dSbluhm 			err(1, "exec %s", argv[0]);
1464a94906dSbluhm 		}
1474a94906dSbluhm 	}
1484a94906dSbluhm 	if (timeout) {
1494a94906dSbluhm 		if (norecv) {
1504a94906dSbluhm 			if (signal(SIGALRM, sigexit) == SIG_ERR)
1514a94906dSbluhm 				err(1, "signal SIGALRM");
1524a94906dSbluhm 		}
153*5a9255e7Sbluhm 		alarm(timeout);
1544a94906dSbluhm 	}
1554a94906dSbluhm 	n = recv(s, msg, sizeof(msg) - 1, 0);
1564a94906dSbluhm 	if (n == -1)
1574a94906dSbluhm 		err(1, "recv");
1584a94906dSbluhm 	msg[n] = '\0';
1594a94906dSbluhm 	fprintf(log, "<<< %s\n", msg);
1604a94906dSbluhm 	fflush(log);
1614a94906dSbluhm 
1624a94906dSbluhm 	if (norecv)
1634a94906dSbluhm 		errx(1, "received %s", msg);
1644a94906dSbluhm 
1654a94906dSbluhm 	if (argc) {
1664a94906dSbluhm 		if (waitpid(pid, &status, 0) == -1)
1674a94906dSbluhm 			err(1, "waitpid %d", pid);
1684a94906dSbluhm 		if (status)
1694a94906dSbluhm 			errx(1, "%s %d", argv[0], status);
1704a94906dSbluhm 	}
1714a94906dSbluhm 
1724a94906dSbluhm 	return 0;
1734a94906dSbluhm }
1744a94906dSbluhm 
1754a94906dSbluhm void
sigexit(int sig)1764a94906dSbluhm sigexit(int sig)
1774a94906dSbluhm {
1784a94906dSbluhm 	_exit(0);
1794a94906dSbluhm }
180