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