phunix/minix/tests/test91.c
David van Moolenbroek 3ba6090f82 tests: add advanced TCP/IP tests (test91-94)
Change-Id: I052102f6122f82b3307595990bf91f64e97a45a8
2017-04-30 13:16:25 +00:00

5397 lines
142 KiB
C

/* 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 <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/udp.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/in6_var.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <machine/vmparam.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#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 */
}