1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2026 Gleb Smirnoff <glebius@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/socket.h> 30 #include <netinet/in.h> 31 #include <arpa/inet.h> 32 #include <netinet/ip.h> 33 #include <net/if.h> 34 #include <errno.h> 35 #include <stdlib.h> 36 37 #include <atf-c.h> 38 39 /* 40 * The 'input' test exercises logic of rip_input(). The best documentation 41 * for raw socket input behavior is collected in Stevens's UNIX Network 42 * Programming, Section 28.4. We create several sockets, with different 43 * remote and local bindings, as well as a socket with multicast membership 44 * and then we send different packets and see which sockets received their 45 * copy. 46 * The table tests[] describes our expectations. 47 */ 48 ATF_TC_WITHOUT_HEAD(input); 49 #define PROT1 253 /* RFC3692 */ 50 #define PROT2 254 /* RFC3692 */ 51 static const struct rcvr { 52 struct in_addr laddr, faddr, maddr; 53 uint8_t proto; 54 } rcvrs[] = { 55 #define WILD { htonl(INADDR_ANY) } 56 #define LOOP(x) { htonl(INADDR_LOOPBACK + (x)) } 57 #define MULT(x) { htonl(INADDR_UNSPEC_GROUP + (x)) } 58 { WILD, WILD, WILD, 0 }, 59 { WILD, WILD, WILD, PROT1 }, 60 { LOOP(0), WILD, WILD, 0 }, 61 { LOOP(0), WILD, WILD, PROT1 }, 62 { LOOP(1), WILD, WILD, 0 }, 63 { LOOP(1), WILD, WILD, PROT1 }, 64 { LOOP(0), LOOP(2), WILD, 0 }, 65 { LOOP(0), LOOP(2), WILD, PROT1 }, 66 { LOOP(0), LOOP(3), WILD, 0 }, 67 { LOOP(0), LOOP(3), WILD, PROT1 }, 68 { LOOP(1), LOOP(3), WILD, 0 }, 69 { LOOP(1), LOOP(3), WILD, PROT1 }, 70 { WILD, WILD, MULT(1), 0 }, 71 }; 72 static const struct test { 73 struct in_addr src, dst; 74 uint8_t proto; 75 bool results[nitems(rcvrs)]; 76 } tests[] = { 77 #define x true 78 #define o false 79 { LOOP(2), LOOP(0), PROT1, 80 { x, x, x, x, o, o, x, x, o, o, o, o, x } }, 81 { LOOP(2), LOOP(0), PROT2, 82 { x, o, x, o, o, o, x, o, o, o, o, o, x } }, 83 { LOOP(3), LOOP(0), PROT1, 84 { x, x, x, x, o, o, o, o, x, x, o, o, x } }, 85 { LOOP(3), LOOP(0), PROT2, 86 { x, o, x, o, o, o, o, o, x, o, o, o, x } }, 87 { LOOP(2), LOOP(1), PROT1, 88 { x, x, o, o, x, x, o, o, o, o, o, o, x } }, 89 { LOOP(2), LOOP(1), PROT2, 90 { x, o, o, o, x, o, o, o, o, o, o, o, x } }, 91 { LOOP(3), LOOP(1), PROT1, 92 { x, x, o, o, x, x, o, o, o, o, x, x, x } }, 93 { LOOP(3), LOOP(1), PROT2, 94 { x, o, o, o, x, o, o, o, o, o, x, o, x } }, 95 { LOOP(3), MULT(1), PROT1, 96 { x, x, o, o, o, o, o, o, o, o, o, o, x } }, 97 { LOOP(3), MULT(2), PROT1, 98 { x, x, o, o, o, o, o, o, o, o, o, o, o } }, 99 #undef WILD 100 #undef LOOP 101 #undef MULT 102 #undef x 103 #undef o 104 }; 105 106 ATF_TC_BODY(input, tc) 107 { 108 struct pkt { 109 struct ip ip; 110 char payload[100]; 111 } __packed pkt = { 112 .ip.ip_v = IPVERSION, 113 .ip.ip_hl = sizeof(struct ip) >> 2, 114 .ip.ip_len = htons(sizeof(struct pkt)), 115 .ip.ip_ttl = 16, 116 }; 117 struct sockaddr_in sin = { 118 .sin_family = AF_INET, 119 .sin_len = sizeof(sin), 120 }; 121 struct ip_mreqn mreqn = { 122 .imr_ifindex = if_nametoindex("lo0"), 123 }; 124 int r[nitems(rcvrs)]; 125 int s; 126 127 /* 128 * This XXX to be removed when kyua provides generic framework for 129 * constructing test jail environments. 130 */ 131 system("/sbin/ifconfig lo0 127.0.0.1/32"); 132 system("/sbin/ifconfig lo0 127.0.0.2/32 alias"); 133 134 for (u_int i = 0; i < nitems(rcvrs); i++) { 135 /* 136 * To avoid a race between send(2) and packet queueing in 137 * netisr(9) and our recv(2), set the very first receiver 138 * socket to blocking mode. Note in the above table that first 139 * receiver is supposed to receive something in every test. 140 */ 141 ATF_REQUIRE((r[i] = socket(PF_INET, SOCK_RAW | 142 (i != 0 ? SOCK_NONBLOCK : 0), 143 rcvrs[i].proto)) != -1); 144 if (rcvrs[i].laddr.s_addr != htonl(INADDR_ANY)) { 145 sin.sin_addr = rcvrs[i].laddr; 146 ATF_REQUIRE(bind(r[i], (struct sockaddr *)&sin, 147 sizeof(sin)) == 0); 148 } 149 if (rcvrs[i].faddr.s_addr != htonl(INADDR_ANY)) { 150 sin.sin_addr = rcvrs[i].faddr; 151 ATF_REQUIRE(connect(r[i], (struct sockaddr *)&sin, 152 sizeof(sin)) == 0); 153 } 154 if (rcvrs[i].maddr.s_addr != htonl(INADDR_ANY)) { 155 mreqn.imr_multiaddr = rcvrs[i].maddr; 156 ATF_REQUIRE(setsockopt(r[i], IPPROTO_IP, 157 IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn)) == 0); 158 } 159 } 160 161 ATF_REQUIRE((s = socket(PF_INET, SOCK_RAW, 0)) != -1); 162 ATF_REQUIRE(setsockopt(s, IPPROTO_IP, IP_HDRINCL, &(int){1}, 163 sizeof(int)) == 0); 164 /* 165 * Make sending socket connected. The socket API requires connected 166 * status to use send(2), even with IP_HDRINCL. Another side effect 167 * is that the sending socket won't receive own datagrams, which we 168 * don't drain out in this program. 169 */ 170 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK + 100); 171 ATF_REQUIRE(connect(s, (struct sockaddr *)&sin, sizeof(sin)) == 0); 172 /* 173 * Force multicast interface for the sending socket to be able to 174 * send to MULT(x) destinations. 175 */ 176 mreqn.imr_multiaddr.s_addr = 0; 177 ATF_REQUIRE(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, 178 sizeof(mreqn)) == 0); 179 180 for (u_int i = 0; i < nitems(tests); i++) { 181 arc4random_buf(&pkt.payload, sizeof(pkt.payload)); 182 pkt.ip.ip_src = tests[i].src; 183 pkt.ip.ip_dst = tests[i].dst; 184 pkt.ip.ip_p = tests[i].proto; 185 ATF_REQUIRE(send(s, &pkt, sizeof(pkt), 0) == sizeof(pkt)); 186 for (u_int j = 0; j < nitems(rcvrs); j++) { 187 char buf[sizeof(pkt)]; 188 char p[4][INET_ADDRSTRLEN]; 189 ssize_t ss; 190 191 ss = recv(r[j], buf, sizeof(buf), 0); 192 193 ATF_REQUIRE_MSG((tests[i].results[j] == true && 194 ss == sizeof(buf) && memcmp(buf + sizeof(struct ip), 195 pkt.payload, sizeof(pkt.payload)) == 0) || 196 (tests[i].results[j] == false && 197 ss == -1 && errno == EAGAIN), 198 "test #%u %s->%s %u unexpected receive of %zd " 199 "bytes errno %d on socket #%u %s->%s %u", i, 200 inet_ntop(AF_INET, &tests[i].src, p[0], 201 INET_ADDRSTRLEN), 202 inet_ntop(AF_INET, &tests[i].dst, p[1], 203 INET_ADDRSTRLEN), 204 tests[i].proto, ss, errno, j, 205 inet_ntop(AF_INET, &rcvrs[j].faddr, p[2], 206 INET_ADDRSTRLEN), 207 inet_ntop(AF_INET, &rcvrs[j].laddr, p[3], 208 INET_ADDRSTRLEN), 209 rcvrs[j].proto); 210 } 211 } 212 } 213 214 ATF_TP_ADD_TCS(tp) 215 { 216 ATF_TP_ADD_TC(tp, input); 217 218 return (atf_no_error()); 219 } 220