1*7c6c9ed7Smvs /* $OpenBSD: ipsec_output.c,v 1.99 2024/12/27 10:15:09 mvs Exp $ */ 2b1efc16cSangelos /* 3b1efc16cSangelos * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) 4b1efc16cSangelos * 50775ebe4Sangelos * Copyright (c) 2000-2001 Angelos D. Keromytis. 6b1efc16cSangelos * 70775ebe4Sangelos * Permission to use, copy, and modify this software with or without fee 8b1efc16cSangelos * is hereby granted, provided that this entire notice is included in 9b1efc16cSangelos * all copies of any software which is or includes a copy or 10b1efc16cSangelos * modification of this software. 11b1efc16cSangelos * You may use this code under the GNU public license if you so wish. Please 12b1efc16cSangelos * contribute changes back to the authors under this freer than GPL license 13b1efc16cSangelos * so that we may further the use of strong encryption without limitations to 14b1efc16cSangelos * all. 15b1efc16cSangelos * 16b1efc16cSangelos * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR 17b1efc16cSangelos * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY 18b1efc16cSangelos * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE 19b1efc16cSangelos * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR 20b1efc16cSangelos * PURPOSE. 21b1efc16cSangelos */ 22b1efc16cSangelos 23aa920ac7Sreyk #include "pf.h" 24aa920ac7Sreyk 25b1efc16cSangelos #include <sys/param.h> 26b1efc16cSangelos #include <sys/systm.h> 27b1efc16cSangelos #include <sys/mbuf.h> 28b1efc16cSangelos #include <sys/socket.h> 29b1efc16cSangelos #include <sys/kernel.h> 30638c25e3Stedu #include <sys/timeout.h> 31b1efc16cSangelos 32b1efc16cSangelos #include <net/if.h> 33377da2e0Sderaadt #include <net/route.h> 34aa920ac7Sreyk 35b1efc16cSangelos #include <netinet/in.h> 36b1efc16cSangelos #include <netinet/ip.h> 37b1efc16cSangelos #include <netinet/in_pcb.h> 38b1efc16cSangelos #include <netinet/ip_var.h> 39940d25acSbluhm #include <netinet6/ip6_var.h> 40b1efc16cSangelos 41377da2e0Sderaadt #if NPF > 0 42377da2e0Sderaadt #include <net/pfvar.h> 43377da2e0Sderaadt #endif 44b1efc16cSangelos 45ed4bea91Smarkus #include <netinet/udp.h> 46ea784840Sbluhm #include <netinet/ip_ipip.h> 47b1efc16cSangelos #include <netinet/ip_ah.h> 48b1efc16cSangelos #include <netinet/ip_esp.h> 493ec0b612Sjjbg #include <netinet/ip_ipcomp.h> 507ced204eSmpi 517ced204eSmpi #include <crypto/cryptodev.h> 526d7c3229Sprovos #include <crypto/xform.h> 53b1efc16cSangelos 54b1efc16cSangelos #ifdef ENCDEBUG 55698a75ddSbluhm #define DPRINTF(fmt, args...) \ 56698a75ddSbluhm do { \ 57698a75ddSbluhm if (encdebug) \ 58698a75ddSbluhm printf("%s: " fmt "\n", __func__, ## args); \ 59698a75ddSbluhm } while (0) 60b1efc16cSangelos #else 61698a75ddSbluhm #define DPRINTF(fmt, args...) \ 62698a75ddSbluhm do { } while (0) 63b1efc16cSangelos #endif 64b1efc16cSangelos 6559d46dd5Sho int udpencap_enable = 1; /* enabled by default */ 66ed4bea91Smarkus int udpencap_port = 4500; /* triggers decapsulation */ 67ed4bea91Smarkus 68b1efc16cSangelos /* 69b1efc16cSangelos * Loop over a tdb chain, taking into consideration protocol tunneling. The 70b1efc16cSangelos * fourth argument is set if the first encapsulation header is already in 71b1efc16cSangelos * place. 72b1efc16cSangelos */ 73b1efc16cSangelos int 74da628552Sangelos ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready) 75b1efc16cSangelos { 76ae52a733Smikeb int hlen, off, error; 77065a3e52Sitojun #ifdef INET6 78065a3e52Sitojun struct ip6_ext ip6e; 79065a3e52Sitojun int nxt; 80065a3e52Sitojun int dstopt = 0; 81065a3e52Sitojun #endif 82b1efc16cSangelos 836d7c3229Sprovos int setdf = 0; 84b1efc16cSangelos struct ip *ip; 85b1efc16cSangelos #ifdef INET6 86b1efc16cSangelos struct ip6_hdr *ip6; 87b1efc16cSangelos #endif /* INET6 */ 88b1efc16cSangelos 893514aacbSmikeb #ifdef ENCDEBUG 903514aacbSmikeb char buf[INET6_ADDRSTRLEN]; 913514aacbSmikeb #endif 923514aacbSmikeb 936830693bSangelos /* Check that the transform is allowed by the administrator. */ 94b1efc16cSangelos if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) || 95*7c6c9ed7Smvs (tdb->tdb_sproto == IPPROTO_AH && !atomic_load_int(&ah_enable)) || 96*7c6c9ed7Smvs (tdb->tdb_sproto == IPPROTO_IPCOMP && 97*7c6c9ed7Smvs !atomic_load_int(&ipcomp_enable))) { 98698a75ddSbluhm DPRINTF("IPsec outbound packet dropped due to policy " 99698a75ddSbluhm "(check your sysctls)"); 10066ffc2ddSbluhm error = EHOSTUNREACH; 10166ffc2ddSbluhm goto drop; 102b1efc16cSangelos } 103b1efc16cSangelos 1046830693bSangelos /* Sanity check. */ 1056830693bSangelos if (!tdb->tdb_xform) { 106698a75ddSbluhm DPRINTF("uninitialized TDB"); 10766ffc2ddSbluhm error = EHOSTUNREACH; 10866ffc2ddSbluhm goto drop; 109b1efc16cSangelos } 110b1efc16cSangelos 1116830693bSangelos /* Check if the SPI is invalid. */ 1126830693bSangelos if (tdb->tdb_flags & TDBF_INVALID) { 113698a75ddSbluhm DPRINTF("attempt to use invalid SA %s/%08x/%u", 114698a75ddSbluhm ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 115698a75ddSbluhm ntohl(tdb->tdb_spi), tdb->tdb_sproto); 11666ffc2ddSbluhm error = ENXIO; 11766ffc2ddSbluhm goto drop; 118b1efc16cSangelos } 119b1efc16cSangelos 120b1efc16cSangelos /* Check that the network protocol is supported */ 1216830693bSangelos switch (tdb->tdb_dst.sa.sa_family) { 122b1efc16cSangelos case AF_INET: 123b1efc16cSangelos break; 124b1efc16cSangelos 125b1efc16cSangelos #ifdef INET6 126b1efc16cSangelos case AF_INET6: 127b1efc16cSangelos break; 128b1efc16cSangelos #endif /* INET6 */ 129b1efc16cSangelos 130b1efc16cSangelos default: 131698a75ddSbluhm DPRINTF("attempt to use SA %s/%08x/%u for protocol family %d", 1323514aacbSmikeb ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 1333514aacbSmikeb ntohl(tdb->tdb_spi), tdb->tdb_sproto, 134698a75ddSbluhm tdb->tdb_dst.sa.sa_family); 135bc489a1cSbluhm error = EPFNOSUPPORT; 13666ffc2ddSbluhm goto drop; 137b1efc16cSangelos } 138b1efc16cSangelos 1396830693bSangelos /* 1406830693bSangelos * Register first use if applicable, setup relevant expiration timer. 1416830693bSangelos */ 1426830693bSangelos if (tdb->tdb_first_use == 0) { 1433209772dScheloha tdb->tdb_first_use = gettime(); 1444b0e5db3Sbluhm if (tdb->tdb_flags & TDBF_FIRSTUSE) { 1454b0e5db3Sbluhm if (timeout_add_sec(&tdb->tdb_first_tmo, 1464b0e5db3Sbluhm tdb->tdb_exp_first_use)) 1474b0e5db3Sbluhm tdb_ref(tdb); 1484b0e5db3Sbluhm } 1494b0e5db3Sbluhm if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) { 1504b0e5db3Sbluhm if (timeout_add_sec(&tdb->tdb_sfirst_tmo, 1514b0e5db3Sbluhm tdb->tdb_soft_first_use)) 1524b0e5db3Sbluhm tdb_ref(tdb); 1534b0e5db3Sbluhm } 154b1efc16cSangelos } 155b1efc16cSangelos 156b1efc16cSangelos /* 157b1efc16cSangelos * Check for tunneling if we don't have the first header in place. 158b1efc16cSangelos * When doing Ethernet-over-IP, we are handed an already-encapsulated 159b1efc16cSangelos * frame, so we don't need to re-encapsulate. 160b1efc16cSangelos */ 1616830693bSangelos if (tunalready == 0) { 162b1efc16cSangelos /* 163b1efc16cSangelos * If the target protocol family is different, we know we'll be 164b1efc16cSangelos * doing tunneling. 165b1efc16cSangelos */ 1666830693bSangelos if (af == tdb->tdb_dst.sa.sa_family) { 1673882ce82Sbluhm switch (af) { 1683882ce82Sbluhm case AF_INET: 169ae52a733Smikeb hlen = sizeof(struct ip); 1703882ce82Sbluhm break; 171b1efc16cSangelos #ifdef INET6 1723882ce82Sbluhm case AF_INET6: 173ae52a733Smikeb hlen = sizeof(struct ip6_hdr); 1743882ce82Sbluhm break; 175b1efc16cSangelos #endif /* INET6 */ 1763882ce82Sbluhm } 177b1efc16cSangelos 1786830693bSangelos /* Bring the network header in the first mbuf. */ 179ae52a733Smikeb if (m->m_len < hlen) { 18066ffc2ddSbluhm if ((m = m_pullup(m, hlen)) == NULL) { 18166ffc2ddSbluhm error = ENOBUFS; 18266ffc2ddSbluhm goto drop; 18366ffc2ddSbluhm } 184b1efc16cSangelos } 185b1efc16cSangelos 186bf3ff4f4Sbluhm if (af == AF_INET) { 187b1efc16cSangelos ip = mtod(m, struct ip *); 1886830693bSangelos 1896830693bSangelos /* 1906830693bSangelos * This is not a bridge packet, remember if we 1916830693bSangelos * had IP_DF. 1926830693bSangelos */ 19385f7599bSitojun setdf = ip->ip_off & htons(IP_DF); 194bf3ff4f4Sbluhm } 195b1efc16cSangelos 196b1efc16cSangelos #ifdef INET6 197bf3ff4f4Sbluhm if (af == AF_INET6) 198b1efc16cSangelos ip6 = mtod(m, struct ip6_hdr *); 199b1efc16cSangelos #endif /* INET6 */ 200b1efc16cSangelos } 201b1efc16cSangelos 2026830693bSangelos /* Do the appropriate encapsulation, if necessary. */ 203b1efc16cSangelos if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */ 2046830693bSangelos (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling needed */ 205b1efc16cSangelos (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */ 206b1efc16cSangelos ((tdb->tdb_dst.sa.sa_family == AF_INET) && 207b1efc16cSangelos (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) && 208b1efc16cSangelos (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) || 209b1efc16cSangelos #ifdef INET6 210b1efc16cSangelos ((tdb->tdb_dst.sa.sa_family == AF_INET6) && 211b1efc16cSangelos (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) && 212b1efc16cSangelos (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, 213b1efc16cSangelos &ip6->ip6_dst))) || 214b1efc16cSangelos #endif /* INET6 */ 2156830693bSangelos 0) { 2166830693bSangelos /* Fix IPv4 header checksum and length. */ 2176830693bSangelos if (af == AF_INET) { 2182b1f9644Sangelos if (m->m_len < sizeof(struct ip)) 2196830693bSangelos if ((m = m_pullup(m, 22066ffc2ddSbluhm sizeof(struct ip))) == NULL) { 22166ffc2ddSbluhm error = ENOBUFS; 22266ffc2ddSbluhm goto drop; 22366ffc2ddSbluhm } 224b1efc16cSangelos 225b1efc16cSangelos ip = mtod(m, struct ip *); 226b1efc16cSangelos ip->ip_len = htons(m->m_pkthdr.len); 227b1efc16cSangelos ip->ip_sum = 0; 228b1efc16cSangelos ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 229b1efc16cSangelos } 230b1efc16cSangelos 231b1efc16cSangelos #ifdef INET6 2326830693bSangelos /* Fix IPv6 header payload length. */ 2336830693bSangelos if (af == AF_INET6) { 2342b1f9644Sangelos if (m->m_len < sizeof(struct ip6_hdr)) 2356830693bSangelos if ((m = m_pullup(m, 23666ffc2ddSbluhm sizeof(struct ip6_hdr))) == NULL) { 23766ffc2ddSbluhm error = ENOBUFS; 23866ffc2ddSbluhm goto drop; 23966ffc2ddSbluhm } 240b1efc16cSangelos 2416830693bSangelos if (m->m_pkthdr.len - sizeof(*ip6) > 2426830693bSangelos IPV6_MAXPACKET) { 2436830693bSangelos /* No jumbogram support. */ 24466ffc2ddSbluhm error = ENXIO; /*?*/ 24566ffc2ddSbluhm goto drop; 246b1efc16cSangelos } 247b1efc16cSangelos ip6 = mtod(m, struct ip6_hdr *); 2486830693bSangelos ip6->ip6_plen = htons(m->m_pkthdr.len 2496830693bSangelos - sizeof(*ip6)); 250b1efc16cSangelos } 251b1efc16cSangelos #endif /* INET6 */ 252b1efc16cSangelos 253ead5a062Sbluhm /* Encapsulate -- m may be changed or set to NULL. */ 254ead5a062Sbluhm error = ipip_output(&m, tdb); 255ead5a062Sbluhm if ((m == NULL) && (!error)) 256b1efc16cSangelos error = EFAULT; 25766ffc2ddSbluhm if (error) 25866ffc2ddSbluhm goto drop; 2596d7c3229Sprovos 2606d7c3229Sprovos if (tdb->tdb_dst.sa.sa_family == AF_INET && setdf) { 2616d7c3229Sprovos if (m->m_len < sizeof(struct ip)) 2626830693bSangelos if ((m = m_pullup(m, 26366ffc2ddSbluhm sizeof(struct ip))) == NULL) { 26466ffc2ddSbluhm error = ENOBUFS; 26566ffc2ddSbluhm goto drop; 26666ffc2ddSbluhm } 2676d7c3229Sprovos 268d9819910Sangelos ip = mtod(m, struct ip *); 26985f7599bSitojun ip->ip_off |= htons(IP_DF); 2706d7c3229Sprovos } 2716d7c3229Sprovos 2726830693bSangelos /* Remember that we appended a tunnel header. */ 27359b9936bSbluhm mtx_enter(&tdb->tdb_mtx); 2746d7c3229Sprovos tdb->tdb_flags |= TDBF_USEDTUNNEL; 27559b9936bSbluhm mtx_leave(&tdb->tdb_mtx); 276b1efc16cSangelos } 277ead5a062Sbluhm } 278b1efc16cSangelos 279b1efc16cSangelos /* 280ead5a062Sbluhm * If this is just an IP-IP TDB and we're told there's already an 281df8d9afdSjsg * encapsulation header or ipip_output() has encapsulated it, move on. 282b1efc16cSangelos */ 283b1efc16cSangelos if (tdb->tdb_xform->xf_type == XF_IP4) 284da628552Sangelos return ipsp_process_done(m, tdb); 285b1efc16cSangelos 2866830693bSangelos /* Extract some information off the headers. */ 2876830693bSangelos switch (tdb->tdb_dst.sa.sa_family) { 288b1efc16cSangelos case AF_INET: 289b1efc16cSangelos ip = mtod(m, struct ip *); 290ae52a733Smikeb hlen = ip->ip_hl << 2; 291b1efc16cSangelos off = offsetof(struct ip, ip_p); 292b1efc16cSangelos break; 293b1efc16cSangelos 294b1efc16cSangelos #ifdef INET6 295b1efc16cSangelos case AF_INET6: 296b1efc16cSangelos ip6 = mtod(m, struct ip6_hdr *); 297ae52a733Smikeb hlen = sizeof(struct ip6_hdr); 298b1efc16cSangelos off = offsetof(struct ip6_hdr, ip6_nxt); 299065a3e52Sitojun nxt = ip6->ip6_nxt; 300065a3e52Sitojun /* 301065a3e52Sitojun * chase mbuf chain to find the appropriate place to 302065a3e52Sitojun * put AH/ESP/IPcomp header. 303065a3e52Sitojun * IPv6 hbh dest1 rthdr ah* [esp* dest2 payload] 304065a3e52Sitojun */ 305065a3e52Sitojun do { 306065a3e52Sitojun switch (nxt) { 307065a3e52Sitojun case IPPROTO_AH: 308065a3e52Sitojun case IPPROTO_ESP: 309065a3e52Sitojun case IPPROTO_IPCOMP: 310065a3e52Sitojun /* 311065a3e52Sitojun * we should not skip security header added 312065a3e52Sitojun * beforehand. 313065a3e52Sitojun */ 314065a3e52Sitojun goto exitip6loop; 315065a3e52Sitojun 316065a3e52Sitojun case IPPROTO_HOPOPTS: 317065a3e52Sitojun case IPPROTO_DSTOPTS: 318065a3e52Sitojun case IPPROTO_ROUTING: 319065a3e52Sitojun /* 320065a3e52Sitojun * if we see 2nd destination option header, 321065a3e52Sitojun * we should stop there. 322065a3e52Sitojun */ 323065a3e52Sitojun if (nxt == IPPROTO_DSTOPTS && dstopt) 324065a3e52Sitojun goto exitip6loop; 325065a3e52Sitojun 326065a3e52Sitojun if (nxt == IPPROTO_DSTOPTS) { 327065a3e52Sitojun /* 328065a3e52Sitojun * seen 1st or 2nd destination option. 329065a3e52Sitojun * next time we see one, it must be 2nd. 330065a3e52Sitojun */ 331065a3e52Sitojun dstopt = 1; 332065a3e52Sitojun } else if (nxt == IPPROTO_ROUTING) { 333065a3e52Sitojun /* 334678831beSjsg * if we see destination option next 335065a3e52Sitojun * time, it must be dest2. 336065a3e52Sitojun */ 337065a3e52Sitojun dstopt = 2; 338065a3e52Sitojun } 3399a3d0d0aSbluhm if (m->m_pkthdr.len < hlen + sizeof(ip6e)) { 34066ffc2ddSbluhm error = EINVAL; 34166ffc2ddSbluhm goto drop; 3429a3d0d0aSbluhm } 343065a3e52Sitojun /* skip this header */ 344ae52a733Smikeb m_copydata(m, hlen, sizeof(ip6e), 345ae52a733Smikeb (caddr_t)&ip6e); 346065a3e52Sitojun nxt = ip6e.ip6e_nxt; 347ae52a733Smikeb off = hlen + offsetof(struct ip6_ext, ip6e_nxt); 348065a3e52Sitojun /* 349065a3e52Sitojun * we will never see nxt == IPPROTO_AH 350065a3e52Sitojun * so it is safe to omit AH case. 351065a3e52Sitojun */ 352ae52a733Smikeb hlen += (ip6e.ip6e_len + 1) << 3; 353065a3e52Sitojun break; 354065a3e52Sitojun default: 355065a3e52Sitojun goto exitip6loop; 356065a3e52Sitojun } 357ae52a733Smikeb } while (hlen < m->m_pkthdr.len); 35866ffc2ddSbluhm exitip6loop: 359b1efc16cSangelos break; 360b1efc16cSangelos #endif /* INET6 */ 36166ffc2ddSbluhm default: 362bc489a1cSbluhm error = EPFNOSUPPORT; 36366ffc2ddSbluhm goto drop; 364b1efc16cSangelos } 365b1efc16cSangelos 3669a3d0d0aSbluhm if (m->m_pkthdr.len < hlen) { 36766ffc2ddSbluhm error = EINVAL; 36866ffc2ddSbluhm goto drop; 3699a3d0d0aSbluhm } 3709a3d0d0aSbluhm 3717ced204eSmpi ipsecstat_add(ipsec_ouncompbytes, m->m_pkthdr.len); 372d997d144Smvs tdbstat_add(tdb, tdb_ouncompbytes, m->m_pkthdr.len); 3737ced204eSmpi 3743ec0b612Sjjbg /* Non expansion policy for IPCOMP */ 3753ec0b612Sjjbg if (tdb->tdb_sproto == IPPROTO_IPCOMP) { 376ae52a733Smikeb if ((m->m_pkthdr.len - hlen) < tdb->tdb_compalgxform->minlen) { 3773ec0b612Sjjbg /* No need to compress, leave the packet untouched */ 3786460338bSvisa ipcompstat_inc(ipcomps_minlen); 3793ec0b612Sjjbg return ipsp_process_done(m, tdb); 3803ec0b612Sjjbg } 3813ec0b612Sjjbg } 3823ec0b612Sjjbg 3836830693bSangelos /* Invoke the IPsec transform. */ 384ead5a062Sbluhm return (*(tdb->tdb_xform->xf_output))(m, tdb, hlen, off); 38566ffc2ddSbluhm 38666ffc2ddSbluhm drop: 38766ffc2ddSbluhm m_freem(m); 38866ffc2ddSbluhm return error; 389b1efc16cSangelos } 390b1efc16cSangelos 391b1efc16cSangelos /* 392b1efc16cSangelos * Called by the IPsec output transform callbacks, to transmit the packet 393b1efc16cSangelos * or do further processing, as necessary. 394b1efc16cSangelos */ 395b1efc16cSangelos int 396da628552Sangelos ipsp_process_done(struct mbuf *m, struct tdb *tdb) 397b1efc16cSangelos { 398b1efc16cSangelos struct ip *ip; 399b1efc16cSangelos #ifdef INET6 400b1efc16cSangelos struct ip6_hdr *ip6; 401b1efc16cSangelos #endif /* INET6 */ 4024b0e5db3Sbluhm struct tdb *tdbo; 40302755a9fSangelos struct tdb_ident *tdbi; 40402755a9fSangelos struct m_tag *mtag; 40566ffc2ddSbluhm int roff, error; 40602755a9fSangelos 4070ae95220Sbluhm NET_ASSERT_LOCKED(); 4080ae95220Sbluhm 4093209772dScheloha tdb->tdb_last_used = gettime(); 410a7914fe2Sangelos 41148caea30Smarkus if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) { 412ed4bea91Smarkus struct mbuf *mi; 413ed4bea91Smarkus struct udphdr *uh; 41498d4dd3fSmikeb int iphlen; 415ed4bea91Smarkus 41648caea30Smarkus if (!udpencap_enable || !udpencap_port) { 41766ffc2ddSbluhm error = ENXIO; 41866ffc2ddSbluhm goto drop; 41948caea30Smarkus } 42098d4dd3fSmikeb 42198d4dd3fSmikeb switch (tdb->tdb_dst.sa.sa_family) { 42298d4dd3fSmikeb case AF_INET: 42398d4dd3fSmikeb iphlen = sizeof(struct ip); 42498d4dd3fSmikeb break; 42598d4dd3fSmikeb #ifdef INET6 42698d4dd3fSmikeb case AF_INET6: 42798d4dd3fSmikeb iphlen = sizeof(struct ip6_hdr); 42898d4dd3fSmikeb break; 42998d4dd3fSmikeb #endif /* INET6 */ 43098d4dd3fSmikeb default: 431698a75ddSbluhm DPRINTF("unknown protocol family (%d)", 432698a75ddSbluhm tdb->tdb_dst.sa.sa_family); 433bc489a1cSbluhm error = EPFNOSUPPORT; 43466ffc2ddSbluhm goto drop; 43598d4dd3fSmikeb } 43698d4dd3fSmikeb 4375db30710Smarkus mi = m_makespace(m, iphlen, sizeof(struct udphdr), &roff); 438ed4bea91Smarkus if (mi == NULL) { 43966ffc2ddSbluhm error = ENOMEM; 44066ffc2ddSbluhm goto drop; 441ed4bea91Smarkus } 4425db30710Smarkus uh = (struct udphdr *)(mtod(mi, caddr_t) + roff); 443ed4bea91Smarkus uh->uh_sport = uh->uh_dport = htons(udpencap_port); 444ed4bea91Smarkus if (tdb->tdb_udpencap_port) 445ed4bea91Smarkus uh->uh_dport = tdb->tdb_udpencap_port; 446ed4bea91Smarkus 44798d4dd3fSmikeb uh->uh_ulen = htons(m->m_pkthdr.len - iphlen); 448ed4bea91Smarkus uh->uh_sum = 0; 44998d4dd3fSmikeb #ifdef INET6 45098d4dd3fSmikeb if (tdb->tdb_dst.sa.sa_family == AF_INET6) 45198d4dd3fSmikeb m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT; 45298d4dd3fSmikeb #endif /* INET6 */ 4536460338bSvisa espstat_inc(esps_udpencout); 454ed4bea91Smarkus } 455ed4bea91Smarkus 4566830693bSangelos switch (tdb->tdb_dst.sa.sa_family) { 457b1efc16cSangelos case AF_INET: 4586830693bSangelos /* Fix the header length, for AH processing. */ 459b1efc16cSangelos ip = mtod(m, struct ip *); 460b1efc16cSangelos ip->ip_len = htons(m->m_pkthdr.len); 461ed4bea91Smarkus if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) 462ed4bea91Smarkus ip->ip_p = IPPROTO_UDP; 463b1efc16cSangelos break; 464b1efc16cSangelos 465b1efc16cSangelos #ifdef INET6 466b1efc16cSangelos case AF_INET6: 4676830693bSangelos /* Fix the header length, for AH processing. */ 468b1efc16cSangelos if (m->m_pkthdr.len < sizeof(*ip6)) { 46966ffc2ddSbluhm error = ENXIO; 47066ffc2ddSbluhm goto drop; 471b1efc16cSangelos } 472b1efc16cSangelos if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { 4736830693bSangelos /* No jumbogram support. */ 47466ffc2ddSbluhm error = ENXIO; 47566ffc2ddSbluhm goto drop; 476b1efc16cSangelos } 477b1efc16cSangelos ip6 = mtod(m, struct ip6_hdr *); 478b1efc16cSangelos ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); 479ed4bea91Smarkus if ((tdb->tdb_flags & TDBF_UDPENCAP) != 0) 480ed4bea91Smarkus ip6->ip6_nxt = IPPROTO_UDP; 481b1efc16cSangelos break; 482b1efc16cSangelos #endif /* INET6 */ 483b1efc16cSangelos 484b1efc16cSangelos default: 485698a75ddSbluhm DPRINTF("unknown protocol family (%d)", 486698a75ddSbluhm tdb->tdb_dst.sa.sa_family); 487bc489a1cSbluhm error = EPFNOSUPPORT; 48866ffc2ddSbluhm goto drop; 489b1efc16cSangelos } 490b1efc16cSangelos 491ed834ee5Sangelos /* 492ed834ee5Sangelos * Add a record of what we've done or what needs to be done to the 493ed834ee5Sangelos * packet. 494ed834ee5Sangelos */ 4950245ebf4Smikeb mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(struct tdb_ident), 49602755a9fSangelos M_NOWAIT); 4976830693bSangelos if (mtag == NULL) { 498698a75ddSbluhm DPRINTF("could not allocate packet tag"); 49966ffc2ddSbluhm error = ENOMEM; 50066ffc2ddSbluhm goto drop; 50102755a9fSangelos } 502ed834ee5Sangelos 50302755a9fSangelos tdbi = (struct tdb_ident *)(mtag + 1); 504d9c935f8Sdhill tdbi->dst = tdb->tdb_dst; 50502755a9fSangelos tdbi->proto = tdb->tdb_sproto; 50602755a9fSangelos tdbi->spi = tdb->tdb_spi; 50705d65ec4Sreyk tdbi->rdomain = tdb->tdb_rdomain; 50802755a9fSangelos 50902755a9fSangelos m_tag_prepend(m, mtag); 51002755a9fSangelos 511e4b51ed2Sbluhm ipsecstat_pkt(ipsec_opackets, ipsec_obytes, m->m_pkthdr.len); 512d997d144Smvs tdbstat_pkt(tdb, tdb_opackets, tdb_obytes, m->m_pkthdr.len); 5132edaa7baSmpi 5146830693bSangelos /* If there's another (bundled) TDB to apply, do so. */ 5154b0e5db3Sbluhm tdbo = tdb_ref(tdb->tdb_onext); 5164b0e5db3Sbluhm if (tdbo != NULL) { 5175ee194bcSbluhm KERNEL_ASSERT_LOCKED(); 5184b0e5db3Sbluhm error = ipsp_process_packet(m, tdbo, 5196830693bSangelos tdb->tdb_dst.sa.sa_family, 0); 5204b0e5db3Sbluhm tdb_unref(tdbo); 5214b0e5db3Sbluhm return error; 5224b0e5db3Sbluhm } 5234a6401e9Sangelos 524aa920ac7Sreyk #if NPF > 0 525aa920ac7Sreyk /* Add pf tag if requested. */ 526f1030b20Sbluhm pf_tag_packet(m, tdb->tdb_tag, -1); 527052ea9d2Shenning pf_pkt_addr_changed(m); 528aa920ac7Sreyk #endif 529652dbf1bStobhe if (tdb->tdb_rdomain != tdb->tdb_rdomain_post) 530652dbf1bStobhe m->m_pkthdr.ph_rtableid = tdb->tdb_rdomain_post; 531aa920ac7Sreyk 532b1efc16cSangelos /* 533b1efc16cSangelos * We're done with IPsec processing, transmit the packet using the 534b1efc16cSangelos * appropriate network protocol (IP or IPv6). SPD lookup will be 535b1efc16cSangelos * performed again there. 536b1efc16cSangelos */ 5376830693bSangelos switch (tdb->tdb_dst.sa.sa_family) { 538b1efc16cSangelos case AF_INET: 539bc489a1cSbluhm error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL, 0); 540bc489a1cSbluhm break; 541b1efc16cSangelos #ifdef INET6 542b1efc16cSangelos case AF_INET6: 543b1efc16cSangelos /* 544b1efc16cSangelos * We don't need massage, IPv6 header fields are always in 5456830693bSangelos * net endian. 546b1efc16cSangelos */ 547bc489a1cSbluhm error = ip6_output(m, NULL, NULL, 0, NULL, NULL); 548bc489a1cSbluhm break; 549b1efc16cSangelos #endif /* INET6 */ 550bc489a1cSbluhm default: 551bc489a1cSbluhm error = EPFNOSUPPORT; 552bc489a1cSbluhm break; 553b1efc16cSangelos } 554bc489a1cSbluhm return error; 55566ffc2ddSbluhm 55666ffc2ddSbluhm drop: 55766ffc2ddSbluhm m_freem(m); 55866ffc2ddSbluhm return error; 559b1efc16cSangelos } 5606d7c3229Sprovos 5616d7c3229Sprovos ssize_t 5626d7c3229Sprovos ipsec_hdrsz(struct tdb *tdbp) 5636d7c3229Sprovos { 5646d7c3229Sprovos ssize_t adjust; 5656d7c3229Sprovos 5666d7c3229Sprovos switch (tdbp->tdb_sproto) { 5678a510a1cSmarkus case IPPROTO_IPIP: 5688a510a1cSmarkus adjust = 0; 5698a510a1cSmarkus break; 5708a510a1cSmarkus 5716d7c3229Sprovos case IPPROTO_ESP: 5726d7c3229Sprovos if (tdbp->tdb_encalgxform == NULL) 5736d7c3229Sprovos return (-1); 5746d7c3229Sprovos 5756d7c3229Sprovos /* Header length */ 5766d7c3229Sprovos adjust = 2 * sizeof(u_int32_t) + tdbp->tdb_ivlen; 5773bea581aSmarkus if (tdbp->tdb_flags & TDBF_UDPENCAP) 5783bea581aSmarkus adjust += sizeof(struct udphdr); 5796d7c3229Sprovos /* Authenticator */ 5806d7c3229Sprovos if (tdbp->tdb_authalgxform != NULL) 5818688c78cSmarkus adjust += tdbp->tdb_authalgxform->authsize; 5826d7c3229Sprovos /* Padding */ 583d0bff1deSmarkus adjust += MAX(4, tdbp->tdb_encalgxform->blocksize); 5846d7c3229Sprovos break; 5856d7c3229Sprovos 5866d7c3229Sprovos case IPPROTO_AH: 5876d7c3229Sprovos if (tdbp->tdb_authalgxform == NULL) 5886d7c3229Sprovos return (-1); 5896d7c3229Sprovos 5906d7c3229Sprovos adjust = AH_FLENGTH + sizeof(u_int32_t); 5916d7c3229Sprovos adjust += tdbp->tdb_authalgxform->authsize; 5926d7c3229Sprovos break; 5936d7c3229Sprovos 5946d7c3229Sprovos default: 5956d7c3229Sprovos return (-1); 5966d7c3229Sprovos } 5976d7c3229Sprovos 5986d7c3229Sprovos if (!(tdbp->tdb_flags & TDBF_TUNNELING) && 5996d7c3229Sprovos !(tdbp->tdb_flags & TDBF_USEDTUNNEL)) 6006d7c3229Sprovos return (adjust); 6016d7c3229Sprovos 6026d7c3229Sprovos switch (tdbp->tdb_dst.sa.sa_family) { 6036d7c3229Sprovos case AF_INET: 6046d7c3229Sprovos adjust += sizeof(struct ip); 6056d7c3229Sprovos break; 6066d7c3229Sprovos #ifdef INET6 6076d7c3229Sprovos case AF_INET6: 6086d7c3229Sprovos adjust += sizeof(struct ip6_hdr); 6096d7c3229Sprovos break; 6106d7c3229Sprovos #endif /* INET6 */ 6116d7c3229Sprovos } 6126d7c3229Sprovos 6136d7c3229Sprovos return (adjust); 6146d7c3229Sprovos } 6156d7c3229Sprovos 6166d7c3229Sprovos void 6176d7c3229Sprovos ipsec_adjust_mtu(struct mbuf *m, u_int32_t mtu) 6186d7c3229Sprovos { 6196d7c3229Sprovos struct tdb_ident *tdbi; 6206d7c3229Sprovos struct tdb *tdbp; 6216d7c3229Sprovos struct m_tag *mtag; 6226d7c3229Sprovos ssize_t adjust; 6236d7c3229Sprovos 62466736630Smpi NET_ASSERT_LOCKED(); 6256d7c3229Sprovos 6266d7c3229Sprovos for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL); mtag; 6276d7c3229Sprovos mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, mtag)) { 6286d7c3229Sprovos tdbi = (struct tdb_ident *)(mtag + 1); 62905d65ec4Sreyk tdbp = gettdb(tdbi->rdomain, tdbi->spi, &tdbi->dst, 63005d65ec4Sreyk tdbi->proto); 6316d7c3229Sprovos if (tdbp == NULL) 6326d7c3229Sprovos break; 6336d7c3229Sprovos 6344b0e5db3Sbluhm if ((adjust = ipsec_hdrsz(tdbp)) == -1) { 6354b0e5db3Sbluhm tdb_unref(tdbp); 6366d7c3229Sprovos break; 6374b0e5db3Sbluhm } 6386d7c3229Sprovos 6396d7c3229Sprovos mtu -= adjust; 6406d7c3229Sprovos tdbp->tdb_mtu = mtu; 6413209772dScheloha tdbp->tdb_mtutimeout = gettime() + ip_mtudisc_timeout; 642698a75ddSbluhm DPRINTF("spi %08x mtu %d adjust %ld mbuf %p", 643698a75ddSbluhm ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust, m); 6444b0e5db3Sbluhm tdb_unref(tdbp); 6456d7c3229Sprovos } 6466d7c3229Sprovos } 647