t_ipsec_policy.c revision 1.6 1 1.6 riastrad /* $NetBSD: t_ipsec_policy.c,v 1.6 2026/10/02 16:14:37 riastradh Exp $ */
2 1.1 riastrad
3 1.1 riastrad /*
4 1.1 riastrad * Copyright (c) 2026 The NetBSD Foundation, Inc.
5 1.1 riastrad * All rights reserved.
6 1.1 riastrad *
7 1.1 riastrad * Redistribution and use in source and binary forms, with or without
8 1.1 riastrad * modification, are permitted provided that the following conditions
9 1.1 riastrad * are met:
10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright
11 1.1 riastrad * notice, this list of conditions and the following disclaimer.
12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the
14 1.1 riastrad * documentation and/or other materials provided with the distribution.
15 1.1 riastrad *
16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE.
27 1.1 riastrad */
28 1.1 riastrad
29 1.1 riastrad #include <sys/cdefs.h>
30 1.6 riastrad __RCSID("$NetBSD: t_ipsec_policy.c,v 1.6 2026/10/02 16:14:37 riastradh Exp $");
31 1.1 riastrad
32 1.1 riastrad #include <sys/socket.h>
33 1.5 riastrad #include <sys/sysctl.h>
34 1.1 riastrad #include <sys/types.h>
35 1.1 riastrad #include <sys/un.h>
36 1.1 riastrad
37 1.1 riastrad #include <arpa/inet.h>
38 1.1 riastrad #include <net/pfkeyv2.h>
39 1.1 riastrad #include <netinet/in.h>
40 1.1 riastrad #include <netipsec/ipsec.h>
41 1.1 riastrad
42 1.1 riastrad #include <atf-c.h>
43 1.1 riastrad #include <stddef.h>
44 1.1 riastrad #include <stdint.h>
45 1.1 riastrad #include <stdio.h>
46 1.1 riastrad
47 1.1 riastrad #include "h_macros.h"
48 1.1 riastrad
49 1.1 riastrad static void
50 1.1 riastrad hexdump(const char *title, const void *buf, size_t len)
51 1.1 riastrad {
52 1.1 riastrad const uint8_t *p = buf;
53 1.1 riastrad size_t i;
54 1.1 riastrad
55 1.4 riastrad fprintf(stderr, "# %s (%zu bytes)\n", title, len);
56 1.1 riastrad for (i = 0; i < len; i++) {
57 1.1 riastrad if ((i % 8) == 0)
58 1.4 riastrad fprintf(stderr, " ");
59 1.4 riastrad fprintf(stderr, " %02hhx", p[i]);
60 1.1 riastrad if ((i % 16) == 15)
61 1.4 riastrad fprintf(stderr, "\n");
62 1.1 riastrad }
63 1.1 riastrad if (i % 16)
64 1.4 riastrad fprintf(stderr, "\n");
65 1.1 riastrad }
66 1.1 riastrad
67 1.1 riastrad static void *
68 1.1 riastrad append(void **bufp, size_t *buflenp, size_t addlen)
69 1.1 riastrad {
70 1.1 riastrad void *p;
71 1.1 riastrad
72 1.1 riastrad if (addlen == 0)
73 1.1 riastrad return NULL;
74 1.1 riastrad
75 1.1 riastrad ATF_REQUIRE(addlen < SIZE_MAX - *buflenp);
76 1.1 riastrad RZ(reallocarr(bufp, *buflenp + addlen, 1));
77 1.1 riastrad p = (char *)*bufp + *buflenp;
78 1.1 riastrad memset(p, 0, addlen);
79 1.1 riastrad *buflenp += addlen;
80 1.1 riastrad return p;
81 1.1 riastrad }
82 1.1 riastrad
83 1.1 riastrad static void
84 1.1 riastrad test_ipsec_policy_roundtrip(int exp_error, uint8_t dir,
85 1.1 riastrad const struct sockaddr *src, const struct sockaddr *dst)
86 1.1 riastrad {
87 1.1 riastrad enum { guardlen = 8 };
88 1.1 riastrad void *buf = NULL;
89 1.1 riastrad size_t len = 0;
90 1.1 riastrad struct sadb_x_policy *xpl, *xpl1;
91 1.1 riastrad size_t xpl_start, xpl1_start;
92 1.1 riastrad size_t reqlen;
93 1.1 riastrad void *copybuf = NULL;
94 1.1 riastrad size_t copylen = 0;
95 1.1 riastrad uint8_t guardbyte[2] = {0x5a, 0x3c};
96 1.1 riastrad size_t guard_start[2];
97 1.1 riastrad socklen_t optlen;
98 1.1 riastrad unsigned i;
99 1.1 riastrad int s;
100 1.1 riastrad
101 1.1 riastrad xpl_start = len;
102 1.1 riastrad xpl = append(&buf, &len, sizeof(*xpl));
103 1.1 riastrad xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
104 1.1 riastrad xpl->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
105 1.1 riastrad xpl->sadb_x_policy_dir = dir;
106 1.1 riastrad xpl = NULL;
107 1.1 riastrad
108 1.1 riastrad for (i = 0; i < 2; i++) {
109 1.1 riastrad const size_t xisr_start = len;
110 1.1 riastrad struct sadb_x_ipsecrequest *xisr;
111 1.1 riastrad
112 1.1 riastrad xisr = append(&buf, &len, sizeof(*xisr));
113 1.1 riastrad xisr->sadb_x_ipsecrequest_proto = IPPROTO_ESP;
114 1.1 riastrad xisr->sadb_x_ipsecrequest_mode = IPSEC_MODE_TUNNEL;
115 1.1 riastrad xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE;
116 1.1 riastrad xisr->sadb_x_ipsecrequest_reqid = 0;
117 1.1 riastrad xisr = NULL;
118 1.1 riastrad
119 1.1 riastrad memcpy(append(&buf, &len, src->sa_len), src, src->sa_len);
120 1.1 riastrad memcpy(append(&buf, &len, dst->sa_len), dst, dst->sa_len);
121 1.1 riastrad
122 1.1 riastrad /* pad */
123 1.1 riastrad const size_t npad = PFKEY_ALIGN8(len - xisr_start) -
124 1.1 riastrad (len - xisr_start);
125 1.1 riastrad (void)append(&buf, &len, npad);
126 1.1 riastrad
127 1.1 riastrad ATF_REQUIRE(PFKEY_ALIGN8(len - xisr_start) == len - xisr_start);
128 1.1 riastrad xisr = (void *)((char *)buf + xisr_start);
129 1.1 riastrad xisr->sadb_x_ipsecrequest_len = len - xisr_start;
130 1.1 riastrad }
131 1.1 riastrad
132 1.1 riastrad ATF_REQUIRE(PFKEY_ALIGN8(len - xpl_start) == len - xpl_start);
133 1.1 riastrad xpl = (void *)((char *)buf + xpl_start);
134 1.1 riastrad xpl->sadb_x_policy_len = PFKEY_UNIT64(len - xpl_start);
135 1.1 riastrad
136 1.1 riastrad reqlen = len - xpl_start;
137 1.1 riastrad
138 1.1 riastrad RL(s = socket(AF_INET6, SOCK_DGRAM, 0));
139 1.1 riastrad if (exp_error) {
140 1.1 riastrad ATF_CHECK_ERRNO(exp_error,
141 1.1 riastrad setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl, reqlen)
142 1.1 riastrad == -1);
143 1.1 riastrad goto out;
144 1.1 riastrad }
145 1.1 riastrad if (setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl, reqlen)
146 1.1 riastrad == -1) {
147 1.1 riastrad int error = errno;
148 1.1 riastrad
149 1.1 riastrad atf_tc_fail_nonfatal("setsockopt(IPV6_IPSEC_POLICY): %d (%s)",
150 1.1 riastrad error, strerror(errno));
151 1.1 riastrad goto out;
152 1.1 riastrad }
153 1.1 riastrad
154 1.1 riastrad guard_start[0] = copylen;
155 1.1 riastrad memset(append(©buf, ©len, guardlen), guardbyte[0], guardlen);
156 1.1 riastrad
157 1.1 riastrad xpl1_start = copylen;
158 1.3 kre (void)append(©buf, ©len, reqlen);
159 1.1 riastrad
160 1.1 riastrad guard_start[1] = copylen;
161 1.1 riastrad memset(append(©buf, ©len, guardlen), guardbyte[1], guardlen);
162 1.1 riastrad
163 1.1 riastrad xpl = (void *)((char *)buf + xpl_start);
164 1.1 riastrad xpl1 = (void *)((char *)copybuf + xpl1_start);
165 1.1 riastrad memcpy(xpl1, xpl, reqlen);
166 1.1 riastrad
167 1.1 riastrad optlen = reqlen;
168 1.1 riastrad ATF_CHECK_ERRNO(EINVAL,
169 1.1 riastrad getsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl1, &optlen));
170 1.1 riastrad
171 1.1 riastrad optlen = reqlen;
172 1.1 riastrad if (getsockopt2(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, xpl1, &optlen)
173 1.1 riastrad == -1) {
174 1.1 riastrad int error = errno;
175 1.1 riastrad
176 1.1 riastrad atf_tc_fail_nonfatal("getsockopt2(IPV6_IPSEC_POLICY): %d (%s)",
177 1.1 riastrad error, strerror(errno));
178 1.1 riastrad goto out;
179 1.1 riastrad }
180 1.1 riastrad ATF_CHECK_EQ_MSG(reqlen, optlen, "reqlen=%zu optlen=%zu",
181 1.1 riastrad (size_t)reqlen, (size_t)optlen);
182 1.1 riastrad
183 1.1 riastrad if (reqlen != optlen || memcmp(xpl, xpl1, len) != 0) {
184 1.1 riastrad hexdump("before", xpl, reqlen);
185 1.1 riastrad hexdump("after", xpl1, optlen);
186 1.4 riastrad fflush(stderr);
187 1.1 riastrad atf_tc_fail_nonfatal("mismatch");
188 1.1 riastrad }
189 1.1 riastrad
190 1.1 riastrad for (i = 0; i < __arraycount(guard_start); i++) {
191 1.1 riastrad const char *guard = (char *)copybuf + guard_start[i];
192 1.1 riastrad size_t j;
193 1.1 riastrad
194 1.1 riastrad for (j = 0; j < guardlen; j++) {
195 1.1 riastrad if (guard[j] != guardbyte[i]) {
196 1.1 riastrad char title[8];
197 1.1 riastrad
198 1.1 riastrad snprintf(title, sizeof(title), "guard %u", i);
199 1.1 riastrad hexdump(title, guard, guardlen);
200 1.4 riastrad fflush(stderr);
201 1.1 riastrad atf_tc_fail_nonfatal("guard %u overwritten",
202 1.1 riastrad i);
203 1.1 riastrad break;
204 1.1 riastrad }
205 1.1 riastrad }
206 1.1 riastrad }
207 1.1 riastrad
208 1.1 riastrad out: RL(close(s));
209 1.1 riastrad }
210 1.1 riastrad
211 1.1 riastrad ATF_TC(pr60669);
212 1.1 riastrad ATF_TC_HEAD(pr60669, tc)
213 1.1 riastrad {
214 1.1 riastrad atf_tc_set_md_var(tc, "descr", "Test IPV6_IPSEC_POLICY round-trip with"
215 1.1 riastrad " v4 src and v4 dst");
216 1.1 riastrad }
217 1.1 riastrad ATF_TC_BODY(pr60669, tc)
218 1.1 riastrad {
219 1.1 riastrad union {
220 1.1 riastrad struct sockaddr sa;
221 1.1 riastrad struct sockaddr_in sin;
222 1.1 riastrad } v4[2] = {
223 1.1 riastrad [0] = {
224 1.1 riastrad .sin = {
225 1.1 riastrad .sin_len = sizeof(v4[0].sin),
226 1.1 riastrad .sin_family = AF_INET,
227 1.1 riastrad .sin_port = 0,
228 1.1 riastrad }
229 1.1 riastrad },
230 1.1 riastrad [1] = {
231 1.1 riastrad .sin = {
232 1.1 riastrad .sin_len = sizeof(v4[1].sin),
233 1.1 riastrad .sin_family = AF_INET,
234 1.1 riastrad .sin_port = 0,
235 1.1 riastrad }
236 1.1 riastrad },
237 1.1 riastrad };
238 1.1 riastrad union {
239 1.1 riastrad struct sockaddr sa;
240 1.1 riastrad struct sockaddr_in6 sin6;
241 1.1 riastrad } v6[2] = {
242 1.1 riastrad [0] = {
243 1.1 riastrad .sin6 = {
244 1.1 riastrad .sin6_len = sizeof(v6[0].sin6),
245 1.1 riastrad .sin6_family = AF_INET6,
246 1.1 riastrad .sin6_port = 0,
247 1.1 riastrad }
248 1.1 riastrad },
249 1.1 riastrad [1] = {
250 1.1 riastrad .sin6 = {
251 1.1 riastrad .sin6_len = sizeof(v6[1].sin6),
252 1.1 riastrad .sin6_family = AF_INET6,
253 1.1 riastrad .sin6_port = 0,
254 1.1 riastrad }
255 1.1 riastrad },
256 1.1 riastrad };
257 1.1 riastrad union {
258 1.1 riastrad struct sockaddr sa;
259 1.1 riastrad struct sockaddr_un sun;
260 1.1 riastrad } un[2] = {
261 1.1 riastrad [0] = {
262 1.1 riastrad .sun = {
263 1.1 riastrad .sun_len = -1,
264 1.1 riastrad .sun_family = AF_LOCAL,
265 1.1 riastrad .sun_path = "/socket0",
266 1.1 riastrad }
267 1.1 riastrad },
268 1.1 riastrad [1] = {
269 1.1 riastrad .sun = {
270 1.1 riastrad .sun_len = -1,
271 1.1 riastrad .sun_family = AF_LOCAL,
272 1.1 riastrad .sun_path = "/socket1",
273 1.1 riastrad }
274 1.1 riastrad },
275 1.1 riastrad };
276 1.1 riastrad struct sockaddr sa[2] = {
277 1.1 riastrad [0] = { .sa_len = sizeof(sa[0]), .sa_family = AF_UNSPEC },
278 1.1 riastrad [1] = { .sa_len = sizeof(sa[1]), .sa_family = AF_UNSPEC },
279 1.1 riastrad };
280 1.1 riastrad struct sockaddr sa1[2] = {
281 1.1 riastrad [0] = { .sa_len = 1, .sa_family = AF_UNSPEC },
282 1.1 riastrad [1] = { .sa_len = 1, .sa_family = AF_UNSPEC },
283 1.1 riastrad };
284 1.1 riastrad struct sockaddr sa0[2] = {
285 1.1 riastrad [0] = { .sa_len = 0, .sa_family = AF_UNSPEC },
286 1.1 riastrad [1] = { .sa_len = 0, .sa_family = AF_UNSPEC },
287 1.1 riastrad };
288 1.1 riastrad const struct {
289 1.1 riastrad struct sockaddr *src;
290 1.1 riastrad struct sockaddr *dst;
291 1.1 riastrad int expected_error;
292 1.1 riastrad const char *xfail;
293 1.1 riastrad } C[] = {
294 1.1 riastrad [0] = { &v4[0].sa, &v4[1].sa, 0, NULL },
295 1.2 riastrad [1] = { &v4[0].sa, &v6[1].sa, EINVAL, NULL },
296 1.1 riastrad [2] = { &v4[0].sa, &un[1].sa, EINVAL, NULL },
297 1.2 riastrad [3] = { &v4[0].sa, &sa[1], EINVAL, NULL },
298 1.1 riastrad [4] = { &v4[0].sa, &sa1[1], EINVAL, NULL },
299 1.1 riastrad [5] = { &v4[0].sa, &sa0[1], EINVAL, NULL },
300 1.1 riastrad
301 1.2 riastrad [6] = { &v6[0].sa, &v4[1].sa, EINVAL, NULL },
302 1.1 riastrad [7] = { &v6[0].sa, &v6[1].sa, 0, NULL },
303 1.1 riastrad [8] = { &v6[0].sa, &un[1].sa, EINVAL, NULL },
304 1.2 riastrad [9] = { &v6[0].sa, &sa[1], EINVAL, NULL },
305 1.1 riastrad [10] = { &v6[0].sa, &sa1[1], EINVAL, NULL },
306 1.1 riastrad [11] = { &v6[0].sa, &sa0[1], EINVAL, NULL },
307 1.1 riastrad
308 1.1 riastrad [12] = { &un[0].sa, &v4[1].sa, EINVAL, NULL },
309 1.1 riastrad [13] = { &un[0].sa, &v6[1].sa, EINVAL, NULL },
310 1.1 riastrad [14] = { &un[0].sa, &un[1].sa, EINVAL, NULL },
311 1.1 riastrad [15] = { &un[0].sa, &sa[1], EINVAL, NULL },
312 1.1 riastrad [16] = { &un[0].sa, &sa1[1], EINVAL, NULL },
313 1.1 riastrad [17] = { &un[0].sa, &sa0[1], EINVAL, NULL },
314 1.1 riastrad
315 1.2 riastrad [18] = { &sa[0], &v4[1].sa, EINVAL, NULL },
316 1.2 riastrad [19] = { &sa[0], &v6[1].sa, EINVAL, NULL },
317 1.1 riastrad [20] = { &sa[0], &un[1].sa, EINVAL, NULL },
318 1.2 riastrad [21] = { &sa[0], &sa[1], EINVAL, NULL },
319 1.1 riastrad [22] = { &sa[0], &sa1[1], EINVAL, NULL },
320 1.1 riastrad [23] = { &sa[0], &sa0[1], EINVAL, NULL },
321 1.1 riastrad
322 1.1 riastrad [24] = { &sa1[0], &v4[1].sa, EINVAL, NULL },
323 1.1 riastrad [25] = { &sa1[0], &v6[1].sa, EINVAL, NULL },
324 1.1 riastrad [26] = { &sa1[0], &un[1].sa, EINVAL, NULL },
325 1.1 riastrad [27] = { &sa1[0], &sa[1], EINVAL, NULL },
326 1.1 riastrad [28] = { &sa1[0], &sa1[1], EINVAL, NULL },
327 1.1 riastrad [29] = { &sa1[0], &sa0[1], EINVAL, NULL },
328 1.1 riastrad
329 1.1 riastrad /*
330 1.1 riastrad * sa0 as the src one doesn't even make sense to test,
331 1.1 riastrad * because sa_len won't even advance past it, so it
332 1.1 riastrad * will be the same as testing a nonempty src and sa0
333 1.1 riastrad * dst above.
334 1.1 riastrad */
335 1.1 riastrad };
336 1.1 riastrad unsigned i;
337 1.5 riastrad int ipsec_enabled;
338 1.5 riastrad size_t len = sizeof(ipsec_enabled);
339 1.5 riastrad
340 1.6 riastrad if (sysctlbyname("net.inet.ipsec.enabled", &ipsec_enabled, &len,
341 1.6 riastrad NULL, 0) == -1) {
342 1.6 riastrad if (errno != ENOENT)
343 1.6 riastrad atf_tc_fail_errno("sysctl net.inet.ipsec.enabled");
344 1.6 riastrad ipsec_enabled = 0;
345 1.6 riastrad }
346 1.5 riastrad if (!ipsec_enabled)
347 1.5 riastrad atf_tc_skip("IPsec disabled, sysctl net.inet.ipsec.enabled=0");
348 1.1 riastrad
349 1.1 riastrad RL(inet_pton(AF_INET, "192.0.2.42", &v4[0].sin.sin_addr));
350 1.1 riastrad RL(inet_pton(AF_INET, "192.51.100.54", &v4[1].sin.sin_addr));
351 1.1 riastrad
352 1.1 riastrad RL(inet_pton(AF_INET6, "2001:db8:1::1", &v6[0].sin6.sin6_addr));
353 1.1 riastrad RL(inet_pton(AF_INET6, "2001:db8:2::1", &v6[1].sin6.sin6_addr));
354 1.1 riastrad
355 1.1 riastrad un[0].sun.sun_len = SUN_LEN(&un[0].sun);
356 1.1 riastrad un[1].sun.sun_len = SUN_LEN(&un[1].sun);
357 1.1 riastrad
358 1.1 riastrad for (i = 0; i < __arraycount(C); i++) {
359 1.1 riastrad const char *const dirname[2] = { "inbound", "outbound" };
360 1.1 riastrad const unsigned dir[2] =
361 1.1 riastrad { IPSEC_DIR_INBOUND, IPSEC_DIR_OUTBOUND };
362 1.1 riastrad unsigned j;
363 1.1 riastrad
364 1.1 riastrad for (j = 0; j < 2; j++) {
365 1.4 riastrad fprintf(stderr, "# case %u %s\n", i, dirname[j]);
366 1.4 riastrad fflush(stderr);
367 1.1 riastrad if (C[i].xfail)
368 1.1 riastrad atf_tc_expect_fail("%s", C[i].xfail);
369 1.1 riastrad test_ipsec_policy_roundtrip(C[i].expected_error,
370 1.1 riastrad dir[j], C[i].src, C[i].dst);
371 1.1 riastrad if (C[i].xfail)
372 1.1 riastrad atf_tc_expect_pass();
373 1.1 riastrad }
374 1.1 riastrad }
375 1.1 riastrad }
376 1.1 riastrad
377 1.1 riastrad ATF_TP_ADD_TCS(tp)
378 1.1 riastrad {
379 1.1 riastrad
380 1.1 riastrad ATF_TP_ADD_TC(tp, pr60669);
381 1.1 riastrad return atf_no_error();
382 1.1 riastrad }
383