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