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