/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "socklib.h" #define ITERATIONS 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 */ }