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

1736 lines
45 KiB
C

/* Tests for RAW sockets (LWIP) - by D.C. van Moolenbroek */
/* This test needs to be run as root: creating raw sockets is root-only. */
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/icmp6.h>
#include <netinet/in_pcb.h>
#include <netinet6/in6_pcb.h>
#include <arpa/inet.h>
#include <machine/vmparam.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include "common.h"
#include "socklib.h"
#define ITERATIONS 2
#define TEST_PROTO 253 /* from RFC 3692 */
#define TEST_ICMPV6_TYPE_A 200 /* from RFC 4443 */
#define TEST_ICMPV6_TYPE_B 201 /* from RFC 4443 */
static const enum state raw_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,
};
static const int raw_results[][__arraycount(raw_states)] = {
[C_ACCEPT] = {
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP,
},
[C_BIND] = {
0, 0, 0, 0,
0, -EINVAL, -EINVAL, -EINVAL,
-EINVAL,
},
[C_CONNECT] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_GETPEERNAME] = {
-ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN,
-ENOTCONN, 0, 0, 0,
0,
},
[C_GETSOCKNAME] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_GETSOCKOPT_ERR] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_GETSOCKOPT_KA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_GETSOCKOPT_RB] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_IOCTL_NREAD] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_LISTEN] = {
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP,
-EOPNOTSUPP,
},
[C_RECV] = {
-EAGAIN, 0, -EAGAIN, 0,
-EAGAIN, -EAGAIN, 0, -EAGAIN,
0,
},
[C_RECVFROM] = {
-EAGAIN, 0, -EAGAIN, 0,
-EAGAIN, -EAGAIN, 0, -EAGAIN,
0,
},
[C_SEND] = {
-EDESTADDRREQ, -EDESTADDRREQ, -EPIPE, -EPIPE,
-EDESTADDRREQ, 1, 1, -EPIPE,
-EPIPE,
},
[C_SENDTO] = {
1, 1, -EPIPE, -EPIPE,
1, 1, 1, -EPIPE,
-EPIPE,
},
[C_SELECT_R] = {
0, 1, 0, 1,
0, 0, 1, 0,
1,
},
[C_SELECT_W] = {
1, 1, 1, 1,
1, 1, 1, 1,
1,
},
[C_SELECT_X] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_SETSOCKOPT_BC] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_SETSOCKOPT_KA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_SETSOCKOPT_L] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_SETSOCKOPT_RA] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_SHUTDOWN_R] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_SHUTDOWN_RW] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
[C_SHUTDOWN_W] = {
0, 0, 0, 0,
0, 0, 0, 0,
0,
},
};
/*
* Set up a RAW 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
raw_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;
int r, fd, fd2;
if (domain == AF_INET) {
memset(&sinA, 0, sizeof(sinA));
sinA.sin_family = domain;
sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
memcpy(&sinB, &sinA, sizeof(sinB));
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;
memcpy(&sin6A.sin6_addr, &in6addr_loopback,
sizeof(sin6A.sin6_addr));
memcpy(&sin6B, &sin6A, sizeof(sin6B));
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:
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: if (shutdown(fd, SHUT_RD)) e(0); break;
case S_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break;
case S_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
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 RAW sockets.
*/
static void
test92a(void)
{
subtest = 1;
socklib_sweep(AF_INET, SOCK_RAW, TEST_PROTO, raw_states,
__arraycount(raw_states), (const int *)raw_results, raw_sweep);
socklib_sweep(AF_INET6, SOCK_RAW, TEST_PROTO, raw_states,
__arraycount(raw_states), (const int *)raw_results, raw_sweep);
}
/*
* Basic I/O test for raw sockets.
*/
static void
test92b(void)
{
struct sockaddr_in sinA, sinB, sinC;
struct sockaddr_in6 sin6A, sin6B, sin6C;
socklen_t len;
unsigned int i;
uint8_t buf[256], packet[5];
int fd, fd2;
subtest = 2;
/* First test IPv4. */
if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
memset(&sinA, 0, sizeof(sinA));
sinA.sin_family = AF_INET;
sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
for (i = 0; i < __arraycount(packet); i++)
packet[i] = (uint8_t)(-i);
if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sinA,
sizeof(sinA)) != sizeof(packet)) e(0);
memset(buf, 0, sizeof(buf));
len = sizeof(sinB);
if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sinB,
&len) != sizeof(struct ip) + sizeof(packet)) e(0);
if (memcmp(&buf[sizeof(struct ip)], packet, sizeof(packet)) != 0) e(0);
if (len != sizeof(sinB)) e(0);
if (sinB.sin_len != sizeof(sinB)) e(0);
if (sinB.sin_family != AF_INET) e(0);
if (sinB.sin_port != htons(0)) e(0);
if (sinB.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0);
/*
* Test two additional things:
*
* 1) a non-zero port number is ignored when sending;
* 2) multiple raw sockets may receive the same packet.
*/
sinA.sin_port = htons(22);
if ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sinA,
sizeof(sinA)) != sizeof(packet)) e(0);
memset(buf, 0, sizeof(buf));
len = sizeof(sinC);
if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sinC,
&len) != sizeof(struct ip) + sizeof(packet)) e(0);
if (memcmp(&buf[sizeof(struct ip)], packet, sizeof(packet)) != 0) e(0);
if (len != sizeof(sinC)) e(0);
if (memcmp(&sinB, &sinC, sizeof(sinB)) != 0) e(0);
memset(buf, 0, sizeof(buf));
len = sizeof(sinC);
if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sinC,
&len) != sizeof(struct ip) + sizeof(packet)) e(0);
if (memcmp(&buf[sizeof(struct ip)], packet, sizeof(packet)) != 0) e(0);
if (len != sizeof(sinC)) e(0);
if (memcmp(&sinB, &sinC, sizeof(sinB)) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
/* Then test IPv6. */
if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0);
memset(&sin6A, 0, sizeof(sin6A));
sin6A.sin6_family = AF_INET6;
memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr));
if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sin6A,
sizeof(sin6A)) != sizeof(packet)) e(0);
memset(buf, 0, sizeof(buf));
len = sizeof(sin6B);
if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6B,
&len) != sizeof(packet)) e(0);
if (memcmp(buf, packet, sizeof(packet)) != 0) e(0);
if (len != sizeof(sin6B)) e(0);
if (sin6B.sin6_len != sizeof(sin6B)) e(0);
if (sin6B.sin6_family != AF_INET6) e(0);
if (sin6B.sin6_port != htons(0)) e(0);
if (memcmp(&sin6B.sin6_addr, &in6addr_loopback,
sizeof(sin6B.sin6_addr)) != 0) e(0);
/* As above. */
sin6A.sin6_port = htons(22);
if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0);
if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sin6A,
sizeof(sin6A)) != sizeof(packet)) e(0);
memset(buf, 0, sizeof(buf));
len = sizeof(sin6C);
if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6C,
&len) != sizeof(packet)) e(0);
if (memcmp(buf, packet, sizeof(packet)) != 0) e(0);
if (len != sizeof(sin6C)) e(0);
if (memcmp(&sin6B, &sin6C, sizeof(sin6B)) != 0) e(0);
memset(buf, 0, sizeof(buf));
len = sizeof(sin6C);
if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sin6C,
&len) != sizeof(packet)) e(0);
if (memcmp(buf, packet, sizeof(packet)) != 0) e(0);
if (len != sizeof(sin6C)) e(0);
if (memcmp(&sin6B, &sin6C, sizeof(sin6B)) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
}
/*
* Test the IPV6_CHECKSUM socket option.
*/
static void
test92c(void)
{
struct sockaddr_in6 sin6;
struct icmp6_hdr icmp6_hdr;
uint8_t buf[6], buf2[6], *buf3;
socklen_t len;
unsigned int i;
int fd, fd2, val;
subtest = 3;
if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0);
if (shutdown(fd, SHUT_RD) != 0) e(0);
/* For non-ICMPv6 sockets, checksumming is disabled by default. */
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != -1) e(0);
/* Test bad offsets. */
val = -2;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != -1) e(0);
if (errno != EINVAL) e(0);
val = 1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != -1) e(0);
if (errno != EINVAL) e(0);
/* Now test real checksum computation. */
val = 0;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0);
if (shutdown(fd2, SHUT_WR) != 0) e(0);
memset(buf, 0, sizeof(buf));
buf[2] = 0xfe;
buf[3] = 0x95;
buf[4] = 0x4d;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr));
if (sendto(fd, buf, 5, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 5)
e(0);
if (recv(fd2, buf2, sizeof(buf2), 0) != 5) e(0);
if (buf2[0] != 0xb3 || buf2[1] != 0x65) e(0);
if (memcmp(&buf2[2], &buf[2], 3) != 0) e(0);
/* Turn on checksum verification on the receiving socket. */
val = 0;
if (setsockopt(fd2, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
/*
* The current value of the checksum field should not be incorporated
* in the checksum, as that would result in an invalid checksum.
*/
buf[0] = 0xab;
buf[1] = 0xcd;
if (sendto(fd, buf, 5, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 5)
e(0);
if (recv(fd2, buf2, sizeof(buf2), 0) != 5) e(0);
if (buf2[0] != 0xb3 || buf2[1] != 0x65) e(0);
if (memcmp(&buf2[2], &buf[2], 3) != 0) e(0);
/*
* Turn off checksum computation on the sending side, so that the
* packet ends up being dropped on the receiving side.
*/
val = -1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
if (sendto(fd, buf, 5, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 5)
e(0);
/* Send some packets that are too small to contain the checksum. */
if (sendto(fd, buf, 0, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 0)
e(0);
if (sendto(fd, buf, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 1)
e(0);
/*
* If this recv call is "too soon" (it should not be) and the packets
* arrive later anyway, then we will get a failure below.
*/
if (recv(fd2, buf2, sizeof(buf2), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
buf[0] = 0;
buf[1] = 0x67;
if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != 4) e(0);
if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0);
if (memcmp(buf, buf2, 4) != 0) e(0);
/*
* We repeat some of the tests with a non-zero checksum offset, just to
* be sure.
*/
val = 2;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != 2) e(0);
buf[0] = 0x56;
buf[1] = 0x78;
for (i = 0; i <= 3; i++) {
if (sendto(fd, buf, i, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != -1) e(0);
if (errno != EINVAL) e(0);
}
val = 2;
if (setsockopt(fd2, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != 4) e(0);
if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0);
if (memcmp(buf, buf2, 2) != 0) e(0);
if (buf2[2] != 0xa8 || buf2[3] != 0x84) e(0);
val = -1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
buf[2] = 0xa8;
buf[3] = 0x85; /* deliberately bad checksum */
/* All these should be dropped on the receiver side. */
for (i = 0; i <= 4; i++) {
if (sendto(fd, buf, i, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != i) e(0);
}
buf[3] = 0x84; /* good checksum */
if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 4)
e(0);
if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0);
if (memcmp(buf, buf2, 4) != 0) e(0);
if (recv(fd2, buf2, sizeof(buf2), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
val = -1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
if (setsockopt(fd2, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
buf[3] = 0x85;
if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 4)
e(0);
if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0);
if (memcmp(buf, buf2, 4) != 0) e(0);
/*
* The following is a lwIP-specific test: lwIP does not support storing
* generated checksums beyond the first pbuf. We do not know the size
* of the first pbuf until we actually send a packet, so the setsockopt
* call will not fail, but sending the packet will. Depending on the
* buffer allocation strategy, the following test may or may not
* trigger this case; simply ensure that we do not crash the service.
*/
if ((buf3 = malloc(4096)) == NULL) e(0);
val = 4094;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != 0) e(0);
/* This call may or may not fail, but if it fails, it yields EINVAL. */
if (sendto(fd, buf3, 4096, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) == -1 && errno != EINVAL) e(0);
free(buf3);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
/* For ICMPv6 packets, checksumming is always enabled. */
if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0);
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != 2) e(0);
val = -1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != -1) e(0);
if (errno != EINVAL) e(0);
memset(&icmp6_hdr, 0, sizeof(icmp6_hdr));
icmp6_hdr.icmp6_type = TEST_ICMPV6_TYPE_A;
icmp6_hdr.icmp6_code = 123;
icmp6_hdr.icmp6_cksum = htons(0);
len = offsetof(struct icmp6_hdr, icmp6_dataun);
if (sendto(fd, &icmp6_hdr, len, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != len) e(0);
if (recv(fd, &icmp6_hdr, sizeof(icmp6_hdr), 0) != len) e(0);
if (icmp6_hdr.icmp6_type != TEST_ICMPV6_TYPE_A) e(0);
if (icmp6_hdr.icmp6_code != 123) e(0);
if (ntohs(icmp6_hdr.icmp6_cksum) != 0x3744) e(0);
if (close(fd) != 0) e(0);
/* For IPv4 and non-RAW IPv6 sockets, the option does not work. */
for (i = 0; i <= 2; i++) {
switch (i) {
case 0: fd = socket(AF_INET6, SOCK_DGRAM, 0); break;
case 1: fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6); break;
case 2: fd = socket(AF_INET, SOCK_RAW, TEST_PROTO); break;
}
if (fd < 0) e(0);
val = -1;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
sizeof(val)) != -1) e(0);
if (errno != ENOPROTOOPT) e(0);
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val,
&len) != -1) e(0);
if (errno != ENOPROTOOPT) e(0);
if (close(fd) != 0) e(0);
}
}
/*
* Test the ICMP6_FILTER socket option.
*/
static void
test92d(void)
{
struct sockaddr_in6 sin6;
struct sockaddr_in sin;
struct icmp6_filter filter;
struct icmp6_hdr packet;
socklen_t len;
struct timeval tv;
unsigned int i;
int fd, fd2;
subtest = 4;
/*
* We use two different sockets to eliminate the possibility that the
* filter is also applied when sending packets--it should not be.
*/
if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0);
if (shutdown(fd, SHUT_WR) != 0) e(0);
len = sizeof(filter);
if (getsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, &len) != 0)
e(0);
/* We do not aim to test the ICMP6_FILTER macros here. */
for (i = 0; i <= UINT8_MAX; i++)
if (!ICMP6_FILTER_WILLPASS(i, &filter)) e(0);
if ((fd2 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0);
ICMP6_FILTER_SETBLOCKALL(&filter);
if (setsockopt(fd2, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
sizeof(filter)) != 0) e(0);
len = sizeof(filter);
if (getsockopt(fd2, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, &len) != 0)
e(0);
for (i = 0; i <= UINT8_MAX; i++)
if (ICMP6_FILTER_WILLPASS(i, &filter)) e(0);
ICMP6_FILTER_SETPASSALL(&filter);
ICMP6_FILTER_SETBLOCK(TEST_ICMPV6_TYPE_A, &filter);
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
sizeof(filter)) != 0) e(0);
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr));
memset(&packet, 0, sizeof(packet));
packet.icmp6_type = TEST_ICMPV6_TYPE_A;
packet.icmp6_code = 12;
if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != sizeof(packet)) e(0);
packet.icmp6_type = TEST_ICMPV6_TYPE_B;
packet.icmp6_code = 34;
if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != sizeof(packet)) e(0);
memset(&packet, 0, sizeof(packet));
if (recv(fd, &packet, sizeof(packet), 0) != sizeof(packet)) e(0);
if (packet.icmp6_type != TEST_ICMPV6_TYPE_B) e(0);
if (packet.icmp6_code != 34) e(0);
if (recv(fd, &packet, sizeof(packet), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(TEST_ICMPV6_TYPE_A, &filter);
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
sizeof(filter)) != 0) e(0);
memset(&packet, 0, sizeof(packet));
packet.icmp6_type = TEST_ICMPV6_TYPE_B;
packet.icmp6_code = 56;
if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != sizeof(packet)) e(0);
packet.icmp6_type = TEST_ICMPV6_TYPE_A;
packet.icmp6_code = 78;
if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != sizeof(packet)) e(0);
/*
* RFC 3542 states that setting a zero-length filter resets the filter.
* This seems like one of those things that a standardization RFC
* should not mandate: it is redundant at the API level (one can set a
* PASSALL filter, which is the required default), it relies on an edge
* case (setsockopt taking a zero-length argument), and as a "shortcut"
* it does not even cover a case that is likely to occur (no actual
* program would reset its filter on a regular basis). Presumably it
* is a way to deallocate filter memory on some platforms, but was that
* worth the RFC inclusion? Anyhow, we support it; NetBSD does not.
*/
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, NULL, 0) != 0) e(0);
packet.icmp6_type = TEST_ICMPV6_TYPE_B;
packet.icmp6_code = 90;
if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != sizeof(packet)) e(0);
memset(&packet, 0, sizeof(packet));
if (recv(fd, &packet, sizeof(packet), 0) != sizeof(packet)) e(0);
if (packet.icmp6_type != TEST_ICMPV6_TYPE_A) e(0);
if (packet.icmp6_code != 78) e(0);
if (recv(fd, &packet, sizeof(packet), 0) != sizeof(packet)) e(0);
if (packet.icmp6_type != TEST_ICMPV6_TYPE_B) e(0);
if (packet.icmp6_code != 90) e(0);
if (recv(fd, &packet, sizeof(packet), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
if (recv(fd2, &packet, sizeof(packet), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
len = sizeof(filter);
if (getsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, &len) != 0)
e(0);
for (i = 0; i <= UINT8_MAX; i++)
if (!ICMP6_FILTER_WILLPASS(i, &filter)) e(0);
if (close(fd2) != 0) e(0);
/*
* Let's get weird and send an ICMPv6 packet from an IPv4 socket.
* Currently, such packets are always dropped based on the rule that
* IPv6 sockets with checksumming enabled drop all IPv4 packets. As it
* happens, that is also all that is keeping this packet from arriving.
*/
if ((fd2 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0);
ICMP6_FILTER_SETBLOCKALL(&filter);
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
sizeof(filter)) != 0) e(0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
memset(&packet, 0, sizeof(packet));
packet.icmp6_type = TEST_ICMPV6_TYPE_A;
packet.icmp6_code = 123;
packet.icmp6_cksum = htons(0); /* TODO: use valid checksum */
if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin,
sizeof(sin)) != sizeof(packet)) e(0);
/*
* If the packet were to arrive at all, it should arrive instantly, so
* this is just an excuse to use SO_RCVTIMEO.
*/
tv.tv_sec = 0;
tv.tv_usec = 100000;
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0)
e(0);
if (recv(fd, &packet, sizeof(packet), 0) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
/* Make sure ICMP6_FILTER works on IPv6-ICMPv6 sockets only. */
for (i = 0; i <= 2; i++) {
switch (i) {
case 0: fd = socket(AF_INET6, SOCK_DGRAM, 0); break;
case 1: fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO); break;
case 2: fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6); break;
}
if (fd < 0) e(0);
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
sizeof(filter)) != -1) e(0);
if (errno != ENOPROTOOPT) e(0);
len = sizeof(filter);
if (getsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
&len) != -1) e(0);
if (errno != ENOPROTOOPT) e(0);
if (close(fd) != 0) e(0);
}
}
/*
* Test that IPPROTO_ICMPV6 has no special value on IPv4 raw sockets. In
* particular, test that no checksum is generated or verified. By now we have
* already tested that none of the IPv6 socket options work on such sockets.
*/
static void
test92e(void)
{
char buf[sizeof(struct ip) + sizeof(struct icmp6_hdr)];
struct sockaddr_in sin;
struct icmp6_hdr packet;
int fd;
subtest = 5;
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0);
memset(&packet, 0, sizeof(packet));
packet.icmp6_type = TEST_ICMPV6_TYPE_A;
packet.icmp6_code = 123;
packet.icmp6_cksum = htons(0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (sendto(fd, &packet, sizeof(packet), 0, (struct sockaddr *)&sin,
sizeof(sin)) != sizeof(packet)) e(0);
if (recv(fd, buf, sizeof(buf), 0) != sizeof(buf)) e(0);
memcpy(&packet, &buf[sizeof(struct ip)], sizeof(packet));
if (packet.icmp6_type != TEST_ICMPV6_TYPE_A) e(0);
if (packet.icmp6_code != 123) e(0);
if (packet.icmp6_cksum != htons(0)) e(0);
if (close(fd) != 0) e(0);
}
struct testpkt {
struct ip ip;
struct udphdr udp;
uint8_t data[6];
} __packed;
/*
* Test the IP_HDRINCL socket option.
*/
static void
test92f(void)
{
struct sockaddr_in sin;
struct testpkt pkt, pkt2;
socklen_t len;
char buf[7];
unsigned int i;
int fd, fd2, val;
subtest = 6;
/* See if we can successfully feign a UDP packet. */
memset(&pkt, 0, sizeof(pkt));
pkt.ip.ip_v = IPVERSION;
pkt.ip.ip_hl = sizeof(pkt.ip) >> 2;
pkt.ip.ip_tos = 123;
pkt.ip.ip_len = sizeof(pkt); /* swapped by OS */
pkt.ip.ip_id = htons(456);
pkt.ip.ip_off = IP_DF; /* swapped by OS */
pkt.ip.ip_ttl = 78;
pkt.ip.ip_p = IPPROTO_UDP;
pkt.ip.ip_sum = htons(0); /* filled by OS */
pkt.ip.ip_src.s_addr = htonl(INADDR_LOOPBACK);
pkt.ip.ip_dst.s_addr = htonl(INADDR_LOOPBACK);
pkt.udp.uh_sport = htons(TEST_PORT_B);
pkt.udp.uh_dport = htons(TEST_PORT_A);
pkt.udp.uh_sum = htons(0); /* lazy.. */
pkt.udp.uh_ulen = htons(sizeof(pkt.udp) + sizeof(pkt.data));
memcpy(pkt.data, "Hello!", sizeof(pkt.data));
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) e(0);
if (shutdown(fd, SHUT_RD) != 0) e(0);
/* IP_HDRINCL is never enabled by default. */
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != 0) e(0);
val = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) != 0)
e(0);
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != 1) 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 (bind(fd2, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0);
sin.sin_port = htons(0);
if (sendto(fd, &pkt, sizeof(pkt), 0, (struct sockaddr *)&sin,
sizeof(sin)) != sizeof(pkt)) e(0);
if (recv(fd2, &buf, sizeof(buf), 0) != sizeof(pkt.data)) e(0);
if (memcmp(buf, pkt.data, sizeof(pkt.data)) != 0) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, &len) != 0) e(0);
if (len != sizeof(val)) e(0);
if (val != 0) e(0);
if (shutdown(fd, SHUT_RD) != 0) e(0);
/* See if we can receive a packet for our own protocol. */
pkt.ip.ip_p = TEST_PROTO;
if ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
val = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) != 0)
e(0);
if (sendto(fd, &pkt, sizeof(pkt), 0, (struct sockaddr *)&sin,
sizeof(sin)) != sizeof(pkt)) e(0);
if (recv(fd2, &pkt2, sizeof(pkt2), 0) != sizeof(pkt2)) e(0);
if (pkt2.ip.ip_v != pkt.ip.ip_v) e(0);
if (pkt2.ip.ip_hl != pkt.ip.ip_hl) e(0);
if (pkt2.ip.ip_tos != pkt.ip.ip_tos) e(0);
if (pkt2.ip.ip_len != pkt.ip.ip_len) e(0);
if (pkt2.ip.ip_id != pkt.ip.ip_id) e(0);
if (pkt2.ip.ip_off != pkt.ip.ip_off) e(0);
if (pkt2.ip.ip_ttl != pkt.ip.ip_ttl) e(0);
if (pkt2.ip.ip_p != pkt.ip.ip_p) e(0);
if (pkt2.ip.ip_sum == htons(0)) e(0);
if (pkt2.ip.ip_src.s_addr != pkt.ip.ip_src.s_addr) e(0);
if (pkt2.ip.ip_dst.s_addr != pkt.ip.ip_dst.s_addr) e(0);
/*
* Test sending packets with weird sizes to ensure that we do not crash
* the service. These packets would never arrive anyway.
*/
if (sendto(fd, &pkt, 0, 0, (struct sockaddr *)&sin,
sizeof(sin)) != -1) e(0);
if (errno != EINVAL) e(0);
if (sendto(fd, &pkt, sizeof(pkt.ip) - 1, 0, (struct sockaddr *)&sin,
sizeof(sin)) != -1) e(0);
if (errno != EINVAL) e(0);
if (sendto(fd, &pkt, sizeof(pkt.ip), 0, (struct sockaddr *)&sin,
sizeof(sin)) != sizeof(pkt.ip)) e(0);
if (recv(fd2, &pkt2, sizeof(pkt2), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
/* Ensure that the socket option does not work on other types. */
for (i = 0; i <= 1; i++) {
switch (i) {
case 0: fd = socket(AF_INET, SOCK_DGRAM, 0); break;
case 1: fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO); break;
}
if (fd < 0) e(0);
len = sizeof(val);
if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val,
&len) != -1) e(0);
if (errno != ENOPROTOOPT) e(0);
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val,
sizeof(val)) != -1) e(0);
if (errno != ENOPROTOOPT) e(0);
if (close(fd) != 0) e(0);
}
}
/*
* Test the IPPROTO_RAW socket protocol. This test mostly shows that the
* IPPROTO_RAW protocol is nothing special: for both IPv4 and IPv6, it sends
* and receives packets with that protocol number. We already tested earlier
* that IP_HDRINCL is disabled by default on IPPROTO_RAW sockets, too.
*/
static void
test92g(void)
{
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
char buf[sizeof(struct ip) + 1];
int fd;
subtest = 7;
if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) e(0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (sendto(fd, "A", 1, 0, (struct sockaddr *)&sin,
sizeof(sin)) != 1) e(0);
if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != sizeof(buf)) e(0);
if (buf[sizeof(struct ip)] != 'A') e(0);
if (close(fd) != 0) e(0);
if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)) < 0) e(0);
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr));
if (sendto(fd, "B", 1, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != 1) e(0);
if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != 1) e(0);
if (buf[0] != 'B') e(0);
if (close(fd) != 0) e(0);
}
/*
* Test that connected raw sockets perform correct source-based filtering.
*/
static void
test92h(void)
{
struct sockaddr_in sinA, sinB;
struct sockaddr_in6 sin6A, sin6B;
struct sockaddr sa;
socklen_t len;
char buf[sizeof(struct ip) + 1];
int fd, fd2;
subtest = 8;
if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
len = sizeof(sinB);
if (getpeername(fd, (struct sockaddr *)&sinB, &len) != -1) e(0);
if (errno != ENOTCONN) e(0);
memset(&sinA, 0, sizeof(sinA));
sinA.sin_family = AF_INET;
sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
/*
* First test that packets with the right source are accepted.
* Unfortunately, source and destination are the same in this case, so
* this test is far from perfect.
*/
if (connect(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0);
if (getpeername(fd, (struct sockaddr *)&sinB, &len) != 0) e(0);
if ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
if (sendto(fd2, "A", 1, 0, (struct sockaddr *)&sinA,
sizeof(sinA)) != 1) e(0);
buf[0] = '\0';
if (recv(fd2, buf, sizeof(buf), 0) != sizeof(struct ip) + 1) e(0);
if (buf[sizeof(struct ip)] != 'A') e(0);
buf[0] = '\0';
if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) !=
sizeof(struct ip) + 1) e(0);
if (buf[sizeof(struct ip)] != 'A') e(0);
memset(&sa, 0, sizeof(sa));
sa.sa_family = AF_UNSPEC;
sinA.sin_addr.s_addr = htonl(INADDR_NONE);
/* While here, test unconnecting the socket. */
if (connect(fd, &sa, sizeof(sa)) != 0) e(0);
if (getpeername(fd, (struct sockaddr *)&sinB, &len) != -1) e(0);
if (errno != ENOTCONN) e(0);
/* Then test that packets with the wrong source are ignored. */
if (connect(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0);
if (sendto(fd2, "B", 1, 0, (struct sockaddr *)&sinB,
sizeof(sinB)) != 1) e(0);
buf[0] = '\0';
if (recv(fd2, buf, sizeof(buf), 0) != sizeof(struct ip) + 1) e(0);
if (buf[sizeof(struct ip)] != 'B') e(0);
if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
/* Repeat for IPv6, but now the other way around. */
if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0);
len = sizeof(sin6B);
if (getpeername(fd, (struct sockaddr *)&sin6B, &len) != -1) e(0);
if (errno != ENOTCONN) e(0);
memset(&sin6A, 0, sizeof(sin6A));
sin6A.sin6_family = AF_INET6;
memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr));
memcpy(&sin6B, &sin6A, sizeof(sin6B));
if (inet_pton(AF_INET6, "::2", &sin6B.sin6_addr) != 1) e(0);
if (connect(fd, (struct sockaddr *)&sin6B, sizeof(sin6B)) != 0) e(0);
if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0);
if (sendto(fd2, "C", 1, 0, (struct sockaddr *)&sin6A,
sizeof(sin6A)) != 1) e(0);
buf[0] = '\0';
if (recv(fd2, buf, sizeof(buf), 0) != 1) e(0);
if (buf[0] != 'C') e(0);
if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0);
if (errno != EWOULDBLOCK) e(0);
if (connect(fd, &sa, sizeof(sa)) != 0) e(0);
if (connect(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0);
if (sendto(fd2, "D", 1, 0, (struct sockaddr *)&sin6A,
sizeof(sin6A)) != 1) e(0);
buf[0] = '\0';
if (recv(fd2, buf, sizeof(buf), 0) != 1) e(0);
if (buf[0] != 'D') e(0);
buf[0] = '\0';
if (recv(fd, buf, sizeof(buf), 0) != 1) e(0);
if (buf[0] != 'D') e(0);
if (close(fd2) != 0) e(0);
if (close(fd) != 0) e(0);
}
/*
* Test sending large and small RAW packets. This test is an altered copy of
* test91e, but has been changed to IPv6 to cover a greater spectrum together.
*/
static void
test92i(void)
{
struct sockaddr_in6 sin6;
struct msghdr msg;
struct iovec iov;
char *buf;
unsigned int i, j;
int r, fd, fd2, val;
subtest = 9;
if ((buf = malloc(65536)) == NULL) e(0);
if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 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(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0);
val = 65536;
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) != 0)
e(0);
if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 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 *)&sin6,
sizeof(sin6))) == val)
break;
if (r != -1) e(0);
if (errno != EMSGSIZE) e(0);
}
if (val != 65535 - sizeof(struct ip6_hdr)) 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 *)&sin6, sizeof(sin6)) !=
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 *)&sin6, sizeof(sin6)) !=
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 *)&sin6, sizeof(sin6)) !=
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 *)&sin6,
sizeof(sin6)) != 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 *)&sin6,
sizeof(sin6)) != 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 sending and receiving with bad pointers.
*/
static void
test92j(void)
{
struct sockaddr_in sin;
char *ptr;
int i, fd;
subtest = 10;
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);
if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0);
memset(ptr, 'A', PAGE_SIZE);
if (sendto(fd, &ptr[PAGE_SIZE / 2], PAGE_SIZE, 0,
(struct sockaddr *)&sin, sizeof(sin)) != -1) e(0);
if (errno != EFAULT) e(0);
memset(ptr, 'B', PAGE_SIZE);
if (sendto(fd, ptr, PAGE_SIZE - sizeof(struct ip), 0,
(struct sockaddr *)&sin, sizeof(sin)) !=
PAGE_SIZE - sizeof(struct ip)) 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 = sizeof(struct ip); i < PAGE_SIZE; i++)
if (ptr[i] != 'B') e(0);
if (close(fd) != 0) e(0);
if (munmap(ptr, PAGE_SIZE) != 0) e(0);
}
/*
* Test basic sysctl(2) socket enumeration support.
*/
static void
test92k(void)
{
struct kinfo_pcb ki;
struct sockaddr_in lsin, rsin;
struct sockaddr_in6 lsin6, rsin6;
int fd, fd2, val;
subtest = 11;
if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0,
&ki) != 0) e(0);
if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
memset(&lsin, 0, sizeof(lsin));
lsin.sin_len = sizeof(lsin);
lsin.sin_family = AF_INET;
memset(&rsin, 0, sizeof(rsin));
rsin.sin_len = sizeof(rsin);
rsin.sin_family = AF_INET;
if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0,
&ki) != 1) e(0);
if (ki.ki_type != SOCK_RAW) 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.inet6.raw6.pcblist", TEST_PROTO, 0, 0,
&ki) != 0) e(0);
lsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(fd, (struct sockaddr *)&lsin, sizeof(lsin)) != 0) e(0);
if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0,
&ki) != 1) e(0);
if (ki.ki_type != SOCK_RAW) 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 (ki.ki_pflags & INP_HDRINCL) e(0);
rsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(fd, (struct sockaddr *)&rsin, sizeof(rsin)) != 0) e(0);
if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0,
&ki) != 1) e(0);
if (ki.ki_type != SOCK_RAW) 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 ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0);
if (sendto(fd2, "ABC", 3, 0, (struct sockaddr *)&lsin,
sizeof(lsin)) != 3) e(0);
if (close(fd2) != 0) e(0);
val = 1;
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) != 0)
e(0);
if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0,
&ki) != 1) e(0);
if (ki.ki_type != SOCK_RAW) 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 (!(ki.ki_pflags & INP_HDRINCL)) e(0);
if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0,
&ki) != 0) e(0);
if (close(fd) != 0) e(0);
/* Test IPv6 sockets as well. */
if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0);
memset(&lsin6, 0, sizeof(lsin6));
lsin6.sin6_len = sizeof(lsin6);
lsin6.sin6_family = AF_INET6;
memset(&rsin6, 0, sizeof(rsin6));
rsin6.sin6_len = sizeof(rsin6);
rsin6.sin6_family = AF_INET6;
if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0,
&ki) != 1) e(0);
if (ki.ki_type != SOCK_RAW) 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);
memcpy(&lsin6.sin6_addr, &in6addr_loopback, sizeof(lsin6.sin6_addr));
if (bind(fd, (struct sockaddr *)&lsin6, sizeof(lsin6)) != 0) e(0);
if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0,
&ki) != 1) e(0);
if (ki.ki_type != SOCK_RAW) 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);
memcpy(&rsin6.sin6_addr, &in6addr_loopback, sizeof(rsin6.sin6_addr));
if (connect(fd, (struct sockaddr *)&rsin6, sizeof(rsin6)) != 0)
e(0);
if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0,
&ki) != 1) e(0);
if (ki.ki_type != SOCK_RAW) 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 (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0,
&ki) != 0) e(0);
if (close(fd) != 0) e(0);
if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0,
&ki) != 0) e(0);
}
/*
* Test local and remote IPv6 address handling. In particular, test scope IDs
* and IPv4-mapped IPv6 addresses.
*/
static void
test92l(void)
{
subtest = 12;
socklib_test_addrs(SOCK_RAW, TEST_PROTO);
}
/*
* Test setting and retrieving basic multicast transmission options.
*/
static void
test92m(void)
{
subtest = 13;
socklib_multicast_tx_options(SOCK_RAW);
}
/*
* Test multicast support.
*/
static void
test92n(void)
{
subtest = 14;
socklib_test_multicast(SOCK_RAW, TEST_PROTO);
}
/*
* Test small and large ICMP echo ("ping") packets. This test aims to confirm
* expected behavior resulting from the LWIP service's memory pool policies:
* lwIP should reply to ICMP echo requests that fit in a single 512-byte buffer
* (including space for ethernet headers, even on loopback interfaces), but not
* to requests exceeding a single buffer.
*/
static void
test92o(void)
{
struct sockaddr_in6 sin6;
struct icmp6_hdr packet;
char buf[512];
int fd;
subtest = 15;
/* IPv6 only for now, for simplicity reasons. */
if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0);
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr));
memset(&packet, 0, sizeof(packet));
packet.icmp6_type = ICMP6_ECHO_REQUEST;
packet.icmp6_code = 0;
packet.icmp6_id = getpid();
packet.icmp6_seq = 1;
memset(buf, 'A', sizeof(buf));
memcpy(buf, &packet, sizeof(packet));
if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != sizeof(buf)) e(0);
packet.icmp6_seq = 2;
memset(buf, 'B', sizeof(buf));
memcpy(buf, &packet, sizeof(packet));
if (sendto(fd, buf, sizeof(buf) - 100, 0, (struct sockaddr *)&sin6,
sizeof(sin6)) != sizeof(buf) - 100) e(0);
do {
memset(buf, '\0', sizeof(buf));
if (recv(fd, buf, sizeof(buf), 0) <= 0) e(0);
memcpy(&packet, buf, sizeof(packet));
} while (packet.icmp6_type == ICMP6_ECHO_REQUEST);
if (packet.icmp6_type != ICMP6_ECHO_REPLY) e(0);
if (packet.icmp6_code != 0) e(0);
if (packet.icmp6_id != getpid()) e(0);
if (packet.icmp6_seq != 2) e(0);
if (buf[sizeof(buf) - 101] != 'B') e(0);
if (close(fd) != 0) e(0);
}
/*
* Test program for LWIP RAW sockets.
*/
int
main(int argc, char ** argv)
{
int i, m;
start(92);
if (argc == 2)
m = atoi(argv[1]);
else
m = 0xFFFF;
for (i = 0; i < ITERATIONS; i++) {
if (m & 0x0001) test92a();
if (m & 0x0002) test92b();
if (m & 0x0004) test92c();
if (m & 0x0008) test92d();
if (m & 0x0010) test92e();
if (m & 0x0020) test92f();
if (m & 0x0040) test92g();
if (m & 0x0080) test92h();
if (m & 0x0100) test92i();
if (m & 0x0200) test92j();
if (m & 0x0400) test92k();
if (m & 0x0400) test92k();
if (m & 0x0800) test92l();
if (m & 0x1000) test92m();
if (m & 0x2000) test92n();
if (m & 0x4000) test92o();
}
quit();
/* NOTREACHED */
}