commit 2553d32fb5ac11521a203a48eeda0067dc6f4d9a Author: Ryota Ozaki Date: Tue May 19 15:45:34 2015 +0900 Run mcast tests on rump kernels diff --git a/distrib/sets/lists/debug/mi b/distrib/sets/lists/debug/mi index 6aedf7d..442b708 100644 --- a/distrib/sets/lists/debug/mi +++ b/distrib/sets/lists/debug/mi @@ -2174,7 +2174,7 @@ ./usr/libdata/debug/usr/tests/net/if/t_compat.debug tests-net-debug debug,atf,rump ./usr/libdata/debug/usr/tests/net/if_loop/t_pr.debug tests-net-debug debug,atf,rump ./usr/libdata/debug/usr/tests/net/in_cksum/in_cksum.debug tests-net-debug debug,atf -./usr/libdata/debug/usr/tests/net/mcast/t_mcast.debug tests-net-debug debug,atf +./usr/libdata/debug/usr/tests/net/mcast/mcast.debug tests-net-debug debug,atf,rump ./usr/libdata/debug/usr/tests/net/net/t_pktinfo.debug tests-net-debug debug,atf ./usr/libdata/debug/usr/tests/net/net/t_raw.debug tests-net-debug debug,atf,rump ./usr/libdata/debug/usr/tests/net/net/t_tcp.debug tests-net-debug debug,atf diff --git a/distrib/sets/lists/tests/mi b/distrib/sets/lists/tests/mi index 879562e..07ec72d 100644 --- a/distrib/sets/lists/tests/mi +++ b/distrib/sets/lists/tests/mi @@ -3151,9 +3151,10 @@ ./usr/tests/net/in_cksum/t_in_cksum tests-net-tests atf ./usr/tests/net/in_cksum/in_cksum tests-net-tests atf ./usr/tests/net/mcast tests-net-tests -./usr/tests/net/mcast/Atffile tests-net-tests atf -./usr/tests/net/mcast/Kyuafile tests-net-tests atf,kyua -./usr/tests/net/mcast/t_mcast tests-net-tests atf +./usr/tests/net/mcast/Atffile tests-net-tests atf,rump +./usr/tests/net/mcast/Kyuafile tests-net-tests atf,rump,kyua +./usr/tests/net/mcast/mcast tests-net-tests atf,rump +./usr/tests/net/mcast/t_mcast tests-net-tests atf,rump ./usr/tests/net/mpls tests-net-tests ./usr/tests/net/mpls/Atffile tests-net-tests atf,rump ./usr/tests/net/mpls/Kyuafile tests-net-tests atf,rump,kyua diff --git a/tests/net/mcast/Makefile b/tests/net/mcast/Makefile index 60754a2..e178a22 100644 --- a/tests/net/mcast/Makefile +++ b/tests/net/mcast/Makefile @@ -5,6 +5,10 @@ TESTSDIR= ${TESTSBASE}/net/mcast -TESTS_C= t_mcast +TESTS_SH= t_mcast + +PROGS= mcast +MAN.mcast= # empty +BINDIR.mcast= ${TESTSDIR} .include diff --git a/tests/net/mcast/mcast.c b/tests/net/mcast/mcast.c new file mode 100644 index 0000000..2e44303 --- /dev/null +++ b/tests/net/mcast/mcast.c @@ -0,0 +1,497 @@ +/* $NetBSD: t_mcast.c,v 1.15 2015/05/19 04:14:04 ozaki-r Exp $ */ + +/*- + * Copyright (c) 2014 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#ifdef __RCSID +__RCSID("$NetBSD: t_mcast.c,v 1.15 2015/05/19 04:14:04 ozaki-r Exp $"); +#else +extern const char *__progname; +#define getprogname() __progname +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ATF +#include + +#define ERRX(ev, msg, ...) ATF_REQUIRE_MSG(0, msg, __VA_ARGS__) + +#define SKIPX(ev, msg, ...) do { \ + atf_tc_skip(msg, __VA_ARGS__); \ + return; \ +} while(/*CONSTCOND*/0) + +#else +#define ERRX(ev, msg, ...) errx(ev, msg, __VA_ARGS__) +#define SKIPX(ev, msg, ...) errx(ev, msg, __VA_ARGS__) +#endif + +static int debug; + +#define TOTAL 10 +#define PORT_V4MAPPED "6666" +#define HOST_V4MAPPED "::FFFF:239.1.1.1" +#define PORT_V4 "6666" +#define HOST_V4 "239.1.1.1" +#define PORT_V6 "6666" +#define HOST_V6 "FF05:1:0:0:0:0:0:1" + +struct message { + size_t seq; + struct timespec ts; +}; + +static int +addmc(int s, struct addrinfo *ai, bool bug) +{ + struct ip_mreq m4; + struct ipv6_mreq m6; + struct sockaddr_in *s4; + struct sockaddr_in6 *s6; + unsigned int ifc; + + switch (ai->ai_family) { + case AF_INET: + s4 = (void *)ai->ai_addr; + assert(sizeof(*s4) == ai->ai_addrlen); + m4.imr_multiaddr = s4->sin_addr; + m4.imr_interface.s_addr = htonl(INADDR_ANY); + return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m4, sizeof(m4)); + case AF_INET6: + s6 = (void *)ai->ai_addr; + /* + * Linux: Does not support the v6 ioctls on v4 mapped + * sockets but it does support the v4 ones and + * it works. + * MacOS/X: Supports the v6 ioctls on v4 mapped sockets, + * but does not work and also does not support + * the v4 ioctls. So no way to make multicasting + * work with mapped addresses. + * NetBSD: Supports both and works for both. + */ + if (bug && IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) { + memcpy(&m4.imr_multiaddr, &s6->sin6_addr.s6_addr[12], + sizeof(m4.imr_multiaddr)); + m4.imr_interface.s_addr = htonl(INADDR_ANY); + return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m4, sizeof(m4)); + } + assert(sizeof(*s6) == ai->ai_addrlen); + memset(&m6, 0, sizeof(m6)); +#if 0 + ifc = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + &ifc, sizeof(ifc)) == -1) + return -1; + ifc = 224; + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &ifc, sizeof(ifc)) == -1) + return -1; + ifc = 1; /* XXX should pick a proper interface */ + if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifc, + sizeof(ifc)) == -1) + return -1; +#else + ifc = 0; /* Let pick an appropriate interface */ +#endif + m6.ipv6mr_interface = ifc; + m6.ipv6mr_multiaddr = s6->sin6_addr; + return setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &m6, sizeof(m6)); + default: + errno = EOPNOTSUPP; + return -1; + } +} + +static int +allowv4mapped(int s, struct addrinfo *ai) +{ + struct sockaddr_in6 *s6; + int zero = 0; + + if (ai->ai_family != AF_INET6) + return 0; + + s6 = (void *)ai->ai_addr; + + if (!IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) + return 0; + return setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)); +} + +static struct sockaddr_storage ss; +static int +connector(int fd, const struct sockaddr *sa, socklen_t slen) +{ + assert(sizeof(ss) > slen); + memcpy(&ss, sa, slen); + return 0; +} + +static void +show(const char *prefix, const struct message *msg) +{ + printf("%10.10s: %zu [%jd.%ld]\n", prefix, msg->seq, (intmax_t) + msg->ts.tv_sec, msg->ts.tv_nsec); +} + +static int +getsocket(const char *host, const char *port, + int (*f)(int, const struct sockaddr *, socklen_t), socklen_t *slen, + bool bug) +{ + int e, s, lasterrno = 0; + struct addrinfo hints, *ai0, *ai; + const char *cause = "?"; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + e = getaddrinfo(host, port, &hints, &ai0); + if (e) + ERRX(EXIT_FAILURE, "Can't resolve %s:%s (%s)", host, port, + gai_strerror(e)); + + s = -1; + for (ai = ai0; ai; ai = ai->ai_next) { + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s == -1) { + lasterrno = errno; + cause = "socket"; + continue; + } + if (allowv4mapped(s, ai) == -1) { + cause = "allow v4 mapped"; + goto out; + } + if ((*f)(s, ai->ai_addr, ai->ai_addrlen) == -1) { + cause = f == bind ? "bind" : "connect"; + goto out; + } + if ((f == bind || f == connector) && addmc(s, ai, bug) == -1) { + cause = "join group"; + goto out; + } + *slen = ai->ai_addrlen; + break; +out: + lasterrno = errno; + close(s); + s = -1; + continue; + } + freeaddrinfo(ai0); + if (s == -1) + ERRX(EXIT_FAILURE, "%s (%s)", cause, strerror(lasterrno)); + return s; +} + +static void +sender(const char *host, const char *port, size_t n, bool conn, bool bug) +{ + int s; + ssize_t l; + struct message msg; + + socklen_t slen; + + s = getsocket(host, port, conn ? connect : connector, &slen, bug); + for (msg.seq = 0; msg.seq < n; msg.seq++) { +#ifdef CLOCK_MONOTONIC + if (clock_gettime(CLOCK_MONOTONIC, &msg.ts) == -1) + ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno)); +#else + struct timeval tv; + if (gettimeofday(&tv, NULL) == -1) + ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno)); + msg.ts.tv_sec = tv.tv_sec; + msg.ts.tv_nsec = tv.tv_usec * 1000; +#endif + if (debug) + show("sending", &msg); + l = conn ? send(s, &msg, sizeof(msg), 0) : + sendto(s, &msg, sizeof(msg), 0, (void *)&ss, slen); + if (l == -1) + ERRX(EXIT_FAILURE, "send (%s)", strerror(errno)); + usleep(100); + } +} + +static void +receiver(const char *host, const char *port, size_t n, bool conn, bool bug) +{ + int s; + ssize_t l; + size_t seq; + struct message msg; + struct pollfd pfd; + socklen_t slen; + + s = getsocket(host, port, bind, &slen, bug); + pfd.fd = s; + pfd.events = POLLIN; + for (seq = 0; seq < n; seq++) { + if (poll(&pfd, 1, 10000) == -1) + ERRX(EXIT_FAILURE, "poll (%s)", strerror(errno)); + l = conn ? recv(s, &msg, sizeof(msg), 0) : + recvfrom(s, &msg, sizeof(msg), 0, (void *)&ss, &slen); + if (l == -1) + ERRX(EXIT_FAILURE, "recv (%s)", strerror(errno)); + if (debug) + show("got", &msg); + if (seq != msg.seq) + ERRX(EXIT_FAILURE, "seq %zu != %zu", seq, msg.seq); + } +} + +static void +run(const char *host, const char *port, size_t n, bool conn, bool bug) +{ + pid_t pid; + int status; + + switch ((pid = fork())) { + case 0: + receiver(host, port, n, conn, bug); + return; + case -1: + ERRX(EXIT_FAILURE, "fork (%s)", strerror(errno)); + default: + usleep(1000); + sender(host, port, n, conn, bug); + usleep(100); + again: + switch (waitpid(pid, &status, WNOHANG)) { + case -1: + ERRX(EXIT_FAILURE, "wait (%s)", strerror(errno)); + case 0: + if (kill(pid, SIGTERM) == -1) + ERRX(EXIT_FAILURE, "kill (%s)", + strerror(errno)); + goto again; + default: + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) == SIGTERM) + ERRX(EXIT_FAILURE, + "receiver got terminated due to " \ + "deadline (%d usec)", 100); + else + ERRX(EXIT_FAILURE, + "receiver got signaled (%s)", + strsignal(WTERMSIG(status))); + } else if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + ERRX(EXIT_FAILURE, + "receiver exited with status %d", + WEXITSTATUS(status)); + } else { + ERRX(EXIT_FAILURE, + "receiver exited with unexpected status %d", + status); + } + break; + } + return; + } +} + +#ifndef ATF +int +main(int argc, char *argv[]) +{ + const char *host, *port; + int c; + size_t n; + bool conn, bug; + + host = HOST_V4; + port = PORT_V4; + n = TOTAL; + bug = conn = false; + + while ((c = getopt(argc, argv, "46bcdmn:")) != -1) + switch (c) { + case '4': + host = HOST_V4; + port = PORT_V4; + break; + case '6': + host = HOST_V6; + port = PORT_V6; + break; + case 'b': + bug = true; + break; + case 'c': + conn = true; + break; + case 'd': + debug++; + break; + case 'm': + host = HOST_V4MAPPED; + port = PORT_V4MAPPED; + break; + case 'n': + n = atoi(optarg); + break; + default: + fprintf(stderr, "Usage: %s [-cdm46] [-n ]", + getprogname()); + return 1; + } + + run(host, port, n, conn, bug); + return 0; +} +#else + +ATF_TC(conninet4); +ATF_TC_HEAD(conninet4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv4"); +} + +ATF_TC_BODY(conninet4, tc) +{ + run(HOST_V4, PORT_V4, TOTAL, true, false); +} + +ATF_TC(connmappedinet4); +ATF_TC_HEAD(connmappedinet4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4"); +} + +ATF_TC_BODY(connmappedinet4, tc) +{ + run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, false); +} + +ATF_TC(connmappedbuginet4); +ATF_TC_HEAD(connmappedbuginet4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4 using the v4 ioctls"); +} + +ATF_TC_BODY(connmappedbuginet4, tc) +{ + run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, true); +} + +ATF_TC(conninet6); +ATF_TC_HEAD(conninet6, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv6"); +} + +ATF_TC_BODY(conninet6, tc) +{ + run(HOST_V6, PORT_V6, TOTAL, true, false); +} + +ATF_TC(unconninet4); +ATF_TC_HEAD(unconninet4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv4"); +} + +ATF_TC_BODY(unconninet4, tc) +{ + run(HOST_V4, PORT_V4, TOTAL, false, false); +} + +ATF_TC(unconnmappedinet4); +ATF_TC_HEAD(unconnmappedinet4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4"); +} + +ATF_TC_BODY(unconnmappedinet4, tc) +{ + run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, false); +} + +ATF_TC(unconnmappedbuginet4); +ATF_TC_HEAD(unconnmappedbuginet4, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4 using the v4 ioctls"); +} + +ATF_TC_BODY(unconnmappedbuginet4, tc) +{ + run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, true); +} + +ATF_TC(unconninet6); +ATF_TC_HEAD(unconninet6, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv6"); +} + +ATF_TC_BODY(unconninet6, tc) +{ + run(HOST_V6, PORT_V6, TOTAL, false, false); +} + +ATF_TP_ADD_TCS(tp) +{ + debug++; + ATF_TP_ADD_TC(tp, conninet4); + ATF_TP_ADD_TC(tp, connmappedinet4); + ATF_TP_ADD_TC(tp, connmappedbuginet4); + ATF_TP_ADD_TC(tp, conninet6); + ATF_TP_ADD_TC(tp, unconninet4); + ATF_TP_ADD_TC(tp, unconnmappedinet4); + ATF_TP_ADD_TC(tp, unconnmappedbuginet4); + ATF_TP_ADD_TC(tp, unconninet6); + + return atf_no_error(); +} +#endif diff --git a/tests/net/mcast/t_mcast.c b/tests/net/mcast/t_mcast.c deleted file mode 100644 index 88ccd2c..0000000 --- a/tests/net/mcast/t_mcast.c +++ /dev/null @@ -1,497 +0,0 @@ -/* $NetBSD: t_mcast.c,v 1.15 2015/05/19 04:14:04 ozaki-r Exp $ */ - -/*- - * Copyright (c) 2014 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include -#ifdef __RCSID -__RCSID("$NetBSD: t_mcast.c,v 1.15 2015/05/19 04:14:04 ozaki-r Exp $"); -#else -extern const char *__progname; -#define getprogname() __progname -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef TEST -#include - -#define ERRX(ev, msg, ...) ATF_REQUIRE_MSG(0, msg, __VA_ARGS__) - -#define SKIPX(ev, msg, ...) do { \ - atf_tc_skip(msg, __VA_ARGS__); \ - return; \ -} while(/*CONSTCOND*/0) - -#else -#define ERRX(ev, msg, ...) errx(ev, msg, __VA_ARGS__) -#define SKIPX(ev, msg, ...) errx(ev, msg, __VA_ARGS__) -#endif - -static int debug; - -#define TOTAL 10 -#define PORT_V4MAPPED "6666" -#define HOST_V4MAPPED "::FFFF:239.1.1.1" -#define PORT_V4 "6666" -#define HOST_V4 "239.1.1.1" -#define PORT_V6 "6666" -#define HOST_V6 "FF05:1:0:0:0:0:0:1" - -struct message { - size_t seq; - struct timespec ts; -}; - -static int -addmc(int s, struct addrinfo *ai, bool bug) -{ - struct ip_mreq m4; - struct ipv6_mreq m6; - struct sockaddr_in *s4; - struct sockaddr_in6 *s6; - unsigned int ifc; - - switch (ai->ai_family) { - case AF_INET: - s4 = (void *)ai->ai_addr; - assert(sizeof(*s4) == ai->ai_addrlen); - m4.imr_multiaddr = s4->sin_addr; - m4.imr_interface.s_addr = htonl(INADDR_ANY); - return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &m4, sizeof(m4)); - case AF_INET6: - s6 = (void *)ai->ai_addr; - /* - * Linux: Does not support the v6 ioctls on v4 mapped - * sockets but it does support the v4 ones and - * it works. - * MacOS/X: Supports the v6 ioctls on v4 mapped sockets, - * but does not work and also does not support - * the v4 ioctls. So no way to make multicasting - * work with mapped addresses. - * NetBSD: Supports both and works for both. - */ - if (bug && IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) { - memcpy(&m4.imr_multiaddr, &s6->sin6_addr.s6_addr[12], - sizeof(m4.imr_multiaddr)); - m4.imr_interface.s_addr = htonl(INADDR_ANY); - return setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &m4, sizeof(m4)); - } - assert(sizeof(*s6) == ai->ai_addrlen); - memset(&m6, 0, sizeof(m6)); -#if 0 - ifc = 1; - if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, - &ifc, sizeof(ifc)) == -1) - return -1; - ifc = 224; - if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &ifc, sizeof(ifc)) == -1) - return -1; - ifc = 1; /* XXX should pick a proper interface */ - if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifc, - sizeof(ifc)) == -1) - return -1; -#else - ifc = 0; /* Let pick an appropriate interface */ -#endif - m6.ipv6mr_interface = ifc; - m6.ipv6mr_multiaddr = s6->sin6_addr; - return setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, - &m6, sizeof(m6)); - default: - errno = EOPNOTSUPP; - return -1; - } -} - -static int -allowv4mapped(int s, struct addrinfo *ai) -{ - struct sockaddr_in6 *s6; - int zero = 0; - - if (ai->ai_family != AF_INET6) - return 0; - - s6 = (void *)ai->ai_addr; - - if (!IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) - return 0; - return setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)); -} - -static struct sockaddr_storage ss; -static int -connector(int fd, const struct sockaddr *sa, socklen_t slen) -{ - assert(sizeof(ss) > slen); - memcpy(&ss, sa, slen); - return 0; -} - -static void -show(const char *prefix, const struct message *msg) -{ - printf("%10.10s: %zu [%jd.%ld]\n", prefix, msg->seq, (intmax_t) - msg->ts.tv_sec, msg->ts.tv_nsec); -} - -static int -getsocket(const char *host, const char *port, - int (*f)(int, const struct sockaddr *, socklen_t), socklen_t *slen, - bool bug) -{ - int e, s, lasterrno = 0; - struct addrinfo hints, *ai0, *ai; - const char *cause = "?"; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - e = getaddrinfo(host, port, &hints, &ai0); - if (e) - ERRX(EXIT_FAILURE, "Can't resolve %s:%s (%s)", host, port, - gai_strerror(e)); - - s = -1; - for (ai = ai0; ai; ai = ai->ai_next) { - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s == -1) { - lasterrno = errno; - cause = "socket"; - continue; - } - if (allowv4mapped(s, ai) == -1) { - cause = "allow v4 mapped"; - goto out; - } - if ((*f)(s, ai->ai_addr, ai->ai_addrlen) == -1) { - cause = f == bind ? "bind" : "connect"; - goto out; - } - if ((f == bind || f == connector) && addmc(s, ai, bug) == -1) { - cause = "join group"; - goto out; - } - *slen = ai->ai_addrlen; - break; -out: - lasterrno = errno; - close(s); - s = -1; - continue; - } - freeaddrinfo(ai0); - if (s == -1) - ERRX(EXIT_FAILURE, "%s (%s)", cause, strerror(lasterrno)); - return s; -} - -static void -sender(const char *host, const char *port, size_t n, bool conn, bool bug) -{ - int s; - ssize_t l; - struct message msg; - - socklen_t slen; - - s = getsocket(host, port, conn ? connect : connector, &slen, bug); - for (msg.seq = 0; msg.seq < n; msg.seq++) { -#ifdef CLOCK_MONOTONIC - if (clock_gettime(CLOCK_MONOTONIC, &msg.ts) == -1) - ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno)); -#else - struct timeval tv; - if (gettimeofday(&tv, NULL) == -1) - ERRX(EXIT_FAILURE, "clock (%s)", strerror(errno)); - msg.ts.tv_sec = tv.tv_sec; - msg.ts.tv_nsec = tv.tv_usec * 1000; -#endif - if (debug) - show("sending", &msg); - l = conn ? send(s, &msg, sizeof(msg), 0) : - sendto(s, &msg, sizeof(msg), 0, (void *)&ss, slen); - if (l == -1) - ERRX(EXIT_FAILURE, "send (%s)", strerror(errno)); - usleep(100); - } -} - -static void -receiver(const char *host, const char *port, size_t n, bool conn, bool bug) -{ - int s; - ssize_t l; - size_t seq; - struct message msg; - struct pollfd pfd; - socklen_t slen; - - s = getsocket(host, port, bind, &slen, bug); - pfd.fd = s; - pfd.events = POLLIN; - for (seq = 0; seq < n; seq++) { - if (poll(&pfd, 1, 10000) == -1) - ERRX(EXIT_FAILURE, "poll (%s)", strerror(errno)); - l = conn ? recv(s, &msg, sizeof(msg), 0) : - recvfrom(s, &msg, sizeof(msg), 0, (void *)&ss, &slen); - if (l == -1) - ERRX(EXIT_FAILURE, "recv (%s)", strerror(errno)); - if (debug) - show("got", &msg); - if (seq != msg.seq) - ERRX(EXIT_FAILURE, "seq %zu != %zu", seq, msg.seq); - } -} - -static void -run(const char *host, const char *port, size_t n, bool conn, bool bug) -{ - pid_t pid; - int status; - - switch ((pid = fork())) { - case 0: - receiver(host, port, n, conn, bug); - return; - case -1: - ERRX(EXIT_FAILURE, "fork (%s)", strerror(errno)); - default: - usleep(1000); - sender(host, port, n, conn, bug); - usleep(100); - again: - switch (waitpid(pid, &status, WNOHANG)) { - case -1: - ERRX(EXIT_FAILURE, "wait (%s)", strerror(errno)); - case 0: - if (kill(pid, SIGTERM) == -1) - ERRX(EXIT_FAILURE, "kill (%s)", - strerror(errno)); - goto again; - default: - if (WIFSIGNALED(status)) { - if (WTERMSIG(status) == SIGTERM) - ERRX(EXIT_FAILURE, - "receiver got terminated due to " \ - "deadline (%d usec)", 100); - else - ERRX(EXIT_FAILURE, - "receiver got signaled (%s)", - strsignal(WTERMSIG(status))); - } else if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) - ERRX(EXIT_FAILURE, - "receiver exited with status %d", - WEXITSTATUS(status)); - } else { - ERRX(EXIT_FAILURE, - "receiver exited with unexpected status %d", - status); - } - break; - } - return; - } -} - -#ifdef TEST -int -main(int argc, char *argv[]) -{ - const char *host, *port; - int c; - size_t n; - bool conn, bug; - - host = HOST_V4; - port = PORT_V4; - n = TOTAL; - bug = conn = false; - - while ((c = getopt(argc, argv, "46bcdmn:")) != -1) - switch (c) { - case '4': - host = HOST_V4; - port = PORT_V4; - break; - case '6': - host = HOST_V6; - port = PORT_V6; - break; - case 'b': - bug = true; - break; - case 'c': - conn = true; - break; - case 'd': - debug++; - break; - case 'm': - host = HOST_V4MAPPED; - port = PORT_V4MAPPED; - break; - case 'n': - n = atoi(optarg); - break; - default: - fprintf(stderr, "Usage: %s [-cdm46] [-n ]", - getprogname()); - return 1; - } - - run(host, port, n, conn, bug); - return 0; -} -#else - -ATF_TC(conninet4); -ATF_TC_HEAD(conninet4, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv4"); -} - -ATF_TC_BODY(conninet4, tc) -{ - run(HOST_V4, PORT_V4, TOTAL, true, false); -} - -ATF_TC(connmappedinet4); -ATF_TC_HEAD(connmappedinet4, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4"); -} - -ATF_TC_BODY(connmappedinet4, tc) -{ - run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, false); -} - -ATF_TC(connmappedbuginet4); -ATF_TC_HEAD(connmappedbuginet4, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks connected multicast for mapped ipv4 using the v4 ioctls"); -} - -ATF_TC_BODY(connmappedbuginet4, tc) -{ - run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, true, true); -} - -ATF_TC(conninet6); -ATF_TC_HEAD(conninet6, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks connected multicast for ipv6"); -} - -ATF_TC_BODY(conninet6, tc) -{ - run(HOST_V6, PORT_V6, TOTAL, true, false); -} - -ATF_TC(unconninet4); -ATF_TC_HEAD(unconninet4, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv4"); -} - -ATF_TC_BODY(unconninet4, tc) -{ - run(HOST_V4, PORT_V4, TOTAL, false, false); -} - -ATF_TC(unconnmappedinet4); -ATF_TC_HEAD(unconnmappedinet4, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4"); -} - -ATF_TC_BODY(unconnmappedinet4, tc) -{ - run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, false); -} - -ATF_TC(unconnmappedbuginet4); -ATF_TC_HEAD(unconnmappedbuginet4, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for mapped ipv4 using the v4 ioctls"); -} - -ATF_TC_BODY(unconnmappedbuginet4, tc) -{ - run(HOST_V4MAPPED, PORT_V4MAPPED, TOTAL, false, true); -} - -ATF_TC(unconninet6); -ATF_TC_HEAD(unconninet6, tc) -{ - atf_tc_set_md_var(tc, "descr", "Checks unconnected multicast for ipv6"); -} - -ATF_TC_BODY(unconninet6, tc) -{ - run(HOST_V6, PORT_V6, TOTAL, false, false); -} - -ATF_TP_ADD_TCS(tp) -{ - debug++; - ATF_TP_ADD_TC(tp, conninet4); - ATF_TP_ADD_TC(tp, connmappedinet4); - ATF_TP_ADD_TC(tp, connmappedbuginet4); - ATF_TP_ADD_TC(tp, conninet6); - ATF_TP_ADD_TC(tp, unconninet4); - ATF_TP_ADD_TC(tp, unconnmappedinet4); - ATF_TP_ADD_TC(tp, unconnmappedbuginet4); - ATF_TP_ADD_TC(tp, unconninet6); - - return atf_no_error(); -} -#endif diff --git a/tests/net/mcast/t_mcast.sh b/tests/net/mcast/t_mcast.sh new file mode 100644 index 0000000..af281fb --- /dev/null +++ b/tests/net/mcast/t_mcast.sh @@ -0,0 +1,109 @@ +# $NetBSD$ +# +# Copyright (c) 2015 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +netserver="rump_server -lrumpnet -lrumpnet_net" +netserver="$netserver -lrumpnet_netinet -lrumpnet_netinet6 -lrumpnet_shmif" +export RUMP_SERVER=unix://commsock + +DEBUG=false + +run_test() +{ + local name="$1" + local opts="$2" + local mcast="$(atf_get_srcdir)/mcast" + + atf_check -s exit:0 ${netserver} ${RUMP_SERVER} + atf_check -s exit:0 rump.ifconfig shmif0 create + atf_check -s exit:0 rump.ifconfig shmif0 linkstr bus1 + atf_check -s exit:0 rump.ifconfig shmif0 10.0.0.2/24 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 fc00::2/64 + atf_check -s exit:0 rump.ifconfig shmif0 up + + atf_check -s exit:0 rump.ifconfig -w 10 + atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep -q tentative" + + # A route to the mcast address is required to join the mcast group + atf_check -s exit:0 -o ignore rump.route add default 10.0.0.1 + atf_check -s exit:0 -o ignore rump.route add -inet6 default fc00::1 + + $DEBUG && rump.ifconfig + $DEBUG && rump.netstat -nr + + export LD_PRELOAD=/usr/lib/librumphijack.so + #$DEBUG && /usr/sbin/ifmcstat # Not yet run on rump kernel + if $DEBUG; then + atf_check -s exit:0 -o ignore $mcast -d ${opts} + else + atf_check -s exit:0 $mcast ${opts} + fi + #$DEBUG && /usr/sbin/ifmcstat # Not yet run on rump kernel + unset LD_PRELOAD +} + +add_test() +{ + local name=$1 + local opts="$2" + local desc="$3" + + atf_test_case "mcast_${name}" cleanup + eval "mcast_${name}_head() { \ + atf_set \"descr\" \"${desc}\"; \ + atf_set \"require.progs\" \"rump_server\"; \ + }; \ + mcast_${name}_body() { \ + run_test \"${name}\" \"${opts}\"; \ + }; \ + mcast_${name}_cleanup() { \ + ${DEBUG} && /usr/bin/shmif_dumpbus -p - bus1 2>/dev/null | \ + /usr/sbin/tcpdump -n -e -r -; \ + env RUMP_SERVER=unix://commsock rump.halt; \ + }" + atf_add_test_case "mcast_${name}" +} + +atf_init_test_cases() +{ + + add_test conninet4 "-c -4" \ + "Checks connected multicast for ipv4" + add_test connmappedinet4 "-c -m -4" \ + "Checks connected multicast for mapped ipv4" + add_test connmappedbuginet4 "-c -m -b -4" \ + "Checks connected multicast for mapped ipv4 using the v4 ioctls" + add_test conninet6 "-c -6" \ + "Checks connected multicast for ipv6" + add_test unconninet4 "-4" \ + "Checks unconnected multicast for ipv4" + add_test unconnmappedinet4 "-m -4" \ + "Checks unconnected multicast for mapped ipv4" + add_test unconnmappedbuginet4 "-m -b -4" \ + "Checks unconnected multicast for mapped ipv4 using the v4 ioctls" + add_test unconninet6 "-6" \ + "Checks unconnected multicast for ipv6" +}