xref: /netbsd-src/tests/net/mcast/mcast.c (revision d9193815d7c9c5ab423036f4f19d4d7a646ed799)
1*d9193815Sozaki-r /*	$NetBSD: mcast.c,v 1.4 2017/02/28 09:23:23 ozaki-r Exp $	*/
28a6480b1Sozaki-r 
38a6480b1Sozaki-r /*-
48a6480b1Sozaki-r  * Copyright (c) 2014 The NetBSD Foundation, Inc.
58a6480b1Sozaki-r  * All rights reserved.
68a6480b1Sozaki-r  *
78a6480b1Sozaki-r  * This code is derived from software contributed to The NetBSD Foundation
88a6480b1Sozaki-r  * by Christos Zoulas.
98a6480b1Sozaki-r  *
108a6480b1Sozaki-r  * Redistribution and use in source and binary forms, with or without
118a6480b1Sozaki-r  * modification, are permitted provided that the following conditions
128a6480b1Sozaki-r  * are met:
138a6480b1Sozaki-r  * 1. Redistributions of source code must retain the above copyright
148a6480b1Sozaki-r  *    notice, this list of conditions and the following disclaimer.
158a6480b1Sozaki-r  * 2. Redistributions in binary form must reproduce the above copyright
168a6480b1Sozaki-r  *    notice, this list of conditions and the following disclaimer in the
178a6480b1Sozaki-r  *    documentation and/or other materials provided with the distribution.
188a6480b1Sozaki-r  *
198a6480b1Sozaki-r  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
208a6480b1Sozaki-r  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
218a6480b1Sozaki-r  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
228a6480b1Sozaki-r  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
238a6480b1Sozaki-r  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
248a6480b1Sozaki-r  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
258a6480b1Sozaki-r  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
268a6480b1Sozaki-r  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
278a6480b1Sozaki-r  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
288a6480b1Sozaki-r  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
298a6480b1Sozaki-r  * POSSIBILITY OF SUCH DAMAGE.
308a6480b1Sozaki-r  */
318a6480b1Sozaki-r #include <sys/cdefs.h>
328a6480b1Sozaki-r #ifdef __RCSID
33*d9193815Sozaki-r __RCSID("$NetBSD: mcast.c,v 1.4 2017/02/28 09:23:23 ozaki-r Exp $");
348a6480b1Sozaki-r #else
358a6480b1Sozaki-r extern const char *__progname;
368a6480b1Sozaki-r #define getprogname() __progname
378a6480b1Sozaki-r #endif
388a6480b1Sozaki-r 
398a6480b1Sozaki-r #include <sys/types.h>
408a6480b1Sozaki-r #include <sys/socket.h>
418a6480b1Sozaki-r #include <sys/wait.h>
428a6480b1Sozaki-r #include <sys/time.h>
438a6480b1Sozaki-r #include <netinet/in.h>
448a6480b1Sozaki-r 
458a6480b1Sozaki-r #include <assert.h>
468a6480b1Sozaki-r #include <netdb.h>
478a6480b1Sozaki-r #include <time.h>
488a6480b1Sozaki-r #include <signal.h>
498a6480b1Sozaki-r #include <stdio.h>
508a6480b1Sozaki-r #include <string.h>
518a6480b1Sozaki-r #include <stdlib.h>
528a6480b1Sozaki-r #include <unistd.h>
538a6480b1Sozaki-r #include <err.h>
548a6480b1Sozaki-r #include <errno.h>
558a6480b1Sozaki-r #include <poll.h>
568a6480b1Sozaki-r #include <stdbool.h>
578a6480b1Sozaki-r 
588a6480b1Sozaki-r #ifdef ATF
598a6480b1Sozaki-r #include <atf-c.h>
608a6480b1Sozaki-r 
618a6480b1Sozaki-r #define ERRX(ev, msg, ...)	ATF_REQUIRE_MSG(0, msg, __VA_ARGS__)
625fd5216cSozaki-r #define ERRX0(ev, msg)		ATF_REQUIRE_MSG(0, msg)
638a6480b1Sozaki-r 
648a6480b1Sozaki-r #define SKIPX(ev, msg, ...)	do {			\
658a6480b1Sozaki-r 	atf_tc_skip(msg, __VA_ARGS__);			\
668a6480b1Sozaki-r 	return;						\
678a6480b1Sozaki-r } while(/*CONSTCOND*/0)
688a6480b1Sozaki-r 
698a6480b1Sozaki-r #else
708a6480b1Sozaki-r #define ERRX(ev, msg, ...)	errx(ev, msg, __VA_ARGS__)
715fd5216cSozaki-r #define ERRX0(ev, msg)		errx(ev, msg)
728a6480b1Sozaki-r #define SKIPX(ev, msg, ...)	errx(ev, msg, __VA_ARGS__)
738a6480b1Sozaki-r #endif
748a6480b1Sozaki-r 
758a6480b1Sozaki-r static int debug;
76*d9193815Sozaki-r static int nsleep;
778a6480b1Sozaki-r 
788a6480b1Sozaki-r #define TOTAL 10
798a6480b1Sozaki-r #define PORT_V4MAPPED "6666"
808a6480b1Sozaki-r #define HOST_V4MAPPED "::FFFF:239.1.1.1"
818a6480b1Sozaki-r #define PORT_V4 "6666"
828a6480b1Sozaki-r #define HOST_V4 "239.1.1.1"
838a6480b1Sozaki-r #define PORT_V6 "6666"
848a6480b1Sozaki-r #define HOST_V6 "FF05:1:0:0:0:0:0:1"
858a6480b1Sozaki-r 
868a6480b1Sozaki-r struct message {
878a6480b1Sozaki-r 	size_t seq;
888a6480b1Sozaki-r 	struct timespec ts;
898a6480b1Sozaki-r };
908a6480b1Sozaki-r 
918a6480b1Sozaki-r static int
addmc(int s,struct addrinfo * ai,bool bug)928a6480b1Sozaki-r addmc(int s, struct addrinfo *ai, bool bug)
938a6480b1Sozaki-r {
948a6480b1Sozaki-r 	struct ip_mreq m4;
958a6480b1Sozaki-r 	struct ipv6_mreq m6;
968a6480b1Sozaki-r 	struct sockaddr_in *s4;
978a6480b1Sozaki-r 	struct sockaddr_in6 *s6;
988a6480b1Sozaki-r 	unsigned int ifc;
998a6480b1Sozaki-r 
1008a6480b1Sozaki-r 	switch (ai->ai_family) {
1018a6480b1Sozaki-r 	case AF_INET:
1028a6480b1Sozaki-r 		s4 = (void *)ai->ai_addr;
1038a6480b1Sozaki-r 		assert(sizeof(*s4) == ai->ai_addrlen);
1048a6480b1Sozaki-r 		m4.imr_multiaddr = s4->sin_addr;
1058a6480b1Sozaki-r 		m4.imr_interface.s_addr = htonl(INADDR_ANY);
1068a6480b1Sozaki-r 		return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1078a6480b1Sozaki-r 		    &m4, sizeof(m4));
1088a6480b1Sozaki-r 	case AF_INET6:
1098a6480b1Sozaki-r 		s6 = (void *)ai->ai_addr;
1108a6480b1Sozaki-r 		/*
1118a6480b1Sozaki-r 		 * Linux:	Does not support the v6 ioctls on v4 mapped
1128a6480b1Sozaki-r 		 *		sockets but it does support the v4 ones and
1138a6480b1Sozaki-r 		 *		it works.
1148a6480b1Sozaki-r 		 * MacOS/X:	Supports the v6 ioctls on v4 mapped sockets,
1158a6480b1Sozaki-r 		 *		but does not work and also does not support
1168a6480b1Sozaki-r 		 *		the v4 ioctls. So no way to make multicasting
1178a6480b1Sozaki-r 		 *		work with mapped addresses.
1188a6480b1Sozaki-r 		 * NetBSD:	Supports both and works for both.
1198a6480b1Sozaki-r 		 */
1208a6480b1Sozaki-r 		if (bug && IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) {
1218a6480b1Sozaki-r 			memcpy(&m4.imr_multiaddr, &s6->sin6_addr.s6_addr[12],
1228a6480b1Sozaki-r 			    sizeof(m4.imr_multiaddr));
1238a6480b1Sozaki-r 			m4.imr_interface.s_addr = htonl(INADDR_ANY);
1248a6480b1Sozaki-r 			return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1258a6480b1Sozaki-r 			    &m4, sizeof(m4));
1268a6480b1Sozaki-r 		}
1278a6480b1Sozaki-r 		assert(sizeof(*s6) == ai->ai_addrlen);
1288a6480b1Sozaki-r 		memset(&m6, 0, sizeof(m6));
1298a6480b1Sozaki-r #if 0
1308a6480b1Sozaki-r 		ifc = 1;
1318a6480b1Sozaki-r 		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
1328a6480b1Sozaki-r 		    &ifc, sizeof(ifc)) == -1)
1338a6480b1Sozaki-r 			return -1;
1348a6480b1Sozaki-r 		ifc = 224;
1358a6480b1Sozaki-r 		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
1368a6480b1Sozaki-r 		    &ifc, sizeof(ifc)) == -1)
1378a6480b1Sozaki-r 			return -1;
1388a6480b1Sozaki-r 		ifc = 1; /* XXX should pick a proper interface */
1398a6480b1Sozaki-r 		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifc,
1408a6480b1Sozaki-r 		    sizeof(ifc)) == -1)
1418a6480b1Sozaki-r 			return -1;
1428a6480b1Sozaki-r #else
1438a6480b1Sozaki-r 		ifc = 0; /* Let pick an appropriate interface */
1448a6480b1Sozaki-r #endif
1458a6480b1Sozaki-r 		m6.ipv6mr_interface = ifc;
1468a6480b1Sozaki-r 		m6.ipv6mr_multiaddr = s6->sin6_addr;
1478a6480b1Sozaki-r 		return setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
1488a6480b1Sozaki-r 		    &m6, sizeof(m6));
1498a6480b1Sozaki-r 	default:
1508a6480b1Sozaki-r 		errno = EOPNOTSUPP;
1518a6480b1Sozaki-r 		return -1;
1528a6480b1Sozaki-r 	}
1538a6480b1Sozaki-r }
1548a6480b1Sozaki-r 
1558a6480b1Sozaki-r static int
allowv4mapped(int s,struct addrinfo * ai)1568a6480b1Sozaki-r allowv4mapped(int s, struct addrinfo *ai)
1578a6480b1Sozaki-r {
1588a6480b1Sozaki-r 	struct sockaddr_in6 *s6;
1598a6480b1Sozaki-r 	int zero = 0;
1608a6480b1Sozaki-r 
1618a6480b1Sozaki-r 	if (ai->ai_family != AF_INET6)
1628a6480b1Sozaki-r 		return 0;
1638a6480b1Sozaki-r 
1648a6480b1Sozaki-r 	s6 = (void *)ai->ai_addr;
1658a6480b1Sozaki-r 
1668a6480b1Sozaki-r 	if (!IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr))
1678a6480b1Sozaki-r 		return 0;
1688a6480b1Sozaki-r 	return setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero));
1698a6480b1Sozaki-r }
1708a6480b1Sozaki-r 
1718a6480b1Sozaki-r static struct sockaddr_storage ss;
1728a6480b1Sozaki-r static int
connector(int fd,const struct sockaddr * sa,socklen_t slen)1738a6480b1Sozaki-r connector(int fd, const struct sockaddr *sa, socklen_t slen)
1748a6480b1Sozaki-r {
1758a6480b1Sozaki-r 	assert(sizeof(ss) > slen);
1768a6480b1Sozaki-r 	memcpy(&ss, sa, slen);
1778a6480b1Sozaki-r 	return 0;
1788a6480b1Sozaki-r }
1798a6480b1Sozaki-r 
1808a6480b1Sozaki-r static void
show(const char * prefix,const struct message * msg)1818a6480b1Sozaki-r show(const char *prefix, const struct message *msg)
1828a6480b1Sozaki-r {
1838a6480b1Sozaki-r 	printf("%10.10s: %zu [%jd.%ld]\n", prefix, msg->seq, (intmax_t)
1848a6480b1Sozaki-r 	    msg->ts.tv_sec, msg->ts.tv_nsec);
1858a6480b1Sozaki-r }
1868a6480b1Sozaki-r 
1878a6480b1Sozaki-r static int
getsocket(const char * host,const char * port,int (* f)(int,const struct sockaddr *,socklen_t),socklen_t * slen,bool bug)1888a6480b1Sozaki-r getsocket(const char *host, const char *port,
1898a6480b1Sozaki-r     int (*f)(int, const struct sockaddr *, socklen_t), socklen_t *slen,
1908a6480b1Sozaki-r     bool bug)
1918a6480b1Sozaki-r {
1928a6480b1Sozaki-r 	int e, s, lasterrno = 0;
1938a6480b1Sozaki-r 	struct addrinfo hints, *ai0, *ai;
1948a6480b1Sozaki-r 	const char *cause = "?";
1958a6480b1Sozaki-r 
1968a6480b1Sozaki-r 	memset(&hints, 0, sizeof(hints));
1978a6480b1Sozaki-r 	hints.ai_family = AF_UNSPEC;
1988a6480b1Sozaki-r 	hints.ai_socktype = SOCK_DGRAM;
1998a6480b1Sozaki-r 	e = getaddrinfo(host, port, &hints, &ai0);
2008a6480b1Sozaki-r 	if (e)
2018a6480b1Sozaki-r 		ERRX(EXIT_FAILURE, "Can't resolve %s:%s (%s)", host, port,
2028a6480b1Sozaki-r 		    gai_strerror(e));
2038a6480b1Sozaki-r 
2048a6480b1Sozaki-r 	s = -1;
2058a6480b1Sozaki-r 	for (ai = ai0; ai; ai = ai->ai_next) {
2068a6480b1Sozaki-r 		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2078a6480b1Sozaki-r 		if (s == -1) {
2088a6480b1Sozaki-r 			lasterrno = errno;
2098a6480b1Sozaki-r 			cause = "socket";
2108a6480b1Sozaki-r 			continue;
2118a6480b1Sozaki-r 		}
2128a6480b1Sozaki-r 		if (allowv4mapped(s, ai) == -1) {
2138a6480b1Sozaki-r 			cause = "allow v4 mapped";
2148a6480b1Sozaki-r 			goto out;
2158a6480b1Sozaki-r 		}
2168a6480b1Sozaki-r 		if ((*f)(s, ai->ai_addr, ai->ai_addrlen) == -1) {
2178a6480b1Sozaki-r 			cause = f == bind ? "bind" : "connect";
2188a6480b1Sozaki-r 			goto out;
2198a6480b1Sozaki-r 		}
2208a6480b1Sozaki-r 		if ((f == bind || f == connector) && addmc(s, ai, bug) == -1) {
2218a6480b1Sozaki-r 			cause = "join group";
2228a6480b1Sozaki-r 			goto out;
2238a6480b1Sozaki-r 		}
2248a6480b1Sozaki-r 		*slen = ai->ai_addrlen;
2258a6480b1Sozaki-r 		break;
2268a6480b1Sozaki-r out:
2278a6480b1Sozaki-r 		lasterrno = errno;
2288a6480b1Sozaki-r 		close(s);
2298a6480b1Sozaki-r 		s = -1;
2308a6480b1Sozaki-r 		continue;
2318a6480b1Sozaki-r 	}
2328a6480b1Sozaki-r 	freeaddrinfo(ai0);
2338a6480b1Sozaki-r 	if (s == -1)
2348a6480b1Sozaki-r 		ERRX(EXIT_FAILURE, "%s (%s)", cause, strerror(lasterrno));
2358a6480b1Sozaki-r 	return s;
2368a6480b1Sozaki-r }
2378a6480b1Sozaki-r 
2385fd5216cSozaki-r static int
synchronize(const int fd,bool waiter)2395fd5216cSozaki-r synchronize(const int fd, bool waiter)
2405fd5216cSozaki-r {
2415fd5216cSozaki-r 	int syncmsg = 0;
2425fd5216cSozaki-r 	int r;
2435fd5216cSozaki-r 	struct pollfd pfd;
2445fd5216cSozaki-r 
2455fd5216cSozaki-r 	if (waiter) {
2465fd5216cSozaki-r 		pfd.fd = fd;
2475fd5216cSozaki-r 		pfd.events = POLLIN;
2485fd5216cSozaki-r 
2495fd5216cSozaki-r 		/* We use poll to avoid lock up when the peer died unexpectedly */
2505fd5216cSozaki-r 		r = poll(&pfd, 1, 10000);
2515fd5216cSozaki-r 		if (r == -1)
2525fd5216cSozaki-r 			ERRX(EXIT_FAILURE, "poll (%s)", strerror(errno));
2535fd5216cSozaki-r 		if (r == 0)
2545fd5216cSozaki-r 			/* Timed out */
2555fd5216cSozaki-r 			return -1;
2565fd5216cSozaki-r 
2575fd5216cSozaki-r 		if (read(fd, &syncmsg, sizeof(syncmsg)) == -1)
2585fd5216cSozaki-r 			ERRX(EXIT_FAILURE, "read (%s)", strerror(errno));
2595fd5216cSozaki-r 	} else {
2605fd5216cSozaki-r 		if (write(fd, &syncmsg, sizeof(syncmsg)) == -1)
2615fd5216cSozaki-r 			ERRX(EXIT_FAILURE, "write (%s)", strerror(errno));
2625fd5216cSozaki-r 	}
2635fd5216cSozaki-r 
2645fd5216cSozaki-r 	return 0;
2655fd5216cSozaki-r }
2665fd5216cSozaki-r 
2675fd5216cSozaki-r static int
sender(const int fd,const char * host,const char * port,size_t n,bool conn,bool bug)2685fd5216cSozaki-r sender(const int fd, const char *host, const char *port, size_t n, bool conn,
2695fd5216cSozaki-r     bool bug)
2708a6480b1Sozaki-r {
2718a6480b1Sozaki-r 	int s;
2728a6480b1Sozaki-r 	ssize_t l;
2738a6480b1Sozaki-r 	struct message msg;
2748a6480b1Sozaki-r 
2758a6480b1Sozaki-r 	socklen_t slen;
2768a6480b1Sozaki-r 
2778a6480b1Sozaki-r 	s = getsocket(host, port, conn ? connect : connector, &slen, bug);
2785fd5216cSozaki-r 
2795fd5216cSozaki-r 	/* Wait until receiver gets ready. */
2805fd5216cSozaki-r 	if (synchronize(fd, true) == -1)
2815fd5216cSozaki-r 		return -1;
2825fd5216cSozaki-r 
2838a6480b1Sozaki-r 	for (msg.seq = 0; msg.seq < n; msg.seq++) {
2848a6480b1Sozaki-r #ifdef CLOCK_MONOTONIC
2858a6480b1Sozaki-r 		if (clock_gettime(CLOCK_MONOTONIC, &msg.ts) == -1)
2868a6480b1Sozaki-r 			ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno));
2878a6480b1Sozaki-r #else
2888a6480b1Sozaki-r 		struct timeval tv;
2898a6480b1Sozaki-r 		if (gettimeofday(&tv, NULL) == -1)
2908a6480b1Sozaki-r 			ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno));
2918a6480b1Sozaki-r 		msg.ts.tv_sec = tv.tv_sec;
2928a6480b1Sozaki-r 		msg.ts.tv_nsec = tv.tv_usec * 1000;
2938a6480b1Sozaki-r #endif
2948a6480b1Sozaki-r 		if (debug)
2958a6480b1Sozaki-r 			show("sending", &msg);
2968a6480b1Sozaki-r 		l = conn ? send(s, &msg, sizeof(msg), 0) :
2978a6480b1Sozaki-r 		    sendto(s, &msg, sizeof(msg), 0, (void *)&ss, slen);
2988a6480b1Sozaki-r 		if (l == -1)
2998a6480b1Sozaki-r 			ERRX(EXIT_FAILURE, "send (%s)", strerror(errno));
3008a6480b1Sozaki-r 		usleep(100);
3018a6480b1Sozaki-r 	}
3025fd5216cSozaki-r 
3035fd5216cSozaki-r 	/* Wait until receiver finishes its work. */
3045fd5216cSozaki-r 	if (synchronize(fd, true) == -1)
3055fd5216cSozaki-r 		return -1;
3065fd5216cSozaki-r 
3075fd5216cSozaki-r 	return 0;
3088a6480b1Sozaki-r }
3098a6480b1Sozaki-r 
3108a6480b1Sozaki-r static void
receiver(const int fd,const char * host,const char * port,size_t n,bool conn,bool bug)3115fd5216cSozaki-r receiver(const int fd, const char *host, const char *port, size_t n, bool conn,
3125fd5216cSozaki-r     bool bug)
3138a6480b1Sozaki-r {
3148a6480b1Sozaki-r 	int s;
3158a6480b1Sozaki-r 	ssize_t l;
3168a6480b1Sozaki-r 	size_t seq;
3178a6480b1Sozaki-r 	struct message msg;
3188a6480b1Sozaki-r 	struct pollfd pfd;
3198a6480b1Sozaki-r 	socklen_t slen;
3208a6480b1Sozaki-r 
3218a6480b1Sozaki-r 	s = getsocket(host, port, bind, &slen, bug);
3228a6480b1Sozaki-r 	pfd.fd = s;
3238a6480b1Sozaki-r 	pfd.events = POLLIN;
3245fd5216cSozaki-r 
3255fd5216cSozaki-r 	/* Tell I'm ready */
3265fd5216cSozaki-r 	synchronize(fd, false);
3275fd5216cSozaki-r 
3288a6480b1Sozaki-r 	for (seq = 0; seq < n; seq++) {
3298a6480b1Sozaki-r 		if (poll(&pfd, 1, 10000) == -1)
3308a6480b1Sozaki-r 			ERRX(EXIT_FAILURE, "poll (%s)", strerror(errno));
3318a6480b1Sozaki-r 		l = conn ? recv(s, &msg, sizeof(msg), 0) :
3328a6480b1Sozaki-r 		    recvfrom(s, &msg, sizeof(msg), 0, (void *)&ss, &slen);
3338a6480b1Sozaki-r 		if (l == -1)
3348a6480b1Sozaki-r 			ERRX(EXIT_FAILURE, "recv (%s)", strerror(errno));
3358a6480b1Sozaki-r 		if (debug)
3368a6480b1Sozaki-r 			show("got", &msg);
3378a6480b1Sozaki-r 		if (seq != msg.seq)
33892795cceSozaki-r 			ERRX(EXIT_FAILURE, "seq: expect=%zu actual=%zu",
33992795cceSozaki-r 			    seq, msg.seq);
3408a6480b1Sozaki-r 	}
3415fd5216cSozaki-r 
342*d9193815Sozaki-r 	if (nsleep)
343*d9193815Sozaki-r 		sleep(nsleep);
3445fd5216cSozaki-r 	/* Tell I'm finished */
3455fd5216cSozaki-r 	synchronize(fd, false);
3468a6480b1Sozaki-r }
3478a6480b1Sozaki-r 
3488a6480b1Sozaki-r static void
run(const char * host,const char * port,size_t n,bool conn,bool bug)3498a6480b1Sozaki-r run(const char *host, const char *port, size_t n, bool conn, bool bug)
3508a6480b1Sozaki-r {
3518a6480b1Sozaki-r 	pid_t pid;
3528a6480b1Sozaki-r 	int status;
3535fd5216cSozaki-r 	int syncfds[2];
3545fd5216cSozaki-r 	int error;
3555fd5216cSozaki-r 
3565fd5216cSozaki-r 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, syncfds) == -1)
3575fd5216cSozaki-r 		ERRX(EXIT_FAILURE, "socketpair (%s)", strerror(errno));
3588a6480b1Sozaki-r 
3598a6480b1Sozaki-r 	switch ((pid = fork())) {
3608a6480b1Sozaki-r 	case 0:
3615fd5216cSozaki-r 		receiver(syncfds[0], host, port, n, conn, bug);
3628a6480b1Sozaki-r 		return;
3638a6480b1Sozaki-r 	case -1:
3648a6480b1Sozaki-r 		ERRX(EXIT_FAILURE, "fork (%s)", strerror(errno));
3658a6480b1Sozaki-r 	default:
3665fd5216cSozaki-r 		error = sender(syncfds[1], host, port, n, conn, bug);
3678a6480b1Sozaki-r 	again:
3688a6480b1Sozaki-r 		switch (waitpid(pid, &status, WNOHANG)) {
3698a6480b1Sozaki-r 		case -1:
3708a6480b1Sozaki-r 			ERRX(EXIT_FAILURE, "wait (%s)", strerror(errno));
3718a6480b1Sozaki-r 		case 0:
3725fd5216cSozaki-r 			if (error == 0)
3735fd5216cSozaki-r 				/*
3745fd5216cSozaki-r 				 * Receiver is still alive, but we know
3755fd5216cSozaki-r 				 * it will exit soon.
3765fd5216cSozaki-r 				 */
3775fd5216cSozaki-r 				goto again;
3785fd5216cSozaki-r 
3798a6480b1Sozaki-r 			if (kill(pid, SIGTERM) == -1)
3808a6480b1Sozaki-r 				ERRX(EXIT_FAILURE, "kill (%s)",
3818a6480b1Sozaki-r 				    strerror(errno));
3828a6480b1Sozaki-r 			goto again;
3838a6480b1Sozaki-r 		default:
3848a6480b1Sozaki-r 			if (WIFSIGNALED(status)) {
3858a6480b1Sozaki-r 				if (WTERMSIG(status) == SIGTERM)
3865fd5216cSozaki-r 					ERRX0(EXIT_FAILURE,
3875fd5216cSozaki-r 					    "receiver failed and was killed" \
3885fd5216cSozaki-r 					    "by sender");
3898a6480b1Sozaki-r 				else
3908a6480b1Sozaki-r 					ERRX(EXIT_FAILURE,
3918a6480b1Sozaki-r 					    "receiver got signaled (%s)",
3928a6480b1Sozaki-r 					    strsignal(WTERMSIG(status)));
3938a6480b1Sozaki-r 			} else if (WIFEXITED(status)) {
3948a6480b1Sozaki-r 				if (WEXITSTATUS(status) != 0)
3958a6480b1Sozaki-r 					ERRX(EXIT_FAILURE,
3968a6480b1Sozaki-r 					    "receiver exited with status %d",
3978a6480b1Sozaki-r 					    WEXITSTATUS(status));
3988a6480b1Sozaki-r 			} else {
3998a6480b1Sozaki-r 				ERRX(EXIT_FAILURE,
4008a6480b1Sozaki-r 				    "receiver exited with unexpected status %d",
4018a6480b1Sozaki-r 				    status);
4028a6480b1Sozaki-r 			}
4038a6480b1Sozaki-r 			break;
4048a6480b1Sozaki-r 		}
4058a6480b1Sozaki-r 		return;
4068a6480b1Sozaki-r 	}
4078a6480b1Sozaki-r }
4088a6480b1Sozaki-r 
4098a6480b1Sozaki-r #ifndef ATF
4108a6480b1Sozaki-r int
main(int argc,char * argv[])4118a6480b1Sozaki-r main(int argc, char *argv[])
4128a6480b1Sozaki-r {
4138a6480b1Sozaki-r 	const char *host, *port;
4148a6480b1Sozaki-r 	int c;
4158a6480b1Sozaki-r 	size_t n;
4168a6480b1Sozaki-r 	bool conn, bug;
4178a6480b1Sozaki-r 
4188a6480b1Sozaki-r 	host = HOST_V4;
4198a6480b1Sozaki-r 	port = PORT_V4;
4208a6480b1Sozaki-r 	n = TOTAL;
4218a6480b1Sozaki-r 	bug = conn = false;
4228a6480b1Sozaki-r 
423*d9193815Sozaki-r 	while ((c = getopt(argc, argv, "46bcdmn:s:")) != -1)
4248a6480b1Sozaki-r 		switch (c) {
4258a6480b1Sozaki-r 		case '4':
4268a6480b1Sozaki-r 			host = HOST_V4;
4278a6480b1Sozaki-r 			port = PORT_V4;
4288a6480b1Sozaki-r 			break;
4298a6480b1Sozaki-r 		case '6':
4308a6480b1Sozaki-r 			host = HOST_V6;
4318a6480b1Sozaki-r 			port = PORT_V6;
4328a6480b1Sozaki-r 			break;
4338a6480b1Sozaki-r 		case 'b':
4348a6480b1Sozaki-r 			bug = true;
4358a6480b1Sozaki-r 			break;
4368a6480b1Sozaki-r 		case 'c':
4378a6480b1Sozaki-r 			conn = true;
4388a6480b1Sozaki-r 			break;
4398a6480b1Sozaki-r 		case 'd':
4408a6480b1Sozaki-r 			debug++;
4418a6480b1Sozaki-r 			break;
4428a6480b1Sozaki-r 		case 'm':
4438a6480b1Sozaki-r 			host = HOST_V4MAPPED;
4448a6480b1Sozaki-r 			port = PORT_V4MAPPED;
4458a6480b1Sozaki-r 			break;
4468a6480b1Sozaki-r 		case 'n':
4478a6480b1Sozaki-r 			n = atoi(optarg);
4488a6480b1Sozaki-r 			break;
449*d9193815Sozaki-r 		case 's':
450*d9193815Sozaki-r 			nsleep = atoi(optarg);
451*d9193815Sozaki-r 			break;
4528a6480b1Sozaki-r 		default:
453*d9193815Sozaki-r 			fprintf(stderr, "Usage: %s [-cdm46] [-n <tot>]"
454*d9193815Sozaki-r 			    " [-s <sleep>]",
4558a6480b1Sozaki-r 			    getprogname());
4568a6480b1Sozaki-r 			return 1;
4578a6480b1Sozaki-r 		}
4588a6480b1Sozaki-r 
4598a6480b1Sozaki-r 	run(host, port, n, conn, bug);
4608a6480b1Sozaki-r 	return 0;
4618a6480b1Sozaki-r }
4628a6480b1Sozaki-r #else
4638a6480b1Sozaki-r 
4648a6480b1Sozaki-r ATF_TC(conninet4);
ATF_TC_HEAD(conninet4,tc)4658a6480b1Sozaki-r ATF_TC_HEAD(conninet4, tc)
4668a6480b1Sozaki-r {
4678a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv4");
4688a6480b1Sozaki-r }
4698a6480b1Sozaki-r 
ATF_TC_BODY(conninet4,tc)4708a6480b1Sozaki-r ATF_TC_BODY(conninet4, tc)
4718a6480b1Sozaki-r {
4728a6480b1Sozaki-r 	run(HOST_V4, PORT_V4, TOTAL, true, false);
4738a6480b1Sozaki-r }
4748a6480b1Sozaki-r 
4758a6480b1Sozaki-r ATF_TC(connmappedinet4);
ATF_TC_HEAD(connmappedinet4,tc)4768a6480b1Sozaki-r ATF_TC_HEAD(connmappedinet4, tc)
4778a6480b1Sozaki-r {
4788a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4");
4798a6480b1Sozaki-r }
4808a6480b1Sozaki-r 
ATF_TC_BODY(connmappedinet4,tc)4818a6480b1Sozaki-r ATF_TC_BODY(connmappedinet4, tc)
4828a6480b1Sozaki-r {
4838a6480b1Sozaki-r 	run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, false);
4848a6480b1Sozaki-r }
4858a6480b1Sozaki-r 
4868a6480b1Sozaki-r ATF_TC(connmappedbuginet4);
ATF_TC_HEAD(connmappedbuginet4,tc)4878a6480b1Sozaki-r ATF_TC_HEAD(connmappedbuginet4, tc)
4888a6480b1Sozaki-r {
4898a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4 using the v4 ioctls");
4908a6480b1Sozaki-r }
4918a6480b1Sozaki-r 
ATF_TC_BODY(connmappedbuginet4,tc)4928a6480b1Sozaki-r ATF_TC_BODY(connmappedbuginet4, tc)
4938a6480b1Sozaki-r {
4948a6480b1Sozaki-r 	run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, true);
4958a6480b1Sozaki-r }
4968a6480b1Sozaki-r 
4978a6480b1Sozaki-r ATF_TC(conninet6);
ATF_TC_HEAD(conninet6,tc)4988a6480b1Sozaki-r ATF_TC_HEAD(conninet6, tc)
4998a6480b1Sozaki-r {
5008a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv6");
5018a6480b1Sozaki-r }
5028a6480b1Sozaki-r 
ATF_TC_BODY(conninet6,tc)5038a6480b1Sozaki-r ATF_TC_BODY(conninet6, tc)
5048a6480b1Sozaki-r {
5058a6480b1Sozaki-r 	run(HOST_V6, PORT_V6, TOTAL, true, false);
5068a6480b1Sozaki-r }
5078a6480b1Sozaki-r 
5088a6480b1Sozaki-r ATF_TC(unconninet4);
ATF_TC_HEAD(unconninet4,tc)5098a6480b1Sozaki-r ATF_TC_HEAD(unconninet4, tc)
5108a6480b1Sozaki-r {
5118a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv4");
5128a6480b1Sozaki-r }
5138a6480b1Sozaki-r 
ATF_TC_BODY(unconninet4,tc)5148a6480b1Sozaki-r ATF_TC_BODY(unconninet4, tc)
5158a6480b1Sozaki-r {
5168a6480b1Sozaki-r 	run(HOST_V4, PORT_V4, TOTAL, false, false);
5178a6480b1Sozaki-r }
5188a6480b1Sozaki-r 
5198a6480b1Sozaki-r ATF_TC(unconnmappedinet4);
ATF_TC_HEAD(unconnmappedinet4,tc)5208a6480b1Sozaki-r ATF_TC_HEAD(unconnmappedinet4, tc)
5218a6480b1Sozaki-r {
5228a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4");
5238a6480b1Sozaki-r }
5248a6480b1Sozaki-r 
ATF_TC_BODY(unconnmappedinet4,tc)5258a6480b1Sozaki-r ATF_TC_BODY(unconnmappedinet4, tc)
5268a6480b1Sozaki-r {
5278a6480b1Sozaki-r 	run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, false);
5288a6480b1Sozaki-r }
5298a6480b1Sozaki-r 
5308a6480b1Sozaki-r ATF_TC(unconnmappedbuginet4);
ATF_TC_HEAD(unconnmappedbuginet4,tc)5318a6480b1Sozaki-r ATF_TC_HEAD(unconnmappedbuginet4, tc)
5328a6480b1Sozaki-r {
5338a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4 using the v4 ioctls");
5348a6480b1Sozaki-r }
5358a6480b1Sozaki-r 
ATF_TC_BODY(unconnmappedbuginet4,tc)5368a6480b1Sozaki-r ATF_TC_BODY(unconnmappedbuginet4, tc)
5378a6480b1Sozaki-r {
5388a6480b1Sozaki-r 	run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, true);
5398a6480b1Sozaki-r }
5408a6480b1Sozaki-r 
5418a6480b1Sozaki-r ATF_TC(unconninet6);
ATF_TC_HEAD(unconninet6,tc)5428a6480b1Sozaki-r ATF_TC_HEAD(unconninet6, tc)
5438a6480b1Sozaki-r {
5448a6480b1Sozaki-r 	atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv6");
5458a6480b1Sozaki-r }
5468a6480b1Sozaki-r 
ATF_TC_BODY(unconninet6,tc)5478a6480b1Sozaki-r ATF_TC_BODY(unconninet6, tc)
5488a6480b1Sozaki-r {
5498a6480b1Sozaki-r 	run(HOST_V6, PORT_V6, TOTAL, false, false);
5508a6480b1Sozaki-r }
5518a6480b1Sozaki-r 
ATF_TP_ADD_TCS(tp)5528a6480b1Sozaki-r ATF_TP_ADD_TCS(tp)
5538a6480b1Sozaki-r {
5548a6480b1Sozaki-r 	debug++;
5558a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, conninet4);
5568a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, connmappedinet4);
5578a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, connmappedbuginet4);
5588a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, conninet6);
5598a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, unconninet4);
5608a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, unconnmappedinet4);
5618a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, unconnmappedbuginet4);
5628a6480b1Sozaki-r 	ATF_TP_ADD_TC(tp, unconninet6);
5638a6480b1Sozaki-r 
5648a6480b1Sozaki-r 	return atf_no_error();
5658a6480b1Sozaki-r }
5668a6480b1Sozaki-r #endif
567