/* * Socket test code library. This file contains code that is worth sharing * between TCP/IP and UDS tests, as well as code that is worth sharing between * various TCP/IP tests. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "socklib.h" #define TEST_PORT_A 12345 /* this port should be free and usable */ #define TEST_PORT_B 12346 /* this port should be free and usable */ #define LOOPBACK_IFNAME "lo0" /* loopback interface name */ #define LOOPBACK_IPV4 "127.0.0.1" /* IPv4 address */ #define LOOPBACK_IPV6_LL "fe80::1" /* link-local IPv6 address */ /* These address should simply eat all packets. */ #define TEST_BLACKHOLE_IPV4 "127.255.0.254" #define TEST_BLACKHOLE_IPV6 "::2" #define TEST_BLACKHOLE_IPV6_LL "fe80::ffff" /* Addresses for multicast-related testing. */ #define TEST_MULTICAST_IPV4 "233.252.0.1" /* RFC 5771 Sec. 9.2 */ #define TEST_MULTICAST_IPV6 "ff0e::db8:0:1" /* RFC 6676 Sec. 3 */ #define TEST_MULTICAST_IPV6_LL "ff02::db8:0:1" #define TEST_MULTICAST_IPV6_BAD "ff00::db8:0:1" #define BAD_IFINDEX 255 /* guaranteed not to belong to an interface */ /* 0 = check, 1 = generate source, 2 = generate CSV */ #define SOCKLIB_SWEEP_GENERATE 0 #if SOCKLIB_SWEEP_GENERATE /* Link against minix/usr.bin/trace/error.o to make this work! */ const char *get_error_name(int err); #if SOCKLIB_SWEEP_GENERATE == 2 static const char *statename[S_MAX] = { "S_NEW", "S_N_SHUT_R", "S_N_SHUT_W", "S_N_SHUT_RW", "S_BOUND", "S_LISTENING", "S_L_SHUT_R", "S_L_SHUT_W", "S_L_SHUT_RW", "S_CONNECTING", "S_C_SHUT_R", "S_C_SHUT_W", "S_C_SHUT_RW", "S_CONNECTED", "S_ACCEPTED", "S_SHUT_R", "S_SHUT_W", "S_SHUT_RW", "S_RSHUT_R", "S_RSHUT_W", "S_RSHUT_RW", "S_SHUT2_R", "S_SHUT2_W", "S_SHUT2_RW", "S_PRE_EOF", "S_AT_EOF", "S_POST_EOF", "S_PRE_SHUT_R", "S_EOF_SHUT_R", "S_POST_SHUT_R", "S_PRE_SHUT_W", "S_EOF_SHUT_W", "S_POST_SHUT_W", "S_PRE_SHUT_RW", "S_EOF_SHUT_RW", "S_POST_SHUT_RW", "S_PRE_RESET", "S_AT_RESET", "S_POST_RESET", "S_FAILED", "S_POST_FAILED", }; #endif static const char *callname[C_MAX] = { "C_ACCEPT", "C_BIND", "C_CONNECT", "C_GETPEERNAME", "C_GETSOCKNAME", "C_GETSOCKOPT_ERR", "C_GETSOCKOPT_KA", "C_GETSOCKOPT_RB", "C_IOCTL_NREAD", "C_LISTEN", "C_RECV", "C_RECVFROM", "C_SEND", "C_SENDTO", "C_SELECT_R", "C_SELECT_W", "C_SELECT_X", "C_SETSOCKOPT_BC", "C_SETSOCKOPT_KA", "C_SETSOCKOPT_L", "C_SETSOCKOPT_RA", "C_SHUTDOWN_R", "C_SHUTDOWN_RW", "C_SHUTDOWN_W", }; #endif static int socklib_sigpipe; /* * Signal handler for SIGPIPE signals. */ static void socklib_signal(int sig) { if (sig != SIGPIPE) e(0); socklib_sigpipe++; } /* * The given socket file descriptor 'fd' has been set up in the desired state. * Perform the given call 'call' on it, possibly using local socket address * 'local_addr' (for binding) or remote socket address 'remote_addr' (for * connecting or to store resulting addresses), both of size 'addr_len'. * Return the result of the call, using a positive value if the call succeeded, * or a negated errno code if the call failed. */ int socklib_sweep_call(enum call call, int fd, struct sockaddr * local_addr, struct sockaddr * remote_addr, socklen_t addr_len) { char data[1]; struct linger l; fd_set fd_set; struct timeval tv; socklen_t len; int i, r, fd2; fd2 = -1; switch (call) { case C_ACCEPT: r = accept(fd, remote_addr, &addr_len); if (r >= 0) fd2 = r; break; case C_BIND: r = bind(fd, local_addr, addr_len); break; case C_CONNECT: r = connect(fd, remote_addr, addr_len); break; case C_GETPEERNAME: r = getpeername(fd, remote_addr, &addr_len); break; case C_GETSOCKNAME: r = getsockname(fd, remote_addr, &addr_len); break; case C_GETSOCKOPT_ERR: len = sizeof(i); r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &i, &len); /* * We assume this call always succeeds, and test against the * pending error. */ if (r != 0) e(0); if (i != 0) { r = -1; errno = i; } break; case C_GETSOCKOPT_KA: len = sizeof(i); r = getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &i, &len); break; case C_GETSOCKOPT_RB: len = sizeof(i); r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &i, &len); break; case C_IOCTL_NREAD: r = ioctl(fd, FIONREAD, &i); /* On success, we test against the returned value here. */ if (r == 0) r = i; break; case C_LISTEN: r = listen(fd, 1); break; case C_RECV: r = recv(fd, data, sizeof(data), 0); break; case C_RECVFROM: r = recvfrom(fd, data, sizeof(data), 0, remote_addr, &addr_len); break; case C_SEND: data[0] = 0; r = send(fd, data, sizeof(data), 0); break; case C_SENDTO: data[0] = 0; r = sendto(fd, data, sizeof(data), 0, remote_addr, addr_len); break; case C_SETSOCKOPT_BC: i = 0; r = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)); break; case C_SETSOCKOPT_KA: i = 1; r = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)); break; case C_SETSOCKOPT_L: l.l_onoff = 1; l.l_linger = 0; r = setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); break; case C_SETSOCKOPT_RA: i = 1; r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); break; case C_SELECT_R: case C_SELECT_W: case C_SELECT_X: FD_ZERO(&fd_set); FD_SET(fd, &fd_set); tv.tv_sec = 0; tv.tv_usec = 0; r = select(fd + 1, (call == C_SELECT_R) ? &fd_set : NULL, (call == C_SELECT_W) ? &fd_set : NULL, (call == C_SELECT_X) ? &fd_set : NULL, &tv); break; case C_SHUTDOWN_R: r = shutdown(fd, SHUT_RD); break; case C_SHUTDOWN_W: r = shutdown(fd, SHUT_WR); break; case C_SHUTDOWN_RW: r = shutdown(fd, SHUT_RDWR); break; default: r = -1; errno = EINVAL; e(0); } if (r < -1) e(0); if (r == -1) r = -errno; if (fd2 >= 0 && close(fd2) != 0) e(0); return r; } /* * Perform a sweep of socket calls vs socket states, testing the outcomes * against provided tables or (if SOCKLIB_SWEEP_GENERATE is set) reporting on * the outcomes instead. The caller must provide the following: * * - the socket domain, type, and protocol to test; these are simply forwarded * to the callback function (see below); * - the set of S_ states to test, as array 'states' with 'nstates' elements; * - unless generating output, a matrix of expected results as 'results', which * is actually a two-dimensional array with dimensions [C_MAX][nstates], with * either positive call output or a negated call errno code in each cell; * - a callback function 'proc' that must set up a socket in the given state * and pass it to socklib_sweep_call(). * * The 'states' array allows each socket sweep test to support a different set * of states, because not every type of socket can be put in every possible * state. All calls are always tried in each state, though. * * The sweep also tests for SIGPIPE generation, which assumes that all calls on * SOCK_STREAM sockets that return EPIPE, also raise a SIGPIPE signal, and that * no other SIGPIPE signal is ever raised otherwise. * * Standard e() error throwing is used for set-up and result mismatches. */ void socklib_sweep(int domain, int type, int protocol, const enum state * states, unsigned int nstates, const int * results, int (* proc)(int domain, int type, int protocol, enum state, enum call)) { struct sigaction act, oact; enum state state; enum call call; #if SOCKLIB_SWEEP_GENERATE const char *name; int res, *nresults; #else int res, exp; #endif memset(&act, 0, sizeof(act)); act.sa_handler = socklib_signal; if (sigaction(SIGPIPE, &act, &oact) != 0) e(0); #if SOCKLIB_SWEEP_GENERATE if ((nresults = malloc(nstates * C_MAX)) == NULL) e(0); #endif for (state = 0; state < nstates; state++) { for (call = 0; call < C_MAX; call++) { socklib_sigpipe = 0; res = proc(domain, type, protocol, states[state], call); /* * If the result was EPIPE and this is a stream-type * socket, we must have received exactly one SIGPIPE * signal. Otherwise, we must not have received one. * Note that technically, the SIGPIPE could arrive * sometime after this check, but with regular system * service scheduling that will never happen. */ if (socklib_sigpipe != (res == -EPIPE && type == SOCK_STREAM)) e(0); #if SOCKLIB_SWEEP_GENERATE nresults[call * nstates + state] = res; #else exp = results[call * nstates + state]; if (res != exp) { printf("FAIL state %d call %d res %d exp %d\n", state, call, res, exp); e(0); } #endif } } if (sigaction(SIGPIPE, &oact, NULL) != 0) e(0); #if SOCKLIB_SWEEP_GENERATE #if SOCKLIB_SWEEP_GENERATE == 1 /* * Generate a table in C form, ready to be pasted into test source. * Obviously, generated results should be hand-checked carefully before * being pasted into a test. Arguably these tables should be hand-made * for maximum scrutiny, but I already checked the results from the * CSV form (#define SOCKLIB_SWEEP_GENERATE 2) and have no desire for * RSI -dcvmoole */ printf("\nstatic const int X_results[][__arraycount(X_states)] = {\n"); for (call = 0; call < C_MAX; call++) { if ((name = callname[call]) == NULL) e(0); printf("\t[%s]%s%s%s= {", name, (strlen(name) <= 21) ? "\t" : "", (strlen(name) <= 13) ? "\t" : "", (strlen(name) <= 5) ? "\t" : ""); for (state = 0; state < nstates; state++) { if (state % 4 == 0) printf("\n\t\t"); res = nresults[call * nstates + state]; name = (res < 0) ? get_error_name(-res) : NULL; if (name != NULL) { printf("-%s,", name); if ((state + 1) % 4 != 0 && state < nstates - 1) printf("%s%s", (strlen(name) <= 13) ? "\t" : "", (strlen(name) <= 5) ? "\t" : ""); } else { printf("%d,", res); if ((state + 1) % 4 != 0 && state < nstates - 1) printf("\t\t"); } } printf("\n\t},\n"); } printf("};\n"); #elif SOCKLIB_SWEEP_GENERATE == 2 /* Generate table in CSV form. */ printf("\n"); for (state = 0; state < nstates; state++) printf(",%s", statename[states[state]] + 2); for (call = 0; call < C_MAX; call++) { printf("\n%s", callname[call] + 2); for (state = 0; state < nstates; state++) { res = nresults[call * nstates + state]; name = (res < 0) ? get_error_name(-res) : NULL; if (name != NULL) printf(",%s", name); else printf(",%d", res); } } printf("\n"); #endif free(nresults); #endif } /* * Test for setting and retrieving UDP/RAW multicast transmission options. * This is an interface-level test only: we do not (yet) test whether the * options have any effect. The given 'type' must be SOCK_DGRAM or SOCK_RAW. */ void socklib_multicast_tx_options(int type) { struct in_addr in_addr; socklen_t len; unsigned int ifindex; uint8_t byte; int fd, val; subtest = 10; if ((fd = socket(AF_INET, type, 0)) < 0) e(0); /* * Initially, the multicast TTL is expected be 1, looping should be * enabled, and the multicast source address should be . */ byte = 0; len = sizeof(byte); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &byte, &len) != 0) e(0); if (len != sizeof(byte)) e(0); if (type != SOCK_STREAM && byte != 1) e(0); byte = 0; len = sizeof(byte); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &byte, &len) != 0) e(0); if (len != sizeof(byte)) e(0); if (byte != 1) e(0); len = sizeof(in_addr); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &in_addr, &len) != 0) e(0); if (len != sizeof(in_addr)) e(0); if (in_addr.s_addr != htonl(INADDR_ANY)) e(0); /* It must not be possible to get/set IPv6 options on IPv4 sockets. */ val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val) /*wrong but it doesn't matter*/) != -1) e(0); if (errno != ENOPROTOOPT) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (close(fd) != 0) e(0); if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); /* * Expect the same defaults as for IPv4. IPV6_MULTICAST_IF uses an * interface index rather than an IP address, though. */ val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (type != SOCK_STREAM && val != 1) e(0); val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 1) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 0) e(0); /* It must not be possible to get/set IPv4 options on IPv6 sockets. */ byte = 0; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &byte, sizeof(byte)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &byte, sizeof(byte)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &byte, sizeof(byte) /* wrong but it doesn't matter */) != -1) e(0); if (errno != ENOPROTOOPT) e(0); len = sizeof(byte); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (close(fd) != 0) e(0); /* Test changing options. */ if ((fd = socket(AF_INET, type, 0)) < 0) e(0); byte = 129; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &byte, sizeof(byte)) != 0) e(0); byte = 0; len = sizeof(byte); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &byte, &len) != 0) e(0); if (len != sizeof(byte)) e(0); if (byte != 129) e(0); byte = 0; if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &byte, sizeof(byte)) != 0) e(0); byte = 1; len = sizeof(byte); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &byte, &len) != 0) e(0); if (len != sizeof(byte)) e(0); if (byte != 0) e(0); in_addr.s_addr = htonl(INADDR_LOOPBACK); if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &in_addr, sizeof(in_addr)) != 0) e(0); in_addr.s_addr = htonl(INADDR_ANY); len = sizeof(in_addr); if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &in_addr, &len) != 0) e(0); if (len != sizeof(in_addr)) e(0); if (in_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); if (close(fd) != 0) e(0); if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); val = 137; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) != 0) e(0); val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 137) e(0); val = -2; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = 256; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 137) e(0); val = -1; /* use default */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) != 0) e(0); val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 1) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != 0) e(0); val = 1; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 0) e(0); val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != 0) e(0); val = -1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = 2; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 1) e(0); val = -1; ifindex = if_nametoindex(LOOPBACK_IFNAME); val = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != ifindex) e(0); val = BAD_IFINDEX; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != -1) e(0); val = -1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != -1) e(0); val = 0; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != ifindex) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); val = ifindex; len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 0) e(0); if (close(fd) != 0) e(0); } /* * Test for large sends and receives on stream sockets with MSG_WAITALL. */ void socklib_large_transfers(int fd[2]) { char *buf; pid_t pid; int i, status; #define LARGE_BUF (4096*1024) if ((buf = malloc(LARGE_BUF)) == NULL) e(0); memset(buf, 0, LARGE_BUF); pid = fork(); switch (pid) { case 0: errct = 0; if (close(fd[0]) != 0) e(0); /* Part 1. */ if (recv(fd[1], buf, LARGE_BUF, MSG_WAITALL) != LARGE_BUF) e(0); for (i = 0; i < LARGE_BUF; i++) if (buf[i] != (char)(i + (i >> 16))) e(0); if (recv(fd[1], buf, LARGE_BUF, MSG_DONTWAIT | MSG_WAITALL) != -1) e(0); if (errno != EWOULDBLOCK) e(0); /* Part 2. */ if (send(fd[1], buf, LARGE_BUF / 2, 0) != LARGE_BUF / 2) e(0); if (shutdown(fd[1], SHUT_WR) != 0) e(0); /* Part 3. */ memset(buf, 'y', LARGE_BUF); if (recv(fd[1], buf, LARGE_BUF, MSG_WAITALL) != LARGE_BUF - 1) e(0); for (i = 0; i < LARGE_BUF - 1; i++) if (buf[i] != (char)(i + (i >> 16))) e(0); if (buf[LARGE_BUF - 1] != 'y') e(0); if (recv(fd[1], buf, LARGE_BUF, MSG_WAITALL) != 0) e(0); exit(errct); case -1: e(0); } if (close(fd[1]) != 0) e(0); /* Part 1: check that a large send fully arrives. */ for (i = 0; i < LARGE_BUF; i++) buf[i] = (char)(i + (i >> 16)); if (send(fd[0], buf, LARGE_BUF, 0) != LARGE_BUF) e(0); /* Part 2: check that remote shutdown terminates a partial receive. */ memset(buf, 'x', LARGE_BUF); if (recv(fd[0], buf, LARGE_BUF, MSG_WAITALL) != LARGE_BUF / 2) e(0); for (i = 0; i < LARGE_BUF / 2; i++) if (buf[i] != (char)(i + (i >> 16))) e(0); for (; i < LARGE_BUF; i++) if (buf[i] != 'x') e(0); if (recv(fd[0], buf, LARGE_BUF, MSG_WAITALL) != 0) e(0); /* Part 3: check that remote close terminates a partial receive. */ for (i = 0; i < LARGE_BUF; i++) buf[i] = (char)(i + (i >> 16)); if (send(fd[0], buf, LARGE_BUF - 1, 0) != LARGE_BUF - 1) e(0); if (close(fd[0]) != 0) e(0); if (waitpid(pid, &status, 0) != pid) e(0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); free(buf); } #define PRINT_STATS 0 /* * A randomized producer-consumer test for stream sockets. As part of this, * we also perform very basic bulk functionality tests of FIONREAD, MSG_PEEK, * MSG_DONTWAIT, and MSG_WAITALL. */ void socklib_producer_consumer(int fd[2]) { char *buf; time_t t; socklen_t len, size, off; ssize_t r; pid_t pid; int i, rcvlen, status, exp, flags, num, stat[3] = { 0, 0, 0 }; len = sizeof(rcvlen); if (getsockopt(fd[0], SOL_SOCKET, SO_RCVBUF, &rcvlen, &len) != 0) e(0); if (len != sizeof(rcvlen)) e(0); size = rcvlen * 3; if ((buf = malloc(size)) == NULL) e(0); t = time(NULL); /* * We vary small versus large (random) send and receive sizes, * splitting the entire transfer in four phases along those lines. * * In theory, the use of an extra system call, the use of MSG_PEEK, and * the fact that without MSG_WAITALL a receive call may return any * partial result, all contribute to the expectation that the consumer * side will fall behind the producer. In order to test both filling * and draining the receive queue, we use a somewhat larger small * receive size for the consumer size (up to 256 bytes rather than 64) * during each half of the four phases. The effectiveness of these * numbers can be verified with statistics (disabled by default). */ #define TRANSFER_SIZE (16 * 1024 * 1024) pid = fork(); switch (pid) { case 0: errct = 0; if (close(fd[0]) != 0) e(0); srand48(t + 1); for (off = 0; off < TRANSFER_SIZE; ) { if (off < TRANSFER_SIZE / 2) len = lrand48() % ((off / (TRANSFER_SIZE / 8) % 2) ? 64 : 256); else len = lrand48() % size; num = lrand48() % 16; flags = 0; if (num & 1) flags |= MSG_PEEK; if (num & 2) flags |= MSG_WAITALL; if (num & 4) flags |= MSG_DONTWAIT; if (num & 8) { /* * Obviously there are race conditions here but * the returned number should be a lower bound. */ if (ioctl(fd[1], FIONREAD, &exp) != 0) e(0); if (exp < 0 || exp > rcvlen) e(0); } else exp = -1; stat[0]++; if ((r = recv(fd[1], buf, len, flags)) == -1) { if (errno != EWOULDBLOCK) e(0); if (exp > 0) e(0); stat[2]++; continue; } if (r < len) { stat[1]++; if (exp > r) e(0); } for (i = 0; i < r; i++) if (buf[i] != (char)((off + i) + ((off + i) >> 16))) e(0); if (!(flags & MSG_PEEK)) { off += r; if ((flags & (MSG_DONTWAIT | MSG_WAITALL)) == MSG_WAITALL && r != len && off < TRANSFER_SIZE) e(0); } } #if PRINT_STATS /* * The second and third numbers should ideally be a large but * non-dominating fraction of the first one. */ printf("RECV: total %d short %d again %d\n", stat[0], stat[1], stat[2]); #endif if (close(fd[1]) != 0) e(0); exit(errct); case -1: e(0); } if (close(fd[1]) != 0) e(0); srand48(t); for (off = 0; off < TRANSFER_SIZE; ) { if (off < TRANSFER_SIZE / 4 || (off >= TRANSFER_SIZE / 2 && off < TRANSFER_SIZE * 3 / 4)) len = lrand48() % 64; else len = lrand48() % size; if (len > TRANSFER_SIZE - off) len = TRANSFER_SIZE - off; for (i = 0; i < len; i++) buf[i] = (off + i) + ((off + i) >> 16); flags = (lrand48() % 2) ? MSG_DONTWAIT : 0; stat[0]++; r = send(fd[0], buf, len, flags); if (r != len) { if (r > (ssize_t)len) e(0); if (!(flags & MSG_DONTWAIT)) e(0); if (r == -1) { if (errno != EWOULDBLOCK) e(0); r = 0; stat[2]++; } else stat[1]++; } if (off / (TRANSFER_SIZE / 4) != (off + r) / (TRANSFER_SIZE / 4)) sleep(1); off += r; } #if PRINT_STATS /* * The second and third numbers should ideally be a large but non- * dominating fraction of the first one. */ printf("SEND: total %d short %d again %d\n", stat[0], stat[1], stat[2]); #endif free(buf); if (close(fd[0]) != 0) e(0); if (waitpid(pid, &status, 0) != pid) e(0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); } /* * Signal handler which just needs to exist, so that invoking it will interrupt * an ongoing system call. */ static void socklib_got_signal(int sig __unused) { /* Nothing. */ } /* * Test for receiving on stream sockets. The quick summary here is that * recv(MSG_WAITALL) should keep suspending until as many bytes as requested * are also received (or the call is interrupted, or no more can possibly be * received - the meaning of the latter depends on the domain), and, * SO_RCVLOWAT acts as an admission test for the receive: nothing is received * until there are at least as many bytes are available in the receive buffer * as the low receive watermark, or the whole receive request length, whichever * is smaller. In addition, select(2) should use the same threshold. */ #define MAX_BYTES 2 /* set to 3 for slightly better(?) testing */ #define USLEEP_TIME 250000 /* increase on wimpy platforms if needed */ static void socklib_stream_recv_sub(int (* socket_pair)(int, int, int, int *), int domain, int type, int idata, int istate, int rlowat, int len, int bits, int act, int (* break_recv)(int, const char *, size_t)) { const char *data = "ABCDE"; /* this limits MAX_BYTES to 3 */ struct sigaction sa; struct timeval tv; fd_set fds; char buf[3]; pid_t pid; int fd[2], val, flags, min, res, err; int pfd[2], edata, tstate, fl, status; if (socket_pair(domain, type, 0, fd) != 0) e(0); /* * Set up the initial condition on the sockets. */ if (idata > 0) if (send(fd[1], data, idata, 0) != idata) e(0); switch (istate) { case 0: break; case 1: if (shutdown(fd[0], SHUT_RD) != 0) e(0); break; case 2: if (shutdown(fd[1], SHUT_WR) != 0) e(0); break; case 3: if (close(fd[1]) != 0) e(0); break; } /* Set the low receive water mark. */ if (setsockopt(fd[0], SOL_SOCKET, SO_RCVLOWAT, &rlowat, sizeof(rlowat)) != 0) e(0); /* SO_RCVLOWAT is always bounded by the actual receive length. */ min = MIN(len, rlowat); /* * Do a quick select test to see if its result indeed matches whether * the available data in the receive buffer meets the threshold. */ FD_ZERO(&fds); FD_SET(fd[0], &fds); tv.tv_sec = 0; tv.tv_usec = 0; res = select(fd[0] + 1, &fds, NULL, NULL, &tv); if (res < 0 || res > 1) e(0); if (res != (idata >= rlowat || istate > 0)) e(0); if (res == 1 && !FD_ISSET(fd[0], &fds)) e(0); /* Also do a quick test for ioctl(FIONREAD). */ if (ioctl(fd[0], FIONREAD, &val) != 0) e(0); if (val != ((istate != 1) ? idata : 0)) e(0); /* Translate the given bits to receive call flags. */ flags = 0; if (bits & 1) flags |= MSG_PEEK; if (bits & 2) flags |= MSG_DONTWAIT; if (bits & 4) flags |= MSG_WAITALL; /* * Cut short a whole lot of cases, to avoid the overhead of forking, * namely when we know the call should return immediately. This is * the case when MSG_DONTWAIT is set, or if a termination condition has * been raised, or if enough initial data are available to meet the * conditions for the receive call. */ if ((flags & MSG_DONTWAIT) || istate > 0 || (idata >= min && ((flags & (MSG_PEEK | MSG_WAITALL)) != MSG_WAITALL || idata >= len))) { res = recv(fd[0], buf, len, flags); if (res == -1 && errno != EWOULDBLOCK) e(0); /* * If the socket has been shutdown locally, we will never get * anything but zero. Otherwise, if we meet the SO_RCVLOWAT * test, we should have received as much as was available and * requested. Otherwise, if the remote end has been shut down * or closed, we expected to get any available data or * otherwise EOF (implied with idata==0). If none of these * cases apply, we should have gotten EWOULDBLOCK. */ if (istate == 1) { if (res != 0) e(0); } else if (idata >= min) { if (res != MIN(len, idata)) e(0); if (strncmp(buf, data, res)) e(0); } else if (istate > 0) { if (res != idata) e(0); if (strncmp(buf, data, res)) e(0); } else if (res != -1) e(0); /* Early cleanup and return to avoid even more code clutter. */ if (istate != 3 && close(fd[1]) != 0) e(0); if (close(fd[0]) != 0) e(0); return; } /* * Now starts the interesting stuff: the receive call should now block, * even though if we add MSG_DONTWAIT it may not return EWOULDBLOCK, * because MSG_DONTWAIT overrides MSG_WAITALL. As such, we can only * test our expectations by actually letting the call block, in a child * process, and waiting. We do test as much of the above assumption as * we can just for safety right here, but this is not a substitute for * actually blocking even in these cases! */ if (!(flags & MSG_WAITALL)) { if (recv(fd[0], buf, len, flags | MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); } /* * If (act < 12), we send 0, 1, or 2 extra data bytes before forcing * the receive call to terminate in one of four ways. * * If (act == 12), we use a signal to interrupt the receive call. */ if (act < 12) { edata = act % 3; tstate = act / 3; } else edata = tstate = 0; if (pipe2(pfd, O_NONBLOCK) != 0) e(0); pid = fork(); switch (pid) { case 0: errct = 0; if (close(fd[1]) != 0) e(0); if (close(pfd[0]) != 0) e(0); if (act == 12) { memset(&sa, 0, sizeof(sa)); sa.sa_handler = socklib_got_signal; if (sigaction(SIGUSR1, &sa, NULL) != 0) e(0); } res = recv(fd[0], buf, len, flags); err = errno; if (write(pfd[1], &res, sizeof(res)) != sizeof(res)) e(0); if (write(pfd[1], &err, sizeof(err)) != sizeof(err)) e(0); if (res > 0 && strncmp(buf, data, res)) e(0); exit(errct); case -1: e(0); } if (close(pfd[1]) != 0) e(0); /* * Allow the child to enter the blocking recv(2), and check the pipe * to see if it is really blocked. */ if (usleep(USLEEP_TIME) != 0) e(0); if (read(pfd[0], buf, 1) != -1) e(0); if (errno != EAGAIN) e(0); if (edata > 0) { if (send(fd[1], &data[idata], edata, 0) != edata) e(0); /* * The threshold for the receive is now met if both the minimum * is met and MSG_WAITALL was not set (or overridden by * MSG_PEEK) or the entire request has been satisfied. */ if (idata + edata >= min && ((flags & (MSG_PEEK | MSG_WAITALL)) != MSG_WAITALL || idata + edata >= len)) { if ((fl = fcntl(pfd[0], F_GETFL, 0)) == -1) e(0); if (fcntl(pfd[0], F_SETFL, fl & ~O_NONBLOCK) != 0) e(0); if (read(pfd[0], &res, sizeof(res)) != sizeof(res)) e(0); if (read(pfd[0], &err, sizeof(err)) != sizeof(err)) e(0); if (res != MIN(idata + edata, len)) e(0); /* Bail out. */ goto cleanup; } /* Sleep and test once more. */ if (usleep(USLEEP_TIME) != 0) e(0); if (read(pfd[0], buf, 1) != -1) e(0); if (errno != EAGAIN) e(0); } if (act < 12) { /* * Now test various ways to terminate the receive call. */ switch (tstate) { case 0: if (shutdown(fd[0], SHUT_RD) != 0) e(0); break; case 1: if (shutdown(fd[1], SHUT_WR) != 0) e(0); break; case 2: if (close(fd[1]) != 0) e(0); fd[1] = -1; break; case 3: fd[1] = break_recv(fd[1], data, strlen(data)); break; } } else if (kill(pid, SIGUSR1) != 0) e(0); if ((fl = fcntl(pfd[0], F_GETFL, 0)) == -1) e(0); if (fcntl(pfd[0], F_SETFL, fl & ~O_NONBLOCK) != 0) e(0); if (read(pfd[0], &res, sizeof(res)) != sizeof(res)) e(0); if (read(pfd[0], &err, sizeof(err)) != sizeof(err)) e(0); if (act < 12) { /* * If there were any data we should have received them now; * after all the receive minimum stops being relevant when * another condition has been raised. There is one exception: * if the receive threshold was never met and we now shut down * the socket for reading, EOF is acceptable as return value. */ if (tstate == 0 && idata + edata < min) { if (res != 0) e(0); } else if (idata + edata > 0) { if (res != MIN(idata + edata, len)) e(0); } else if (tstate == 3) { if (fd[1] == -1) { if (res != -1) e(0); if (err != ECONNRESET) e(0); } else if (res != len) e(0); } else if (res != 0) e(0); } else { /* * If the receive met the threshold before being interrupted, * we should have received at least something. Otherwise, the * receive was never admitted and should just return EINTR. */ if (idata >= min) { if (res != MIN(idata, len)) e(0); } else { if (res != -1) e(0); if (err != EINTR) e(0); } } cleanup: if (close(pfd[0]) != 0) e(0); if (wait(&status) != pid) e(0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); if (fd[1] != -1 && close(fd[1]) != 0) e(0); if (close(fd[0]) != 0) e(0); } /* * Test for receiving on stream sockets. In particular, test SO_RCVLOWAT, * MSG_PEEK, MSG_DONTWAIT, and MSG_WAITALL. */ void socklib_stream_recv(int (* socket_pair)(int, int, int, int *), int domain, int type, int (* break_recv)(int, const char *, size_t)) { int idata, istate, rlowat, len, bits, act; /* Insanity. */ for (idata = 0; idata <= MAX_BYTES; idata++) for (istate = 0; istate <= 3; istate++) for (rlowat = 1; rlowat <= MAX_BYTES; rlowat++) for (len = 1; len <= MAX_BYTES; len++) for (bits = 0; bits < 8; bits++) for (act = 0; act <= 12; act++) socklib_stream_recv_sub (socket_pair, domain, type, idata, istate, rlowat, len, bits, act, break_recv); } /* * Obtain information for a matching protocol control block, using sysctl(7). * The PCB is to be obtained through the given sysctl path string, and must * match the other given parameters. Return 1 if found with 'ki' filled with * the PCB information, or 0 if not. */ int socklib_find_pcb(const char * path, int protocol, uint16_t local_port, uint16_t remote_port, struct kinfo_pcb * ki) { struct sockaddr_in sin; struct sockaddr_in6 sin6; struct kinfo_pcb *array; size_t i, miblen, oldlen; uint16_t lport, rport; int mib[CTL_MAXNAME], found; miblen = __arraycount(mib); if (sysctlnametomib(path, mib, &miblen) != 0) e(0); if (miblen > __arraycount(mib) - 4) e(0); mib[miblen++] = 0; mib[miblen++] = 0; mib[miblen++] = sizeof(*array); mib[miblen++] = 0; if (sysctl(mib, miblen, NULL, &oldlen, NULL, 0) != 0) e(0); if (oldlen == 0) return 0; /* should not happen due to added slop space */ if (oldlen % sizeof(*array)) e(0); if ((array = (struct kinfo_pcb *)malloc(oldlen)) == NULL) e(0); if (sysctl(mib, miblen, array, &oldlen, NULL, 0) != 0) e(0); if (oldlen % sizeof(*array)) e(0); found = -1; for (i = 0; i < oldlen / sizeof(*array); i++) { /* Perform some basic checks. */ if (array[i].ki_pcbaddr == 0) e(0); if (array[i].ki_ppcbaddr == 0) e(0); if (array[i].ki_family != mib[1]) e(0); if (mib[1] == AF_INET6) { memcpy(&sin6, &array[i].ki_src, sizeof(sin6)); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); lport = ntohs(sin6.sin6_port); memcpy(&sin6, &array[i].ki_dst, sizeof(sin6)); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); rport = ntohs(sin6.sin6_port); } else { memcpy(&sin, &array[i].ki_src, sizeof(sin)); if (sin.sin_family != AF_INET) e(0); if (sin.sin_len != sizeof(sin)) e(0); lport = ntohs(sin.sin_port); memcpy(&sin, &array[i].ki_dst, sizeof(sin)); if (sin.sin_family != AF_UNSPEC) { if (sin.sin_family != AF_INET) e(0); if (sin.sin_len != sizeof(sin)) e(0); rport = ntohs(sin.sin_port); } else rport = 0; } /* Try to match every PCB. We must find at most one match. */ if (array[i].ki_protocol == protocol && lport == local_port && rport == remote_port) { if (found != -1) e(0); found = (int)i; } } if (found >= 0) memcpy(ki, &array[found], sizeof(*ki)); free(array); return (found != -1); } #ifdef NO_INET6 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; void inet6_getscopeid(struct sockaddr_in6 * sin6 __unused, int flags __unused) { /* * Nothing. The tests linked to socklib make heavy use of IPv6, and * are expected to fail if IPv6 support is disabled at compile time. * Therefore, what this replacement function does is not relevant. */ } #endif /* NO_INET6 */ #define F_ANY 0x01 /* not bound, or bound to an 'any' address */ #define F_V4 0x02 /* address is IPv4-mapped IPv6 address */ #define F_REM 0x04 /* address is remote (not assigned to an interface) */ #define F_MIX 0x08 /* address has non-loopback scope */ /* * Test local and remote IPv6 address handling on TCP or UDP sockets. */ void socklib_test_addrs(int type, int protocol) { struct sockaddr_in6 sin6, sin6_any, sin6_any_scope, sin6_lo, sin6_lo_scope, sin6_ll_all, sin6_ll_lo, sin6_ll_rem, sin6_ll_kame, sin6_ll_bad, sin6_ll_mix, sin6_rem, sin6_v4_any, sin6_v4_lo, sin6_v4_rem, rsin6; const struct sockaddr_in6 *sin6p; const struct { const struct sockaddr_in6 *addr; int res; int flags; const struct sockaddr_in6 *name; } bind_array[] = { { NULL, 0, F_ANY, &sin6_any }, { &sin6_any, 0, F_ANY, &sin6_any }, { &sin6_any_scope, 0, F_ANY, &sin6_any }, { &sin6_lo, 0, 0, &sin6_lo }, { &sin6_lo_scope, 0, 0, &sin6_lo }, { &sin6_ll_lo, 0, 0, &sin6_ll_lo }, { &sin6_v4_lo, 0, F_V4, &sin6_v4_lo }, { &sin6_rem, EADDRNOTAVAIL }, { &sin6_ll_all, EADDRNOTAVAIL }, { &sin6_ll_rem, EADDRNOTAVAIL }, { &sin6_ll_kame, EINVAL }, { &sin6_ll_bad, ENXIO }, { &sin6_v4_any, EADDRNOTAVAIL }, { &sin6_v4_rem, EADDRNOTAVAIL }, /* The following entry MUST be last. */ { &sin6_ll_mix, EADDRNOTAVAIL }, }, *bp; const struct { const struct sockaddr_in6 *addr; int res; int flags; const struct sockaddr_in6 *name; } conn_array[] = { { &sin6_any, EHOSTUNREACH, 0 }, { &sin6_any_scope, EHOSTUNREACH, 0 }, { &sin6_ll_kame, EINVAL, 0 }, { &sin6_ll_bad, ENXIO, 0 }, { &sin6_v4_any, EHOSTUNREACH, F_V4 }, { &sin6_lo, 0, 0, &sin6_lo }, { &sin6_lo_scope, 0, 0, &sin6_lo }, { &sin6_ll_all, 0, 0, &sin6_ll_lo }, { &sin6_ll_lo, 0, 0, &sin6_ll_lo }, { &sin6_v4_lo, 0, F_V4, &sin6_v4_lo }, { &sin6_rem, 0, F_REM, &sin6_rem }, { &sin6_ll_rem, 0, F_REM, &sin6_ll_rem }, { &sin6_v4_rem, 0, F_V4|F_REM, &sin6_v4_rem }, /* The following entry MUST be last. */ { &sin6_ll_mix, 0, F_REM|F_MIX, &sin6_ll_mix }, }, *cp; struct ifaddrs *ifa, *ifp, *ifp2; struct in6_ifreq ifr; char name[IF_NAMESIZE], buf[1]; socklen_t len; uint32_t port; unsigned int i, j, ifindex, ifindex2, have_mix, found; int r, fd, fd2, fd3, val, sfl, exp, link_state; ifindex = if_nametoindex(LOOPBACK_IFNAME); if (ifindex == 0) e(0); /* An IPv6 'any' address - ::0. */ memset(&sin6_any, 0, sizeof(sin6_any)); sin6_any.sin6_len = sizeof(sin6_any); sin6_any.sin6_family = AF_INET6; memcpy(&sin6_any.sin6_addr, &in6addr_any, sizeof(sin6_any.sin6_addr)); /* An IPv6 'any' address, but with a bad scope ID set. */ memcpy(&sin6_any_scope, &sin6_any, sizeof(sin6_any_scope)); sin6_any_scope.sin6_scope_id = BAD_IFINDEX; /* An IPv6 loopback address - ::1. */ memcpy(&sin6_lo, &sin6_any, sizeof(sin6_lo)); memcpy(&sin6_lo.sin6_addr, &in6addr_loopback, sizeof(sin6_lo.sin6_addr)); /* An IPv6 loopback address, but with a bad scope ID set. */ memcpy(&sin6_lo_scope, &sin6_lo, sizeof(sin6_lo_scope)); sin6_lo_scope.sin6_scope_id = BAD_IFINDEX; /* An IPv6 link-local address without scope - fe80::1. */ memcpy(&sin6_ll_all, &sin6_any, sizeof(sin6_ll_all)); if (inet_pton(AF_INET6, LOOPBACK_IPV6_LL, &sin6_ll_all.sin6_addr) != 1) e(0); /* An IPv6 link-local address with the loopback scope - fe80::1%lo0. */ memcpy(&sin6_ll_lo, &sin6_ll_all, sizeof(sin6_ll_lo)); sin6_ll_lo.sin6_scope_id = ifindex; /* An unassigned IPv6 link-local address - fe80::ffff%lo0. */ memcpy(&sin6_ll_rem, &sin6_ll_lo, sizeof(sin6_ll_rem)); if (inet_pton(AF_INET6, TEST_BLACKHOLE_IPV6_LL, &sin6_ll_rem.sin6_addr) != 1) e(0); /* A KAME-style IPv6 link-local loopback address - fe80:ifindex::1. */ memcpy(&sin6_ll_kame, &sin6_ll_all, sizeof(sin6_ll_kame)); sin6_ll_kame.sin6_addr.s6_addr[2] = ifindex >> 8; sin6_ll_kame.sin6_addr.s6_addr[3] = ifindex % 0xff; /* An IPv6 link-local address with a bad scope - fe80::1%. */ memcpy(&sin6_ll_bad, &sin6_ll_all, sizeof(sin6_ll_bad)); sin6_ll_bad.sin6_scope_id = BAD_IFINDEX; /* A global IPv6 address not assigned to any interface - ::2. */ memcpy(&sin6_rem, &sin6_any, sizeof(sin6_rem)); if (inet_pton(AF_INET6, TEST_BLACKHOLE_IPV6, &sin6_rem.sin6_addr) != 1) e(0); /* An IPv4-mapped IPv6 address for 'any' - ::ffff:0.0.0.0. */ memcpy(&sin6_v4_any, &sin6_any, sizeof(sin6_v4_any)); if (inet_pton(AF_INET6, "::ffff:0:0", &sin6_v4_any.sin6_addr) != 1) e(0); /* An IPv4-mapped IPv6 loopback address - ::ffff:127.0.0.1. */ memcpy(&sin6_v4_lo, &sin6_any, sizeof(sin6_v4_lo)); if (inet_pton(AF_INET6, "::ffff:"LOOPBACK_IPV4, &sin6_v4_lo.sin6_addr) != 1) e(0); /* An unassigned IPv4-mapped IPv6 address - ::ffff:127.255.0.254. */ memcpy(&sin6_v4_rem, &sin6_any, sizeof(sin6_v4_rem)); if (inet_pton(AF_INET6, "::ffff:"TEST_BLACKHOLE_IPV4, &sin6_v4_rem.sin6_addr) != 1) e(0); /* * An IPv6 link-local address with a scope for another interface, for * example fe80::1%em0. Since no other interfaces may be present, we * may not be able to generate such an address. */ have_mix = 0; for (i = 1; i < BAD_IFINDEX; i++) { if (if_indextoname(i, name) == NULL) { if (errno != ENXIO) e(0); continue; } if (!strcmp(name, LOOPBACK_IFNAME)) continue; /* Found one! */ memcpy(&sin6_ll_mix, &sin6_ll_all, sizeof(sin6_ll_mix)); sin6_ll_mix.sin6_scope_id = i; have_mix = 1; break; } /* * Test a whole range of combinations of local and remote addresses, * both for TCP and UDP, and for UDP both for connect+send and sendto. * Not all addresses and not all combinations are compatible, and that * is exactly what we want to test. We first test binding to local * addresses. Then we test connect (and for UDP, on success, send) * with remote addresses on those local addresses that could be bound * to. Finally, for UDP sockets, we separately test sendto. */ for (i = 0; i < __arraycount(bind_array) - !have_mix; i++) { bp = &bind_array[i]; /* Test bind(2) and getsockname(2). */ if (bind_array[i].addr != NULL) { if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); r = bind(fd, (struct sockaddr *)bp->addr, sizeof(*bp->addr)); /* Did the bind(2) call produce the expected result? */ if (r == 0) { if (bp->res != 0) e(0); } else if (r != -1 || bp->res != errno) e(0); /* The rest is for successful bind(2) calls. */ if (r != 0) { if (close(fd) != 0) e(0); continue; } /* Get the bound address. */ len = sizeof(sin6); if (getsockname(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); /* A port must be set. Clear it for the comparison. */ if ((sin6.sin6_port == 0) == (type != SOCK_RAW)) e(0); sin6.sin6_port = 0; if (memcmp(&sin6, bp->name, sizeof(sin6)) != 0) e(0); if (close(fd) != 0) e(0); } /* Test connect(2), send(2), and getpeername(2). */ for (j = 0; j < __arraycount(conn_array) - !have_mix; j++) { cp = &conn_array[j]; /* * We cannot test remote addresses without having bound * to a local address, because we may end up generating * external traffic as a result. */ if ((bp->flags & F_ANY) && (cp->flags & F_REM)) continue; /* * Use non-blocking sockets only if connecting is going * to take a while before ultimately failing; TCP only. */ sfl = ((cp->flags & F_REM) && (type == SOCK_STREAM)) ? SOCK_NONBLOCK : 0; if ((fd = socket(AF_INET6, type | sfl, protocol)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); if (bp->addr != NULL) { if (bind(fd, (struct sockaddr *)bp->addr, sizeof(*bp->addr)) != 0) e(0); len = sizeof(sin6); if (getsockname(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); port = sin6.sin6_port; } else port = 0; memcpy(&sin6, cp->addr, sizeof(sin6)); sin6.sin6_port = htons(TEST_PORT_A); if ((exp = cp->res) == 0 && type == SOCK_STREAM) { if (cp->flags & F_REM) exp = EINPROGRESS; if (cp->flags & F_MIX) exp = EHOSTUNREACH; } /* * The IPv4/IPv6 mismatch check precedes most other * checks, but (currently) not the bad-scope-ID check. */ if (exp != ENXIO && !(bp->flags & F_ANY) && ((bp->flags ^ cp->flags) & F_V4)) exp = EINVAL; /* * Create a listening or receiving socket if we expect * the test to succeed and operate on a loopback target * so that we can test addresses on that end as well. */ if (exp == 0 && !(cp->flags & F_REM)) { if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); val = 0; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); val = 1; if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); memcpy(&rsin6, cp->name, sizeof(rsin6)); rsin6.sin6_port = htons(TEST_PORT_A); if (bind(fd2, (struct sockaddr *)&rsin6, sizeof(rsin6)) != 0) e(0); if (type == SOCK_STREAM && listen(fd2, 1) != 0) e(0); } else fd2 = -1; r = connect(fd, (struct sockaddr *)&sin6, sizeof(sin6)); if (r == 0) { if (exp != 0) e(0); } else if (r != -1 || exp != errno) e(0); if (r != 0) { if (close(fd) != 0) e(0); continue; } /* * Connecting should always assign a local address if * no address was assigned, even if a port was assigned * already. In the latter case, the port number must * obviously not change. Test getsockname(2) again, if * we can. */ len = sizeof(sin6); if (getsockname(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (type != SOCK_RAW) { if (sin6.sin6_port == 0) e(0); if (port != 0 && port != sin6.sin6_port) e(0); } else if (sin6.sin6_port != 0) e(0); port = sin6.sin6_port; if (!(bp->flags & F_ANY)) sin6p = bp->name; else if (!(cp->flags & F_REM)) sin6p = cp->name; else sin6p = NULL; /* can't test: may vary */ if (sin6p != NULL) { sin6.sin6_port = 0; if (memcmp(&sin6, sin6p, sizeof(sin6)) != 0) e(0); } /* * Test getpeername(2). It should always be the * "normalized" version of the target address. */ len = sizeof(sin6); if (getpeername(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (type != SOCK_RAW) { if (sin6.sin6_port != htons(TEST_PORT_A)) e(0); } else { if (sin6.sin6_port != 0) e(0); } sin6.sin6_port = 0; if (memcmp(&sin6, cp->name, sizeof(sin6)) != 0) e(0); /* Test send(2) on UDP sockets. */ if (type != SOCK_STREAM) { r = send(fd, "A", 1, 0); /* * For remote (rejected) addresses and scope * mixing, actual send calls may fail after the * connect succeeded. */ if (r == -1 && !(cp->flags & (F_REM | F_MIX))) e(0); else if (r != -1 && r != 1) e(0); if (r != 1 && fd2 != -1) { if (close(fd2) != 0) e(0); fd2 = -1; } } if (fd2 == -1) { if (close(fd) != 0) e(0); continue; } /* * The connect or send call succeeded, so we should now * be able to check the other end. */ if (type == SOCK_STREAM) { /* Test accept(2). */ len = sizeof(sin6); if ((fd3 = accept(fd2, (struct sockaddr *)&sin6, &len)) < 0) e(0); if (len != sizeof(sin6)) e(0); if (close(fd2) != 0) e(0); if (sin6.sin6_port != port) e(0); sin6.sin6_port = 0; if (memcmp(&sin6, sin6p, sizeof(sin6)) != 0) e(0); /* Test getpeername(2). */ if (getpeername(fd3, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_port != port) e(0); sin6.sin6_port = 0; if (memcmp(&sin6, sin6p, sizeof(sin6)) != 0) e(0); /* Test getsockname(2). */ if (getsockname(fd3, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_port != htons(TEST_PORT_A)) e(0); sin6.sin6_port = 0; if (memcmp(&sin6, cp->name, sizeof(sin6)) != 0) e(0); if (close(fd3) != 0) e(0); } else { /* Test recvfrom(2). */ len = sizeof(sin6); if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &len) != 1) e(0); if (buf[0] != 'A') e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_port != port) e(0); sin6.sin6_port = 0; if (memcmp(&sin6, sin6p, sizeof(sin6)) != 0) e(0); if (close(fd2) != 0) e(0); } if (close(fd) != 0) e(0); } if (type == SOCK_STREAM) continue; /* Test sendto(2). */ for (j = 0; j < __arraycount(conn_array) - !have_mix; j++) { cp = &conn_array[j]; /* * We cannot test remote addresses without having bound * to a local address, because we may end up generating * external traffic as a result. */ if ((bp->flags & F_ANY) && (cp->flags & F_REM)) continue; if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); if (bp->addr != NULL) { if (bind(fd, (struct sockaddr *)bp->addr, sizeof(*bp->addr)) != 0) e(0); len = sizeof(sin6); if (getsockname(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); port = sin6.sin6_port; } else port = 0; memcpy(&sin6, cp->addr, sizeof(sin6)); if (type != SOCK_RAW) sin6.sin6_port = htons(TEST_PORT_B); if ((exp = cp->res) == 0) { if (cp->flags & (F_REM | F_MIX)) exp = EHOSTUNREACH; } /* * The IPv4/IPv6 mismatch check precedes most other * checks, but (currently) not the bad-scope-ID check. */ if (exp != ENXIO && !(bp->flags & F_ANY) && ((bp->flags ^ cp->flags) & F_V4)) exp = EINVAL; /* * If we expect the sendto(2) call to succeed and to be * able to receive the packet, create a receiving * socket to test recvfrom(2) addresses. */ if (exp == 0 && !(cp->flags & F_REM)) { if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); val = 0; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); val = 1; if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); memcpy(&rsin6, cp->name, sizeof(rsin6)); if (type != SOCK_RAW) rsin6.sin6_port = htons(TEST_PORT_B); if (bind(fd2, (struct sockaddr *)&rsin6, sizeof(rsin6)) != 0) e(0); } else fd2 = -1; r = sendto(fd, "B", 1, 0, (struct sockaddr *)&sin6, sizeof(sin6)); if (r != 1) { if (r != -1 || exp != errno) e(0); if (close(fd) != 0) e(0); continue; } if (exp != 0) e(0); /* * The sendto(2) call should assign a local port to the * socket if none was assigned before, but it must not * assign a local address. */ len = sizeof(sin6); if (getsockname(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (type != SOCK_RAW) { if (sin6.sin6_port == 0) e(0); if (port != 0 && port != sin6.sin6_port) e(0); } else if (sin6.sin6_port != 0) e(0); port = sin6.sin6_port; sin6.sin6_port = 0; if (memcmp(&sin6, bp->name, sizeof(sin6)) != 0) e(0); if (fd2 != -1) { /* Test recvfrom(2) on the receiving socket. */ len = sizeof(sin6); if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &len) != 1) e(0); if (buf[0] != 'B') e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_port != port) e(0); sin6.sin6_port = 0; if (bp->flags & F_ANY) sin6p = cp->name; else sin6p = bp->name; if (memcmp(&sin6, sin6p, sizeof(sin6)) != 0) e(0); if (close(fd2) != 0) e(0); } if (close(fd) != 0) e(0); } } /* * Test that scoped addresses actually work as expected. For this we * need two interfaces with assigned link-local addresses, one of which * being the loopback interface. Start by finding another one. */ if (getifaddrs(&ifa) != 0) e(0); found = 0; for (ifp = ifa; ifp != NULL; ifp = ifp->ifa_next) { if (strcmp(ifp->ifa_name, LOOPBACK_IFNAME) == 0) continue; if (!(ifp->ifa_flags & IFF_UP) || ifp->ifa_addr == NULL || ifp->ifa_addr->sa_family != AF_INET6) continue; memcpy(&sin6, ifp->ifa_addr, sizeof(sin6)); if (!IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) continue; /* * Not only the interface, but also the link has to be up for * this to work. lwIP will drop all packets, including those * sent to locally assigned addresses, if the link is down. * Of course, figuring out whether the interface link is down * is by no means convenient, especially if we want to do it * right (i.e., not rely on getifaddrs' address sorting). */ link_state = LINK_STATE_DOWN; for (ifp2 = ifa; ifp2 != NULL; ifp2 = ifp2->ifa_next) { if (!strcmp(ifp2->ifa_name, ifp->ifa_name) && ifp2->ifa_addr != NULL && ifp2->ifa_addr->sa_family == AF_LINK && ifp2->ifa_data != NULL) { memcpy(&link_state, &((struct if_data *) ifp2->ifa_data)->ifi_link_state, sizeof(link_state)); break; } } if (link_state == LINK_STATE_DOWN) continue; /* * In addition, the address has to be in a state where it can * be used as source address. In practice, that means it must * not be in ND6 duplicated or tentative state. */ memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifp->ifa_name, sizeof(ifr.ifr_name)); memcpy(&ifr.ifr_addr, &sin6, sizeof(sin6)); if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); if (ioctl(fd, SIOCGIFAFLAG_IN6, &ifr) != 0) e(0); if (close(fd) != 0) e(0); if (ifr.ifr_ifru.ifru_flags6 & (IN6_IFF_DUPLICATED | IN6_IFF_TENTATIVE)) continue; /* Compensate for poor decisions made by the KAME project. */ inet6_getscopeid(&sin6, INET6_IS_ADDR_LINKLOCAL); if (sin6.sin6_scope_id == 0 || sin6.sin6_scope_id == ifindex) e(0); found = 1; break; } freeifaddrs(ifa); /* * If no second interface with a link-local address was found, we * cannot perform the rest of this subtest. */ if (!found) return; /* * Create one socket that binds to the link-local address of the * non-loopback interface. The main goal of this subtest is to ensure * that traffic directed to that same link-local address but with the * loopback scope ID does not arrive on this socket. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); len = sizeof(sin6); if (getsockname(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); ifindex2 = sin6.sin6_scope_id; if (type == SOCK_STREAM) { if (listen(fd, 2) != 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); /* Connecting to the loopback-scope address should time out. */ signal(SIGALRM, socklib_got_signal); alarm(1); sin6.sin6_scope_id = ifindex; if (connect(fd2, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EINTR) e(0); if (close(fd2) != 0) e(0); /* Connecting to the real interface's address should work. */ if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); sin6.sin6_scope_id = ifindex2; if (connect(fd2, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); if (close(fd2) != 0) e(0); } else { /* * First connect+send. Sending to the loopback-scope address * should result in a rejected packet. */ if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); sin6.sin6_scope_id = ifindex; if (connect(fd2, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); if (send(fd2, "C", 1, 0) != -1) e(0); if (errno != EHOSTUNREACH) e(0); if (close(fd2) != 0) e(0); /* Sending to the real-interface address should work. */ if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); sin6.sin6_scope_id = ifindex2; if (connect(fd2, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); if (send(fd2, "D", 1, 0) != 1) e(0); if (close(fd2) != 0) e(0); /* * Then sendto. Sending to the loopback-scope address should * result in a rejected packet. */ if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); sin6.sin6_scope_id = ifindex; if (sendto(fd2, "E", 1, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EHOSTUNREACH) e(0); if (close(fd2) != 0) e(0); /* Sending to the real-interface address should work. */ if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); sin6.sin6_scope_id = ifindex2; if (sendto(fd2, "F", 1, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 1) e(0); if (close(fd2) != 0) e(0); len = sizeof(sin6); if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &len) != 1) e(0); if (buf[0] != 'D') e(0); if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &len) != 1) e(0); if (buf[0] != 'F') e(0); } if (close(fd) != 0) e(0); } /* * Test multicast support for the given socket type, which may be SOCK_DGRAM or * SOCK_RAW. */ void socklib_test_multicast(int type, int protocol) { struct sockaddr_in sinA, sinB, sin_array[3]; struct sockaddr_in6 sin6A, sin6B, sin6_array[3]; struct ip_mreq imr; struct ipv6_mreq ipv6mr; struct in6_pktinfo ipi6; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; socklen_t len, hdrlen; unsigned int count, ifindex, ifindex2; union { struct cmsghdr cmsg; char buf[256]; } control; char buf[sizeof(struct ip) + 1], *buf2, name[IF_NAMESIZE]; uint8_t byte, ttl; int i, j, r, fd, fd2, val; /* * Start with testing join/leave mechanics, for both IPv4 and IPv6. * Note that we cannot test specifying no interface along with a * multicast address (except for scoped IPv6 addresses), because the * auto-selected interface is likely a public one, and joining the * group will thus create external traffic, which is generally * something we want to avoid in the tests. */ if ((fd = socket(AF_INET, type, protocol)) < 0) e(0); memset(&imr, 0, sizeof(imr)); /* Basic join-leave combo. */ imr.imr_multiaddr.s_addr = inet_addr(TEST_MULTICAST_IPV4); imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); /* Joining the same multicast group twice is an error. */ if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != EEXIST) e(0); /* If an interface address is specified, it must match an interface. */ imr.imr_interface.s_addr = htonl(TEST_BLACKHOLE_IPV4); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); /* The given multicast address must be an actual multicast address. */ imr.imr_multiaddr.s_addr = htonl(INADDR_ANY); imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); imr.imr_multiaddr.s_addr = htonl(INADDR_LOOPBACK); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); /* Leaving a multicast group not joined is an error. */ imr.imr_multiaddr.s_addr = htonl(ntohl(inet_addr(TEST_MULTICAST_IPV4)) + 1); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != ESRCH) e(0); /* * When leaving a group, an interface address need not be specified, * even if one was specified when joining. As mentioned, we cannot * test joining the same address on multiple interfaces, though. */ imr.imr_multiaddr.s_addr = inet_addr(TEST_MULTICAST_IPV4); imr.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); /* There must be a reasonable per-socket group membership limit. */ imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); for (count = 0; count < IP_MAX_MEMBERSHIPS + 1; count++) { imr.imr_multiaddr.s_addr = htonl(ntohl(inet_addr(TEST_MULTICAST_IPV4)) + count); r = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); if (r != 0) { if (r != -1 || errno != ENOBUFS) e(0); break; } } if (count < 8 || count > IP_MAX_MEMBERSHIPS) e(0); /* Test leaving a group at the start of the per-socket list. */ imr.imr_multiaddr.s_addr = htonl(ntohl(inet_addr(TEST_MULTICAST_IPV4)) + count - 1); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != ESRCH) e(0); /* Test leaving a group in the middle of the per-socket list. */ imr.imr_multiaddr.s_addr = htonl(ntohl(inet_addr(TEST_MULTICAST_IPV4)) + count / 2); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != ESRCH) e(0); /* Test leaving a group at the end of the per-socket list. */ imr.imr_multiaddr.s_addr = inet_addr(TEST_MULTICAST_IPV4); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != -1) e(0); if (errno != ESRCH) e(0); if (close(fd) != 0) e(0); /* Still basic join/leave mechanics.. on to IPv6.. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&ipv6mr, 0, sizeof(ipv6mr)); /* Basic join-leave combo. */ ifindex = if_nametoindex(LOOPBACK_IFNAME); if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); /* Joining the same multicast group twice is an error. */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != EEXIST) e(0); /* If an interface index is specified, it must be valid. */ ipv6mr.ipv6mr_interface = BAD_IFINDEX; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ENXIO) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ENXIO) e(0); ipv6mr.ipv6mr_interface = 0x80000000UL | ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ENXIO) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ENXIO) e(0); /* The given multicast address must be an actual multicast address. */ ipv6mr.ipv6mr_interface = ifindex; memcpy(&ipv6mr.ipv6mr_multiaddr, &in6addr_any, sizeof(in6addr_any)); if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); memcpy(&ipv6mr.ipv6mr_multiaddr, &in6addr_loopback, sizeof(in6addr_loopback)); if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); /* Leaving a multicast group not joined is an error. */ if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_multiaddr.s6_addr[15]++; if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ESRCH) e(0); /* * When leaving a group, an interface index need not be specified, * even if one was specified when joining. If one is specified, it * must match, though. As mentioned, we cannot test joining the same * address on multiple interfaces, though. */ ipv6mr.ipv6mr_multiaddr.s6_addr[15]--; ipv6mr.ipv6mr_interface = ifindex + 1; /* lazy: may or may not exist */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ENXIO && errno != ESRCH) e(0); ipv6mr.ipv6mr_interface = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); /* For link-local addresses, an interface must always be specified. */ if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6_LL, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); ipv6mr.ipv6mr_interface = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); /* IPv4-mapped IPv6 multicast addresses are currently not supported. */ val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); if (inet_pton(AF_INET6, "::ffff:"TEST_MULTICAST_IPV4, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); /* * There must be a reasonable per-socket group membership limit. * Apparently there is no IPv6 equivalent of IP_MAX_MEMBERSHIPS.. */ if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; for (count = 0; count < IP_MAX_MEMBERSHIPS + 1; count++) { r = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)); if (r != 0) { if (r != -1 || errno != ENOBUFS) e(0); break; } ipv6mr.ipv6mr_multiaddr.s6_addr[15]++; } if (count < 8 || count > IP_MAX_MEMBERSHIPS) e(0); /* Test leaving a group at the start of the per-socket list. */ ipv6mr.ipv6mr_multiaddr.s6_addr[15]--; if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ESRCH) e(0); /* Test leaving a group in the middle of the per-socket list. */ ipv6mr.ipv6mr_multiaddr.s6_addr[15] -= count / 2; if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ESRCH) e(0); /* Test leaving a group at the end of the per-socket list. */ if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != ESRCH) e(0); if (close(fd) != 0) e(0); /* * Test sending multicast packets, multicast transmission options, and * basic receipt. Note that we cannot test IP(V6)_MULTICAST_LOOP * because no extra duplicates are generated on loopback interfaces. */ if ((fd = socket(AF_INET, type, protocol)) < 0) e(0); /* For UDP, get an assigned port number. */ memset(&sinA, 0, sizeof(sinA)); sinA.sin_family = AF_INET; if (type == SOCK_DGRAM) { sinA.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); len = sizeof(sinA); if (getsockname(fd, (struct sockaddr *)&sinA, &len) != 0) e(0); } imr.imr_multiaddr.s_addr = inet_addr(TEST_MULTICAST_IPV4); imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if ((fd2 = socket(AF_INET, type, protocol)) < 0) e(0); /* Regular packet, default unicast TTL, sendto. */ sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (sendto(fd2, "A", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); /* Multicast packet, default multicast TTL, sendto. */ if (setsockopt(fd2, IPPROTO_IP, IP_MULTICAST_IF, &sinA.sin_addr, sizeof(sinA.sin_addr)) != 0) e(0); sinA.sin_addr.s_addr = inet_addr(TEST_MULTICAST_IPV4); if (sendto(fd2, "B", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); /* Multicast packet, custom multicast TTL, connect+send. */ byte = 123; if (setsockopt(fd2, IPPROTO_IP, IP_MULTICAST_TTL, &byte, sizeof(byte)) != 0) e(0); if (connect(fd2, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); if (send(fd2, "C", 1, 0) != 1) e(0); /* Receive and validate what we sent. */ len = sizeof(sinA); if (getsockname(fd2, (struct sockaddr *)&sinA, &len) != 0) e(0); len = sizeof(ttl); if (getsockopt(fd2, IPPROTO_IP, IP_TTL, &ttl, &len) != 0) e(0); val = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &val, sizeof(val)) != 0) e(0); hdrlen = (type == SOCK_RAW) ? sizeof(struct ip) : 0; memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = hdrlen + 1; for (i = 0; i < 3; ) { memset(&msg, 0, sizeof(msg)); msg.msg_name = &sinB; msg.msg_namelen = sizeof(sinB); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = sizeof(control); r = recvmsg(fd, &msg, 0); if (r < 0) e(0); if (msg.msg_namelen != sizeof(sinB)) e(0); /* * There is a tiny possibility that we receive other packets * on the receiving socket, as it is not bound to a particular * address, and there is currently no way to bind a socket to * a particular interface. We therefore skip packets not from * the sending socket, conveniently testing the accuracy of the * reported source address as a side effect. */ if (memcmp(&sinA, &sinB, sizeof(sinA))) continue; if (r != hdrlen + 1) e(0); if (buf[hdrlen] != 'A' + i) e(0); if (msg.msg_flags & MSG_BCAST) e(0); if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) e(0); if (cmsg->cmsg_level != IPPROTO_IP) e(0); if (cmsg->cmsg_type != IP_TTL) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(byte))) e(0); memcpy(&byte, CMSG_DATA(cmsg), sizeof(byte)); switch (i) { case 0: if (msg.msg_flags & MSG_MCAST) e(0); if (byte != ttl) e(0); break; case 1: if (!(msg.msg_flags & MSG_MCAST)) e(0); if (byte != 1) e(0); break; case 2: if (!(msg.msg_flags & MSG_MCAST)) e(0); if (byte != 123) e(0); break; } i++; } if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* Still the send tests, but now IPv6.. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); /* For UDP, get an assigned port number. */ memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = AF_INET6; if (type == SOCK_DGRAM) { if (bind(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); len = sizeof(sin6A); if (getsockname(fd, (struct sockaddr *)&sin6A, &len) != 0) e(0); } memcpy(&sin6B, &sin6A, sizeof(sin6B)); if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); /* Regular packet, default unicast TTL, sendto. */ if (inet_pton(AF_INET6, LOOPBACK_IPV6_LL, &sin6A.sin6_addr) != 1) e(0); sin6A.sin6_scope_id = ifindex; if (sendto(fd2, "D", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); /* Multicast packet, default multicast TTL, sendto. */ val = (int)ifindex; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &sin6A.sin6_addr) != 1) e(0); sin6A.sin6_scope_id = 0; if (sendto(fd2, "E", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); /* Multicast packet, custom multicast TTL, connect+send. */ val = 125; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) != 0) e(0); if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (send(fd2, "F", 1, 0) != 1) e(0); len = sizeof(sin6A); if (getsockname(fd2, (struct sockaddr *)&sin6A, &len) != 0) e(0); /* * Repeat the last two tests, but now with a link-local multicast * address. In particular link-local destination addresses do not need * a zone ID, and the system should be smart enough to pick the right * zone ID if an outgoing multicast interface is configured. Zone * violations should be detected and result in errors. */ if (close(fd2) != 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); if (bind(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); memcpy(&sin6A, &sin6B, sizeof(sin6A)); if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6_LL, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); /* Link-local multicast packet, sendto. */ val = (int)ifindex; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6_LL, &sin6A.sin6_addr) != 1) e(0); sin6A.sin6_scope_id = 0; if (sendto(fd2, "G", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); sin6A.sin6_scope_id = ifindex + 1; /* lazy: may or may not be valid */ if (sendto(fd2, "X", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != -1) e(0); if (errno != ENXIO && errno != EHOSTUNREACH) e(0); sin6A.sin6_scope_id = ifindex; if (sendto(fd2, "H", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); /* Link-local multicast packet, connect+send. */ sin6A.sin6_scope_id = 0; if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (send(fd2, "I", 1, 0) != 1) e(0); len = sizeof(sin6A); if (getsockname(fd2, (struct sockaddr *)&sin6A, &len) != 0) e(0); /* Receive and validate what we sent. */ len = sizeof(val); if (getsockopt(fd2, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, &len) != 0) e(0); ttl = (uint8_t)val; val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)) != 0) e(0); memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = 1; for (i = 0; i < 6; ) { memset(&msg, 0, sizeof(msg)); msg.msg_name = &sin6B; msg.msg_namelen = sizeof(sin6B); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = sizeof(control); r = recvmsg(fd, &msg, 0); if (r < 0) e(0); if (msg.msg_namelen != sizeof(sin6B)) e(0); if (memcmp(&sin6A, &sin6B, sizeof(sin6A))) continue; if (r != 1) e(0); if (buf[0] != 'D' + i) e(0); if (msg.msg_flags & MSG_BCAST) e(0); if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) e(0); if (cmsg->cmsg_level != IPPROTO_IPV6) e(0); if (cmsg->cmsg_type != IPV6_HOPLIMIT) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(val))) e(0); memcpy(&val, CMSG_DATA(cmsg), sizeof(val)); switch (i) { case 0: if (msg.msg_flags & MSG_MCAST) e(0); if (val != (int)ttl) e(0); break; case 1: case 3: case 4: case 5: if (!(msg.msg_flags & MSG_MCAST)) e(0); if (val != 1) e(0); break; case 2: if (!(msg.msg_flags & MSG_MCAST)) e(0); if (val != 125) e(0); break; } i++; } if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* * Test receiving multicast packets on a bound socket. We have already * tested receiving packets on an unbound socket, so we need not * incorporate that into this test as well. */ memset(sin_array, 0, sizeof(sin_array)); sin_array[0].sin_family = AF_INET; sin_array[0].sin_addr.s_addr = htonl(INADDR_LOOPBACK); sin_array[1].sin_family = AF_INET; sin_array[1].sin_addr.s_addr = inet_addr(TEST_MULTICAST_IPV4); sin_array[2].sin_family = AF_INET; sin_array[2].sin_addr.s_addr = htonl(ntohl(sin_array[1].sin_addr.s_addr) + 1); for (i = 0; i < __arraycount(sin_array); i++) { if ((fd = socket(AF_INET, type, protocol)) < 0) e(0); if (bind(fd, (struct sockaddr *)&sin_array[i], sizeof(sin_array[i])) != 0) e(0); imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); memcpy(&imr.imr_multiaddr, &sin_array[1].sin_addr, sizeof(imr.imr_multiaddr)); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); memcpy(&imr.imr_multiaddr, &sin_array[2].sin_addr, sizeof(imr.imr_multiaddr)); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); len = sizeof(sinA); if (getsockname(fd, (struct sockaddr *)&sinA, &len) != 0) e(0); if ((fd2 = socket(AF_INET, type, protocol)) < 0) e(0); if (setsockopt(fd2, IPPROTO_IP, IP_MULTICAST_IF, &imr.imr_interface, sizeof(imr.imr_interface)) != 0) e(0); for (j = 0; j < __arraycount(sin_array); j++) { memcpy(&sinA.sin_addr, &sin_array[j].sin_addr, sizeof(sinA.sin_addr)); byte = 'A' + j; if (sendto(fd2, &byte, sizeof(byte), 0, (struct sockaddr *)&sinA, sizeof(sinA)) != sizeof(byte)) e(0); } if (recv(fd, buf, sizeof(buf), 0) != hdrlen + sizeof(byte)) e(0); if (buf[hdrlen] != 'A' + i) e(0); if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); } /* Still testing receiving on bound sockets, now IPv6.. */ memset(sin6_array, 0, sizeof(sin6_array)); sin6_array[0].sin6_family = AF_INET6; memcpy(&sin6_array[0].sin6_addr, &in6addr_loopback, sizeof(sin6_array[0].sin6_addr)); sin6_array[1].sin6_family = AF_INET6; if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &sin6_array[1].sin6_addr) != 1) e(0); sin6_array[2].sin6_family = AF_INET6; if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6_LL, &sin6_array[2].sin6_addr) != 1) e(0); /* * As with unicast addresses, binding to link-local multicast addresses * requires a proper zone ID. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); if (bind(fd, (struct sockaddr *)&sin6_array[2], sizeof(sin6_array[2])) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); sin6_array[2].sin6_scope_id = BAD_IFINDEX; if (bind(fd, (struct sockaddr *)&sin6_array[2], sizeof(sin6_array[2])) != -1) e(0); if (errno != ENXIO) e(0); sin6_array[2].sin6_scope_id = ifindex; if (close(fd) != 0) e(0); for (i = 0; i < __arraycount(sin6_array); i++) { if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); if (bind(fd, (struct sockaddr *)&sin6_array[i], sizeof(sin6_array[i])) != 0) e(0); ipv6mr.ipv6mr_interface = ifindex; memcpy(&ipv6mr.ipv6mr_multiaddr, &sin6_array[1].sin6_addr, sizeof(ipv6mr.ipv6mr_multiaddr)); if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); memcpy(&ipv6mr.ipv6mr_multiaddr, &sin6_array[2].sin6_addr, sizeof(ipv6mr.ipv6mr_multiaddr)); if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); len = sizeof(sin6A); if (getsockname(fd, (struct sockaddr *)&sin6A, &len) != 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); val = (int)ifindex; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); for (j = 0; j < __arraycount(sin6_array); j++) { memcpy(&sin6A.sin6_addr, &sin6_array[j].sin6_addr, sizeof(sin6A.sin6_addr)); byte = 'A' + j; if (sendto(fd2, &byte, sizeof(byte), 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != sizeof(byte)) e(0); } if (recv(fd, buf, sizeof(buf), 0) != sizeof(byte)) e(0); if (buf[0] != 'A' + i) e(0); if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); } /* * Now test *sending* on a socket bound to a multicast address. The * multicast address must not show up as the packet's source address. * No actual multicast groups are involved here. */ if ((fd = socket(AF_INET, type, protocol)) < 0) e(0); if (bind(fd, (struct sockaddr *)&sin_array[1], sizeof(sin_array[1])) != 0) e(0); if ((fd2 = socket(AF_INET, type, protocol)) < 0) e(0); if (bind(fd2, (struct sockaddr *)&sin_array[0], sizeof(sin_array[0])) != 0) e(0); len = sizeof(sinA); if (getsockname(fd2, (struct sockaddr *)&sinA, &len) != 0) e(0); if (sendto(fd, "D", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); len = sizeof(sinB); if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sinB, &len) != hdrlen + 1) e(0); if (buf[hdrlen] != 'D') e(0); if (sinB.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* Sending from a bound socket, IPv6 version.. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); if (bind(fd, (struct sockaddr *)&sin6_array[1], sizeof(sin6_array[1])) != 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); if (bind(fd2, (struct sockaddr *)&sin6_array[0], sizeof(sin6_array[0])) != 0) e(0); len = sizeof(sin6A); if (getsockname(fd2, (struct sockaddr *)&sin6A, &len) != 0) e(0); if (sendto(fd, "E", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); len = sizeof(sin6B); if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sin6B, &len) != 1) e(0); if (buf[0] != 'E') e(0); if (!IN6_IS_ADDR_LOOPBACK(&sin6B.sin6_addr)) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* * A quick, partial test to see if connecting to a particular address * does not accidentally block packet receipt. What we do not test is * whether connecting does filter traffic from other sources. */ if ((fd = socket(AF_INET, type, protocol)) < 0) e(0); memset(&sinA, 0, sizeof(sinA)); sinA.sin_family = AF_INET; sinA.sin_addr.s_addr = inet_addr(TEST_MULTICAST_IPV4); if (bind(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); len = sizeof(sinA); if (getsockname(fd, (struct sockaddr *)&sinA, &len) != 0) e(0); imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); imr.imr_multiaddr.s_addr = sinA.sin_addr.s_addr; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if ((fd2 = socket(AF_INET, type, protocol)) < 0) e(0); memset(&sinB, 0, sizeof(sinB)); sinB.sin_family = AF_INET; sinB.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bind(fd2, (struct sockaddr *)&sinB, sizeof(sinB)) != 0) e(0); len = sizeof(sinB); if (getsockname(fd2, (struct sockaddr *)&sinB, &len) != 0) e(0); if (connect(fd, (struct sockaddr *)&sinB, sizeof(sinB)) != 0) e(0); /* Note that binding to a particular source address is not enough! */ if (setsockopt(fd2, IPPROTO_IP, IP_MULTICAST_IF, &imr.imr_interface, sizeof(imr.imr_interface)) != 0) e(0); if (sendto(fd2, "F", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != hdrlen + 1) e(0); if (buf[hdrlen] != 'F') e(0); if (close(fd) != 0) e(0); if (close(fd2) != 0) e(0); /* Also try connecting with IPv6. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = AF_INET6; if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6_LL, &sin6A.sin6_addr) != 1) e(0); sin6A.sin6_scope_id = ifindex; if (bind(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); len = sizeof(sin6A); if (getsockname(fd, (struct sockaddr *)&sin6A, &len) != 0) e(0); ipv6mr.ipv6mr_interface = ifindex; memcpy(&ipv6mr.ipv6mr_multiaddr, &sin6A.sin6_addr, sizeof(ipv6mr.ipv6mr_multiaddr)); if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&sin6B, 0, sizeof(sin6B)); sin6B.sin6_family = AF_INET6; memcpy(&sin6B.sin6_addr, &in6addr_loopback, sizeof(sin6B.sin6_addr)); if (bind(fd2, (struct sockaddr *)&sin6B, sizeof(sin6B)) != 0) e(0); len = sizeof(sin6B); if (getsockname(fd2, (struct sockaddr *)&sin6B, &len) != 0) e(0); if (connect(fd, (struct sockaddr *)&sin6B, sizeof(sin6B)) != 0) e(0); /* Unlike with IPv4, here the interface is implied by the zone. */ if (sendto(fd2, "G", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'G') e(0); if (close(fd) != 0) e(0); if (close(fd2) != 0) e(0); /* * Test multiple receivers. For UDP, we need to set the SO_REUSEADDR * option on all sockets for this to be guaranteed to work. */ if ((fd = socket(AF_INET, type, protocol)) < 0) e(0); if ((fd2 = socket(AF_INET, type, protocol)) < 0) e(0); memset(&sinA, 0, sizeof(sinA)); sinA.sin_family = AF_INET; if (type == SOCK_DGRAM) { if (bind(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); len = sizeof(sinA); if (getsockname(fd, (struct sockaddr *)&sinA, &len) != 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (bind(fd2, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); } imr.imr_multiaddr.s_addr = inet_addr(TEST_MULTICAST_IPV4); imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd2, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr.imr_interface, sizeof(imr.imr_interface)) != 0) e(0); sinA.sin_addr.s_addr = imr.imr_multiaddr.s_addr; if (sendto(fd, "H", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != hdrlen + 1) e(0); if (buf[hdrlen] != 'H') e(0); if (recv(fd2, buf, sizeof(buf), 0) != hdrlen + 1) e(0); if (buf[hdrlen] != 'H') e(0); /* * Also test with a larger buffer, to ensure that packet duplication * actually works properly. As of writing, we need to patch lwIP to * make this work at all. */ len = 8000; if ((buf2 = malloc(hdrlen + len + 1)) == NULL) e(0); buf2[len - 1] = 'I'; if (sendto(fd, buf2, len, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != len) e(0); buf2[hdrlen + len - 1] = '\0'; if (recv(fd, buf2, hdrlen + len + 1, 0) != hdrlen + len) e(0); if (buf2[hdrlen + len - 1] != 'I') e(0); buf2[hdrlen + len - 1] = '\0'; if (recv(fd2, buf2, hdrlen + len + 1, 0) != hdrlen + len) e(0); if (buf2[hdrlen + len - 1] != 'I') e(0); free(buf2); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* Multiple-receivers test, IPv6 version. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = AF_INET6; if (type == SOCK_DGRAM) { if (bind(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); len = sizeof(sin6A); if (getsockname(fd, (struct sockaddr *)&sin6A, &len) != 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (bind(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); } if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (setsockopt(fd2, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); val = (int)ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); memcpy(&sin6A.sin6_addr, &ipv6mr.ipv6mr_multiaddr, sizeof(sin6A.sin6_addr)); if (sendto(fd, "J", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'J') e(0); if (recv(fd2, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'J') e(0); len = 8000; if ((buf2 = malloc(len + 1)) == NULL) e(0); buf2[len - 1] = 'K'; if (sendto(fd, buf2, len, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != len) e(0); buf2[len - 1] = '\0'; if (recv(fd, buf2, len + 1, 0) != len) e(0); if (buf2[len - 1] != 'K') e(0); buf2[len - 1] = '\0'; if (recv(fd2, buf2, len + 1, 0) != len) e(0); if (buf2[len - 1] != 'K') e(0); free(buf2); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* * Test proper multicast group departure. This test relies on the fact * that actual group membership is not checked on arrival of a * multicast-destined packet, so that membership of one socket can be * tested by another socket sending packets to itself while having * joined a different group. We test both explicit group departure * and implicit departure on close. */ if ((fd = socket(AF_INET, type, protocol)) < 0) e(0); if ((fd2 = socket(AF_INET, type, protocol)) < 0) e(0); memset(&sinA, 0, sizeof(sinA)); sinA.sin_family = AF_INET; if (type == SOCK_DGRAM) { if (bind(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); len = sizeof(sinA); if (getsockname(fd, (struct sockaddr *)&sinA, &len) != 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (bind(fd2, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); } imr.imr_multiaddr.s_addr = inet_addr(TEST_MULTICAST_IPV4); imr.imr_interface.s_addr = htonl(INADDR_LOOPBACK); if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); imr.imr_multiaddr.s_addr = htonl(ntohl(imr.imr_multiaddr.s_addr) + 1); if (setsockopt(fd2, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr.imr_interface, sizeof(imr.imr_interface)) != 0) e(0); sinA.sin_addr.s_addr = imr.imr_multiaddr.s_addr; if (sendto(fd, "L", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); if (recv(fd2, buf, sizeof(buf), 0) != hdrlen + 1) e(0); if (buf[hdrlen] != 'L') e(0); if (setsockopt(fd2, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (sendto(fd, "M", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != hdrlen + 1) e(0); if (buf[hdrlen] != 'L') e(0); if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (setsockopt(fd2, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) != 0) e(0); if (sendto(fd, "N", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); if (recv(fd2, buf, sizeof(buf), 0) != hdrlen + 1) e(0); if (buf[hdrlen] != 'N') e(0); if (close(fd2) != 0) e(0); if (sendto(fd, "O", 1, 0, (struct sockaddr *)&sinA, sizeof(sinA)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != hdrlen + 1) e(0); if (buf[hdrlen] != 'N') e(0); if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (close(fd) != 0) e(0); /* Multicast group departure, now IPv6.. this is getting boring. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = AF_INET6; if (type == SOCK_DGRAM) { if (bind(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); len = sizeof(sin6A); if (getsockname(fd, (struct sockaddr *)&sin6A, &len) != 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (bind(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); } if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); ipv6mr.ipv6mr_multiaddr.s6_addr[15]++; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); val = (int)ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); memcpy(&sin6A.sin6_addr, &ipv6mr.ipv6mr_multiaddr, sizeof(sin6A.sin6_addr)); if (sendto(fd, "P", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); if (recv(fd2, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'P') e(0); if (setsockopt(fd2, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (sendto(fd, "Q", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'P') e(0); if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (setsockopt(fd2, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if (sendto(fd, "R", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); if (recv(fd2, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'R') e(0); if (close(fd2) != 0) e(0); if (sendto(fd, "S", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'R') e(0); if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (close(fd) != 0) e(0); /* * Lastly, some IPv6-only tests. */ /* * Test that IPV6_PKTINFO overrides IPV6_MULTICAST_IF. For this we * need two valid interface indices. If we cannot find a second one, * simply test that the IPV6_PKTINFO information is used at all. */ for (ifindex2 = 1; ifindex2 < BAD_IFINDEX; ifindex2++) { if (if_indextoname(ifindex2, name) == NULL) { if (errno != ENXIO) e(0); continue; } if (strcmp(name, LOOPBACK_IFNAME)) break; } if (ifindex2 == BAD_IFINDEX) ifindex2 = 0; /* too bad; fallback mode */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = AF_INET6; if (type == SOCK_DGRAM) { if (bind(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); len = sizeof(sin6A); if (getsockname(fd, (struct sockaddr *)&sin6A, &len) != 0) e(0); } if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6_LL, &ipv6mr.ipv6mr_multiaddr) != 1) e(0); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != 0) e(0); if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); memcpy(&sin6A.sin6_addr, &ipv6mr.ipv6mr_multiaddr, sizeof(sin6A.sin6_addr)); val = (int)ifindex2; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); memset(&iov, 0, sizeof(iov)); iov.iov_base = "T"; iov.iov_len = 1; memset(&ipi6, 0, sizeof(ipi6)); memcpy(&ipi6.ipi6_addr, &in6addr_loopback, sizeof(ipi6.ipi6_addr)); ipi6.ipi6_ifindex = ifindex; control.cmsg.cmsg_len = CMSG_LEN(sizeof(ipi6)); control.cmsg.cmsg_level = IPPROTO_IPV6; control.cmsg.cmsg_type = IPV6_PKTINFO; memcpy(CMSG_DATA(&control.cmsg), &ipi6, sizeof(ipi6)); memset(&msg, 0, sizeof(msg)); msg.msg_name = &sin6A; msg.msg_namelen = sizeof(sin6A); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = control.cmsg.cmsg_len; if (sendmsg(fd2, &msg, 0) != 1) e(0); len = sizeof(sin6B); if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6B, &len) != 1) e(0); if (buf[0] != 'T') e(0); if (len != sizeof(sin6B)) e(0); if (sin6B.sin6_len != sizeof(sin6B)) e(0); if (sin6B.sin6_family != AF_INET6) e(0); if (memcmp(&sin6B.sin6_addr, &in6addr_loopback, sizeof(sin6B.sin6_addr)) != 0) e(0); if (close(fd2) != 0) e(0); /* Repeat the same test, but now with a sticky IPV6_PKTINFO setting. */ if ((fd2 = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&ipi6, 0, sizeof(ipi6)); memcpy(&ipi6.ipi6_addr, &in6addr_loopback, sizeof(ipi6.ipi6_addr)); ipi6.ipi6_ifindex = ifindex; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_PKTINFO, &ipi6, sizeof(ipi6)) != 0) e(0); val = (int)ifindex2; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_MULTICAST_IF, &val, sizeof(val)) != 0) e(0); if (sendto(fd2, "U", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 1) e(0); len = sizeof(sin6B); if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6B, &len) != 1) e(0); if (buf[0] != 'U') e(0); if (len != sizeof(sin6B)) e(0); if (sin6B.sin6_len != sizeof(sin6B)) e(0); if (sin6B.sin6_family != AF_INET6) e(0); if (memcmp(&sin6B.sin6_addr, &in6addr_loopback, sizeof(sin6B.sin6_addr)) != 0) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* * Test that invalid multicast addresses are not accepted anywhere. */ if ((fd = socket(AF_INET6, type, protocol)) < 0) e(0); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = AF_INET6; if (inet_pton(AF_INET6, TEST_MULTICAST_IPV6_BAD, &sin6A.sin6_addr) != 1) e(0); if (bind(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != -1) e(0); if (errno != EINVAL) e(0); sin6A.sin6_port = htons(TEST_PORT_A); if (connect(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != -1) e(0); if (errno != EINVAL) e(0); if (sendto(fd, "X", 1, 0, (struct sockaddr *)&sin6A, sizeof(sin6A)) != -1) e(0); if (errno != EINVAL) e(0); memcpy(&ipv6mr.ipv6mr_multiaddr, &sin6A.sin6_addr, sizeof(ipv6mr.ipv6mr_multiaddr)); ipv6mr.ipv6mr_interface = ifindex; if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6mr, sizeof(ipv6mr)) != -1) e(0); if (errno != EINVAL) e(0); if (close(fd) != 0) e(0); }