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