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