/* Advanced tests for TCP and UDP sockets (LWIP) - by D.C. van Moolenbroek */ /* * This is a somewhat random collection of in-depth tests, complementing the * more general functionality tests in test80 and test81. The overall test set * is still by no means expected to be "complete." The subtests are in random * order. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "socklib.h" #define ITERATIONS 1 static const enum state tcp_states[] = { S_NEW, S_N_SHUT_R, 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 }; static const int tcp_results[][__arraycount(tcp_states)] = { [C_ACCEPT] = { -EINVAL, -EINVAL, -EINVAL, -EAGAIN, -ECONNABORTED, -ECONNABORTED, -ECONNABORTED, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, }, [C_BIND] = { 0, 0, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, }, [C_CONNECT] = { -EINPROGRESS, -EINPROGRESS, -EINPROGRESS, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EALREADY, -EALREADY, -EINVAL, -EINVAL, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EINVAL, -EINVAL, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EISCONN, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, }, [C_GETPEERNAME] = { -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENOTCONN, -ENOTCONN, 0, 0, 0, 0, 0, 0, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, }, [C_GETSOCKNAME] = { 0, 0, 0, 0, -EINVAL, -EINVAL, -EINVAL, 0, 0, -EINVAL, -EINVAL, 0, 0, 0, 0, 0, 0, 0, 0, 0, -EINVAL, -EINVAL, 0, 0, 0, 0, 0, 0, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, }, [C_GETSOCKOPT_ERR] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ECONNRESET, -ECONNRESET, 0, -ECONNREFUSED, 0, }, [C_GETSOCKOPT_KA] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_GETSOCKOPT_RB] = { 0, 0, 0, 0, -ECONNRESET, -ECONNRESET, -ECONNRESET, 0, 0, -ECONNRESET, -ECONNRESET, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ECONNRESET, -ECONNRESET, 0, 0, 0, 0, 0, 0, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, -ECONNRESET, }, [C_IOCTL_NREAD] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, }, [C_LISTEN] = { 0, 0, 0, 0, 0, 0, 0, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, }, [C_RECV] = { -ENOTCONN, 0, -ENOTCONN, -ENOTCONN, 0, 0, 0, -EAGAIN, 0, 0, 0, -EAGAIN, -EAGAIN, 0, -EAGAIN, 0, -EAGAIN, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, -ECONNRESET, 0, -ECONNREFUSED, 0, }, [C_RECVFROM] = { -ENOTCONN, 0, -ENOTCONN, -ENOTCONN, 0, 0, 0, -EAGAIN, 0, 0, 0, -EAGAIN, -EAGAIN, 0, -EAGAIN, 0, -EAGAIN, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, -ECONNRESET, 0, -ECONNREFUSED, 0, }, [C_SEND] = { -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -EPIPE, -EPIPE, -EPIPE, -EAGAIN, -EAGAIN, -EPIPE, -EPIPE, 1, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, 1, 1, 1, -EPIPE, -EPIPE, -EPIPE, -EPIPE, -EPIPE, -EPIPE, -ECONNRESET, -ECONNRESET, -EPIPE, -ECONNREFUSED, -EPIPE, }, [C_SENDTO] = { -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -EPIPE, -EPIPE, -EPIPE, -EAGAIN, -EAGAIN, -EPIPE, -EPIPE, 1, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, 1, 1, 1, -EPIPE, -EPIPE, -EPIPE, -EPIPE, -EPIPE, -EPIPE, -ECONNRESET, -ECONNRESET, -EPIPE, -ECONNREFUSED, -EPIPE, }, [C_SELECT_R] = { 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, [C_SELECT_W] = { 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, [C_SELECT_X] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_BC] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_KA] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_L] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_RA] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SHUTDOWN_R] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SHUTDOWN_RW] = { -ENOTCONN, -ENOTCONN, -ENOTCONN, 0, -ENOTCONN, -ENOTCONN, -ENOTCONN, 0, 0, -ENOTCONN, -ENOTCONN, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENOTCONN, -ENOTCONN, 0, 0, 0, 0, 0, 0, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, }, [C_SHUTDOWN_W] = { -ENOTCONN, -ENOTCONN, -ENOTCONN, 0, -ENOTCONN, -ENOTCONN, -ENOTCONN, 0, 0, -ENOTCONN, -ENOTCONN, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENOTCONN, -ENOTCONN, 0, 0, 0, 0, 0, 0, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, }, }; /* * Set up a TCP socket file descriptor in the requested state and pass it to * socklib_sweep_call() along with local and remote addresses and their length. */ static int tcp_sweep(int domain, int type, int protocol, enum state state, enum call call) { struct sockaddr_in sinA, sinB, sinC, sinD; struct sockaddr_in6 sin6A, sin6B, sin6C, sin6D; struct sockaddr *addrA, *addrB, *addrC, *addrD; socklen_t addr_len, len; struct linger l; fd_set fds; char buf[1]; int r, fd, fd2, fd3, tmpfd, val; if (domain == AF_INET) { memset(&sin6A, 0, sizeof(sin6A)); sinA.sin_family = domain; sinA.sin_port = htons(TEST_PORT_A); sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK); memcpy(&sinB, &sinA, sizeof(sinB)); sinB.sin_port = htons(0); memcpy(&sinC, &sinA, sizeof(sinC)); sinC.sin_addr.s_addr = inet_addr(TEST_BLACKHOLE_IPV4); memcpy(&sinD, &sinA, sizeof(sinD)); sinD.sin_port = htons(TEST_PORT_B); addrA = (struct sockaddr *)&sinA; addrB = (struct sockaddr *)&sinB; addrC = (struct sockaddr *)&sinC; addrD = (struct sockaddr *)&sinD; addr_len = sizeof(sinA); } else { assert(domain == AF_INET6); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = domain; sin6A.sin6_port = htons(TEST_PORT_A); memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr)); memcpy(&sin6B, &sin6A, sizeof(sin6B)); sin6B.sin6_port = htons(0); memcpy(&sin6C, &sin6A, sizeof(sin6C)); if (inet_pton(domain, TEST_BLACKHOLE_IPV6, &sin6C.sin6_addr) != 1) e(0); memcpy(&sin6D, &sin6A, sizeof(sin6D)); sin6D.sin6_port = htons(TEST_PORT_B); addrA = (struct sockaddr *)&sin6A; addrB = (struct sockaddr *)&sin6B; addrC = (struct sockaddr *)&sin6C; addrD = (struct sockaddr *)&sin6D; addr_len = sizeof(sin6A); } /* Create a bound remote socket. */ if ((fd2 = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); if (bind(fd2, addrB, addr_len) != 0) e(0); len = addr_len; if (getsockname(fd2, addrB, &len) != 0) e(0); if (len != addr_len) e(0); if (listen(fd2, 1) != 0) e(0); fd3 = -1; switch (state) { case S_NEW: case S_N_SHUT_R: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (state == S_N_SHUT_R && shutdown(fd, SHUT_RD)) e(0); break; case S_BOUND: case S_LISTENING: case S_L_SHUT_R: case S_L_SHUT_W: case S_L_SHUT_RW: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (bind(fd, addrA, addr_len) != 0) e(0); if (state == S_BOUND) break; if (listen(fd, 1) != 0) e(0); switch (state) { case S_L_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; case S_L_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; case S_L_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; default: break; } break; case S_CONNECTING: case S_C_SHUT_R: case S_C_SHUT_W: case S_C_SHUT_RW: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); if (connect(fd, addrC, addr_len) != -1) e(0); if (errno != EINPROGRESS) e(0); switch (state) { case S_C_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; case S_C_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; case S_C_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; default: break; } break; case S_CONNECTED: case S_ACCEPTED: case S_SHUT_R: case S_SHUT_W: case S_SHUT_RW: case S_RSHUT_R: case S_RSHUT_W: case S_RSHUT_RW: case S_SHUT2_R: case S_SHUT2_W: case S_SHUT2_RW: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); if (connect(fd, addrB, addr_len) != -1) e(0); if (errno != EINPROGRESS) e(0); /* Just to make sure, wait for the socket to be acceptable. */ FD_ZERO(&fds); FD_SET(fd2, &fds); if (select(fd2 + 1, &fds, NULL, NULL, NULL) != 1) e(0); len = addr_len; if ((fd3 = accept(fd2, addrC, &len)) < 0) e(0); /* Just to make sure, wait for the socket to be connected. */ FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, NULL, &fds, NULL, NULL) != 1) e(0); switch (state) { case S_SHUT_R: case S_SHUT2_R: if (shutdown(fd, SHUT_RD)) e(0); break; case S_SHUT_W: case S_SHUT2_W: if (shutdown(fd, SHUT_WR)) e(0); break; case S_SHUT_RW: case S_SHUT2_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; default: break; } switch (state) { case S_RSHUT_R: case S_SHUT2_R: if (shutdown(fd3, SHUT_RD)) e(0); break; case S_RSHUT_W: case S_SHUT2_W: if (shutdown(fd3, SHUT_WR)) e(0); break; case S_RSHUT_RW: case S_SHUT2_RW: if (shutdown(fd3, SHUT_RDWR)) e(0); break; default: break; } if (state == S_ACCEPTED) { tmpfd = fd; fd = fd3; fd3 = tmpfd; } break; case S_PRE_EOF: case S_AT_EOF: case S_POST_EOF: case S_PRE_SHUT_R: case S_EOF_SHUT_R: case S_POST_SHUT_R: case S_PRE_SHUT_W: case S_EOF_SHUT_W: case S_POST_SHUT_W: case S_PRE_SHUT_RW: case S_EOF_SHUT_RW: case S_POST_SHUT_RW: case S_PRE_RESET: case S_AT_RESET: case S_POST_RESET: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); if (connect(fd, addrB, addr_len) != -1) e(0); if (errno != EINPROGRESS) e(0); /* Just to make sure, wait for the socket to be acceptable. */ FD_ZERO(&fds); FD_SET(fd2, &fds); if (select(fd2 + 1, &fds, NULL, NULL, NULL) != 1) e(0); len = addr_len; if ((fd3 = accept(fd2, addrC, &len)) < 0) e(0); if (send(fd3, "", 1, 0) != 1) e(0); switch (state) { case S_PRE_RESET: case S_AT_RESET: case S_POST_RESET: l.l_onoff = 1; l.l_linger = 0; if (setsockopt(fd3, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0); break; default: break; } if (close(fd3) != 0) e(0); fd3 = -1; /* Just to make sure, wait for the socket to receive data. */ FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); switch (state) { case S_AT_EOF: case S_EOF_SHUT_R: case S_EOF_SHUT_W: case S_EOF_SHUT_RW: case S_AT_RESET: if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); break; case S_POST_EOF: case S_POST_SHUT_R: case S_POST_SHUT_W: case S_POST_SHUT_RW: if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); if (recv(fd, buf, sizeof(buf), 0) != 0) e(0); break; case S_POST_RESET: if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); (void)recv(fd, buf, sizeof(buf), 0); break; default: break; } switch (state) { case S_PRE_SHUT_R: case S_EOF_SHUT_R: case S_POST_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; case S_PRE_SHUT_W: case S_EOF_SHUT_W: case S_POST_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; case S_PRE_SHUT_RW: case S_EOF_SHUT_RW: case S_POST_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; default: break; } break; case S_FAILED: case S_POST_FAILED: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); if (connect(fd, addrD, addr_len) != -1) e(0); if (errno != EINPROGRESS) e(0); FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); if (state == S_POST_FAILED) { if (recv(fd, buf, sizeof(buf), 0) != -1) e(0); if (errno != ECONNREFUSED) e(0); } break; default: fd = -1; e(0); } r = socklib_sweep_call(call, fd, addrA, addrB, addr_len); if (close(fd) != 0) e(0); if (fd2 != -1 && close(fd2) != 0) e(0); if (fd3 != -1 && close(fd3) != 0) e(0); return r; } static const enum state udp_states[] = { S_NEW, S_N_SHUT_R, S_N_SHUT_W, S_N_SHUT_RW, S_BOUND, S_CONNECTED, 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_RESET, S_AT_RESET, S_POST_RESET }; static const int udp_results[][__arraycount(udp_states)] = { [C_ACCEPT] = { -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, }, [C_BIND] = { 0, 0, 0, 0, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, -EINVAL, }, [C_CONNECT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_GETPEERNAME] = { -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_GETSOCKNAME] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_GETSOCKOPT_ERR] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_GETSOCKOPT_KA] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_GETSOCKOPT_RB] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_IOCTL_NREAD] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, }, [C_LISTEN] = { -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, }, [C_RECV] = { -EAGAIN, 0, -EAGAIN, 0, -EAGAIN, -EAGAIN, 0, -EAGAIN, 0, -EAGAIN, -EAGAIN, -EAGAIN, 0, -EAGAIN, 0, 1, -EAGAIN, -EAGAIN, }, [C_RECVFROM] = { -EAGAIN, 0, -EAGAIN, 0, -EAGAIN, -EAGAIN, 0, -EAGAIN, 0, -EAGAIN, -EAGAIN, -EAGAIN, 0, -EAGAIN, 0, 1, -EAGAIN, -EAGAIN, }, [C_SEND] = { -EDESTADDRREQ, -EDESTADDRREQ, -EPIPE, -EPIPE, -EDESTADDRREQ, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, }, [C_SENDTO] = { 1, 1, -EPIPE, -EPIPE, 1, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, 1, -EPIPE, -EPIPE, 1, 1, 1, }, [C_SELECT_R] = { 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, }, [C_SELECT_W] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, [C_SELECT_X] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_BC] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_KA] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_L] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SETSOCKOPT_RA] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SHUTDOWN_R] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SHUTDOWN_RW] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, [C_SHUTDOWN_W] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, }; /* * Set up a UDP socket file descriptor in the requested state and pass it to * socklib_sweep_call() along with local and remote addresses and their length. */ static int udp_sweep(int domain, int type, int protocol, enum state state, enum call call) { struct sockaddr_in sinA, sinB; struct sockaddr_in6 sin6A, sin6B; struct sockaddr *addrA, *addrB; socklen_t addr_len; char buf[1]; int r, fd, fd2; if (domain == AF_INET) { memset(&sinA, 0, sizeof(sinA)); sinA.sin_family = domain; sinA.sin_port = htons(TEST_PORT_A); sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK); memcpy(&sinB, &sinA, sizeof(sinB)); sinB.sin_port = htons(TEST_PORT_B); addrA = (struct sockaddr *)&sinA; addrB = (struct sockaddr *)&sinB; addr_len = sizeof(sinA); } else { assert(domain == AF_INET6); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = domain; sin6A.sin6_port = htons(TEST_PORT_A); memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr)); memcpy(&sin6B, &sin6A, sizeof(sin6B)); sin6B.sin6_port = htons(TEST_PORT_B); addrA = (struct sockaddr *)&sin6A; addrB = (struct sockaddr *)&sin6B; addr_len = sizeof(sin6A); } /* Create a bound remote socket. */ if ((fd2 = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); if (bind(fd2, addrB, addr_len) != 0) e(0); switch (state) { case S_NEW: case S_N_SHUT_R: case S_N_SHUT_W: case S_N_SHUT_RW: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); switch (state) { case S_N_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; case S_N_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; case S_N_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; default: break; } break; case S_BOUND: case S_CONNECTED: case S_SHUT_R: case S_SHUT_W: case S_SHUT_RW: case S_RSHUT_R: case S_RSHUT_W: case S_RSHUT_RW: case S_SHUT2_R: case S_SHUT2_W: case S_SHUT2_RW: case S_PRE_RESET: case S_AT_RESET: case S_POST_RESET: if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); if (bind(fd, addrA, addr_len) != 0) e(0); if (state == S_BOUND) break; if (connect(fd, addrB, addr_len) != 0) e(0); switch (state) { case S_SHUT_R: case S_SHUT2_R: if (shutdown(fd, SHUT_RD)) e(0); break; case S_SHUT_W: case S_SHUT2_W: if (shutdown(fd, SHUT_WR)) e(0); break; case S_SHUT_RW: case S_SHUT2_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; default: break; } switch (state) { case S_RSHUT_R: case S_SHUT2_R: if (shutdown(fd2, SHUT_RD)) e(0); break; case S_RSHUT_W: case S_SHUT2_W: if (shutdown(fd2, SHUT_WR)) e(0); break; case S_RSHUT_RW: case S_SHUT2_RW: if (shutdown(fd2, SHUT_RDWR)) e(0); break; case S_PRE_RESET: case S_AT_RESET: case S_POST_RESET: if (sendto(fd2, "", 1, 0, addrA, addr_len) != 1) e(0); if (close(fd2) != 0) e(0); fd2 = -1; if (state != S_PRE_RESET) { if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); } if (state == S_POST_RESET) { (void)recv(fd, buf, sizeof(buf), 0); } default: break; } break; default: fd = -1; e(0); } r = socklib_sweep_call(call, fd, addrA, addrB, addr_len); if (close(fd) != 0) e(0); if (fd2 != -1 && close(fd2) != 0) e(0); return r; } /* * Sweep test for socket calls versus socket states of TCP and UDP sockets. */ static void test91a(void) { subtest = 1; socklib_sweep(AF_INET, SOCK_STREAM, 0, tcp_states, __arraycount(tcp_states), (const int *)tcp_results, tcp_sweep); socklib_sweep(AF_INET6, SOCK_STREAM, 0, tcp_states, __arraycount(tcp_states), (const int *)tcp_results, tcp_sweep); socklib_sweep(AF_INET, SOCK_DGRAM, 0, udp_states, __arraycount(udp_states), (const int *)udp_results, udp_sweep); socklib_sweep(AF_INET6, SOCK_DGRAM, 0, udp_states, __arraycount(udp_states), (const int *)udp_results, udp_sweep); } #define F_SKIP -1 /* skip this entry */ #define F_NO 0 /* binding or connecting should fail */ #define F_YES 1 /* binding or connecting should succeed */ #define F_DUAL 2 /* always fails on IPV6_V6ONLY sockets */ #define F_ZONE 4 /* binding works only if a scope ID is given */ #define F_UDP 8 /* do not test on TCP sockets */ #define F_BAD 16 /* operations on this address result in EINVAL */ static const struct { const char *addr; int may_bind; int may_connect; /* UDP only */ } addrs_v4[] = { { "0.0.0.0", F_YES, F_NO }, { "0.0.0.1", F_NO, F_SKIP }, { "127.0.0.1", F_YES, F_YES }, { "127.0.0.255", F_NO, F_YES }, { "127.255.255.255", F_NO, F_YES }, { "172.31.255.254", F_NO, F_SKIP }, /* may be valid.. */ { "224.0.0.0", F_YES | F_UDP, F_SKIP }, { "239.255.255.255", F_YES | F_UDP, F_SKIP }, { "240.0.0.0", F_NO, F_SKIP }, { "255.255.255.255", F_NO, F_SKIP }, }; static const struct { const char *addr; int may_bind; int may_connect; /* UDP only */ } addrs_v6[] = { { "::0", F_YES, F_NO }, { "::1", F_YES, F_YES }, { "::2", F_NO, F_YES }, { "::127.0.0.1", F_NO, F_YES }, { "::ffff:7f00:1", F_YES | F_DUAL, F_YES | F_DUAL }, { "::ffff:7f00:ff", F_NO | F_DUAL, F_YES | F_DUAL }, { "100::1", F_NO, F_SKIP }, { "2fff:ffff::", F_NO, F_SKIP }, { "fc00::1", F_NO, F_SKIP }, { "fe00::1", F_NO, F_SKIP }, { "fe80::1", F_YES | F_ZONE, F_YES | F_ZONE }, { "fec0::1", F_NO, F_SKIP }, { "ff01::1", F_YES | F_ZONE | F_UDP, F_YES | F_ZONE }, { "ff02::1", F_YES | F_ZONE | F_UDP, F_YES | F_ZONE }, { "ff02::2", F_YES | F_ZONE | F_UDP, F_YES | F_ZONE }, { "ff0e::1", F_YES | F_UDP, F_SKIP }, { "ffff::1", F_NO | F_UDP | F_BAD, F_NO | F_BAD }, }; /* * Test binding sockets of a particular type to various addresses. */ static void sub91b(int type) { struct sockaddr_in sin, lsin; struct sockaddr_in6 sin6, lsin6; socklen_t len; unsigned int i, ifindex; int r, fd, val; ifindex = if_nametoindex(LOOPBACK_IFNAME); /* Test binding IPv4 sockets to IPv4 addresses. */ for (i = 0; i < __arraycount(addrs_v4); i++) { if (type == SOCK_STREAM && (addrs_v4[i].may_bind & F_UDP)) continue; if ((fd = socket(AF_INET, type, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if (inet_pton(AF_INET, addrs_v4[i].addr, &sin.sin_addr) != 1) e(0); r = bind(fd, (struct sockaddr *)&sin, sizeof(sin)); if (r == -1 && errno != EADDRNOTAVAIL) e(0); if (r + 1 != !!(addrs_v4[i].may_bind & F_YES)) e(0); len = sizeof(lsin); if (getsockname(fd, (struct sockaddr *)&lsin, &len) != 0) e(0); if (lsin.sin_len != sizeof(lsin)) e(0); if (lsin.sin_family != AF_INET) e(0); if (r == 0) { if (lsin.sin_port == 0) e(0); if (lsin.sin_addr.s_addr != sin.sin_addr.s_addr) e(0); } else { if (lsin.sin_port != 0) e(0); if (lsin.sin_addr.s_addr != htonl(INADDR_ANY)) e(0); } /* Rebinding never works; binding after a failed bind does. */ sin.sin_addr.s_addr = htonl(INADDR_ANY); r = bind(fd, (struct sockaddr *)&sin, sizeof(sin)); if (r == -1 && errno != EINVAL) e(0); if (!!r != !!(addrs_v4[i].may_bind & F_YES)) e(0); if (close(fd) != 0) e(0); } /* Test binding IPv6 sockets to IPv6 addresses. */ for (i = 0; i < __arraycount(addrs_v6); i++) { if (type == SOCK_STREAM && (addrs_v6[i].may_bind & F_UDP)) continue; /* Try without IPV6_V6ONLY. */ if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); /* IPV6_V6ONLY may or may not be enabled by default.. */ val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; if (inet_pton(AF_INET6, addrs_v6[i].addr, &sin6.sin6_addr) != 1) e(0); if (addrs_v6[i].may_bind & F_ZONE) { if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); sin6.sin6_scope_id = ifindex; } r = bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)); if (r == -1) { if (addrs_v6[i].may_bind & F_BAD) { if (errno != EINVAL) e(0); } else { if (errno != EADDRNOTAVAIL) e(0); } } if (r + 1 != !!(addrs_v6[i].may_bind & F_YES)) e(0); len = sizeof(lsin6); if (getsockname(fd, (struct sockaddr *)&lsin6, &len) != 0) e(0); if (lsin6.sin6_len != sizeof(lsin6)) e(0); if (lsin6.sin6_family != AF_INET6) e(0); if (r == 0) { if (lsin6.sin6_port == 0) e(0); if (memcmp(&lsin6.sin6_addr, &sin6.sin6_addr, sizeof(lsin6.sin6_addr))) e(0); if (lsin6.sin6_scope_id != ((addrs_v6[i].may_bind & F_ZONE) ? ifindex : 0)) e(0); } else { if (lsin6.sin6_port != 0) e(0); if (!IN6_IS_ADDR_UNSPECIFIED(&lsin6.sin6_addr)) e(0); if (lsin6.sin6_scope_id != 0) e(0); } if (close(fd) != 0) e(0); /* Try with IPV6_V6ONLY. */ if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); r = bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)); if (r == -1) { if (addrs_v6[i].may_bind & (F_BAD | F_DUAL)) { if (errno != EINVAL) e(0); } else if (errno != EADDRNOTAVAIL) e(0); } if (r + 1 != ((addrs_v6[i].may_bind & (F_YES | F_DUAL)) == F_YES)) e(0); if (close(fd) != 0) e(0); } /* Test binding an IPv6 socket to an IPv4 address. */ if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno != EINVAL) e(0); assert(sizeof(sin) <= sizeof(sin6)); memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6, &sin, sizeof(sin)); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EAFNOSUPPORT) e(0); if (close(fd) != 0) e(0); /* Test binding an IPv4 socket to an IPv6 address. */ if ((fd = socket(AF_INET, type, 0)) < 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &in6addr_any, sizeof(sin6.sin6_addr)); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EINVAL) e(0); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin)) != -1) e(0); if (errno != EAFNOSUPPORT) e(0); if (close(fd) != 0) e(0); /* Test binding a socket to AF_UNSPEC. */ if ((fd = socket(AF_INET, type, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_UNSPEC; if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno != EAFNOSUPPORT) e(0); if (close(fd) != 0) e(0); } /* * Test binding sockets to various addresses. */ static void test91b(void) { subtest = 2; sub91b(SOCK_STREAM); sub91b(SOCK_DGRAM); } /* * Test connecting TCP sockets to various addresses. We cannot test much here, * because we do not actually want this test to generate outgoing traffic. In * effect, we test calls that should fail only. */ static void sub91c_tcp(void) { struct sockaddr_in sin; struct sockaddr_in6 sin6; int fd, val; /* * Test connecting to address zero (0.0.0.0 and ::0). Apparently the * traditional BSD behavior for IPv4 is to use the first interface's * local address as destination instead, but our implementation does * not support that at this time: these 'any' addresses always result * in connection failures right away, hopefully eliminating some tricky * implementation boundary cases. */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); sin.sin_addr.s_addr = htonl(INADDR_ANY); if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno != EHOSTUNREACH && errno != ENETUNREACH) e(0); if (close(fd) != 0) e(0); if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); memcpy(&sin6.sin6_addr, &in6addr_any, sizeof(sin6.sin6_addr)); if (connect(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EHOSTUNREACH && errno != ENETUNREACH) e(0); if (close(fd) != 0) e(0); /* * Test connecting to an IPv6-mapped IPv4 address on an IPv6 socket * with INET6_V6ONLY enabled. */ if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); if (inet_pton(AF_INET6, "::ffff:"LOOPBACK_IPV4, &sin6.sin6_addr) != 1) e(0); if (connect(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EINVAL) e(0); if (close(fd) != 0) e(0); /* Test connecting to an AF_UNSPEC address. */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_UNSPEC; if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno != EAFNOSUPPORT) e(0); if (close(fd) != 0) e(0); /* Test connecting to port zero. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(0); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); if (close(fd) != 0) e(0); } /* * Test connecting UDP sockets to various addresses. */ static void sub91c_udp(void) { struct sockaddr_in sin, rsin; struct sockaddr_in6 sin6, rsin6; socklen_t len; unsigned int i, ifindex; int r, fd, val; ifindex = if_nametoindex(LOOPBACK_IFNAME); /* Test connecting IPv4 sockets to IPv4 addresses. */ for (i = 0; i < __arraycount(addrs_v4); i++) { if (addrs_v4[i].may_connect == F_SKIP) continue; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); if (inet_pton(AF_INET, addrs_v4[i].addr, &sin.sin_addr) != 1) e(0); r = connect(fd, (struct sockaddr *)&sin, sizeof(sin)); if (r + 1 != !!(addrs_v4[i].may_connect & F_YES)) e(0); len = sizeof(rsin); if (r == 0) { if (getpeername(fd, (struct sockaddr *)&rsin, &len) != 0) e(0); if (rsin.sin_len != sizeof(rsin)) e(0); if (rsin.sin_family != AF_INET) e(0); if (rsin.sin_port != htons(TEST_PORT_A)) e(0); if (rsin.sin_addr.s_addr != sin.sin_addr.s_addr) e(0); } else { if (getpeername(fd, (struct sockaddr *)&rsin, &len) != -1) e(0); if (errno != ENOTCONN) e(0); } sin.sin_addr.s_addr = htonl(INADDR_ANY); r = bind(fd, (struct sockaddr *)&sin, sizeof(sin)); if (r == -1 && errno != EINVAL) e(0); if (r + 1 != !(addrs_v4[i].may_connect & F_YES)) e(0); if (close(fd) != 0) e(0); } /* Test connecting IPv6 sockets to IPv6 addresses. */ for (i = 0; i < __arraycount(addrs_v6); i++) { if (addrs_v6[i].may_connect == F_SKIP) continue; /* Try without IPV6_V6ONLY. */ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); /* IPV6_V6ONLY may or may not be enabled by default.. */ val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); if (inet_pton(AF_INET6, addrs_v6[i].addr, &sin6.sin6_addr) != 1) e(0); sin6.sin6_scope_id = ifindex; r = connect(fd, (struct sockaddr *)&sin6, sizeof(sin6)); if (r + 1 != !!(addrs_v6[i].may_connect & F_YES)) e(0); len = sizeof(rsin6); if (r == 0) { if (getpeername(fd, (struct sockaddr *)&rsin6, &len) != 0) e(0); if (rsin6.sin6_len != sizeof(rsin6)) e(0); if (rsin6.sin6_family != AF_INET6) e(0); if (rsin6.sin6_port != htons(TEST_PORT_A)) e(0); if (memcmp(&rsin6.sin6_addr, &sin6.sin6_addr, sizeof(rsin6.sin6_addr))) e(0); if (rsin6.sin6_scope_id != ((addrs_v6[i].may_connect & F_ZONE) ? ifindex : 0)) e(0); } else { if (getpeername(fd, (struct sockaddr *)&rsin, &len) != -1) e(0); if (errno != ENOTCONN) e(0); } if (close(fd) != 0) e(0); /* Try with IPV6_V6ONLY. */ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); r = connect(fd, (struct sockaddr *)&sin6, sizeof(sin6)); if (r == -1 && errno != EINVAL && errno != EHOSTUNREACH) e(0); if (r + 1 != ((addrs_v6[i].may_connect & (F_YES | F_DUAL)) == F_YES)) e(0); if (close(fd) != 0) e(0); } /* Test connecting an IPv6 socket to an IPv4 address. */ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno != EINVAL) e(0); assert(sizeof(sin) <= sizeof(sin6)); memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6, &sin, sizeof(sin)); if (connect(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EAFNOSUPPORT) e(0); if (close(fd) != 0) e(0); /* Test connecting an IPv4 socket to an IPv6 address. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &in6addr_any, sizeof(sin6.sin6_addr)); if (connect(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EINVAL) e(0); if (connect(fd, (struct sockaddr *)&sin6, sizeof(sin)) != -1) e(0); if (errno != EAFNOSUPPORT) e(0); if (close(fd) != 0) e(0); /* Test unconnecting a socket using AF_UNSPEC. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_UNSPEC; if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); len = sizeof(rsin); if (getpeername(fd, (struct sockaddr *)&rsin, &len) != -1) e(0); if (errno != ENOTCONN) e(0); if (close(fd) != 0) e(0); /* Test connecting to port zero. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(0); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno != EADDRNOTAVAIL) e(0); if (close(fd) != 0) e(0); } /* * Test connecting sockets to various addresses. */ static void test91c(void) { subtest = 3; sub91c_tcp(); sub91c_udp(); } /* * Test binding with IPv4/IPv6 on the same port for the given socket type. */ static void sub91d(int type) { struct sockaddr_in sin; struct sockaddr_in6 sin6; int r, fd, fd2, val; if ((fd = socket(AF_INET, type, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); sin.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); /* IPv4 bound; IPv6 bind without IPV6_V6ONLY may or may not work. */ if ((fd2 = socket(AF_INET6, type, 0)) < 0) e(0); val = 0; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); memcpy(&sin6.sin6_addr, &in6addr_any, sizeof(sin6.sin6_addr)); r = bind(fd2, (struct sockaddr *)&sin6, sizeof(sin6)); if (r == -1 && errno != EADDRINUSE) e(0); if (close(fd2) != 0) e(0); /* IPv4 bound; IPv6 bind with IPV6_V6ONLY should work. */ if ((fd2 = socket(AF_INET6, type, 0)) < 0) e(0); val = 1; if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd2, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); if (bind(fd2, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* IPv6 bound with IPV6_V6ONLY; IPv4 bind may or may not work. */ if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); if ((fd2 = socket(AF_INET, type, 0)) < 0) e(0); r = bind(fd2, (struct sockaddr *)&sin, sizeof(sin)); if (r == -1 && errno != EADDRINUSE) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* IPv6 bound with IPV6_V6ONLY; IPv4 bind should work. */ if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); if ((fd2 = socket(AF_INET, type, 0)) < 0) e(0); if (bind(fd2, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); } /* * Test binding with IPv4/IPv6 on the same port, and IPV6_V6ONLY. */ static void test91d(void) { subtest = 4; sub91d(SOCK_STREAM); sub91d(SOCK_DGRAM); } /* * Test sending large and small UDP packets. */ static void test91e(void) { struct sockaddr_in sin; struct msghdr msg; struct iovec iov; char *buf; unsigned int i, j; int r, fd, fd2, val; subtest = 5; if ((buf = malloc(65536)) == NULL) e(0); if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); val = 65536; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) != 0) e(0); if ((fd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); /* * A maximum send buffer size of a full packet size's worth may always * be set, although this is not necessarily the actual maximum. */ val = 65535; if (setsockopt(fd2, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) != 0) e(0); /* Find the largest possible packet size that can actually be sent. */ for (i = 0; i < val; i += sizeof(int)) { j = i ^ 0xdeadbeef; memcpy(&buf[i], &j, sizeof(j)); } for (val = 65536; val > 0; val--) { if ((r = sendto(fd2, buf, val, 0, (struct sockaddr *)&sin, sizeof(sin))) == val) break; if (r != -1) e(0); if (errno != EMSGSIZE) e(0); } if (val != 65535 - sizeof(struct udphdr) - sizeof(struct ip)) e(0); memset(buf, 0, val); buf[val] = 'X'; memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = val + 1; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (recvmsg(fd, &msg, 0) != val) e(0); if (msg.msg_flags != 0) e(0); for (i = 0; i < val; i += sizeof(int)) { j = i ^ 0xdeadbeef; if (memcmp(&buf[i], &j, MIN(sizeof(j), val - i))) e(0); } if (buf[val] != 'X') e(0); if (sendto(fd2, buf, val, 0, (struct sockaddr *)&sin, sizeof(sin)) != val) e(0); /* * Make sure that there are no off-by-one errors in the receive code, * and that MSG_TRUNC is set (only) when not the whole packet was * received. */ memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = val; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (recvmsg(fd, &msg, 0) != val) e(0); if (msg.msg_flags != 0) e(0); if (sendto(fd2, buf, val, 0, (struct sockaddr *)&sin, sizeof(sin)) != val) e(0); buf[val - 1] = 'Y'; memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = val - 1; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (recvmsg(fd, &msg, 0) != val - 1) e(0); if (msg.msg_flags != MSG_TRUNC) e(0); for (i = 0; i < val - 1; i += sizeof(int)) { j = i ^ 0xdeadbeef; if (memcmp(&buf[i], &j, MIN(sizeof(j), val - 1 - i))) e(0); } if (buf[val - 1] != 'Y') e(0); if (sendto(fd2, buf, val, 0, (struct sockaddr *)&sin, sizeof(sin)) != val) e(0); buf[0] = 'Z'; memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = 0; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (recvmsg(fd, &msg, 0) != 0) e(0); if (msg.msg_flags != MSG_TRUNC) e(0); if (buf[0] != 'Z') e(0); /* Make sure that zero-sized packets can be sent and received. */ if (sendto(fd2, buf, 0, 0, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); /* * Note how we currently assume that packets sent over localhost will * arrive immediately, so that we can use MSG_DONTWAIT to avoid that * the test freezes. */ memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (recvmsg(fd, &msg, MSG_DONTWAIT) != 0) e(0); if (msg.msg_flags != 0) e(0); if (buf[0] != 'Z') e(0); if (recv(fd, buf, val, MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); /* * When sending lots of small packets, ensure that fewer packets arrive * than we sent. This sounds weird, but we cannot actually check the * internal TCP/IP buffer granularity and yet we want to make sure that * the receive queue is measured in terms of buffers rather than packet * sizes. In addition, we check that older packets are favored, * instead discarding new ones when the receive buffer is full. */ for (i = 0; i < 65536 / sizeof(j); i++) { j = i; if (sendto(fd2, &j, sizeof(j), 0, (struct sockaddr *)&sin, sizeof(sin)) != sizeof(j)) e(0); } for (i = 0; i < 1025; i++) { r = recv(fd, &j, sizeof(j), MSG_DONTWAIT); if (r == -1) { if (errno != EWOULDBLOCK) e(0); break; } if (r != sizeof(j)) e(0); if (i != j) e(0); } if (i == 1025) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); free(buf); } /* * Test setting and retrieving IP-level options for the given socket type. For * TCP sockets, we cannot test whether they are actually applied, but for UDP * sockets, we do a more complete test later on. */ static void sub91f(int type) { socklen_t len; int fd, val, def; /* Test IPv4 first. */ if ((fd = socket(AF_INET, type, 0)) < 0) e(0); /* Test obtaining the default TOS and TTL values. */ len = sizeof(val); if (getsockopt(fd, IPPROTO_IP, IP_TOS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 0) e(0); len = sizeof(def); if (getsockopt(fd, IPPROTO_IP, IP_TTL, &def, &len) != 0) e(0); if (len != sizeof(def)) e(0); if (def < 16 || def > UINT8_MAX) e(0); /* Test changing the TOS field. */ for (val = 0; val <= UINT8_MAX; val++) if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) != 0) e(0); val = -1; /* not a special value for IPv4 */ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = UINT8_MAX + 1; if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IP, IP_TOS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != UINT8_MAX) e(0); /* Test changing the TTL field. */ for (val = 0; val <= UINT8_MAX; val++) if (setsockopt(fd, IPPROTO_IP, IP_TTL, &val, sizeof(val)) != 0) e(0); val = 39; if (setsockopt(fd, IPPROTO_IP, IP_TTL, &val, sizeof(val)) != 0) e(0); val = -1; /* not a special value for IPv4 */ if (setsockopt(fd, IPPROTO_IP, IP_TTL, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = UINT8_MAX + 1; if (setsockopt(fd, IPPROTO_IP, IP_TTL, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IP, IP_TTL, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 39) e(0); /* It must not be possible to set IPv6 options on IPv4 sockets. */ val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (close(fd) != 0) e(0); /* Test IPv6 next. */ if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); /* Test obtaining the default TCLASS and HOPS values. */ len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 0) e(0); len = sizeof(def); if (getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &def, &len) != 0) e(0); if (len != sizeof(def)) e(0); if (def < 16 || def > UINT8_MAX) e(0); /* Test changing the TCLASS field. */ for (val = 0; val <= UINT8_MAX; val++) if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) != 0) e(0); val = -2; if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = UINT8_MAX + 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != UINT8_MAX) e(0); val = -1; /* reset to default */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) != 0) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 0) e(0); /* Test changing the HOPS field. */ for (val = 0; val <= UINT8_MAX; val++) if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) != 0) e(0); val = 49; if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) != 0) e(0); val = -2; if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); val = UINT8_MAX + 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) != -1) e(0); if (errno != EINVAL) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 49) e(0); val = -1; /* reset to default */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) != 0) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != def) e(0); /* It must not be possible to set IPv4 options on IPv6 sockets. */ val = 0; if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (setsockopt(fd, IPPROTO_IP, IP_TTL, &val, sizeof(val)) != -1) e(0); if (errno != ENOPROTOOPT) e(0); len = sizeof(val); if (getsockopt(fd, IPPROTO_IP, IP_TOS, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (getsockopt(fd, IPPROTO_IP, IP_TTL, &val, &len) != -1) e(0); if (errno != ENOPROTOOPT) e(0); if (close(fd) != 0) e(0); } /* * Test setting and retrieving IP-level options. */ static void test91f(void) { subtest = 6; sub91f(SOCK_STREAM); sub91f(SOCK_DGRAM); } /* * Test setting and retrieving IP-level options on UDP sockets and packets. * As part of this, ensure that the maximum set of supported control options * can be both sent and received, both for IPv4 and IPv6. Any options that are * newly added to the service and may be combined with the existing ones should * be added to this subtest as well. The control data handling code is shared * between UDP and RAW, so there is no need to repeat this test for the latter. */ static void test91g(void) { struct sockaddr_in6 sin6; struct sockaddr_in sin; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg, cmsg2; struct in_pktinfo ipi; struct in6_pktinfo ipi6; unsigned int ifindex; char buf[1]; union { struct cmsghdr cmsg; char buf[256]; } control; uint8_t byte; size_t size; int fd, fd2, val, seen_tos, seen_ttl, seen_pktinfo; subtest = 7; ifindex = if_nametoindex(LOOPBACK_IFNAME); if (ifindex == 0) e(0); if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); val = 1; /* Strangely, IP_RECVTOS is not a thing.. */ if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd, IPPROTO_IP, IP_RECVPKTINFO, &val, sizeof(val)) != 0) e(0); if ((fd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); iov.iov_base = "A"; iov.iov_len = 1; val = 39; control.cmsg.cmsg_len = CMSG_LEN(sizeof(val)); control.cmsg.cmsg_level = IPPROTO_IP; control.cmsg.cmsg_type = IP_TTL; memcpy(CMSG_DATA(&control.cmsg), &val, sizeof(val)); memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr *)&sin; msg.msg_namelen = sizeof(sin); 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); iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = sizeof(control); if (recvmsg(fd, &msg, 0) != 1) e(0); if (buf[0] != 'A') e(0); seen_ttl = seen_pktinfo = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != IPPROTO_IP) e(0); switch (cmsg->cmsg_type) { case IP_TTL: /* The odd one out, using a uint8_t.. */ if (seen_ttl++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(byte))) e(0); memcpy(&byte, CMSG_DATA(cmsg), sizeof(byte)); if (byte != 39) e(0); break; case IP_PKTINFO: if (seen_pktinfo++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(ipi))) e(0); memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); if (ipi.ipi_addr.s_addr != sin.sin_addr.s_addr) e(0); if (ipi.ipi_ifindex != ifindex) e(0); break; default: e(0); } } if (!seen_ttl) e(0); if (!seen_pktinfo) e(0); /* Test that we can provide all supported IPv4 options at once. */ iov.iov_base = "B"; iov.iov_len = 1; val = 1; control.cmsg.cmsg_len = CMSG_LEN(sizeof(val)); control.cmsg.cmsg_level = IPPROTO_IP; control.cmsg.cmsg_type = IP_TOS; memcpy(CMSG_DATA(&control.cmsg), &val, sizeof(val)); size = CMSG_SPACE(sizeof(val)); if ((cmsg = CMSG_NXTHDR(&msg, &control.cmsg)) == NULL) e(0); val = 41; cmsg2.cmsg_len = CMSG_LEN(sizeof(val)); cmsg2.cmsg_level = IPPROTO_IP; cmsg2.cmsg_type = IP_TTL; memcpy(cmsg, &cmsg2, sizeof(cmsg2)); memcpy(CMSG_DATA(cmsg), &val, sizeof(val)); size += CMSG_SPACE(sizeof(val)); memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr *)&sin; msg.msg_namelen = sizeof(sin); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = size; if (sendmsg(fd2, &msg, 0) != 1) e(0); iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = sizeof(control); if (recvmsg(fd, &msg, 0) != 1) e(0); if (buf[0] != 'B') e(0); /* Check just the TTL this time. */ seen_ttl = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != IPPROTO_IP) e(0); if (cmsg->cmsg_type == IP_TTL) { /* The odd one out, using a uint8_t.. */ if (seen_ttl++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(byte))) e(0); memcpy(&byte, CMSG_DATA(cmsg), sizeof(byte)); if (byte != 41) e(0); } } if (!seen_ttl) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* That was IPv4, onto IPv6.. */ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)) != 0) e(0); if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)) != 0) e(0); if ((fd2 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); val = 94; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) != 0) e(0); iov.iov_base = "C"; iov.iov_len = 1; val = 39; control.cmsg.cmsg_len = CMSG_LEN(sizeof(val)); control.cmsg.cmsg_level = IPPROTO_IPV6; control.cmsg.cmsg_type = IPV6_HOPLIMIT; memcpy(CMSG_DATA(&control.cmsg), &val, sizeof(val)); memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr *)&sin6; msg.msg_namelen = sizeof(sin6); 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); iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = sizeof(control); if (recvmsg(fd, &msg, 0) != 1) e(0); if (buf[0] != 'C') e(0); seen_tos = seen_ttl = seen_pktinfo = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != IPPROTO_IPV6) e(0); switch (cmsg->cmsg_type) { case IPV6_TCLASS: if (seen_tos++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(val))) e(0); memcpy(&val, CMSG_DATA(cmsg), sizeof(val)); if (val != 94) e(0); break; case IPV6_HOPLIMIT: if (seen_ttl++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(val))) e(0); memcpy(&val, CMSG_DATA(cmsg), sizeof(val)); if (val != 39) e(0); break; case IPV6_PKTINFO: if (seen_pktinfo++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(ipi6))) e(0); memcpy(&ipi6, CMSG_DATA(cmsg), sizeof(ipi6)); if (memcmp(&ipi6.ipi6_addr, &in6addr_loopback, sizeof(ipi6.ipi6_addr))) e(0); if (ipi6.ipi6_ifindex != ifindex) e(0); break; default: e(0); } } if (!seen_tos) e(0); if (!seen_ttl) e(0); if (!seen_pktinfo) e(0); /* * Test that (for IPv6) an option of -1 overrides setsockopt. * Also test that we can provide all supported IPv6 options at once. */ val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)) != 0) e(0); iov.iov_base = "D"; iov.iov_len = 1; memset(&msg, 0, sizeof(msg)); msg.msg_control = control.buf; msg.msg_controllen = sizeof(control.buf); val = -1; control.cmsg.cmsg_len = CMSG_LEN(sizeof(val)); control.cmsg.cmsg_level = IPPROTO_IPV6; control.cmsg.cmsg_type = IPV6_TCLASS; memcpy(CMSG_DATA(&control.cmsg), &val, sizeof(val)); size = CMSG_SPACE(sizeof(val)); if ((cmsg = CMSG_NXTHDR(&msg, &control.cmsg)) == NULL) e(0); val = 78; cmsg2.cmsg_len = CMSG_LEN(sizeof(val)); cmsg2.cmsg_level = IPPROTO_IPV6; cmsg2.cmsg_type = IPV6_HOPLIMIT; memcpy(cmsg, &cmsg2, sizeof(cmsg2)); memcpy(CMSG_DATA(cmsg), &val, sizeof(val)); size += CMSG_SPACE(sizeof(val)); if ((cmsg = CMSG_NXTHDR(&msg, cmsg)) == NULL) e(0); cmsg2.cmsg_len = CMSG_LEN(sizeof(ipi6)); cmsg2.cmsg_level = IPPROTO_IPV6; cmsg2.cmsg_type = IPV6_PKTINFO; memcpy(cmsg, &cmsg2, sizeof(cmsg2)); memset(&ipi6, 0, sizeof(ipi6)); memcpy(CMSG_DATA(cmsg), &ipi6, sizeof(ipi6)); size += CMSG_SPACE(sizeof(ipi6)); if (size > sizeof(control.buf)) e(0); memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr *)&sin6; msg.msg_namelen = sizeof(sin6); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = size; if (sendmsg(fd2, &msg, 0) != 1) e(0); iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control.buf; msg.msg_controllen = sizeof(control); if (recvmsg(fd, &msg, 0) != 1) e(0); if (buf[0] != 'D') e(0); seen_tos = seen_ttl = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level != IPPROTO_IPV6) e(0); switch (cmsg->cmsg_type) { case IPV6_TCLASS: if (seen_tos++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(val))) e(0); memcpy(&val, CMSG_DATA(cmsg), sizeof(val)); if (val != 0) e(0); break; case IPV6_HOPLIMIT: if (seen_ttl++) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(val))) e(0); memcpy(&val, CMSG_DATA(cmsg), sizeof(val)); if (val != 78) e(0); break; default: e(0); } } if (!seen_tos) e(0); if (!seen_ttl) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); } /* * Test receiving IPv4 packets on IPv6 sockets. */ static void test91h(void) { struct sockaddr_in6 sin6; struct sockaddr_in sin; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; struct in6_pktinfo ipi6; unsigned int ifindex; char buf[1], buf2[256]; int fd, fd2, val; subtest = 8; ifindex = if_nametoindex(LOOPBACK_IFNAME); if (ifindex == 0) e(0); if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); val = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); if ((fd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(TEST_PORT_A); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (sendto(fd2, "A", 1, 0, (struct sockaddr *)&sin, sizeof(sin)) != 1) e(0); iov.iov_base = buf; iov.iov_len = sizeof(buf); memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr *)&sin6; msg.msg_namelen = sizeof(sin6); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buf2; msg.msg_controllen = sizeof(buf2); if (recvmsg(fd, &msg, 0) != 1) e(0); if (buf[0] != 'A') e(0); if (msg.msg_namelen != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) e(0); if (cmsg->cmsg_level != IPPROTO_IPV6) e(0); if (cmsg->cmsg_type != IPV6_PKTINFO) e(0); if (cmsg->cmsg_len != CMSG_LEN(sizeof(ipi6))) e(0); /* * The packet was sent from loopback to loopback, both with IPv4-mapped * IPv6 addresses, so we can simply compare source and destination. */ memcpy(&ipi6, CMSG_DATA(cmsg), sizeof(ipi6)); if (memcmp(&sin6.sin6_addr, &ipi6.ipi6_addr, sizeof(sin6.sin6_addr))) e(0); if (ipi6.ipi6_ifindex != ifindex) e(0); if (CMSG_NXTHDR(&msg, cmsg) != NULL) e(0); /* * Sqeeze in a quick test to see what happens if the receiver end does * not provide a control buffer after having requested control data, * because a half-complete version of this test triggered a bug there.. */ if (sendto(fd2, "B", 1, 0, (struct sockaddr *)&sin, sizeof(sin)) != 1) e(0); if (recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL) != 1) e(0); if (buf[0] != 'B') e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); } /* * Test that binding a socket of the given type to a privileged port is * disallowed. */ static void sub91i(int type) { struct sockaddr_in sin; struct sockaddr_in6 sin6; int fd, port; if ((fd = socket(AF_INET, type, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); for (port = IPPORT_RESERVED - 1; port >= 0; port--) { sin.sin_port = htons(port); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); if (errno == EADDRINUSE) continue; if (errno != EACCES) e(0); break; } for (port = IPPORT_RESERVED; port <= UINT16_MAX; port++) { sin.sin_port = htons(port); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == 0) break; if (errno != EADDRINUSE) e(0); } if (close(fd) != 0) e(0); if ((fd = socket(AF_INET6, type, 0)) < 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); for (port = IPV6PORT_RESERVED - 1; port >= 0; port--) { sin6.sin6_port = htons(port); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno == EADDRINUSE) continue; if (errno != EACCES) e(0); break; } for (port = IPV6PORT_RESERVED; port <= UINT16_MAX; port++) { sin6.sin6_port = htons(port); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == 0) break; if (errno != EADDRINUSE) e(0); } if (close(fd) != 0) e(0); } /* * Test that binding to privileged ports is disallowed for non-root users. * Also make sure that such users cannot create raw sockets at all. This test * is not to be run by root, but for convenience we first try to drop * privileges for the duration of the test anyway. */ static void test91i(void) { int i; subtest = 9; (void)seteuid(1); sub91i(SOCK_STREAM); sub91i(SOCK_DGRAM); for (i = 0; i < IPPROTO_MAX; i++) { if (socket(AF_INET, SOCK_RAW, i) != -1) e(0); if (errno != EACCES) e(0); if (socket(AF_INET6, SOCK_RAW, i) != -1) e(0); if (errno != EACCES) e(0); } (void)seteuid(0); } /* * Test setting and getting basic UDP/RAW multicast transmission options. */ static void test91j(void) { subtest = 10; socklib_multicast_tx_options(SOCK_DGRAM); } /* * Test TCP socket state changes related to the listen queue. This test is * derived from test90y, but sufficiently different to be its own copy. */ static void test91k(void) { struct sockaddr_in6 sin6A, sin6B, sin6C; socklen_t len; struct timeval tv; struct linger l; fd_set fds; char buf[7]; int fd, fd2, fd3, fd4, val, fl; subtest = 11; if ((fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) e(0); val = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != 0) e(0); memset(&sin6A, 0, sizeof(sin6A)); sin6A.sin6_family = AF_INET6; sin6A.sin6_port = htons(TEST_PORT_A); memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr)); if (bind(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); /* * Any socket options should be inherited from the listening socket at * connect time, and not be re-inherited at accept time, to the extent * that they are inherited at all. TCP/IP level options are not. */ val = 123; if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) != 0) e(0); val = 32768; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) != 0) e(0); if (listen(fd, 5) != 0) e(0); if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); memset(&sin6B, 0, sizeof(sin6B)); sin6B.sin6_family = AF_INET6; sin6B.sin6_port = htons(0); memcpy(&sin6B.sin6_addr, &in6addr_loopback, sizeof(sin6B.sin6_addr)); val = 1; if (setsockopt(fd2, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != 0) e(0); 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 (len != sizeof(sin6B)) e(0); if (sin6B.sin6_port == htons(0)) e(0); if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); val = 456; if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) != 0) e(0); val = 16384; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) != 0) e(0); /* * Obtaining the peer name should work. As always, the name should be * inherited from the listening socket. */ len = sizeof(sin6C); if (getpeername(fd2, (struct sockaddr *)&sin6C, &len) != 0) e(0); if (sin6C.sin6_len != sizeof(sin6C)) e(0); if (sin6C.sin6_family != AF_INET6) e(0); if (sin6C.sin6_port != htons(TEST_PORT_A)) e(0); if (memcmp(&sin6C.sin6_addr, &in6addr_loopback, sizeof(sin6C.sin6_addr)) != 0) e(0); /* * Sending to the socket should work, and it should be possible to * receive the data from the other side once accepted. */ if (send(fd2, "Hello, ", 7, 0) != 7) e(0); if (send(fd2, "world!", 6, 0) != 6) e(0); /* Shutdown settings should be visible after accepting, too. */ if (shutdown(fd2, SHUT_RDWR) != 0) e(0); memset(&sin6C, 0, sizeof(sin6C)); len = sizeof(sin6C); if ((fd3 = accept(fd, (struct sockaddr *)&sin6C, &len)) < 0) e(0); if (sin6C.sin6_len != sizeof(sin6C)) e(0); if (sin6C.sin6_family != AF_INET6) e(0); if (sin6C.sin6_port != sin6B.sin6_port) e(0); if (memcmp(&sin6C.sin6_addr, &in6addr_loopback, sizeof(sin6C.sin6_addr)) != 0) e(0); len = sizeof(val); if (getsockopt(fd3, SOL_SOCKET, SO_SNDLOWAT, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 123) e(0); len = sizeof(val); if (getsockopt(fd3, SOL_SOCKET, SO_RCVBUF, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 32768) e(0); if ((fl = fcntl(fd3, F_GETFL)) == -1) e(0); if (!(fl & O_NONBLOCK)) e(0); if (fcntl(fd3, F_SETFL, fl & ~O_NONBLOCK) != 0) e(0); if (recv(fd3, buf, 7, 0) != 7) e(0); if (memcmp(buf, "Hello, ", 7) != 0) e(0); if (recv(fd3, buf, 7, 0) != 6) e(0); if (memcmp(buf, "world!", 6) != 0) e(0); if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0); /* * Unlike in the UDS test, the other side's shutdown-for-reading is not * visible to this side, so sending data should work just fine until we * close or shut down the socket ourselves. The other side will simply * discard the incoming data. */ if (send(fd3, "", 1, MSG_NOSIGNAL) != 1) e(0); if (close(fd2) != 0) e(0); if (close(fd3) != 0) e(0); /* * If the connection pending acceptance is closed, the connection must * remain on the queue, and the accepting party will read EOF from it. * Try once without pending data, once with pending data. */ if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (close(fd2) != 0) e(0); len = sizeof(sin6B); if ((fd3 = accept(fd, (struct sockaddr *)&sin6B, &len)) < 0) e(0); len = sizeof(val); if (getsockopt(fd3, SOL_SOCKET, SO_SNDLOWAT, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 456) e(0); len = sizeof(val); if (getsockopt(fd3, SOL_SOCKET, SO_RCVBUF, &val, &len) != 0) e(0); if (len != sizeof(val)) e(0); if (val != 16384) e(0); if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0); if (close(fd3) != 0) e(0); if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (send(fd2, "Hello!", 6, 0) != 6) e(0); if (close(fd2) != 0) e(0); len = sizeof(sin6B); if ((fd3 = accept(fd, (struct sockaddr *)&sin6B, &len)) < 0) e(0); if (recv(fd3, buf, sizeof(buf), 0) != 6) e(0); if (memcmp(buf, "Hello!", 6) != 0) e(0); if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0); if (close(fd3) != 0) e(0); /* * If the connection pending acceptance is aborted, the listening * socket should pretend as though the connection was never there. */ if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 0; tv.tv_usec = 0; if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0); if (!FD_ISSET(fd, &fds)) e(0); memset(&l, 0, sizeof(l)); l.l_onoff = 1; l.l_linger = 0; if (setsockopt(fd2, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0); if (close(fd2) != 0) e(0); if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); if (FD_ISSET(fd, &fds)) e(0); len = sizeof(sin6B); if (accept(fd, (struct sockaddr *)&sin6B, &len) != -1) e(0); if (errno != EWOULDBLOCK) e(0); /* * Try the same thing, but now with the connection sandwiched between * two different pending connections, which should be left intact. */ if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (send(fd2, "A", 1, 0) != 1) e(0); if ((fd3 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd3, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (send(fd3, "B", 1, 0) != 1) e(0); if ((fd4 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd4, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (send(fd4, "C", 1, 0) != 1) e(0); if (setsockopt(fd3, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0); if (close(fd3) != 0) e(0); len = sizeof(sin6B); if ((fd3 = accept(fd, (struct sockaddr *)&sin6B, &len)) < 0) e(0); if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'A') e(0); if (close(fd3) != 0) e(0); if (close(fd2) != 0) e(0); FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = 0; tv.tv_usec = 0; if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0); if (!FD_ISSET(fd, &fds)) e(0); len = sizeof(sin6B); if ((fd3 = accept(fd, (struct sockaddr *)&sin6B, &len)) < 0) e(0); if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'C') e(0); if (close(fd3) != 0) e(0); if (close(fd4) != 0) e(0); if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); if (FD_ISSET(fd, &fds)) e(0); len = sizeof(sin6B); if (accept(fd, (struct sockaddr *)&sin6B, &len) != -1) e(0); if (errno != EWOULDBLOCK) e(0); /* * If the listening socket was closed, the sockets pending acceptance * should be reset. */ if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if ((fd3 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd3, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); if (close(fd) != 0) e(0); if (recv(fd2, buf, sizeof(buf), 0) != -1) e(0); if (errno != ECONNRESET) e(0); if (recv(fd2, buf, sizeof(buf), 0) != 0) e(0); if (recv(fd3, buf, sizeof(buf), 0) != -1) e(0); if (errno != ECONNRESET) e(0); if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0); if (close(fd3) != 0) e(0); if (close(fd2) != 0) e(0); } /* * Obtain a pair of connected TCP socket. */ static int get_tcp_pair(int domain, int type, int protocol, int fd[2]) { struct sockaddr_in6 sin6; struct sockaddr_in sin; struct sockaddr *addr; socklen_t addr_len, len; int lfd, val; if (domain == AF_INET6) { memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); addr = (struct sockaddr *)&sin6; addr_len = sizeof(sin6); } else { assert(domain == AF_INET); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr = (struct sockaddr *)&sin; addr_len = sizeof(sin); } if ((lfd = socket(domain, type, protocol)) < 0) e(0); if (bind(lfd, addr, addr_len) != 0) e(0); len = addr_len; if (getsockname(lfd, addr, &len) != 0) e(0); if (len != addr_len) e(0); if (listen(lfd, 1) != 0) e(0); if ((fd[0] = socket(domain, type, protocol)) < 0) e(0); val = 1; if (setsockopt(fd[0], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != 0) e(0); if (connect(fd[0], addr, addr_len) != 0) e(0); len = addr_len; if ((fd[1] = accept(lfd, addr, &len)) < 0) e(0); if (len != addr_len) e(0); if (setsockopt(fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != 0) e(0); if (close(lfd) != 0) e(0); return 0; } /* * Test large transfers and MSG_WAITALL. */ static void test91l(void) { int fd[2]; subtest = 12; get_tcp_pair(AF_INET6, SOCK_STREAM, 0, fd); socklib_large_transfers(fd); get_tcp_pair(AF_INET, SOCK_STREAM, 0, fd); socklib_large_transfers(fd); } /* * 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. */ static void test91m(void) { int fd[2]; subtest = 13; get_tcp_pair(AF_INET6, SOCK_STREAM, 0, fd); socklib_producer_consumer(fd); get_tcp_pair(AF_INET, SOCK_STREAM, 0, fd); socklib_producer_consumer(fd); } /* * Cause a receive call on the peer side of the connection of 'fd' to be * aborted in a protocol-specific way. Return -1 to indicate that the given * file descriptor has been closed. */ static int test91_reset(int fd, const char * data __unused, size_t len __unused) { struct linger l; l.l_onoff = 1; l.l_linger = 0; if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0); if (close(fd) != 0) e(0); return -1; } /* * Test for receiving on stream sockets. In particular, test SO_RCVLOWAT, * MSG_PEEK, MSG_DONTWAIT, and MSG_WAITALL. */ static void test91n(void) { subtest = 14; socklib_stream_recv(get_tcp_pair, AF_INET, SOCK_STREAM, test91_reset); } /* * Return the send and receive buffer sizes for sockets of the given type. The * two individual values are stored in 'sndbuf' and 'rcvbuf', for each that is * not NULL, and the sum is returned from the call. */ static int get_buf_sizes(int type, int * sndbufp, int * rcvbufp) { socklen_t len; int fd, sndbuf, rcvbuf; if ((fd = socket(AF_INET, type, 0)) < 0) e(0); len = sizeof(sndbuf); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, &len) != 0) e(0); if (len != sizeof(sndbuf)) e(0); if (sndbufp != NULL) *sndbufp = sndbuf; len = sizeof(rcvbuf); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len) != 0) e(0); if (len != sizeof(rcvbuf)) e(0); if (rcvbufp != NULL) *rcvbufp = rcvbuf; if (close(fd) != 0) e(0); return sndbuf + rcvbuf; } /* * The following constant should be set to the window size used within lwIP. * There is currently no way to obtain this constant from the LWIP service, nor * would that be information that should ever be used by general applications, * but we need it to fill socket receive queues in a reliable way. TODO: find * a better solution for this general problem. */ #define WINDOW_SIZE 16384 /* TCP_WND in lwipopt.h, keep in sync! */ #define CHUNK 4096 /* base I/O chunk size */ #define USLEEP_TIME 250000 /* increase on wimpy platforms if needed */ /* * Fill the receive of socket 'rfd' with data, and if 'fill_send' is non-zero, * also the send queue of socket 'sfd'. If 'fill_send' is zero, 'delta' may be * a non-zero value indicating how many bytes extra (delta > 0) or fewer * (delta < 0) should be sent compared to the receive queue size. */ static void fill_tcp_bufs(int sfd, int rfd, int fill_send, int delta) { unsigned char buf[CHUNK], c; socklen_t len; int sndbuf, rcvbuf, mss, chunk, left, res; assert(!fill_send || delta == 0); (void)get_buf_sizes(SOCK_STREAM, &sndbuf, &rcvbuf); len = sizeof(mss); if (getsockopt(sfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len) != 0) e(0); left = rcvbuf; if (delta < 0) left += delta; memset(buf, 0, sizeof(buf)); /* * In general, TCP is not designed for what we want to do here, which * is to control the contents of the receive buffer down to the last * byte. We already assume that the caller has disabled the Nagle * algorithm, but we still have to deal with other algorithms that * effectively get in the way of full control of the receive buffer. * * In particular, we have to work around an issue where lwIP decides to * start shrinking the window earlier than necessary. This issue * triggers during the transition from a fully open window to a reduced * window. If no acknowledgement is sent when exactly that point is * reached, the next acknowlegment will not announce the full size of * the remainder of the window. This appears to be part of the silly * window avoidance logic, so it is probably intentional behavior and * thus we have to work around it. * * So far it appears that filling up just the window size does the job, * as long as the last segment is a full MSS-sized segment and each * segment is acknowledged (which is why we send data in the other * direction). Anything short of that may trigger edge cases that, in * some cases, show up only on slow platforms (e.g. BeagleBones). * * Note that while test91z also fills up receive queues using its own * algorithm, it sets the receive queue to the window size, thereby * avoiding the need for this more complicated algorithm. */ for (left = rcvbuf - WINDOW_SIZE; left > 0; left -= chunk) { chunk = (left % mss != 0) ? (left % mss) : mss; assert(chunk <= left); if (send(sfd, buf, chunk, 0) != chunk) e(0); if (send(rfd, ".", 1, 0) != 1) e(0); if (recv(sfd, &c, 1, 0) != 1) e(0); if (c != '.') e(0); } /* We are done with the hard part. Now fill up the rest. */ if (fill_send) delta = sndbuf; for (left = WINDOW_SIZE + delta; left > 0; left -= res) { chunk = MIN(left, sizeof(buf)); res = send(sfd, buf, chunk, 0); if (res <= 0) e(0); if (res > chunk) e(0); } } /* * Signal handler which just needs to exist, so that invoking it will interrupt * an ongoing system call. */ static void test91_got_signal(int sig __unused) { /* Nothing. */ } /* * Test for sending on stream sockets. The quick summary here is that send() * should basically act as the mirror of recv(MSG_WAITALL), i.e., it should * keep suspending until all data is sent (or the call is interrupted or no * more can possibly be sent), and, SO_SNDLOWAT, mirroring SO_RCVLOWAT, acts as * an admission test for the send: nothing is sent until there is room in the * send buffer (i.e., the peer's receive buffer) for at least the low send * watermark, or the whole send request length, whichever is smaller. In * addition, select(2) should use the same threshold. * * This test is a copy of test90v, and would be in socklib instead, were it not * for the fact that TCP's segmentation and silly window avoidance make it * impossible to perform the same exact, byte-granular test. Instead, this TCP * implementation paints with a somewhat broader brush, using send and receive * chunk sizes large enough to overcome the normally desirable TCP features * that are now getting in the way. As a result, this copy of the test is not * only somewhat less effective but also a bit more reliant on specific (TCP) * settings, although the whole test is still way too useful to skip at all. */ static void sub91o(int iroom, int istate, int slowat, int len, int bits, int act) { struct sigaction sa; struct timeval tv; char buf[CHUNK * 4]; fd_set fds; pid_t pid; int fd[2], min, flags, res, err; int pfd[2], orig_iroom, eroom, tstate, fl, status; if (get_tcp_pair(AF_INET6, SOCK_STREAM, 0, fd) != 0) e(0); /* * Set up the initial condition on the sockets. */ fill_tcp_bufs(fd[0], fd[1], 1 /*fill_send*/, 0 /*delta*/); /* * Receive a bit more than we send, to free up enough room (the MSS) to * get things going again. */ orig_iroom = iroom; iroom += iroom / 2; if (iroom > 0) if (recv(fd[1], buf, iroom, 0) != iroom) e(0); switch (istate) { case 0: break; case 1: if (shutdown(fd[0], SHUT_WR) != 0) e(0); break; case 2: if (close(fd[1]) != 0) e(0); break; } if (setsockopt(fd[0], SOL_SOCKET, SO_SNDLOWAT, &slowat, sizeof(slowat)) != 0) e(0); /* SO_SNDLOWAT is always bounded by the actual send length. */ min = MIN(len, slowat); flags = MSG_NOSIGNAL; if (bits & 1) flags |= MSG_DONTWAIT; /* * Do a quick select test to see if its result indeed matches whether * the available space in the "send" 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, NULL, &fds, NULL, &tv); if (res < 0 || res > 1) e(0); if (res != (iroom >= slowat || istate > 0)) e(0); if (res == 1 && !FD_ISSET(fd[0], &fds)) e(0); /* * 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 the socket state disallows further sending, or when all * data could be sent, or when the call was non-blocking. The low * send watermark only helps determine whether anything was sent here. */ if (istate > 0 || iroom >= len || (flags & MSG_DONTWAIT)) { res = send(fd[0], buf, len, flags); if (istate > 0) { if (res != -1) e(0); if (errno != EPIPE && errno != ECONNRESET) e(0); } else if (iroom >= len) { if (res != len) e(0); } else if (iroom >= min) { if (res < orig_iroom || res > iroom) e(0); } else { if (res != -1) e(0); if (errno != EWOULDBLOCK) e(0); } /* Early cleanup and return to avoid even more code clutter. */ if (istate != 2 && close(fd[1]) != 0) e(0); if (close(fd[0]) != 0) e(0); return; } /* * Now starts the interesting stuff: the send call should now block, * even though if we add MSG_DONTWAIT it may not return EWOULDBLOCK, * because MSG_DONTWAIT prevents the send from blocking after partial * completion. As such, we can only test our expectations by letting * the call block, in a child process, and waiting. We do test as much * of the above assumption as we can for safety right here, but this is * not a substitute for actually blocking even in these cases! */ if (iroom < min) { if (send(fd[0], buf, len, flags | MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); } /* * If (act < 9), we receive 0, 1, or 2 bytes from the receive queue * before forcing the send call to terminate in one of three ways. * * If (act == 9), we use a signal to interrupt the send call. */ if (act < 9) { eroom = (act % 3) * (CHUNK + CHUNK / 2 - 1); tstate = act / 3; } else eroom = 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 == 9) { memset(&sa, 0, sizeof(sa)); sa.sa_handler = test91_got_signal; if (sigaction(SIGUSR1, &sa, NULL) != 0) e(0); } res = send(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); exit(errct); case -1: e(0); } if (close(pfd[1]) != 0) e(0); /* * Allow the child to enter the blocking send(2), and check the pipe * to see if it is really blocked. */ if (usleep(USLEEP_TIME) != 0) e(0); if (read(pfd[0], &res, sizeof(res)) != -1) e(0); if (errno != EAGAIN) e(0); if (eroom > 0) { if (recv(fd[1], buf, eroom, 0) != eroom) e(0); /* * The threshold for the send is now met if the entire request * has been satisfied. */ if (iroom + eroom >= 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 != len) e(0); /* Bail out. */ goto cleanup; } } if (act < 9) { /* * Now test various ways to terminate the send call. * * For other socket drivers, there should also be a case where * a socket error is raised instead. For UDS there is no way * to do that on stream-type sockets, not even with SO_LINGER. */ switch (tstate) { case 0: if (shutdown(fd[0], SHUT_WR) != 0) e(0); break; case 1: if (close(fd[1]) != 0) e(0); fd[1] = -1; break; case 2: fd[1] = test91_reset(fd[1], NULL, 0); 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 the send met the threshold before being terminate or interrupted, * we should at least have sent something. Otherwise, the send was * never admitted and should return EPIPE or ECONNRESET (if the send * was terminated) or EINTR (if the child was killed). */ if (iroom + eroom >= min) { if (res < MIN(orig_iroom, len)) e(0); if (res > MIN(iroom + eroom, len)) e(0); } else { if (res != -1) e(0); if (act < 9) { if (err != EPIPE && err != ECONNRESET) e(0); } else 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 sending on stream sockets. In particular, test SO_SNDLOWAT and * MSG_DONTWAIT. */ static void test91o(void) { int iroom, istate, slowat, len, bits, act; subtest = 15; /* Insanity. */ for (iroom = 0; iroom <= CHUNK * 2; iroom += CHUNK) for (istate = 0; istate <= 2; istate++) for (slowat = CHUNK; slowat <= CHUNK * 2; slowat += CHUNK) for (len = CHUNK; len <= CHUNK * 2; len += CHUNK) for (bits = 0; bits < 2; bits++) for (act = 0; act <= 9; act++) sub91o(iroom, istate, slowat, len, bits, act); } /* * Test filling up the TCP receive queue. In particular, verify that one bug I * ran into (lwIP bug #49128) is resolved. */ static void test91p(void) { char buf[CHUNK]; size_t total, left; ssize_t res; int fd[2]; subtest = 16; if (get_tcp_pair(AF_INET, SOCK_STREAM, 0, fd) != 0) e(0); /* * Fill up the sockets' queues. */ total = get_buf_sizes(SOCK_STREAM, NULL, NULL); fill_tcp_bufs(fd[0], fd[1], 1 /*fill_send*/, 0 /*delta*/); /* * Wait long enough for the zero window probing to kick in, which used * to cause an ACK storm livelock (lwIP bug #49128). */ sleep(1); /* * Actually sleep a bit longer, so that the polling timer kicks in and * at least attempts to send more. This is merely an attempt to * exercise some of the polling code, and should not have any actual * effect on the rest of the test. */ sleep(5); /* * Make sure all the data still arrives. */ for (left = total; left > 0; left -= res) { res = recv(fd[1], buf, sizeof(buf), 0); if (res <= 0) e(0); if (res > left) e(0); } if (recv(fd[1], buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); /* * Attempt to shut down the socket for writing after filling up the * send queue. The TCP FIN should then arrive after all the data. */ for (left = total; left > 0; left -= res) { res = send(fd[0], buf, MIN(left, sizeof(buf)), 0); if (res <= 0) e(0); if (res > left) e(0); } if (shutdown(fd[0], SHUT_WR) != 0) e(0); for (left = total; left > 0; left -= res) { res = recv(fd[1], buf, sizeof(buf), 0); if (res <= 0) e(0); if (res > left) e(0); } if (recv(fd[1], buf, sizeof(buf), 0) != 0) e(0); if (send(fd[1], "A", 1, 0) != 1) e(0); if (recv(fd[0], buf, sizeof(buf), 0) != 1) e(0); if (buf[0] != 'A') e(0); if (close(fd[1]) != 0) e(0); if (close(fd[0]) != 0) e(0); } /* * Attempt to fill up a TCP send queue with small amounts of data. While it * may or may not be possible to fill up the entire send queue with small * requests, but at least trying should not cause any problems, like the one I * filed as lwIP bug #49218. */ static void test91q(void) { ssize_t res; size_t count; char c, c2; int fd[2]; subtest = 17; if (get_tcp_pair(AF_INET6, SOCK_STREAM, 0, fd) != 0) e(0); count = 0; for (c = 0; (res = send(fd[0], &c, sizeof(c), MSG_DONTWAIT)) > 0; c++) count += res; if (res != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (count < CHUNK) e(0); if (shutdown(fd[0], SHUT_WR) != 0) e(0); for (c2 = 0; count > 0; count--, c2++) { if (recv(fd[1], &c, sizeof(c), 0) != 1) e(0); if (c != c2) e(0); } if (recv(fd[1], &c, sizeof(c), 0) != 0) e(0); if (close(fd[0]) != 0) e(0); if (close(fd[1]) != 0) e(0); } /* * Test that SO_RCVLOWAT is limited to the size of the receive buffer. */ static void sub91r_recv(int fill_delta, int rlowat_delta, int exp_delta) { char *buf; size_t buflen; int fd[2], rlowat, rcvlen, res; if (get_tcp_pair(AF_INET, SOCK_STREAM, 0, fd) != 0) e(0); /* * Fill up the socket's receive queue, possibly minus one byte. */ (void)get_buf_sizes(SOCK_STREAM, NULL, &rcvlen); buflen = MAX(CHUNK, rcvlen + 1); if ((buf = malloc(buflen)) == NULL) e(0); fill_tcp_bufs(fd[1], fd[0], 0 /*fill_send*/, fill_delta); rlowat = rcvlen + rlowat_delta; if (setsockopt(fd[0], SOL_SOCKET, SO_RCVLOWAT, &rlowat, sizeof(rlowat)) != 0) e(0); if (ioctl(fd[0], FIONREAD, &res) != 0) e(0); if (res != rcvlen + fill_delta) e(0); res = recv(fd[0], buf, rcvlen + 1, MSG_DONTWAIT); if (exp_delta < 0) { if (res != -1) e(0); if (errno != EWOULDBLOCK) e(0); } else if (res != rcvlen - exp_delta) e(0); free(buf); if (close(fd[0]) != 0) e(0); if (close(fd[1]) != 0) e(0); } /* * Test that SO_SNDLOWAT is limited to the size of the send buffer. */ static void sub91r_send(int fill, int slowat_delta, int exp_delta) { char *buf; size_t buflen; int fd[2], sndlen, slowat, res; if (get_tcp_pair(AF_INET6, SOCK_STREAM, 0, fd) != 0) e(0); /* * Fill up the socket's receive queue, and possibly put one extra byte * in the other socket's send queue. */ (void)get_buf_sizes(SOCK_STREAM, &sndlen, NULL); buflen = MAX(CHUNK, sndlen + 1); if ((buf = malloc(buflen)) == NULL) e(0); memset(buf, 0, buflen); fill_tcp_bufs(fd[0], fd[1], 0 /*fill_send*/, 0 /*delta*/); slowat = sndlen + slowat_delta; if (fill > 0) { memset(buf, 0, fill); if (send(fd[0], buf, fill, 0) != fill) e(0); } if (setsockopt(fd[0], SOL_SOCKET, SO_SNDLOWAT, &slowat, sizeof(slowat)) != 0) e(0); res = send(fd[0], buf, sndlen + 1, MSG_DONTWAIT); if (exp_delta < 0) { if (res != -1) e(0); if (errno != EWOULDBLOCK) e(0); } else if (res != sndlen - exp_delta) e(0); free(buf); if (close(fd[0]) != 0) e(0); if (close(fd[1]) != 0) e(0); } /* * Test that on stream sockets, SO_RCVLOWAT and SO_SNDLOWAT are limited to * their respective buffer sizes. This test is derived from test90w, but * merging the two into socklib would get too messy unfortunately. */ static void test91r(void) { subtest = 18; /* * With the receive buffer filled except for one byte, all data should * be retrieved unless the threshold is not met. */ sub91r_recv(-1, -1, 1); sub91r_recv(-1, 0, -1); sub91r_recv(-1, 1, -1); /* * With the receive buffer filled completely, all data should be * retrieved in all cases. */ sub91r_recv(0, -1, 0); sub91r_recv(0, 0, 0); sub91r_recv(0, 1, 0); /* * With a send buffer that contains one byte, all data should be sent * unless the threshold is not met. */ sub91r_send(1, -1, 1); sub91r_send(1, 0, -1); sub91r_send(1, 1, -1); /* * With the send buffer filled completely, all data should be sent * in all cases. */ sub91r_send(0, -1, 0); sub91r_send(0, 0, 0); sub91r_send(0, 1, 0); } /* * Test sending and receiving with bad pointers on a TCP socket. */ static void sub91s_tcp(char * ptr) { int fd[2]; memset(ptr, 'X', PAGE_SIZE); if (get_tcp_pair(AF_INET, SOCK_STREAM, 0, fd) != 0) e(0); if (send(fd[0], "A", 1, 0) != 1) e(0); if (send(fd[0], ptr, PAGE_SIZE * 2, MSG_DONTWAIT) != -1) e(0); if (errno != EFAULT) e(0); if (send(fd[0], "B", 1, 0) != 1) e(0); if (shutdown(fd[0], SHUT_WR) != 0) e(0); if (recv(fd[1], &ptr[PAGE_SIZE - 1], PAGE_SIZE, MSG_WAITALL) != -1) e(0); if (errno != EFAULT) e(0); if (recv(fd[1], ptr, 3, MSG_DONTWAIT) != 2) e(0); if (ptr[0] != 'A') e(0); if (ptr[1] != 'B') e(0); if (close(fd[0]) != 0) e(0); if (close(fd[1]) != 0) e(0); } /* * Test sending and receiving with bad pointers on a UDP socket. */ static void sub91s_udp(char * ptr) { struct sockaddr_in6 sin6; int i, fd; if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); memset(ptr, 'A', PAGE_SIZE); if (sendto(fd, &ptr[PAGE_SIZE / 2], PAGE_SIZE, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != -1) e(0); if (errno != EFAULT) e(0); memset(ptr, 'B', PAGE_SIZE); if (sendto(fd, ptr, PAGE_SIZE, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != PAGE_SIZE) e(0); memset(ptr, 0, PAGE_SIZE); if (recvfrom(fd, &ptr[PAGE_SIZE / 2], PAGE_SIZE, 0, NULL, 0) != -1) e(0); if (errno != EFAULT) e(0); if (recvfrom(fd, ptr, PAGE_SIZE * 2, 0, NULL, 0) != PAGE_SIZE) e(0); for (i = 0; i < PAGE_SIZE; i++) if (ptr[i] != 'B') e(0); if (close(fd) != 0) e(0); } /* * Test sending and receiving with bad pointers. */ static void test91s(void) { char *ptr; subtest = 19; if ((ptr = mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) e(0); if (munmap(&ptr[PAGE_SIZE], PAGE_SIZE) != 0) e(0); sub91s_tcp(ptr); sub91s_udp(ptr); if (munmap(ptr, PAGE_SIZE) != 0) e(0); } /* * Test closing TCP sockets and SO_LINGER. */ static void test91t(void) { char buf[CHUNK]; size_t total, left; ssize_t res; int i, fd[2]; subtest = 20; total = get_buf_sizes(SOCK_STREAM, NULL, NULL); memset(buf, 0, sizeof(buf)); /* * Test two cases of handling connection closure: * * 1) the FIN+ACK case, where the closing side finishes the close * operation once its FIN has been acknowledged; * 2) the FIN+FIN case, where the closing side finishes the close * operation once it has sent its own FIN (possibly without getting * an ACK yet) and also receives a FIN from the other side. * * Since lwIP prevents us from detecting #1 without polling, which * happens twice a second, we can test #2 by shutting down the peer * connection immediately after (i=0/2) or even before (i=4/5) closing * this side. */ for (i = 0; i <= 5; i++) { if (get_tcp_pair(AF_INET, SOCK_STREAM, 0, fd) != 0) e(0); fill_tcp_bufs(fd[0], fd[1], 1 /*fill_send*/, 0 /*delta*/); if (close(fd[0]) != 0) e(0); if (i >= 4 && shutdown(fd[1], SHUT_WR) != 0) e(0); for (left = total; left > 0; left -= res) { res = recv(fd[1], buf, sizeof(buf), 0); if (res <= 0) e(0); if (res > left) e(0); } if (recv(fd[1], buf, sizeof(buf), 0) != 0) e(0); sleep(i & 1); /* * We can still send to the receiving end, but this will cause * a reset. We do this only if we have not just shut down the * writing end of this socket. Also test regular closing. */ if (i / 2 == 1) { if (send(fd[1], "B", 1, 0) != 1) e(0); if (recv(fd[1], buf, sizeof(buf), 0) != -1) e(0); if (errno != ECONNRESET) e(0); } if (close(fd[1]) != 0) e(0); } /* * Test that closing a socket with data still in its receive queue * causes a RST to be issued. */ if (get_tcp_pair(AF_INET6, SOCK_STREAM, 0, fd) != 0) e(0); if (send(fd[0], "C", 1, 0) != 1) e(0); if (recv(fd[1], buf, sizeof(buf), MSG_PEEK) != 1) e(0); if (close(fd[1]) != 0) e(0); if (recv(fd[0], buf, sizeof(buf), 0) != -1) e(0); if (errno != ECONNRESET) e(0); if (close(fd[0]) != 0) e(0); } /* * Test closing a socket with a particular SO_LINGER setting. */ static void sub91u(int nb, int mode, int intr, int onoff, int linger) { char buf[CHUNK]; struct timeval tv1, tv2; struct linger l; pid_t pid; int fd[2], pfd[2], fl, val, res, status; get_tcp_pair((mode & 1) ? AF_INET6 : AF_INET, SOCK_STREAM, 0, fd); /* * Set up the socket pair. */ fill_tcp_bufs(fd[0], fd[1], 0 /*fill_send*/, 1 /*delta*/); if (mode == 3 && shutdown(fd[1], SHUT_WR) != 0) e(0); l.l_onoff = onoff; l.l_linger = (linger) ? (2 + intr) : 0; if (setsockopt(fd[0], SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0); if (nb) { if ((fl = fcntl(fd[0], F_GETFL)) == -1) e(0); if (fcntl(fd[0], F_SETFL, fl | O_NONBLOCK) != 0) e(0); } /* We need two-way parent-child communication for this test. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) != 0) e(0); pid = fork(); switch (pid) { case 0: errct = 0; if (close(pfd[1]) != 0) e(0); if (close(fd[1]) != 0) e(0); signal(SIGUSR1, test91_got_signal); /* * Do not start closing the file descriptor until after the * parent has closed its copy. */ if (read(pfd[0], &val, sizeof(val)) != sizeof(val)) e(0); if (val != 0) e(0); if (gettimeofday(&tv1, NULL) != 0) e(0); /* Perform the possibly blocking close(2) call. */ if (intr) { if (close(fd[0]) != -1) e(0); if (errno != EINPROGRESS) e(0); } else if (close(fd[0]) != 0) e(0); if (gettimeofday(&tv2, NULL) != 0) e(0); timersub(&tv2, &tv1, &tv1); /* Polling may take 500ms. */ val = tv1.tv_sec + ((tv1.tv_usec > 750000) ? 1 : 0); if (val < 0 || val > 2) e(0); /* Tell the parent how long the close(2) took, in seconds. */ if (write(pfd[0], &val, sizeof(val)) != sizeof(val)) e(0); exit(errct); case -1: e(0); } /* Close file descriptors here and then let the child run. */ if (close(pfd[0]) != 0) e(0); if (close(fd[0]) != 0) e(0); val = 0; if (write(pfd[1], &val, sizeof(val)) != sizeof(val)) e(0); /* * Wait one second until we try to close the connection ourselves, if * applicable. If we are killing the child, we add yet another second * to tell the difference between a clean close and a timeout/reset. */ sleep(1); if (intr) { if (kill(pid, SIGUSR1) != 0) e(0); sleep(1); } /* * Trigger various ways in which the connection is closed, or not, in * which case the linger timeout should cause a reset. */ switch (mode) { case 0: /* do nothing; expect reset */ break; case 1: /* FIN + rFIN */ if (shutdown(fd[1], SHUT_WR) != 0) e(0); /* * The FIN cannot yet be sent due to the zero-sized receive * window. Make some room so that it can be sent. */ /* FALLTHROUGH */ case 2: /* FIN + ACK */ case 3: /* rFIN + FIN */ if (recv(fd[1], buf, sizeof(buf), 0) <= 0) e(0); break; case 4: /* RST */ l.l_onoff = 1; l.l_linger = 0; if (setsockopt(fd[1], SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0); if (close(fd[1]) != 0) e(0); fd[1] = -1; break; default: e(0); } /* * Make absolutely sure that the linger timer has triggered and we do * not end up exploiting race conditions in the tests below. As a * result this subtest takes over a minute but at least it has already * triggered a whole bunch of bugs (and produced lwIP patch #9125). */ sleep(2); /* Get the number of seconds spent in the close(2) call. */ if (read(pfd[1], &val, sizeof(val)) != sizeof(val)) e(0); /* * See if the close(2) call took as long as expected and check that the * other side of the connection sees either EOF or a reset as expected. */ if (mode == 0) { if (nb) { if (val != 0) e(0); sleep(2); } else if (!intr) { if (val != linger * 2) e(0); } else if (val != 1) e(0); /* See if the connection was indeed reset. */ while ((res = recv(fd[1], buf, sizeof(buf), 0)) > 0) ; if (res != -1) e(0); if (errno != ECONNRESET) e(0); } else { if (val != ((onoff && !nb) || intr)) e(0); /* Check for EOF unless we already closed the socket. */ if (fd[1] != -1) { while ((res = recv(fd[1], buf, sizeof(buf), 0)) > 0) ; if (res != 0) e(0); } } /* Clean up. */ if (fd[1] != -1 && close(fd[1]) != 0) e(0); if (close(pfd[1]) != 0) e(0); if (wait(&status) != pid) e(0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); } /* * Test SO_LINGER support in various configurations. It is worth noting that I * implemented a somewhat broken version of SO_LINGER because lwIP does not * allow for proper detection of our FIN being acknowledged in all cases (this * is documented in the service). As a result, a close(2) call may return * earlier than it is supposed to, namely as soon as 1) we sent a FIN, and * 2) we received a FIN from the other side. We also test the somewhat broken * behavior here, as above all else the aim is to make sure that the service * code works as expected. */ static void test91u(void) { int nb, mode; subtest = 21; /* * * In all of the following scenarios, close(2) should only ever return * success, so that the caller knows that the file descriptor has been * closed. */ for (nb = 0; nb <= 1; nb++) { /* * SO_LINGER off: the close(2) call should return immediately, * and the connection should be closed in the background. */ for (mode = 1; mode <= 4; mode++) sub91u(nb, mode, 0, 0, 0); /* * SO_LINGER on with a zero timeout: the close(2) call should * return immediately, and the connection should be reset. */ sub91u(nb, 0, 0, 1, 0); /* * SO_LINGER on with a non-zero timeout: the close(2) call * should return immediately for non-blocking sockets only, and * otherwise as soon as either the connection is closed or the * timeout triggers, in which case the connection is reset. */ for (mode = 0; mode <= 4; mode++) sub91u(nb, mode, 0, 1, 1); } /* * Test signal-interrupting blocked close(2) calls with SO_LINGER. In * such cases, the close(2) should return EINPROGRESS to indicate that * the file descriptor has been closed, and the original close action * (with the original timeout) should proceed in the background. */ for (mode = 0; mode <= 4; mode++) sub91u(0, mode, 1, 1, 1); } /* * Test shutdown on listening TCP sockets. */ static void sub91v(int how) { struct sockaddr_in sin; socklen_t len; char c; int fd, fd2, fd3, fl; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); len = sizeof(sin); if (getsockname(fd, (struct sockaddr *)&sin, &len) != 0) e(0); if (len != sizeof(sin)) e(0); if (listen(fd, 1) != 0) e(0); if ((fd2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd2, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); if (shutdown(fd, how) != 0) e(0); len = sizeof(sin); if ((fd3 = accept(fd, (struct sockaddr *)&sin, &len)) < 0) e(0); if (len != sizeof(sin)) e(0); if (write(fd2, "A", 1) != 1) e(0); if (read(fd3, &c, 1) != 1) e(0); if (c != 'A') e(0); if (write(fd3, "B", 1) != 1) e(0); if (read(fd2, &c, 1) != 1) e(0); if (c != 'B') e(0); len = sizeof(sin); if (accept(fd, (struct sockaddr *)&sin, &len) != -1) e(0); if (errno != ECONNABORTED) e(0); if ((fl = fcntl(fd, F_GETFL)) == -1) e(0); if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0); len = sizeof(sin); if (accept(fd, (struct sockaddr *)&sin, &len) != -1) e(0); if (errno != ECONNABORTED) e(0); if (close(fd3) != 0) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); } /* * Test shutdown on listening TCP sockets. This test is derived from test90x. */ static void test91v(void) { const int hows[] = { SHUT_RD, SHUT_WR, SHUT_RDWR }; int i; subtest = 22; for (i = 0; i < __arraycount(hows); i++) sub91v(hows[i]); } /* * Test basic sysctl(2) socket enumeration support. */ static void test91w(void) { struct kinfo_pcb ki; struct sockaddr_in lsin, rsin; struct sockaddr_in6 lsin6, rsin6; char buf[CHUNK]; uint16_t local_port, remote_port; socklen_t len; int fd[2], val, sndbuf, rcvbuf; subtest = 23; /* * First test TCP. */ get_tcp_pair(AF_INET, SOCK_STREAM, 0, fd); val = 0; if (setsockopt(fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != 0) e(0); len = sizeof(lsin); if (getsockname(fd[0], (struct sockaddr *)&lsin, &len) != 0) e(0); if (len != sizeof(lsin)) e(0); local_port = ntohs(lsin.sin_port); if (getpeername(fd[0], (struct sockaddr *)&rsin, &len) != 0) e(0); if (len != sizeof(rsin)) e(0); remote_port = ntohs(rsin.sin_port); if (send(fd[0], "ABCDE", 5, 0) != 5) e(0); /* Allow the data to reach the other side and be acknowledged. */ sleep(1); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_ESTABLISHED) e(0); if (!(ki.ki_tflags & TF_NODELAY)) e(0); if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, remote_port, local_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_ESTABLISHED) e(0); if (ki.ki_tflags & TF_NODELAY) e(0); if (memcmp(&ki.ki_src, &rsin, sizeof(rsin)) != 0) e(0); if (memcmp(&ki.ki_dst, &lsin, sizeof(lsin)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 5) e(0); if (recv(fd[1], buf, sizeof(buf), 0) != 5) e(0); if (close(fd[0]) != 0) e(0); if (close(fd[1]) != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_TIME_WAIT) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); /* Test IPv6 sockets as well. */ get_tcp_pair(AF_INET6, SOCK_STREAM, 0, fd); len = sizeof(lsin6); if (getsockname(fd[0], (struct sockaddr *)&lsin6, &len) != 0) e(0); if (len != sizeof(lsin6)) e(0); local_port = ntohs(lsin6.sin6_port); if (getpeername(fd[0], (struct sockaddr *)&rsin6, &len) != 0) e(0); if (len != sizeof(rsin6)) e(0); remote_port = ntohs(rsin6.sin6_port); memset(buf, 0, sizeof(buf)); /* We fill up the queues so we do not need to sleep in this case. */ (void)get_buf_sizes(SOCK_STREAM, &sndbuf, &rcvbuf); fill_tcp_bufs(fd[0], fd[1], 1 /*fill_send*/, 0 /*delta*/); if (send(fd[0], buf, 1, MSG_DONTWAIT) != -1) e(0); if (errno != EWOULDBLOCK) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_ESTABLISHED) e(0); if (!(ki.ki_tflags & TF_NODELAY)) e(0); if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); if (ki.ki_sndq != (size_t)sndbuf) e(0); if (ki.ki_rcvq != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 0) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, remote_port, local_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_ESTABLISHED) e(0); if (!(ki.ki_tflags & TF_NODELAY)) e(0); if (memcmp(&ki.ki_src, &rsin6, sizeof(rsin6)) != 0) e(0); if (memcmp(&ki.ki_dst, &lsin6, sizeof(lsin6)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != (size_t)rcvbuf) e(0); if (close(fd[0]) != 0) e(0); if (close(fd[1]) != 0) e(0); /* Bound and listening sockets should show up as well. */ if ((fd[0] = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, 0, 0, &ki) != 0) e(0); memset(&lsin, 0, sizeof(lsin)); lsin.sin_len = sizeof(lsin); lsin.sin_family = AF_INET; lsin.sin_port = htons(TEST_PORT_A); lsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bind(fd[0], (struct sockaddr *)&lsin, sizeof(lsin)) != 0) e(0); memset(&rsin, 0, sizeof(rsin)); rsin.sin_len = sizeof(rsin); rsin.sin_family = AF_INET; if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, TEST_PORT_A, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_CLOSED) e(0); if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); if (listen(fd[0], 1)) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, TEST_PORT_A, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_LISTEN) e(0); if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); if (close(fd[0]) != 0) e(0); /* Test IPv6 sockets as well. */ if ((fd[0] = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, 0, 0, &ki) != 0) e(0); val = 1; if (setsockopt(fd[0], IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&lsin6, 0, sizeof(lsin6)); lsin6.sin6_len = sizeof(lsin6); lsin6.sin6_family = AF_INET6; lsin6.sin6_port = htons(TEST_PORT_A); memcpy(&lsin6.sin6_addr, &in6addr_loopback, sizeof(lsin6.sin6_addr)); if (bind(fd[0], (struct sockaddr *)&lsin6, sizeof(lsin6)) != 0) e(0); memset(&rsin6, 0, sizeof(rsin6)); rsin6.sin6_len = sizeof(rsin6); rsin6.sin6_family = AF_INET6; if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, TEST_PORT_A, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_CLOSED) e(0); if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); if (!(ki.ki_pflags & IN6P_IPV6_V6ONLY)) e(0); if (listen(fd[0], 1)) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, TEST_PORT_A, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_LISTEN) e(0); if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); if (!(ki.ki_pflags & IN6P_IPV6_V6ONLY)) e(0); if (close(fd[0]) != 0) e(0); /* * I do not dare binding to ANY so we cannot test IPV6_V6ONLY properly * here. Instead we repeat the test and ensure the IN6P_IPV6_V6ONLY * flag accurately represents the current state. */ if ((fd[0] = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, 0, 0, &ki) != 0) e(0); val = 0; if (setsockopt(fd[0], IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); if (bind(fd[0], (struct sockaddr *)&lsin6, sizeof(lsin6)) != 0) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, TEST_PORT_A, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_CLOSED) e(0); if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); if (!(ki.ki_pflags & IN6P_IPV6_V6ONLY)) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, TEST_PORT_A, 0, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_TCP, TEST_PORT_A, 0, &ki) != 0) e(0); if (close(fd[0]) != 0) e(0); /* * Then test UDP. */ if ((fd[0] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_UDP, 0, 0, &ki) != 0) e(0); memset(&lsin, 0, sizeof(lsin)); lsin.sin_len = sizeof(lsin); lsin.sin_family = AF_INET; lsin.sin_port = htons(TEST_PORT_A); lsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); memset(&rsin, 0, sizeof(rsin)); rsin.sin_len = sizeof(rsin); rsin.sin_family = AF_INET; if (bind(fd[0], (struct sockaddr *)&lsin, sizeof(lsin)) != 0) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_UDP, TEST_PORT_A, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_DGRAM) e(0); if (ki.ki_tstate != 0) e(0); if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); rsin.sin_port = htons(TEST_PORT_B); rsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(fd[0], (struct sockaddr *)&rsin, sizeof(rsin)) != 0) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_UDP, TEST_PORT_A, TEST_PORT_B, &ki) != 1) e(0); if (ki.ki_type != SOCK_DGRAM) e(0); if (ki.ki_tstate != 0) e(0); if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_UDP, TEST_PORT_B, TEST_PORT_A, &ki) != 0) e(0); if ((fd[1] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); if (bind(fd[1], (struct sockaddr *)&rsin, sizeof(rsin)) != 0) e(0); if (sendto(fd[1], "ABC", 3, 0, (struct sockaddr *)&lsin, sizeof(lsin)) != 3) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_UDP, TEST_PORT_A, TEST_PORT_B, &ki) != 1) e(0); if (ki.ki_type != SOCK_DGRAM) e(0); if (ki.ki_tstate != 0) e(0); if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq < 3) e(0); /* size is rounded up */ if (socklib_find_pcb("net.inet6.udp6.pcblist", IPPROTO_UDP, TEST_PORT_A, TEST_PORT_B, &ki) != 0) e(0); if (close(fd[0]) != 0) e(0); if (close(fd[1]) != 0) e(0); /* Test IPv6 sockets as well. */ if ((fd[0] = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); if (socklib_find_pcb("net.inet6.udp6.pcblist", IPPROTO_UDP, 0, 0, &ki) != 0) e(0); memset(&lsin6, 0, sizeof(lsin6)); lsin6.sin6_len = sizeof(lsin6); lsin6.sin6_family = AF_INET6; lsin6.sin6_port = htons(TEST_PORT_A); memcpy(&lsin6.sin6_addr, &in6addr_loopback, sizeof(lsin6.sin6_addr)); if (bind(fd[0], (struct sockaddr *)&lsin6, sizeof(lsin6)) != 0) e(0); memset(&rsin6, 0, sizeof(rsin6)); rsin6.sin6_len = sizeof(rsin6); rsin6.sin6_family = AF_INET6; if (socklib_find_pcb("net.inet6.udp6.pcblist", IPPROTO_UDP, TEST_PORT_A, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_DGRAM) e(0); if (ki.ki_tstate != 0) e(0); if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); if (!(ki.ki_pflags & IN6P_IPV6_V6ONLY)) e(0); rsin6.sin6_port = htons(TEST_PORT_B); memcpy(&rsin6.sin6_addr, &in6addr_loopback, sizeof(rsin6.sin6_addr)); if (connect(fd[0], (struct sockaddr *)&rsin6, sizeof(rsin6)) != 0) e(0); if (socklib_find_pcb("net.inet6.udp6.pcblist", IPPROTO_UDP, TEST_PORT_A, TEST_PORT_B, &ki) != 1) e(0); if (ki.ki_type != SOCK_DGRAM) e(0); if (ki.ki_tstate != 0) e(0); if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); if (ki.ki_sndq != 0) e(0); if (ki.ki_rcvq != 0) e(0); if (!(ki.ki_pflags & IN6P_IPV6_V6ONLY)) e(0); if (close(fd[0]) != 0) e(0); if (socklib_find_pcb("net.inet6.udp6.pcblist", IPPROTO_UDP, TEST_PORT_A, TEST_PORT_B, &ki) != 0) e(0); } /* * Test socket enumeration of sockets using IPv4-mapped IPv6 addresses. */ static void test91x(void) { struct sockaddr_in6 sin6; struct sockaddr_in sin; socklen_t len; struct kinfo_pcb ki; unsigned short local_port, remote_port; int fd, fd2, fd3, val; subtest = 24; /* * Test that information from an IPv6 socket bound to an IPv4-mapped * IPv6 address is as expected. For socket enumeration, due to lwIP * limitations we return an IPv4 address instead of an IPv4-mapped IPv6 * address, and that is what this test checks for various sockets. */ if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; if (inet_pton(AF_INET6, "::ffff:"LOOPBACK_IPV4, &sin6.sin6_addr) != 1) 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); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); local_port = ntohs(sin6.sin6_port); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, local_port, 0, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, local_port, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_CLOSED) e(0); memcpy(&sin, &ki.ki_src, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(local_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); memcpy(&sin, &ki.ki_dst, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(0)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_ANY)) e(0); if (listen(fd, 1) != 0) e(0); /* * Test that information from an accepted (IPv6) socket is correct * for a connection from an IPv4 address. */ if ((fd2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(local_port); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(fd2, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); len = sizeof(sin); if (getsockname(fd2, (struct sockaddr *)&sin, &len) != 0) e(0); if (len != sizeof(sin)) e(0); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); remote_port = ntohs(sin.sin_port); len = sizeof(sin6); if ((fd3 = accept(fd, (struct sockaddr *)&sin6, &len)) < 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(remote_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); len = sizeof(sin6); if (getsockname(fd3, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(local_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); len = sizeof(sin6); if (getpeername(fd3, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(remote_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_ESTABLISHED) e(0); memcpy(&sin, &ki.ki_src, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(local_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); memcpy(&sin, &ki.ki_dst, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(remote_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); if (close(fd3) != 0) e(0); if (close(fd2) != 0) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_TIME_WAIT) e(0); memcpy(&sin, &ki.ki_src, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(local_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); memcpy(&sin, &ki.ki_dst, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(remote_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); /* * Test that information from a connected (IPv6) socket is correct * after connecting it to an IPv4 address. */ if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); val = 0; if (setsockopt(fd2, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(local_port); if (inet_pton(AF_INET6, "::ffff:"LOOPBACK_IPV4, &sin6.sin6_addr) != 1) e(0); if (connect(fd2, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); len = sizeof(sin6); if (getsockname(fd2, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); remote_port = ntohs(sin6.sin6_port); len = sizeof(sin6); if (getpeername(fd2, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(local_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, remote_port, local_port, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, remote_port, local_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_ESTABLISHED) e(0); memcpy(&sin, &ki.ki_src, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(remote_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); memcpy(&sin, &ki.ki_dst, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(local_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); len = sizeof(sin6); if ((fd3 = accept(fd, (struct sockaddr *)&sin6, &len)) < 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(remote_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); if (close(fd2) != 0) e(0); if (close(fd3) != 0) e(0); if (close(fd) != 0) e(0); /* * Do one more test on an accepted socket, now without binding the * listening socket to an IPv4-mapped IPv6 address. */ if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &in6addr_any, sizeof(sin6.sin6_addr)); 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); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); local_port = ntohs(sin6.sin6_port); if (listen(fd, 1) != 0) e(0); if ((fd2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(local_port); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (connect(fd2, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); len = sizeof(sin); if (getsockname(fd2, (struct sockaddr *)&sin, &len) != 0) e(0); if (len != sizeof(sin)) e(0); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); remote_port = ntohs(sin.sin_port); len = sizeof(sin6); if ((fd3 = accept(fd, (struct sockaddr *)&sin6, &len)) < 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(remote_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); len = sizeof(sin6); if (getsockname(fd3, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(local_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); len = sizeof(sin6); if (getpeername(fd3, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port != htons(remote_port)) e(0); if (!IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) e(0); if (sin6.sin6_addr.__u6_addr.__u6_addr32[3] != htonl(INADDR_LOOPBACK)) e(0); if (socklib_find_pcb("net.inet6.tcp6.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.tcp.pcblist", IPPROTO_TCP, local_port, remote_port, &ki) != 1) e(0); if (ki.ki_type != SOCK_STREAM) e(0); if (ki.ki_tstate != TCPS_ESTABLISHED) e(0); memcpy(&sin, &ki.ki_src, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(local_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); memcpy(&sin, &ki.ki_dst, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(remote_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); if (close(fd3) != 0) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); /* * Do some very simple UDP socket enumeration tests. The rest is * already tested elsewhere. */ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; if (inet_pton(AF_INET6, "::ffff:"LOOPBACK_IPV4, &sin6.sin6_addr) != 1) 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); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); local_port = ntohs(sin6.sin6_port); if (socklib_find_pcb("net.inet6.udp6.pcblist", IPPROTO_UDP, local_port, 0, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_UDP, local_port, 0, &ki) != 1) e(0); if (ki.ki_type != SOCK_DGRAM) e(0); if (ki.ki_tstate != 0) e(0); memcpy(&sin, &ki.ki_src, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(local_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); memcpy(&sin, &ki.ki_dst, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(0)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_ANY)) e(0); if (close(fd) != 0) e(0); if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); val = 0; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(TEST_PORT_A); if (inet_pton(AF_INET6, "::ffff:"LOOPBACK_IPV4, &sin6.sin6_addr) != 1) e(0); if (connect(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); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); local_port = ntohs(sin6.sin6_port); if (socklib_find_pcb("net.inet6.udp6.pcblist", IPPROTO_UDP, local_port, TEST_PORT_A, &ki) != 0) e(0); if (socklib_find_pcb("net.inet.udp.pcblist", IPPROTO_UDP, local_port, TEST_PORT_A, &ki) != 1) e(0); if (ki.ki_type != SOCK_DGRAM) e(0); if (ki.ki_tstate != 0) e(0); memcpy(&sin, &ki.ki_src, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(local_port)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); memcpy(&sin, &ki.ki_dst, sizeof(sin)); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port != htons(TEST_PORT_A)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); if (close(fd) != 0) e(0); } /* * Test local and remote IPv6 address handling. In particular, test scope IDs * and IPv4-mapped IPv6 addresses. */ static void test91y(void) { subtest = 25; socklib_test_addrs(SOCK_STREAM, 0); socklib_test_addrs(SOCK_DGRAM, 0); } /* * Test low-memory conditions for TCP. */ static void test91z(void) { struct sockaddr_in6 sin6; socklen_t len; unsigned char buf[CHUNK]; struct timeval tv; unsigned int i, j, k; ssize_t res, left; pid_t pid, pid2; static int fds[OPEN_MAX]; static size_t pos[OPEN_MAX]; int lfd, pfd[2], val, sndlen, rcvlen, status; subtest = 26; /* * We use custom send and receive buffer sizes, such that we can * trigger the case that we run out of send buffers without causing * buffers used on the receiving side to empty the buffer pool first. * While the latter case is not unrealistic for practical scenarios, it * is not what we want to test here. It would also cause practical * problems for this test, as the result may be that the loopback * interface (that we use here) starts dropping packets due to being * unable to make copies. * * The aim with these two is that the ratio is such that we run into * the 75% usage limit for the send side without using the other 25% * for receiving purposes. Since our TCP buffer merging guarantees at * most a 50% overhead on the receiving side, the minimum ratio of 5:1 * translates to a worst-case ratio is 10:3 which is just above 75%. * Thus, we should be able to use 80K:16K. Instead, we use 128K:16K, * because otherwise we will run out of sockets before we run out of * buffers. After all, we are not generating any traffic on the socket * pairs in the other direction--something for which we do provision. */ sndlen = 131072; rcvlen = 16384; /* * Unfortunately, filling up receive queues is not easy, and for any * size other than the window size (which is by nature also the minimum * receive queue length that may be set) we would need to work around * the same issue described in fill_tcp_bufs(), which would massively * complicate the implementation of this subtest. For now, make sure * that inconsistent internal changes will trigger this assert. */ assert(rcvlen == WINDOW_SIZE); if ((lfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); if (bind(lfd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); len = sizeof(sin6); if (getsockname(lfd, (struct sockaddr *)&sin6, &len) != 0) e(0); if (listen(lfd, 1) != 0) e(0); /* * Start a child process for the receiving ends. We have to use * another process because we aim to open a total concurrent number of * TCP sockets that exceeds OPEN_MAX. */ if (pipe(pfd) != 0) e(0); pid = fork(); switch (pid) { case 0: errct = 0; if (close(lfd) != 0) e(0); if (close(pfd[1]) != 0) e(0); /* Create socket pairs. */ for (i = 0; ; i++) { if (i == __arraycount(fds)) e(0); if ((fds[i] = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fds[i], (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); val = 1; if (setsockopt(fds[i], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != 0) e(0); if (setsockopt(fds[i], SOL_SOCKET, SO_RCVBUF, &rcvlen, sizeof(rcvlen)) != 0) e(0); /* Synchronization point A. */ if (read(pfd[0], &k, sizeof(k)) != sizeof(k)) e(0); if (k == 0) break; } /* Synchronization point B. */ if (read(pfd[0], &k, sizeof(k)) != sizeof(k)) e(0); if (k != 2) e(0); /* Receive some data from one socket. */ pos[0] = 0; for (left = sizeof(buf) * 2; left > 0; left -= res) { res = recv(fds[0], buf, MIN(left, sizeof(buf)), 0); if (res <= 0) e(0); if (res > left) e(0); for (j = 0; j < res; j++) if (buf[j] != (unsigned char)(pos[0]++)) e(0); } /* Synchronization point C. */ if (read(pfd[0], &k, sizeof(k)) != sizeof(k)) e(0); if (k != 3) e(0); /* * Receive all remaining data from all sockets. Do this in two * steps. First enlarge the receive buffer and empty it, so * that upon resumption, all remaining data is transferred from * the sender to the receiver in one go. Then actually wait * for any remaining data, and the EOF. If we do both in one * step, this part of the test will take several minutes to * complete. Note that the last socket needs special treatment * because its send queue may not have been filled entirely. */ for (k = 0; k <= i; k++) { if (setsockopt(fds[i], SOL_SOCKET, SO_RCVBUF, &rcvlen, sizeof(rcvlen)) != 0) e(0); pos[k] = (k == 0) ? (sizeof(buf) * 2) : 0; for (left = sndlen + rcvlen - pos[k]; left > 0; left -= res) { res = recv(fds[k], buf, MIN(left, sizeof(buf)), MSG_DONTWAIT); if (res == -1 && errno == EWOULDBLOCK) break; if (res == 0 && k == i) { pos[i] = sndlen + rcvlen; break; } if (res <= 0) e(0); if (res > left) e(0); for (j = 0; j < res; j++) if (buf[j] != (unsigned char)(k + pos[k]++)) e(0); } } for (k = 0; k <= i; k++) { for (left = sndlen + rcvlen - pos[k]; left > 0; left -= res) { res = recv(fds[k], buf, MIN(left, sizeof(buf)), 0); if (res == 0 && k == i) break; if (res <= 0) e(0); if (res > left) e(0); for (j = 0; j < res; j++) if (buf[j] != (unsigned char)(k + pos[k]++)) e(0); } if (recv(fds[k], buf, 1, 0) != 0) e(0); } /* Clean up. */ do { if (close(fds[i]) != 0) e(0); } while (i-- > 0); exit(errct); case -1: e(0); } if (close(pfd[0]) != 0) e(0); for (i = 0; ; i++) { if (i == __arraycount(fds)) e(0); len = sizeof(sin6); if ((fds[i] = accept(lfd, (struct sockaddr *)&sin6, &len)) < 0) e(0); val = 1; if (setsockopt(fds[i], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != 0) e(0); if (setsockopt(fds[i], SOL_SOCKET, SO_SNDBUF, &sndlen, sizeof(sndlen)) != 0) e(0); /* * Try to pump as much data into one end of the socket. This * may fail at any time due to being out of buffers, so we use * a send timeout to break the resulting blocking call. */ tv.tv_sec = 1; tv.tv_usec = 0; if (setsockopt(fds[i], SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) e(0); /* * Since buffer corruption is most likely to be detected when * lots of buffers are actually in use, also make sure that we * (eventually) receive what we send. */ res = sizeof(buf); pos[i] = 0; for (left = sndlen + rcvlen; left > 0; left -= res) { /* One byte at a time, for simplicity.. */ for (j = sizeof(buf) - res; j < sizeof(buf); j++) buf[j] = (unsigned char)(i + pos[i]++); res = send(fds[i], buf, MIN(left, sizeof(buf)), 0); if (res == -1 && errno == EWOULDBLOCK) break; if (res <= 0) e(0); if (res > left) e(0); if (res < sizeof(buf)) memmove(buf, &buf[res], sizeof(buf) - res); } /* Synchronization point A. */ k = (left == 0); if (write(pfd[1], &k, sizeof(k)) != sizeof(k)) e(0); if (left > 0) break; } if (close(lfd) != 0) e(0); /* * We should always be able to fill at least two socket pairs' buffers * completely this way; in fact with a 512x512 pool it should be three, * but some sockets may be in use in the background. With the default * settings of the memory pool system, we should ideally be able to get * up to 96 socket pairs. */ if (i < 3) e(0); /* * Mix things up a bit by fully shutting down one file descriptor and * closing another, both on the sending side. */ if (shutdown(fds[1], SHUT_RDWR) != 0) e(0); if (close(fds[2]) != 0) e(0); /* * Make sure that when there is buffer space available again, pending * send() calls get woken up. We do this using a child process that * blocks on a send() call and a parent process that frees up some * buffer space by receiving from another socket. */ pid2 = fork(); switch (pid2) { case 0: errct = 0; /* Disable the timeout again. */ tv.tv_sec = 0; tv.tv_usec = 0; if (setsockopt(fds[i], SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) e(0); /* * Try sending. This should block until there are more buffers * available. */ res = send(fds[i], buf, MIN(left, sizeof(buf)), 0); if (res <= 0) e(0); if (res > left) e(0); exit(errct); case -1: e(0); } /* Make sure the child's send() call is indeed hanging. */ sleep(2); if (waitpid(pid2, &status, WNOHANG) != 0) e(0); /* Then receive some data on another socket. */ /* Synchronization point B. */ k = 2; if (write(pfd[1], &k, sizeof(k)) != sizeof(k)) e(0); /* The send() call should now be woken up, eventually. */ if (waitpid(pid2, &status, 0) != pid2) e(0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); /* * Shut down all (remaining) sending file descriptors for sending, so * that we can receive until we get EOF. For all but the last socket, * we must get the full size of what we intended to send; for the first * socket, we have already received two buffers worth of data. Note * that the receipt may take a while, mainly because it takes some time * for sockets that were previously blocked to get going again. */ for (k = 0; k <= i; k++) { if (k != 1 && k != 2 && shutdown(fds[k], SHUT_WR) != 0) e(0); } /* Synchronization point C. */ k = 3; if (write(pfd[1], &k, sizeof(k)) != sizeof(k)) e(0); if (close(pfd[1]) != 0) e(0); /* Wait for the child to receive everything and terminate. */ if (waitpid(pid, &status, 0) != pid) e(0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); /* Clean up. */ do { if (i != 2 && close(fds[i]) != 0) e(0); } while (i-- > 0); } /* * Test multicast support. */ static void test91aa(void) { subtest = 27; socklib_test_multicast(SOCK_DGRAM, 0); } /* * Test that putting an unbound TCP socket in listening mode will bind the * socket to a port. */ static void test91ab(void) { struct sockaddr_in sin; struct sockaddr_in6 sin6; socklen_t len; int fd; subtest = 28; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) e(0); if (listen(fd, 1) != 0) e(0); len = sizeof(sin); if (getsockname(fd, (struct sockaddr *)&sin, &len) != 0) e(0); if (len != sizeof(sin)) e(0); if (sin.sin_len != sizeof(sin)) e(0); if (sin.sin_family != AF_INET) e(0); if (sin.sin_port == htons(0)) e(0); if (sin.sin_addr.s_addr != htonl(INADDR_ANY)) e(0); if (close(fd) != 0) e(0); if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (listen(fd, 1) != 0) e(0); len = sizeof(sin6); if (getsockname(fd, (struct sockaddr *)&sin6, &len) != 0) e(0); if (len != sizeof(sin6)) e(0); if (sin6.sin6_len != sizeof(sin6)) e(0); if (sin6.sin6_family != AF_INET6) e(0); if (sin6.sin6_port == htons(0)) e(0); if (memcmp(&sin6.sin6_addr, &in6addr_any, sizeof(sin6.sin6_addr)) != 0) e(0); if (close(fd) != 0) e(0); } /* * Test for connecting to the same remote TCP endpoint with the same local * endpoint twice in a row. The second connection should fail due to the * TIME_WAIT state left behind from the first connection, but this previously * caused an infinite loop instead. lwIP bug #50498. */ static void test91ac(void) { struct sockaddr_in6 lsin6, rsin6; socklen_t len; int fd, fd2, fd3; subtest = 29; if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); memset(&rsin6, 0, sizeof(rsin6)); rsin6.sin6_family = AF_INET6; memcpy(&rsin6.sin6_addr, &in6addr_loopback, sizeof(rsin6.sin6_addr)); if (bind(fd, (struct sockaddr *)&rsin6, sizeof(rsin6)) != 0) e(0); len = sizeof(rsin6); if (getsockname(fd, (struct sockaddr *)&rsin6, &len) != 0) e(0); if (len != sizeof(rsin6)) e(0); if (listen(fd, 1) != 0) e(0); if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (connect(fd2, (struct sockaddr *)&rsin6, sizeof(rsin6)) != 0) e(0); if ((fd3 = accept(fd, (struct sockaddr *)&lsin6, &len)) < 0) e(0); if (len != sizeof(rsin6)) e(0); /* The server end must initiate the close for this to work. */ if (close(fd3) != 0) e(0); if (close(fd2) != 0) e(0); if ((fd2 = socket(AF_INET6, SOCK_STREAM, 0)) < 0) e(0); if (bind(fd2, (struct sockaddr *)&lsin6, sizeof(lsin6)) != 0) e(0); /* * The timeout should occur almost immediately, due to a shortcut in * lwIP (which was also the source of the problem here). The actual * error code is not really important though. In fact, if in the * future the connection does get established, that is still not an * issue - in fact, it would be nice to have a working rsh(1), which is * how this problem showed up in the first place - but at the very * least the service should keep operating. */ if (connect(fd2, (struct sockaddr *)&rsin6, sizeof(rsin6)) != -1) e(0); if (close(fd2) != 0) e(0); if (close(fd) != 0) e(0); } /* * Test program for LWIP TCP/UDP sockets. */ int main(int argc, char ** argv) { unsigned int m; int i; start(91); if (argc == 2) m = atoi(argv[1]); else m = 0xFFFFFFFF; for (i = 0; i < ITERATIONS; i++) { if (m & 0x00000001) test91a(); if (m & 0x00000002) test91b(); if (m & 0x00000004) test91c(); if (m & 0x00000008) test91d(); if (m & 0x00000010) test91e(); if (m & 0x00000020) test91f(); if (m & 0x00000040) test91g(); if (m & 0x00000080) test91h(); if (m & 0x00000100) test91i(); if (m & 0x00000200) test91j(); if (m & 0x00000400) test91k(); if (m & 0x00000800) test91l(); if (m & 0x00001000) test91m(); if (m & 0x00002000) test91n(); if (m & 0x00004000) test91o(); if (m & 0x00008000) test91p(); if (m & 0x00010000) test91q(); if (m & 0x00020000) test91r(); if (m & 0x00040000) test91s(); if (m & 0x00080000) test91t(); if (m & 0x00100000) test91u(); if (m & 0x00200000) test91v(); if (m & 0x00400000) test91w(); if (m & 0x00800000) test91x(); if (m & 0x01000000) test91y(); if (m & 0x02000000) test91z(); if (m & 0x04000000) test91aa(); if (m & 0x08000000) test91ab(); if (m & 0x10000000) test91ac(); } quit(); /* NOTREACHED */ }