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