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
ATF_TC_BODY(input,tc)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
ATF_TP_ADD_TCS(tp)214 ATF_TP_ADD_TCS(tp)
215 {
216 ATF_TP_ADD_TC(tp, input);
217
218 return (atf_no_error());
219 }
220