jcs's openbsd hax
openbsd
at jcs 658 lines 16 kB view raw
1/* $OpenBSD: ipsec_output.c,v 1.105 2025/07/08 00:47:41 jsg Exp $ */ 2/* 3 * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) 4 * 5 * Copyright (c) 2000-2001 Angelos D. Keromytis. 6 * 7 * Permission to use, copy, and modify this software with or without fee 8 * is hereby granted, provided that this entire notice is included in 9 * all copies of any software which is or includes a copy or 10 * modification of this software. 11 * You may use this code under the GNU public license if you so wish. Please 12 * contribute changes back to the authors under this freer than GPL license 13 * so that we may further the use of strong encryption without limitations to 14 * all. 15 * 16 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY 18 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE 19 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR 20 * PURPOSE. 21 */ 22 23#include "pf.h" 24 25#include <sys/param.h> 26#include <sys/systm.h> 27#include <sys/mbuf.h> 28#include <sys/socket.h> 29#include <sys/timeout.h> 30 31#include <net/if.h> 32#include <net/route.h> 33 34#include <netinet/in.h> 35#include <netinet/ip.h> 36#include <netinet/ip6.h> 37#include <netinet/ip_ipsp.h> 38#include <netinet/ip_var.h> 39#include <netinet6/ip6_var.h> 40 41#if NPF > 0 42#include <net/pfvar.h> 43#endif 44 45#include <netinet/udp.h> 46#include <netinet/ip_ipip.h> 47#include <netinet/ip_ah.h> 48#include <netinet/ip_esp.h> 49#include <netinet/ip_ipcomp.h> 50 51#include <crypto/xform.h> 52 53/* 54 * Locks used to protect data: 55 * a atomic 56 */ 57 58#ifdef ENCDEBUG 59#define DPRINTF(fmt, args...) \ 60 do { \ 61 if (atomic_load_int(&encdebug)) \ 62 printf("%s: " fmt "\n", __func__, ## args); \ 63 } while (0) 64#else 65#define DPRINTF(fmt, args...) \ 66 do { } while (0) 67#endif 68 69int udpencap_enable = 1; /* [a] enabled by default */ 70int udpencap_port = 4500; /* [a] triggers decapsulation */ 71 72/* 73 * Loop over a tdb chain, taking into consideration protocol tunneling. The 74 * fourth argument is set if the first encapsulation header is already in 75 * place. 76 */ 77int 78ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready, 79 int setdf) 80{ 81 int hlen, off, error; 82#ifdef INET6 83 struct ip6_ext ip6e; 84 int nxt; 85 int dstopt = 0; 86#endif 87 88 struct ip *ip; 89#ifdef INET6 90 struct ip6_hdr *ip6; 91#endif /* INET6 */ 92 93#ifdef ENCDEBUG 94 char buf[INET6_ADDRSTRLEN]; 95#endif 96 97 /* Check that the transform is allowed by the administrator. */ 98 if ((tdb->tdb_sproto == IPPROTO_ESP && !atomic_load_int(&esp_enable)) || 99 (tdb->tdb_sproto == IPPROTO_AH && !atomic_load_int(&ah_enable)) || 100 (tdb->tdb_sproto == IPPROTO_IPCOMP && 101 !atomic_load_int(&ipcomp_enable))) { 102 DPRINTF("IPsec outbound packet dropped due to policy " 103 "(check your sysctls)"); 104 error = EHOSTUNREACH; 105 goto drop; 106 } 107 108 /* Sanity check. */ 109 if (!tdb->tdb_xform) { 110 DPRINTF("uninitialized TDB"); 111 error = EHOSTUNREACH; 112 goto drop; 113 } 114 115 /* Check if the SPI is invalid. */ 116 if (tdb->tdb_flags & TDBF_INVALID) { 117 DPRINTF("attempt to use invalid SA %s/%08x/%u", 118 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 119 ntohl(tdb->tdb_spi), tdb->tdb_sproto); 120 error = ENXIO; 121 goto drop; 122 } 123 124 /* Check that the network protocol is supported */ 125 switch (tdb->tdb_dst.sa.sa_family) { 126 case AF_INET: 127 break; 128 129#ifdef INET6 130 case AF_INET6: 131 break; 132#endif /* INET6 */ 133 134 default: 135 DPRINTF("attempt to use SA %s/%08x/%u for protocol family %d", 136 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 137 ntohl(tdb->tdb_spi), tdb->tdb_sproto, 138 tdb->tdb_dst.sa.sa_family); 139 error = EPFNOSUPPORT; 140 goto drop; 141 } 142 143 /* 144 * Register first use if applicable, setup relevant expiration timer. 145 */ 146 if (tdb->tdb_first_use == 0) { 147 tdb->tdb_first_use = gettime(); 148 if (tdb->tdb_flags & TDBF_FIRSTUSE) { 149 if (timeout_add_sec(&tdb->tdb_first_tmo, 150 tdb->tdb_exp_first_use)) 151 tdb_ref(tdb); 152 } 153 if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) { 154 if (timeout_add_sec(&tdb->tdb_sfirst_tmo, 155 tdb->tdb_soft_first_use)) 156 tdb_ref(tdb); 157 } 158 } 159 160 /* 161 * Check for tunneling if we don't have the first header in place. 162 * When doing Ethernet-over-IP, we are handed an already-encapsulated 163 * frame, so we don't need to re-encapsulate. 164 */ 165 if (tunalready == 0) { 166 /* 167 * If the target protocol family is different, we know we'll be 168 * doing tunneling. 169 */ 170 if (af == tdb->tdb_dst.sa.sa_family) { 171 switch (af) { 172 case AF_INET: 173 hlen = sizeof(struct ip); 174 break; 175#ifdef INET6 176 case AF_INET6: 177 hlen = sizeof(struct ip6_hdr); 178 break; 179#endif /* INET6 */ 180 } 181 182 /* Bring the network header in the first mbuf. */ 183 if (m->m_len < hlen) { 184 if ((m = m_pullup(m, hlen)) == NULL) { 185 error = ENOBUFS; 186 goto drop; 187 } 188 } 189 190 if (af == AF_INET) { 191 ip = mtod(m, struct ip *); 192 193 /* 194 * This is not a bridge packet, remember if we 195 * had IP_DF. 196 */ 197 if (setdf == IPSP_DF_INHERIT) { 198 setdf = 199 ISSET(ip->ip_off, htons(IP_DF)) ? 200 IPSP_DF_ON : IPSP_DF_OFF; 201 } 202 } 203 204#ifdef INET6 205 if (af == AF_INET6) 206 ip6 = mtod(m, struct ip6_hdr *); 207#endif /* INET6 */ 208 } 209 210 /* Do the appropriate encapsulation, if necessary. */ 211 if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */ 212 (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */ 213 (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */ 214 ((tdb->tdb_dst.sa.sa_family == AF_INET) && 215 (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) && 216 (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) || 217#ifdef INET6 218 ((tdb->tdb_dst.sa.sa_family == AF_INET6) && 219 (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) && 220 (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, 221 &ip6->ip6_dst))) || 222#endif /* INET6 */ 223 0) { 224 /* Fix IPv4 header checksum and length. */ 225 if (af == AF_INET) { 226 if (m->m_len < sizeof(struct ip)) 227 if ((m = m_pullup(m, 228 sizeof(struct ip))) == NULL) { 229 error = ENOBUFS; 230 goto drop; 231 } 232 233 ip = mtod(m, struct ip *); 234 ip->ip_len = htons(m->m_pkthdr.len); 235 ip->ip_sum = 0; 236 ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 237 } 238 239#ifdef INET6 240 /* Fix IPv6 header payload length. */ 241 if (af == AF_INET6) { 242 if (m->m_len < sizeof(struct ip6_hdr)) 243 if ((m = m_pullup(m, 244 sizeof(struct ip6_hdr))) == NULL) { 245 error = ENOBUFS; 246 goto drop; 247 } 248 249 if (m->m_pkthdr.len - sizeof(*ip6) > 250 IPV6_MAXPACKET) { 251 /* No jumbogram support. */ 252 error = ENXIO; /*?*/ 253 goto drop; 254 } 255 ip6 = mtod(m, struct ip6_hdr *); 256 ip6->ip6_plen = htons(m->m_pkthdr.len 257 - sizeof(*ip6)); 258 } 259#endif /* INET6 */ 260 261 /* Encapsulate -- m may be changed or set to NULL. */ 262 error = ipip_output(&m, tdb); 263 if ((m == NULL) && (!error)) 264 error = EFAULT; 265 if (error) 266 goto drop; 267 268 if (tdb->tdb_dst.sa.sa_family == AF_INET && 269 setdf == IPSP_DF_ON) { 270 if (m->m_len < sizeof(struct ip)) 271 if ((m = m_pullup(m, 272 sizeof(struct ip))) == NULL) { 273 error = ENOBUFS; 274 goto drop; 275 } 276 277 ip = mtod(m, struct ip *); 278 ip->ip_off |= htons(IP_DF); 279 } 280 281 /* Remember that we appended a tunnel header. */ 282 mtx_enter(&tdb->tdb_mtx); 283 tdb->tdb_flags |= TDBF_USEDTUNNEL; 284 mtx_leave(&tdb->tdb_mtx); 285 } 286 } 287 288 /* 289 * If this is just an IP-IP TDB and we're told there's already an 290 * encapsulation header or ipip_output() has encapsulated it, move on. 291 */ 292 if (tdb->tdb_xform->xf_type == XF_IP4) 293 return ipsp_process_done(m, tdb); 294 295 /* Extract some information off the headers. */ 296 switch (tdb->tdb_dst.sa.sa_family) { 297 case AF_INET: 298 ip = mtod(m, struct ip *); 299 hlen = ip->ip_hl << 2; 300 off = offsetof(struct ip, ip_p); 301 break; 302 303#ifdef INET6 304 case AF_INET6: 305 ip6 = mtod(m, struct ip6_hdr *); 306 hlen = sizeof(struct ip6_hdr); 307 off = offsetof(struct ip6_hdr, ip6_nxt); 308 nxt = ip6->ip6_nxt; 309 /* 310 * chase mbuf chain to find the appropriate place to 311 * put AH/ESP/IPcomp header. 312 * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] 313 */ 314 do { 315 switch (nxt) { 316 case IPPROTO_AH: 317 case IPPROTO_ESP: 318 case IPPROTO_IPCOMP: 319 /* 320 * we should not skip security header added 321 * beforehand. 322 */ 323 goto exitip6loop; 324 325 case IPPROTO_HOPOPTS: 326 case IPPROTO_DSTOPTS: 327 case IPPROTO_ROUTING: 328 /* 329 * if we see 2nd destination option header, 330 * we should stop there. 331 */ 332 if (nxt == IPPROTO_DSTOPTS && dstopt) 333 goto exitip6loop; 334 335 if (nxt == IPPROTO_DSTOPTS) { 336 /* 337 * seen 1st or 2nd destination option. 338 * next time we see one, it must be 2nd. 339 */ 340 dstopt = 1; 341 } else if (nxt == IPPROTO_ROUTING) { 342 /* 343 * if we see destination option next 344 * time, it must be dest2. 345 */ 346 dstopt = 2; 347 } 348 if (m->m_pkthdr.len < hlen + sizeof(ip6e)) { 349 error = EINVAL; 350 goto drop; 351 } 352 /* skip this header */ 353 m_copydata(m, hlen, sizeof(ip6e), 354 (caddr_t)&ip6e); 355 nxt = ip6e.ip6e_nxt; 356 off = hlen + offsetof(struct ip6_ext, ip6e_nxt); 357 /* 358 * we will never see nxt == IPPROTO_AH 359 * so it is safe to omit AH case. 360 */ 361 hlen += (ip6e.ip6e_len + 1) << 3; 362 break; 363 default: 364 goto exitip6loop; 365 } 366 } while (hlen < m->m_pkthdr.len); 367 exitip6loop: 368 break; 369#endif /* INET6 */ 370 default: 371 error = EPFNOSUPPORT; 372 goto drop; 373 } 374 375 if (m->m_pkthdr.len < hlen) { 376 error = EINVAL; 377 goto drop; 378 } 379 380 ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdr.len); 381 tdbstat_add(tdb, tdb_ouncompbytes, m->m_pkthdr.len); 382 383 /* Non expansion policy for IPCOMP */ 384 if (tdb->tdb_sproto == IPPROTO_IPCOMP) { 385 if ((m->m_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) { 386 /* No need to compress, leave the packet untouched */ 387 ipcompstat_inc(ipcomps_minlen); 388 return ipsp_process_done(m, tdb); 389 } 390 } 391 392 /* Invoke the IPsec transform. */ 393 return (*(tdb->tdb_xform->xf_output))(m, tdb, hlen, off); 394 395 drop: 396 m_freem(m); 397 return error; 398} 399 400/* 401 * Called by the IPsec output transform callbacks, to transmit the packet 402 * or do further processing, as necessary. 403 */ 404int 405ipsp_process_done(struct mbuf *m, struct tdb *tdb) 406{ 407 struct ip *ip; 408#ifdef INET6 409 struct ip6_hdr *ip6; 410#endif /* INET6 */ 411 struct tdb *tdbo; 412 struct tdb_ident *tdbi; 413 struct m_tag *mtag; 414 int roff, error; 415 416 NET_ASSERT_LOCKED(); 417 418 tdb->tdb_last_used = gettime(); 419 420 if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) { 421 struct mbuf *mi; 422 struct udphdr *uh; 423 int iphlen; 424 int udpencap_port_local = atomic_load_int(&udpencap_port); 425 426 if (!atomic_load_int(&udpencap_enable) || 427 !udpencap_port_local) { 428 error = ENXIO; 429 goto drop; 430 } 431 432 switch (tdb->tdb_dst.sa.sa_family) { 433 case AF_INET: 434 iphlen = sizeof(struct ip); 435 break; 436#ifdef INET6 437 case AF_INET6: 438 iphlen = sizeof(struct ip6_hdr); 439 break; 440#endif /* INET6 */ 441 default: 442 DPRINTF("unknown protocol family (%d)", 443 tdb->tdb_dst.sa.sa_family); 444 error = EPFNOSUPPORT; 445 goto drop; 446 } 447 448 mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff); 449 if (mi == NULL) { 450 error = ENOMEM; 451 goto drop; 452 } 453 uh = (struct udphdr *)(mtod(mi, caddr_t) + roff); 454 uh->uh_sport = uh->uh_dport = htons(udpencap_port_local); 455 if (tdb->tdb_udpencap_port) 456 uh->uh_dport = tdb->tdb_udpencap_port; 457 458 uh->uh_ulen = htons(m->m_pkthdr.len - iphlen); 459 uh->uh_sum = 0; 460#ifdef INET6 461 if (tdb->tdb_dst.sa.sa_family == AF_INET6) 462 m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT; 463#endif /* INET6 */ 464 espstat_inc(esps_udpencout); 465 } 466 467 switch (tdb->tdb_dst.sa.sa_family) { 468 case AF_INET: 469 /* Fix the header length, for AH processing. */ 470 ip = mtod(m, struct ip *); 471 ip->ip_len = htons(m->m_pkthdr.len); 472 if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) 473 ip->ip_p = IPPROTO_UDP; 474 break; 475 476#ifdef INET6 477 case AF_INET6: 478 /* Fix the header length, for AH processing. */ 479 if (m->m_pkthdr.len < sizeof(*ip6)) { 480 error = ENXIO; 481 goto drop; 482 } 483 if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { 484 /* No jumbogram support. */ 485 error = ENXIO; 486 goto drop; 487 } 488 ip6 = mtod(m, struct ip6_hdr *); 489 ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); 490 if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) 491 ip6->ip6_nxt = IPPROTO_UDP; 492 break; 493#endif /* INET6 */ 494 495 default: 496 DPRINTF("unknown protocol family (%d)", 497 tdb->tdb_dst.sa.sa_family); 498 error = EPFNOSUPPORT; 499 goto drop; 500 } 501 502 /* 503 * Add a record of what we've done or what needs to be done to the 504 * packet. 505 */ 506 mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(struct tdb_ident), 507 M_NOWAIT); 508 if (mtag == NULL) { 509 DPRINTF("could not allocate packet tag"); 510 error = ENOMEM; 511 goto drop; 512 } 513 514 tdbi = (struct tdb_ident *)(mtag + 1); 515 tdbi->dst = tdb->tdb_dst; 516 tdbi->proto = tdb->tdb_sproto; 517 tdbi->spi = tdb->tdb_spi; 518 tdbi->rdomain = tdb->tdb_rdomain; 519 520 m_tag_prepend(m, mtag); 521 522 ipsecstat_pkt(ipsec_opackets, ipsec_obytes, m->m_pkthdr.len); 523 tdbstat_pkt(tdb, tdb_opackets, tdb_obytes, m->m_pkthdr.len); 524 525 /* If there's another (bundled) TDB to apply, do so. */ 526 tdbo = tdb_ref(tdb->tdb_onext); 527 if (tdbo != NULL) { 528 KERNEL_ASSERT_LOCKED(); 529 error = ipsp_process_packet(m, tdbo, 530 tdb->tdb_dst.sa.sa_family, 0, IPSP_DF_INHERIT); 531 tdb_unref(tdbo); 532 return error; 533 } 534 535#if NPF > 0 536 /* Add pf tag if requested. */ 537 pf_tag_packet(m, tdb->tdb_tag, -1); 538 pf_pkt_addr_changed(m); 539#endif 540 if (tdb->tdb_rdomain != tdb->tdb_rdomain_post) 541 m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post; 542 543 /* 544 * We're done with IPsec processing, transmit the packet using the 545 * appropriate network protocol (IP or IPv6). SPD lookup will be 546 * performed again there. 547 */ 548 switch (tdb->tdb_dst.sa.sa_family) { 549 case AF_INET: 550 error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0); 551 break; 552#ifdef INET6 553 case AF_INET6: 554 /* 555 * We don't need massage, IPv6 header fields are always in 556 * net endian. 557 */ 558 error = ip6_output(m, NULL, NULL, 0, NULL, NULL); 559 break; 560#endif /* INET6 */ 561 default: 562 error = EPFNOSUPPORT; 563 break; 564 } 565 return error; 566 567 drop: 568 m_freem(m); 569 return error; 570} 571 572ssize_t 573ipsec_hdrsz(struct tdb *tdbp) 574{ 575 ssize_t adjust; 576 577 switch (tdbp->tdb_sproto) { 578 case IPPROTO_IPIP: 579 adjust = 0; 580 break; 581 582 case IPPROTO_ESP: 583 if (tdbp->tdb_encalgxform == NULL) 584 return (-1); 585 586 /* Header length */ 587 adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen; 588 if (tdbp->tdb_flags & TDBF_UDPENCAP) 589 adjust += sizeof(struct udphdr); 590 /* Authenticator */ 591 if (tdbp->tdb_authalgxform != NULL) 592 adjust += tdbp->tdb_authalgxform->authsize; 593 /* Padding */ 594 adjust += MAX(4, tdbp->tdb_encalgxform->blocksize); 595 break; 596 597 case IPPROTO_AH: 598 if (tdbp->tdb_authalgxform == NULL) 599 return (-1); 600 601 adjust = AH_FLENGTH + sizeof(u_int32_t); 602 adjust += tdbp->tdb_authalgxform->authsize; 603 break; 604 605 default: 606 return (-1); 607 } 608 609 if (!(tdbp->tdb_flags & TDBF_TUNNELING) && 610 !(tdbp->tdb_flags & TDBF_USEDTUNNEL)) 611 return (adjust); 612 613 switch (tdbp->tdb_dst.sa.sa_family) { 614 case AF_INET: 615 adjust += sizeof(struct ip); 616 break; 617#ifdef INET6 618 case AF_INET6: 619 adjust += sizeof(struct ip6_hdr); 620 break; 621#endif /* INET6 */ 622 } 623 624 return (adjust); 625} 626 627void 628ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu) 629{ 630 struct tdb_ident *tdbi; 631 struct tdb *tdbp; 632 struct m_tag *mtag; 633 ssize_t adjust; 634 635 NET_ASSERT_LOCKED(); 636 637 for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag; 638 mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) { 639 tdbi = (struct tdb_ident *)(mtag + 1); 640 tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst, 641 tdbi->proto); 642 if (tdbp == NULL) 643 break; 644 645 if ((adjust = ipsec_hdrsz(tdbp)) == -1) { 646 tdb_unref(tdbp); 647 break; 648 } 649 650 mtu -= adjust; 651 tdbp->tdb_mtu = mtu; 652 tdbp->tdb_mtutimeout = gettime() + 653 atomic_load_int(&ip_mtudisc_timeout); 654 DPRINTF("spi %08x mtu %d adjust %ld mbuf %p", 655 ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m); 656 tdb_unref(tdbp); 657 } 658}