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