#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <ifaddrs.h>
int
runtest(int *sockp, struct addrinfo *ai, int reuseaddr, int reuseport,
void *mreq, int expected)
{
int error, optval;
struct ip_mreq imr;
*sockp = socket(ai->ai_family, ai->ai_socktype, 0);
if (*sockp == -1) {
warn("%s : socket()", ai->ai_canonname);
return (3);
}
if (reuseaddr) {
optval = 1;
error = setsockopt(*sockp, SOL_SOCKET, SO_REUSEADDR,
&optval, sizeof(int));
if (error) {
warn("%s : setsockopt(SO_REUSEADDR)", ai->ai_canonname);
return (2);
}
}
if (reuseport) {
optval = 1;
error = setsockopt(*sockp, SOL_SOCKET, SO_REUSEPORT,
&optval, sizeof(int));
if (error) {
warn("%s : setsockopt(SO_REUSEPORT)", ai->ai_canonname);
return (2);
}
}
if (mreq) {
switch (ai->ai_family) {
case AF_INET6:
error = setsockopt(*sockp, IPPROTO_IPV6, IPV6_JOIN_GROUP,
mreq, sizeof(struct ipv6_mreq));
if (error) {
warn("%s : setsockopt(IPV6_JOIN_GROUP)",
ai->ai_canonname);
return (2);
}
break;
case AF_INET:
error = setsockopt(*sockp, IPPROTO_IP, IP_ADD_MEMBERSHIP,
mreq, sizeof(struct ip_mreq));
if (error) {
warn("%s : setsockopt(IP_ADD_MEMBERSHIP)",
ai->ai_canonname);
return (2);
}
break;
default:
warnx("%s : trying to join multicast group in unknown AF",
ai->ai_canonname);
return (2);
}
}
error = bind(*sockp, ai->ai_addr, ai->ai_addrlen);
if (error && (expected == 0 || expected != errno)) {
warn("bind(%s,%s,%s)", ai->ai_canonname,
reuseaddr ? "REUSEADDR" : "", reuseport ? "REUSEPORT" : "");
return (1);
}
if (error == 0 && expected != 0) {
warnx("bind(%s,%s,%s) succeeded, expected : %s", ai->ai_canonname,
reuseaddr ? "REUSEADDR" : "", reuseport ? "REUSEPORT" : "",
strerror(expected));
return (1);
}
return (0);
}
void
cleanup(int *fds, int num_fds)
{
while (num_fds-- > 0)
if (close(*fds++) && errno != EBADF)
err(2, "unable to clean up sockets, aborting");
}
int
unicast_testsuite(struct addrinfo *local, struct addrinfo *any)
{
int test_rc, rc, *s;
int sockets[4];
test_rc = 0;
rc = 0; s = sockets;
rc |= runtest(s++, local, 0, 0, NULL, 0);
rc |= runtest(s++, any, 0, 0, NULL, EADDRINUSE);
rc |= runtest(s++, any, 1, 0, NULL, 0);
cleanup(sockets, 3);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 1);
rc = 0; s = sockets;
rc |= runtest(s++, any, 0, 0, NULL, 0);
rc |= runtest(s++, local, 0, 0, NULL, EADDRINUSE);
rc |= runtest(s++, local, 1, 0, NULL, 0);
cleanup(sockets, 3);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 2);
rc = 0; s = sockets;
rc |= runtest(s++, local, 0, 1, NULL, 0);
rc |= runtest(s++, local, 0, 1, NULL, 0);
rc |= runtest(s++, local, 1, 0, NULL, EADDRINUSE);
rc |= runtest(s++, local, 0, 0, NULL, EADDRINUSE);
cleanup(sockets, 4);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 3);
rc = 0; s = sockets;
rc |= runtest(s++, any, 0, 1, NULL, 0);
rc |= runtest(s++, any, 0, 1, NULL, 0);
rc |= runtest(s++, any, 1, 0, NULL, EADDRINUSE);
rc |= runtest(s++, any, 0, 0, NULL, EADDRINUSE);
cleanup(sockets, 4);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 4);
rc = 0; s = sockets;
rc |= runtest(s++, local, 1, 0, NULL, 0);
rc |= runtest(s++, local, 1, 0, NULL, EADDRINUSE);
rc |= runtest(s++, local, 0, 1, NULL, EADDRINUSE);
cleanup(sockets, 3);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 5);
rc = 0; s = sockets;
rc |= runtest(s++, any, 1, 0, NULL, 0);
rc |= runtest(s++, any, 1, 0, NULL, EADDRINUSE);
rc |= runtest(s++, any, 0, 1, NULL, EADDRINUSE);
cleanup(sockets, 3);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 6);
return (test_rc);
}
int
mcast_reuse_testsuite(struct addrinfo *local, void *mr)
{
int test_rc, rc, *s;
int sockets[6];
int testnum = 1;
test_rc = 0;
rc = 0; s = sockets;
rc |= runtest(s++, local, 0, 0, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, EADDRINUSE);
rc |= runtest(s++, local, 0, 1, mr, EADDRINUSE);
rc |= runtest(s++, local, 1, 1, mr, EADDRINUSE);
cleanup(sockets, 4);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 1);
rc = 0; s = sockets;
rc |= runtest(s++, local, 0, 1, mr, 0);
rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
rc |= runtest(s++, local, 0, 1, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 1, 1, mr, 0);
cleanup(sockets, 5);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 2);
rc = 0; s = sockets;
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 0, 1, mr, 0);
rc |= runtest(s++, local, 1, 1, mr, 0);
cleanup(sockets, 5);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 3);
rc = 0; s = sockets;
rc |= runtest(s++, local, 1, 1, mr, 0);
rc |= runtest(s++, local, 0, 0, mr, EADDRINUSE);
rc |= runtest(s++, local, 0, 1, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 1, 1, mr, 0);
cleanup(sockets, 5);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 4);
#if 0
rc = 0; s = sockets;
rc |= runtest(s++, local, 1, 1, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 0, 1, mr, 0);
cleanup(sockets, 3);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 5);
rc = 0; s = sockets;
rc |= runtest(s++, local, 1, 1, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 1, 1, mr, 0);
rc |= runtest(s++, local, 0, 1, mr, 0);
cleanup(sockets, 5);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 6);
rc = 0; s = sockets;
rc |= runtest(s++, local, 1, 1, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 1, 1, mr, 0);
rc |= runtest(s++, local, 1, 0, mr, 0);
rc |= runtest(s++, local, 0, 1, mr, 0);
cleanup(sockets, 5);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 7);
#endif
return (test_rc);
}
int
mcast6_testsuite(struct addrinfo *local, struct ipv6_mreq *local_mreq,
struct addrinfo *any, struct ipv6_mreq *any_mreq)
{
int test_rc, rc, *s;
int sockets[4];
int testnum = 1;
test_rc = 0;
rc = 0; s = sockets;
rc |= runtest(s++, local, 0, 0, local_mreq, 0);
rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
rc |= runtest(s++, any, 1, 0, any_mreq, 0);
cleanup(sockets, 3);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 1);
rc = 0; s = sockets;
rc |= runtest(s++, any, 0, 0, any_mreq, 0);
rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
rc |= runtest(s++, local, 1, 0, local_mreq, 0);
cleanup(sockets, 3);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 2);
rc = 0; s = sockets;
rc |= runtest(s++, local, 0, 1, local_mreq, 0);
rc |= runtest(s++, local, 0, 1, local_mreq, 0);
rc |= runtest(s++, local, 1, 0, local_mreq, 0);
rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
cleanup(sockets, 4);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 3);
rc = 0; s = sockets;
rc |= runtest(s++, any, 0, 1, any_mreq, 0);
rc |= runtest(s++, any, 0, 1, any_mreq, 0);
rc |= runtest(s++, any, 1, 0, any_mreq, EADDRINUSE);
rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
cleanup(sockets, 4);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 4);
rc = 0; s = sockets;
rc |= runtest(s++, local, 1, 0, local_mreq, 0);
rc |= runtest(s++, local, 1, 0, local_mreq, 0);
rc |= runtest(s++, local, 0, 1, local_mreq, 0);
rc |= runtest(s++, local, 0, 0, local_mreq, EADDRINUSE);
cleanup(sockets, 4);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 5);
rc = 0; s = sockets;
rc |= runtest(s++, any, 1, 0, any_mreq, 0);
rc |= runtest(s++, any, 1, 0, any_mreq, EADDRINUSE);
rc |= runtest(s++, any, 0, 1, any_mreq, EADDRINUSE);
rc |= runtest(s++, any, 0, 0, any_mreq, EADDRINUSE);
cleanup(sockets, 4);
test_rc |= rc;
if (rc)
warnx("%s : test #%d failed", __func__, 6);
return (test_rc);
}
int
main(int argc, char *argv[])
{
int error, rc;
char *baddr_s, *bport_s, *bmifa_s;
struct addrinfo hints, *baddr, *any, *mifa;
struct ifaddrs *ifap, *curifa;
struct ip_mreq local_imr, any_imr;
struct ipv6_mreq local_i6mr, any_i6mr;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
int *s;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST | AI_NUMERICSERV | \
AI_PASSIVE;
hints.ai_socktype = SOCK_DGRAM;
baddr_s = argv[1];
bport_s = argv[2];
if ((error = getaddrinfo(baddr_s, bport_s, &hints, &baddr)))
errx(2, "getaddrinfo(%s,%s): %s", baddr_s, bport_s,
gai_strerror(error));
baddr->ai_canonname = baddr_s;
hints.ai_family = baddr->ai_family;
if ((error = getaddrinfo(NULL, bport_s, &hints, &any)))
errx(2, "getaddrinfo(NULL,%s): %s", bport_s,
gai_strerror(error));
any->ai_canonname = "*";
switch (baddr->ai_family) {
case AF_INET:
sin = (struct sockaddr_in *)baddr->ai_addr;
if (!IN_MULTICAST( ntohl(sin->sin_addr.s_addr) )) {
puts("executing unicast testsuite");
return unicast_testsuite(baddr, any);
}
bmifa_s = argv[3];
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
if ((error = getaddrinfo(bmifa_s, NULL, &hints, &mifa)))
errx(2, "getaddrinfo(%s,NULL): %s", bmifa_s,
gai_strerror(error));
local_imr.imr_interface =
((struct sockaddr_in *)mifa->ai_addr)->sin_addr;
local_imr.imr_multiaddr =
((struct sockaddr_in *)baddr->ai_addr)->sin_addr;
puts("executing ipv4 multicast testsuite");
return mcast_reuse_testsuite(baddr, &local_imr);
case AF_INET6:
sin6 = (struct sockaddr_in6 *)baddr->ai_addr;
if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
puts("executing unicast testsuite");
return unicast_testsuite(baddr, any);
}
bmifa_s = argv[3];
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST;
if ((error = getaddrinfo(bmifa_s, NULL, &hints, &mifa)))
errx(2, "getaddrinfo(%s,NULL): %s", bmifa_s,
gai_strerror(error));
if (getifaddrs(&ifap))
err(2, "getifaddrs()");
curifa = ifap;
while (curifa) {
if (curifa->ifa_addr != NULL &&
memcmp(curifa->ifa_addr,
mifa->ai_addr,
mifa->ai_addrlen) == 0)
break;
curifa = curifa->ifa_next;
}
if (curifa == NULL)
errx(2, "no interface configured with %s", argv[4]);
local_i6mr.ipv6mr_interface =
if_nametoindex(curifa->ifa_name);
if (local_i6mr.ipv6mr_interface == 0)
errx(2, "unable to get \"%s\" index",
curifa->ifa_name);
freeifaddrs(ifap);
local_i6mr.ipv6mr_multiaddr =
((struct sockaddr_in6 *)baddr->ai_addr)->sin6_addr;
any_i6mr.ipv6mr_interface = local_i6mr.ipv6mr_interface;
any_i6mr.ipv6mr_multiaddr =
((struct sockaddr_in6 *)any->ai_addr)->sin6_addr;
puts("executing ipv6 multicast testsuite");
rc = 0;
rc |= mcast_reuse_testsuite(baddr, &local_i6mr);
if (geteuid() == 0)
rc |= mcast6_testsuite(baddr, &local_i6mr, any, &any_i6mr);
else
warnx("skipping mcast6_testsuite() due to insufficient privs, please run again as root");
return (rc);
default:
errx(2,"unknown AF");
}
return (2);
}