1 /* $NetBSD: mcast.c,v 1.3 2015/05/28 10:19:17 ozaki-r Exp $ */
4 * Copyright (c) 2014 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: mcast.c,v 1.3 2015/05/28 10:19:17 ozaki-r Exp $");
35 extern const char *__progname
;
36 #define getprogname() __progname
39 #include <sys/types.h>
40 #include <sys/socket.h>
43 #include <netinet/in.h>
61 #define ERRX(ev, msg, ...) ATF_REQUIRE_MSG(0, msg, __VA_ARGS__)
62 #define ERRX0(ev, msg) ATF_REQUIRE_MSG(0, msg)
64 #define SKIPX(ev, msg, ...) do { \
65 atf_tc_skip(msg, __VA_ARGS__); \
67 } while(/*CONSTCOND*/0)
70 #define ERRX(ev, msg, ...) errx(ev, msg, __VA_ARGS__)
71 #define ERRX0(ev, msg) errx(ev, msg)
72 #define SKIPX(ev, msg, ...) errx(ev, msg, __VA_ARGS__)
78 #define PORT_V4MAPPED "6666"
79 #define HOST_V4MAPPED "::FFFF:239.1.1.1"
80 #define PORT_V4 "6666"
81 #define HOST_V4 "239.1.1.1"
82 #define PORT_V6 "6666"
83 #define HOST_V6 "FF05:1:0:0:0:0:0:1"
91 addmc(int s
, struct addrinfo
*ai
, bool bug
)
95 struct sockaddr_in
*s4
;
96 struct sockaddr_in6
*s6
;
99 switch (ai
->ai_family
) {
101 s4
= (void *)ai
->ai_addr
;
102 assert(sizeof(*s4
) == ai
->ai_addrlen
);
103 m4
.imr_multiaddr
= s4
->sin_addr
;
104 m4
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
105 return setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
108 s6
= (void *)ai
->ai_addr
;
110 * Linux: Does not support the v6 ioctls on v4 mapped
111 * sockets but it does support the v4 ones and
113 * MacOS/X: Supports the v6 ioctls on v4 mapped sockets,
114 * but does not work and also does not support
115 * the v4 ioctls. So no way to make multicasting
116 * work with mapped addresses.
117 * NetBSD: Supports both and works for both.
119 if (bug
&& IN6_IS_ADDR_V4MAPPED(&s6
->sin6_addr
)) {
120 memcpy(&m4
.imr_multiaddr
, &s6
->sin6_addr
.s6_addr
[12],
121 sizeof(m4
.imr_multiaddr
));
122 m4
.imr_interface
.s_addr
= htonl(INADDR_ANY
);
123 return setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
126 assert(sizeof(*s6
) == ai
->ai_addrlen
);
127 memset(&m6
, 0, sizeof(m6
));
130 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
,
131 &ifc
, sizeof(ifc
)) == -1)
134 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
,
135 &ifc
, sizeof(ifc
)) == -1)
137 ifc
= 1; /* XXX should pick a proper interface */
138 if (setsockopt(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, &ifc
,
142 ifc
= 0; /* Let pick an appropriate interface */
144 m6
.ipv6mr_interface
= ifc
;
145 m6
.ipv6mr_multiaddr
= s6
->sin6_addr
;
146 return setsockopt(s
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
,
155 allowv4mapped(int s
, struct addrinfo
*ai
)
157 struct sockaddr_in6
*s6
;
160 if (ai
->ai_family
!= AF_INET6
)
163 s6
= (void *)ai
->ai_addr
;
165 if (!IN6_IS_ADDR_V4MAPPED(&s6
->sin6_addr
))
167 return setsockopt(s
, IPPROTO_IPV6
, IPV6_V6ONLY
, &zero
, sizeof(zero
));
170 static struct sockaddr_storage ss
;
172 connector(int fd
, const struct sockaddr
*sa
, socklen_t slen
)
174 assert(sizeof(ss
) > slen
);
175 memcpy(&ss
, sa
, slen
);
180 show(const char *prefix
, const struct message
*msg
)
182 printf("%10.10s: %zu [%jd.%ld]\n", prefix
, msg
->seq
, (intmax_t)
183 msg
->ts
.tv_sec
, msg
->ts
.tv_nsec
);
187 getsocket(const char *host
, const char *port
,
188 int (*f
)(int, const struct sockaddr
*, socklen_t
), socklen_t
*slen
,
191 int e
, s
, lasterrno
= 0;
192 struct addrinfo hints
, *ai0
, *ai
;
193 const char *cause
= "?";
195 memset(&hints
, 0, sizeof(hints
));
196 hints
.ai_family
= AF_UNSPEC
;
197 hints
.ai_socktype
= SOCK_DGRAM
;
198 e
= getaddrinfo(host
, port
, &hints
, &ai0
);
200 ERRX(EXIT_FAILURE
, "Can't resolve %s:%s (%s)", host
, port
,
204 for (ai
= ai0
; ai
; ai
= ai
->ai_next
) {
205 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
211 if (allowv4mapped(s
, ai
) == -1) {
212 cause
= "allow v4 mapped";
215 if ((*f
)(s
, ai
->ai_addr
, ai
->ai_addrlen
) == -1) {
216 cause
= f
== bind
? "bind" : "connect";
219 if ((f
== bind
|| f
== connector
) && addmc(s
, ai
, bug
) == -1) {
220 cause
= "join group";
223 *slen
= ai
->ai_addrlen
;
233 ERRX(EXIT_FAILURE
, "%s (%s)", cause
, strerror(lasterrno
));
238 synchronize(const int fd
, bool waiter
)
248 /* We use poll to avoid lock up when the peer died unexpectedly */
249 r
= poll(&pfd
, 1, 10000);
251 ERRX(EXIT_FAILURE
, "poll (%s)", strerror(errno
));
256 if (read(fd
, &syncmsg
, sizeof(syncmsg
)) == -1)
257 ERRX(EXIT_FAILURE
, "read (%s)", strerror(errno
));
259 if (write(fd
, &syncmsg
, sizeof(syncmsg
)) == -1)
260 ERRX(EXIT_FAILURE
, "write (%s)", strerror(errno
));
267 sender(const int fd
, const char *host
, const char *port
, size_t n
, bool conn
,
276 s
= getsocket(host
, port
, conn
? connect
: connector
, &slen
, bug
);
278 /* Wait until receiver gets ready. */
279 if (synchronize(fd
, true) == -1)
282 for (msg
.seq
= 0; msg
.seq
< n
; msg
.seq
++) {
283 #ifdef CLOCK_MONOTONIC
284 if (clock_gettime(CLOCK_MONOTONIC
, &msg
.ts
) == -1)
285 ERRX(EXIT_FAILURE
, "clock (%s)", strerror(errno
));
288 if (gettimeofday(&tv
, NULL
) == -1)
289 ERRX(EXIT_FAILURE
, "clock (%s)", strerror(errno
));
290 msg
.ts
.tv_sec
= tv
.tv_sec
;
291 msg
.ts
.tv_nsec
= tv
.tv_usec
* 1000;
294 show("sending", &msg
);
295 l
= conn
? send(s
, &msg
, sizeof(msg
), 0) :
296 sendto(s
, &msg
, sizeof(msg
), 0, (void *)&ss
, slen
);
298 ERRX(EXIT_FAILURE
, "send (%s)", strerror(errno
));
302 /* Wait until receiver finishes its work. */
303 if (synchronize(fd
, true) == -1)
310 receiver(const int fd
, const char *host
, const char *port
, size_t n
, bool conn
,
320 s
= getsocket(host
, port
, bind
, &slen
, bug
);
325 synchronize(fd
, false);
327 for (seq
= 0; seq
< n
; seq
++) {
328 if (poll(&pfd
, 1, 10000) == -1)
329 ERRX(EXIT_FAILURE
, "poll (%s)", strerror(errno
));
330 l
= conn
? recv(s
, &msg
, sizeof(msg
), 0) :
331 recvfrom(s
, &msg
, sizeof(msg
), 0, (void *)&ss
, &slen
);
333 ERRX(EXIT_FAILURE
, "recv (%s)", strerror(errno
));
337 ERRX(EXIT_FAILURE
, "seq: expect=%zu actual=%zu",
341 /* Tell I'm finished */
342 synchronize(fd
, false);
346 run(const char *host
, const char *port
, size_t n
, bool conn
, bool bug
)
353 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, syncfds
) == -1)
354 ERRX(EXIT_FAILURE
, "socketpair (%s)", strerror(errno
));
356 switch ((pid
= fork())) {
358 receiver(syncfds
[0], host
, port
, n
, conn
, bug
);
361 ERRX(EXIT_FAILURE
, "fork (%s)", strerror(errno
));
363 error
= sender(syncfds
[1], host
, port
, n
, conn
, bug
);
365 switch (waitpid(pid
, &status
, WNOHANG
)) {
367 ERRX(EXIT_FAILURE
, "wait (%s)", strerror(errno
));
371 * Receiver is still alive, but we know
376 if (kill(pid
, SIGTERM
) == -1)
377 ERRX(EXIT_FAILURE
, "kill (%s)",
381 if (WIFSIGNALED(status
)) {
382 if (WTERMSIG(status
) == SIGTERM
)
384 "receiver failed and was killed" \
388 "receiver got signaled (%s)",
389 strsignal(WTERMSIG(status
)));
390 } else if (WIFEXITED(status
)) {
391 if (WEXITSTATUS(status
) != 0)
393 "receiver exited with status %d",
394 WEXITSTATUS(status
));
397 "receiver exited with unexpected status %d",
408 main(int argc
, char *argv
[])
410 const char *host
, *port
;
420 while ((c
= getopt(argc
, argv
, "46bcdmn:")) != -1)
440 host
= HOST_V4MAPPED
;
441 port
= PORT_V4MAPPED
;
447 fprintf(stderr
, "Usage: %s [-cdm46] [-n <tot>]",
452 run(host
, port
, n
, conn
, bug
);
458 ATF_TC_HEAD(conninet4
, tc
)
460 atf_tc_set_md_var(tc
, "descr", "Checks connected multicast for ipv4");
463 ATF_TC_BODY(conninet4
, tc
)
465 run(HOST_V4
, PORT_V4
, TOTAL
, true, false);
468 ATF_TC(connmappedinet4
);
469 ATF_TC_HEAD(connmappedinet4
, tc
)
471 atf_tc_set_md_var(tc
, "descr", "Checks connected multicast for mapped ipv4");
474 ATF_TC_BODY(connmappedinet4
, tc
)
476 run(HOST_V4MAPPED
, PORT_V4MAPPED
, TOTAL
, true, false);
479 ATF_TC(connmappedbuginet4
);
480 ATF_TC_HEAD(connmappedbuginet4
, tc
)
482 atf_tc_set_md_var(tc
, "descr", "Checks connected multicast for mapped ipv4 using the v4 ioctls");
485 ATF_TC_BODY(connmappedbuginet4
, tc
)
487 run(HOST_V4MAPPED
, PORT_V4MAPPED
, TOTAL
, true, true);
491 ATF_TC_HEAD(conninet6
, tc
)
493 atf_tc_set_md_var(tc
, "descr", "Checks connected multicast for ipv6");
496 ATF_TC_BODY(conninet6
, tc
)
498 run(HOST_V6
, PORT_V6
, TOTAL
, true, false);
502 ATF_TC_HEAD(unconninet4
, tc
)
504 atf_tc_set_md_var(tc
, "descr", "Checks unconnected multicast for ipv4");
507 ATF_TC_BODY(unconninet4
, tc
)
509 run(HOST_V4
, PORT_V4
, TOTAL
, false, false);
512 ATF_TC(unconnmappedinet4
);
513 ATF_TC_HEAD(unconnmappedinet4
, tc
)
515 atf_tc_set_md_var(tc
, "descr", "Checks unconnected multicast for mapped ipv4");
518 ATF_TC_BODY(unconnmappedinet4
, tc
)
520 run(HOST_V4MAPPED
, PORT_V4MAPPED
, TOTAL
, false, false);
523 ATF_TC(unconnmappedbuginet4
);
524 ATF_TC_HEAD(unconnmappedbuginet4
, tc
)
526 atf_tc_set_md_var(tc
, "descr", "Checks unconnected multicast for mapped ipv4 using the v4 ioctls");
529 ATF_TC_BODY(unconnmappedbuginet4
, tc
)
531 run(HOST_V4MAPPED
, PORT_V4MAPPED
, TOTAL
, false, true);
535 ATF_TC_HEAD(unconninet6
, tc
)
537 atf_tc_set_md_var(tc
, "descr", "Checks unconnected multicast for ipv6");
540 ATF_TC_BODY(unconninet6
, tc
)
542 run(HOST_V6
, PORT_V6
, TOTAL
, false, false);
548 ATF_TP_ADD_TC(tp
, conninet4
);
549 ATF_TP_ADD_TC(tp
, connmappedinet4
);
550 ATF_TP_ADD_TC(tp
, connmappedbuginet4
);
551 ATF_TP_ADD_TC(tp
, conninet6
);
552 ATF_TP_ADD_TC(tp
, unconninet4
);
553 ATF_TP_ADD_TC(tp
, unconnmappedinet4
);
554 ATF_TP_ADD_TC(tp
, unconnmappedbuginet4
);
555 ATF_TP_ADD_TC(tp
, unconninet6
);
557 return atf_no_error();