ipsec_output.c revision 252693
1112758Ssam/*- 2112758Ssam * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 3112758Ssam * All rights reserved. 4112758Ssam * 5112758Ssam * Redistribution and use in source and binary forms, with or without 6112758Ssam * modification, are permitted provided that the following conditions 7112758Ssam * are met: 8112758Ssam * 1. Redistributions of source code must retain the above copyright 9112758Ssam * notice, this list of conditions and the following disclaimer. 10112758Ssam * 2. Redistributions in binary form must reproduce the above copyright 11112758Ssam * notice, this list of conditions and the following disclaimer in the 12112758Ssam * documentation and/or other materials provided with the distribution. 13112758Ssam * 14112758Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15112758Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16112758Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17112758Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18112758Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19112758Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20112758Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21112758Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22112758Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23112758Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24112758Ssam * SUCH DAMAGE. 25112758Ssam * 26112758Ssam * $FreeBSD: stable/9/sys/netipsec/ipsec_output.c 252693 2013-07-04 08:59:34Z ae $ 27112758Ssam */ 28105197Ssam 29105197Ssam/* 30105197Ssam * IPsec output processing. 31105197Ssam */ 32105197Ssam#include "opt_inet.h" 33105197Ssam#include "opt_inet6.h" 34105197Ssam#include "opt_ipsec.h" 35159965Sthompsa#include "opt_enc.h" 36105197Ssam 37105197Ssam#include <sys/param.h> 38105197Ssam#include <sys/systm.h> 39105197Ssam#include <sys/mbuf.h> 40105197Ssam#include <sys/domain.h> 41105197Ssam#include <sys/protosw.h> 42105197Ssam#include <sys/socket.h> 43105197Ssam#include <sys/errno.h> 44105197Ssam#include <sys/syslog.h> 45105197Ssam 46105197Ssam#include <net/if.h> 47171497Sbz#include <net/pfil.h> 48105197Ssam#include <net/route.h> 49195699Srwatson#include <net/vnet.h> 50105197Ssam 51105197Ssam#include <netinet/in.h> 52105197Ssam#include <netinet/in_systm.h> 53105197Ssam#include <netinet/ip.h> 54105197Ssam#include <netinet/ip_var.h> 55105197Ssam#include <netinet/in_var.h> 56105197Ssam#include <netinet/ip_ecn.h> 57105197Ssam#ifdef INET6 58105197Ssam#include <netinet6/ip6_ecn.h> 59105197Ssam#endif 60105197Ssam 61105197Ssam#include <netinet/ip6.h> 62105197Ssam#ifdef INET6 63105197Ssam#include <netinet6/ip6_var.h> 64105197Ssam#endif 65105197Ssam#include <netinet/in_pcb.h> 66105197Ssam#ifdef INET6 67105197Ssam#include <netinet/icmp6.h> 68105197Ssam#endif 69105197Ssam 70105197Ssam#include <netipsec/ipsec.h> 71105197Ssam#ifdef INET6 72105197Ssam#include <netipsec/ipsec6.h> 73105197Ssam#endif 74105197Ssam#include <netipsec/ah_var.h> 75105197Ssam#include <netipsec/esp_var.h> 76105197Ssam#include <netipsec/ipcomp_var.h> 77105197Ssam 78105197Ssam#include <netipsec/xform.h> 79105197Ssam 80105197Ssam#include <netipsec/key.h> 81105197Ssam#include <netipsec/keydb.h> 82105197Ssam#include <netipsec/key_debug.h> 83105197Ssam 84105197Ssam#include <machine/in_cksum.h> 85105197Ssam 86194062Svanhu#ifdef IPSEC_NAT_T 87194062Svanhu#include <netinet/udp.h> 88194062Svanhu#endif 89194062Svanhu 90181627Svanhu#ifdef DEV_ENC 91181627Svanhu#include <net/if_enc.h> 92181627Svanhu#endif 93181627Svanhu 94181627Svanhu 95105197Ssamint 96105197Ssamipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) 97105197Ssam{ 98105197Ssam struct tdb_ident *tdbi; 99105197Ssam struct m_tag *mtag; 100105197Ssam struct secasvar *sav; 101105197Ssam struct secasindex *saidx; 102105197Ssam int error; 103105197Ssam 104120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 105120585Ssam IPSEC_ASSERT(isr != NULL, ("null ISR")); 106105197Ssam sav = isr->sav; 107120585Ssam IPSEC_ASSERT(sav != NULL, ("null SA")); 108120585Ssam IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); 109105197Ssam 110105197Ssam saidx = &sav->sah->saidx; 111105197Ssam switch (saidx->dst.sa.sa_family) { 112105197Ssam#ifdef INET 113105197Ssam case AF_INET: 114105197Ssam /* Fix the header length, for AH processing. */ 115105197Ssam mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 116105197Ssam break; 117105197Ssam#endif /* INET */ 118105197Ssam#ifdef INET6 119105197Ssam case AF_INET6: 120105197Ssam /* Fix the header length, for AH processing. */ 121105197Ssam if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 122105197Ssam error = ENXIO; 123105197Ssam goto bad; 124105197Ssam } 125105197Ssam if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 126105197Ssam /* No jumbogram support. */ 127105197Ssam error = ENXIO; /*?*/ 128105197Ssam goto bad; 129105197Ssam } 130105197Ssam mtod(m, struct ip6_hdr *)->ip6_plen = 131105197Ssam htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 132105197Ssam break; 133105197Ssam#endif /* INET6 */ 134105197Ssam default: 135120585Ssam DPRINTF(("%s: unknown protocol family %u\n", __func__, 136105197Ssam saidx->dst.sa.sa_family)); 137105197Ssam error = ENXIO; 138105197Ssam goto bad; 139105197Ssam } 140105197Ssam 141105197Ssam /* 142105197Ssam * Add a record of what we've done or what needs to be done to the 143105197Ssam * packet. 144105197Ssam */ 145105197Ssam mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 146105197Ssam sizeof(struct tdb_ident), M_NOWAIT); 147105197Ssam if (mtag == NULL) { 148120585Ssam DPRINTF(("%s: could not get packet tag\n", __func__)); 149105197Ssam error = ENOMEM; 150105197Ssam goto bad; 151105197Ssam } 152105197Ssam 153105197Ssam tdbi = (struct tdb_ident *)(mtag + 1); 154105197Ssam tdbi->dst = saidx->dst; 155105197Ssam tdbi->proto = saidx->proto; 156105197Ssam tdbi->spi = sav->spi; 157105197Ssam m_tag_prepend(m, mtag); 158105197Ssam 159105197Ssam /* 160105197Ssam * If there's another (bundled) SA to apply, do so. 161105197Ssam * Note that this puts a burden on the kernel stack size. 162105197Ssam * If this is a problem we'll need to introduce a queue 163105197Ssam * to set the packet on so we can unwind the stack before 164105197Ssam * doing further processing. 165105197Ssam */ 166105197Ssam if (isr->next) { 167252692Sae IPSECSTAT_INC(ips_out_bundlesa); 168238777Sbz /* XXX-BZ currently only support same AF bundles. */ 169221129Sbz switch (saidx->dst.sa.sa_family) { 170221129Sbz#ifdef INET 171221129Sbz case AF_INET: 172221129Sbz return ipsec4_process_packet(m, isr->next, 0, 0); 173221129Sbz /* NOTREACHED */ 174221129Sbz#endif 175221129Sbz#ifdef notyet 176221129Sbz#ifdef INET6 177221129Sbz case AF_INET6: 178221129Sbz /* XXX */ 179221129Sbz ipsec6_output_trans() 180221129Sbz ipsec6_output_tunnel() 181221129Sbz /* NOTREACHED */ 182221129Sbz#endif /* INET6 */ 183221129Sbz#endif 184221129Sbz default: 185221129Sbz DPRINTF(("%s: unknown protocol family %u\n", __func__, 186221129Sbz saidx->dst.sa.sa_family)); 187221129Sbz error = ENXIO; 188221129Sbz goto bad; 189221129Sbz } 190105197Ssam } 191117056Ssam key_sa_recordxfer(sav, m); /* record data transfer */ 192105197Ssam 193223637Sbz m_addr_changed(m); 194223637Sbz 195105197Ssam /* 196105197Ssam * We're done with IPsec processing, transmit the packet using the 197105197Ssam * appropriate network protocol (IP or IPv6). SPD lookup will be 198105197Ssam * performed again there. 199105197Ssam */ 200105197Ssam switch (saidx->dst.sa.sa_family) { 201105197Ssam#ifdef INET 202105197Ssam struct ip *ip; 203105197Ssam case AF_INET: 204105197Ssam ip = mtod(m, struct ip *); 205105197Ssam ip->ip_len = ntohs(ip->ip_len); 206105197Ssam ip->ip_off = ntohs(ip->ip_off); 207105197Ssam 208194062Svanhu#ifdef IPSEC_NAT_T 209194062Svanhu /* 210194062Svanhu * If NAT-T is enabled, now that all IPsec processing is done 211194062Svanhu * insert UDP encapsulation header after IP header. 212194062Svanhu */ 213194062Svanhu if (sav->natt_type) { 214194062Svanhu#ifdef _IP_VHL 215194062Svanhu const int hlen = IP_VHL_HL(ip->ip_vhl); 216194062Svanhu#else 217194062Svanhu const int hlen = (ip->ip_hl << 2); 218194062Svanhu#endif 219194062Svanhu int size, off; 220194062Svanhu struct mbuf *mi; 221194062Svanhu struct udphdr *udp; 222194062Svanhu 223194062Svanhu size = sizeof(struct udphdr); 224194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 225194062Svanhu /* 226194062Svanhu * draft-ietf-ipsec-nat-t-ike-0[01].txt and 227194062Svanhu * draft-ietf-ipsec-udp-encaps-(00/)01.txt, 228194062Svanhu * ignoring possible AH mode 229194062Svanhu * non-IKE marker + non-ESP marker 230194062Svanhu * from draft-ietf-ipsec-udp-encaps-00.txt. 231194062Svanhu */ 232194062Svanhu size += sizeof(u_int64_t); 233194062Svanhu } 234194062Svanhu mi = m_makespace(m, hlen, size, &off); 235194062Svanhu if (mi == NULL) { 236194062Svanhu DPRINTF(("%s: m_makespace for udphdr failed\n", 237194062Svanhu __func__)); 238194062Svanhu error = ENOBUFS; 239194062Svanhu goto bad; 240194062Svanhu } 241194062Svanhu 242194062Svanhu udp = (struct udphdr *)(mtod(mi, caddr_t) + off); 243194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 244194062Svanhu udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); 245194062Svanhu else 246194062Svanhu udp->uh_sport = 247194062Svanhu KEY_PORTFROMSADDR(&sav->sah->saidx.src); 248194062Svanhu udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); 249194062Svanhu udp->uh_sum = 0; 250194062Svanhu udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 251194062Svanhu ip->ip_len = m->m_pkthdr.len; 252194062Svanhu ip->ip_p = IPPROTO_UDP; 253194062Svanhu 254194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 255194062Svanhu *(u_int64_t *)(udp + 1) = 0; 256194062Svanhu } 257194062Svanhu#endif /* IPSEC_NAT_T */ 258194062Svanhu 259105197Ssam return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 260105197Ssam#endif /* INET */ 261105197Ssam#ifdef INET6 262105197Ssam case AF_INET6: 263105197Ssam /* 264105197Ssam * We don't need massage, IPv6 header fields are always in 265105197Ssam * net endian. 266105197Ssam */ 267105197Ssam return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 268105197Ssam#endif /* INET6 */ 269105197Ssam } 270105197Ssam panic("ipsec_process_done"); 271105197Ssambad: 272105197Ssam m_freem(m); 273105197Ssam return (error); 274105197Ssam} 275105197Ssam 276105197Ssamstatic struct ipsecrequest * 277105197Ssamipsec_nextisr( 278105197Ssam struct mbuf *m, 279105197Ssam struct ipsecrequest *isr, 280105197Ssam int af, 281105197Ssam struct secasindex *saidx, 282105197Ssam int *error 283105197Ssam) 284105197Ssam{ 285252693Sae#define IPSEC_OSTAT(name) do { \ 286252693Sae if (isr->saidx.proto == IPPROTO_ESP) \ 287252693Sae ESPSTAT_INC(esps_##name); \ 288252693Sae else if (isr->saidx.proto == IPPROTO_AH)\ 289252693Sae AHSTAT_INC(ahs_##name); \ 290252693Sae else \ 291252693Sae IPCOMPSTAT_INC(ipcomps_##name); \ 292252693Sae} while (0) 293105197Ssam struct secasvar *sav; 294105197Ssam 295120585Ssam IPSECREQUEST_LOCK_ASSERT(isr); 296120585Ssam 297120585Ssam IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 298120585Ssam ("invalid address family %u", af)); 299105197Ssamagain: 300105197Ssam /* 301105197Ssam * Craft SA index to search for proper SA. Note that 302105197Ssam * we only fillin unspecified SA peers for transport 303105197Ssam * mode; for tunnel mode they must already be filled in. 304105197Ssam */ 305105197Ssam *saidx = isr->saidx; 306105197Ssam if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 307105197Ssam /* Fillin unspecified SA peers only for transport mode */ 308105197Ssam if (af == AF_INET) { 309105197Ssam struct sockaddr_in *sin; 310105197Ssam struct ip *ip = mtod(m, struct ip *); 311105197Ssam 312105197Ssam if (saidx->src.sa.sa_len == 0) { 313105197Ssam sin = &saidx->src.sin; 314105197Ssam sin->sin_len = sizeof(*sin); 315105197Ssam sin->sin_family = AF_INET; 316105197Ssam sin->sin_port = IPSEC_PORT_ANY; 317105197Ssam sin->sin_addr = ip->ip_src; 318105197Ssam } 319105197Ssam if (saidx->dst.sa.sa_len == 0) { 320105197Ssam sin = &saidx->dst.sin; 321105197Ssam sin->sin_len = sizeof(*sin); 322105197Ssam sin->sin_family = AF_INET; 323105197Ssam sin->sin_port = IPSEC_PORT_ANY; 324105197Ssam sin->sin_addr = ip->ip_dst; 325105197Ssam } 326105197Ssam } else { 327105197Ssam struct sockaddr_in6 *sin6; 328105197Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 329105197Ssam 330105197Ssam if (saidx->src.sin6.sin6_len == 0) { 331105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->src; 332105197Ssam sin6->sin6_len = sizeof(*sin6); 333105197Ssam sin6->sin6_family = AF_INET6; 334105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 335105197Ssam sin6->sin6_addr = ip6->ip6_src; 336105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 337105197Ssam /* fix scope id for comparing SPD */ 338105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 339105197Ssam sin6->sin6_scope_id = 340105197Ssam ntohs(ip6->ip6_src.s6_addr16[1]); 341105197Ssam } 342105197Ssam } 343105197Ssam if (saidx->dst.sin6.sin6_len == 0) { 344105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->dst; 345105197Ssam sin6->sin6_len = sizeof(*sin6); 346105197Ssam sin6->sin6_family = AF_INET6; 347105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 348105197Ssam sin6->sin6_addr = ip6->ip6_dst; 349105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 350105197Ssam /* fix scope id for comparing SPD */ 351105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 352105197Ssam sin6->sin6_scope_id = 353105197Ssam ntohs(ip6->ip6_dst.s6_addr16[1]); 354105197Ssam } 355105197Ssam } 356105197Ssam } 357105197Ssam } 358105197Ssam 359105197Ssam /* 360105197Ssam * Lookup SA and validate it. 361105197Ssam */ 362105197Ssam *error = key_checkrequest(isr, saidx); 363105197Ssam if (*error != 0) { 364105197Ssam /* 365105197Ssam * IPsec processing is required, but no SA found. 366105197Ssam * I assume that key_acquire() had been called 367105197Ssam * to get/establish the SA. Here I discard 368105197Ssam * this packet because it is responsibility for 369105197Ssam * upper layer to retransmit the packet. 370105197Ssam */ 371252692Sae IPSECSTAT_INC(ips_out_nosa); 372105197Ssam goto bad; 373105197Ssam } 374105197Ssam sav = isr->sav; 375177175Sbz if (sav == NULL) { 376120585Ssam IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 377120585Ssam ("no SA found, but required; level %u", 378105197Ssam ipsec_get_reqlevel(isr))); 379120585Ssam IPSECREQUEST_UNLOCK(isr); 380105197Ssam isr = isr->next; 381177175Sbz /* 382177175Sbz * If isr is NULL, we found a 'use' policy w/o SA. 383177175Sbz * Return w/o error and w/o isr so we can drop out 384177175Sbz * and continue w/o IPsec processing. 385177175Sbz */ 386177175Sbz if (isr == NULL) 387105197Ssam return isr; 388120585Ssam IPSECREQUEST_LOCK(isr); 389105197Ssam goto again; 390105197Ssam } 391105197Ssam 392105197Ssam /* 393105197Ssam * Check system global policy controls. 394105197Ssam */ 395181803Sbz if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 396181803Sbz (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 397181803Sbz (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 398120585Ssam DPRINTF(("%s: IPsec outbound packet dropped due" 399120585Ssam " to policy (check your sysctls)\n", __func__)); 400252693Sae IPSEC_OSTAT(pdrops); 401105197Ssam *error = EHOSTUNREACH; 402105197Ssam goto bad; 403105197Ssam } 404105197Ssam 405105197Ssam /* 406105197Ssam * Sanity check the SA contents for the caller 407105197Ssam * before they invoke the xform output method. 408105197Ssam */ 409105197Ssam if (sav->tdb_xform == NULL) { 410120585Ssam DPRINTF(("%s: no transform for SA\n", __func__)); 411252693Sae IPSEC_OSTAT(noxform); 412105197Ssam *error = EHOSTUNREACH; 413105197Ssam goto bad; 414105197Ssam } 415105197Ssam return isr; 416105197Ssambad: 417120585Ssam IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 418120585Ssam IPSECREQUEST_UNLOCK(isr); 419105197Ssam return NULL; 420105197Ssam#undef IPSEC_OSTAT 421105197Ssam} 422105197Ssam 423105197Ssam#ifdef INET 424105197Ssam/* 425105197Ssam * IPsec output logic for IPv4. 426105197Ssam */ 427105197Ssamint 428105197Ssamipsec4_process_packet( 429105197Ssam struct mbuf *m, 430105197Ssam struct ipsecrequest *isr, 431105197Ssam int flags, 432105197Ssam int tunalready) 433105197Ssam{ 434105197Ssam struct secasindex saidx; 435105197Ssam struct secasvar *sav; 436105197Ssam struct ip *ip; 437119643Ssam int error, i, off; 438105197Ssam 439120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 440120585Ssam IPSEC_ASSERT(isr != NULL, ("null isr")); 441105197Ssam 442120585Ssam IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 443105197Ssam 444105197Ssam isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 445177175Sbz if (isr == NULL) { 446177175Sbz if (error != 0) 447177175Sbz goto bad; 448177175Sbz return EJUSTRETURN; 449177175Sbz } 450105197Ssam 451105197Ssam sav = isr->sav; 452159965Sthompsa 453159965Sthompsa#ifdef DEV_ENC 454181627Svanhu encif->if_opackets++; 455181627Svanhu encif->if_obytes += m->m_pkthdr.len; 456181627Svanhu 457174054Sbz /* pass the mbuf to enc0 for bpf processing */ 458174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 459159965Sthompsa /* pass the mbuf to enc0 for packet filtering */ 460174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 461159965Sthompsa goto bad; 462159965Sthompsa#endif 463159965Sthompsa 464105197Ssam if (!tunalready) { 465105197Ssam union sockaddr_union *dst = &sav->sah->saidx.dst; 466105197Ssam int setdf; 467105197Ssam 468105197Ssam /* 469105197Ssam * Collect IP_DF state from the outer header. 470105197Ssam */ 471105197Ssam if (dst->sa.sa_family == AF_INET) { 472105197Ssam if (m->m_len < sizeof (struct ip) && 473105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 474105197Ssam error = ENOBUFS; 475105197Ssam goto bad; 476105197Ssam } 477105197Ssam ip = mtod(m, struct ip *); 478105197Ssam /* Honor system-wide control of how to handle IP_DF */ 479181803Sbz switch (V_ip4_ipsec_dfbit) { 480105197Ssam case 0: /* clear in outer header */ 481105197Ssam case 1: /* set in outer header */ 482181803Sbz setdf = V_ip4_ipsec_dfbit; 483105197Ssam break; 484105197Ssam default: /* propagate to outer header */ 485105197Ssam setdf = ntohs(ip->ip_off & IP_DF); 486105197Ssam break; 487105197Ssam } 488105197Ssam } else { 489105197Ssam ip = NULL; /* keep compiler happy */ 490105197Ssam setdf = 0; 491105197Ssam } 492105197Ssam /* Do the appropriate encapsulation, if necessary */ 493105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 494105197Ssam dst->sa.sa_family != AF_INET || /* PF mismatch */ 495105197Ssam#if 0 496105197Ssam (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ 497105197Ssam sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ 498105197Ssam#endif 499105197Ssam (dst->sa.sa_family == AF_INET && /* Proxy */ 500105197Ssam dst->sin.sin_addr.s_addr != INADDR_ANY && 501105197Ssam dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 502105197Ssam struct mbuf *mp; 503105197Ssam 504105197Ssam /* Fix IPv4 header checksum and length */ 505105197Ssam if (m->m_len < sizeof (struct ip) && 506105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 507105197Ssam error = ENOBUFS; 508105197Ssam goto bad; 509105197Ssam } 510105197Ssam ip = mtod(m, struct ip *); 511105197Ssam ip->ip_len = htons(m->m_pkthdr.len); 512105197Ssam ip->ip_sum = 0; 513105197Ssam#ifdef _IP_VHL 514105197Ssam if (ip->ip_vhl == IP_VHL_BORING) 515105197Ssam ip->ip_sum = in_cksum_hdr(ip); 516105197Ssam else 517105197Ssam ip->ip_sum = in_cksum(m, 518105197Ssam _IP_VHL_HL(ip->ip_vhl) << 2); 519105197Ssam#else 520105197Ssam ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 521105197Ssam#endif 522105197Ssam 523105197Ssam /* Encapsulate the packet */ 524105197Ssam error = ipip_output(m, isr, &mp, 0, 0); 525105197Ssam if (mp == NULL && !error) { 526105197Ssam /* Should never happen. */ 527120585Ssam DPRINTF(("%s: ipip_output returns no mbuf and " 528120585Ssam "no error!", __func__)); 529105197Ssam error = EFAULT; 530105197Ssam } 531105197Ssam if (error) { 532124765Ssam if (mp) { 533124765Ssam /* XXX: Should never happen! */ 534105197Ssam m_freem(mp); 535124765Ssam } 536124765Ssam m = NULL; /* ipip_output() already freed it */ 537105197Ssam goto bad; 538105197Ssam } 539105197Ssam m = mp, mp = NULL; 540105197Ssam /* 541105197Ssam * ipip_output clears IP_DF in the new header. If 542105197Ssam * we need to propagate IP_DF from the outer header, 543105197Ssam * then we have to do it here. 544105197Ssam * 545105197Ssam * XXX shouldn't assume what ipip_output does. 546105197Ssam */ 547105197Ssam if (dst->sa.sa_family == AF_INET && setdf) { 548105197Ssam if (m->m_len < sizeof (struct ip) && 549105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 550105197Ssam error = ENOBUFS; 551105197Ssam goto bad; 552105197Ssam } 553105197Ssam ip = mtod(m, struct ip *); 554105197Ssam ip->ip_off = ntohs(ip->ip_off); 555105197Ssam ip->ip_off |= IP_DF; 556105197Ssam ip->ip_off = htons(ip->ip_off); 557105197Ssam } 558105197Ssam } 559105197Ssam } 560105197Ssam 561159965Sthompsa#ifdef DEV_ENC 562159965Sthompsa /* pass the mbuf to enc0 for bpf processing */ 563174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_AFTER); 564174054Sbz /* pass the mbuf to enc0 for packet filtering */ 565174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 566174054Sbz goto bad; 567159965Sthompsa#endif 568159965Sthompsa 569105197Ssam /* 570105197Ssam * Dispatch to the appropriate IPsec transform logic. The 571105197Ssam * packet will be returned for transmission after crypto 572105197Ssam * processing, etc. are completed. For encapsulation we 573105197Ssam * bypass this call because of the explicit call done above 574105197Ssam * (necessary to deal with IP_DF handling for IPv4). 575105197Ssam * 576105197Ssam * NB: m & sav are ``passed to caller'' who's reponsible for 577105197Ssam * for reclaiming their resources. 578105197Ssam */ 579105197Ssam if (sav->tdb_xform->xf_type != XF_IP4) { 580105197Ssam ip = mtod(m, struct ip *); 581105197Ssam i = ip->ip_hl << 2; 582105197Ssam off = offsetof(struct ip, ip_p); 583105197Ssam error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 584105197Ssam } else { 585105197Ssam error = ipsec_process_done(m, isr); 586105197Ssam } 587120585Ssam IPSECREQUEST_UNLOCK(isr); 588105197Ssam return error; 589105197Ssambad: 590120585Ssam if (isr) 591120585Ssam IPSECREQUEST_UNLOCK(isr); 592105197Ssam if (m) 593105197Ssam m_freem(m); 594105197Ssam return error; 595105197Ssam} 596105197Ssam#endif 597105197Ssam 598105197Ssam#ifdef INET6 599105197Ssam/* 600105197Ssam * Chop IP6 header from the payload. 601105197Ssam */ 602105197Ssamstatic struct mbuf * 603105197Ssamipsec6_splithdr(struct mbuf *m) 604105197Ssam{ 605105197Ssam struct mbuf *mh; 606105197Ssam struct ip6_hdr *ip6; 607105197Ssam int hlen; 608105197Ssam 609120585Ssam IPSEC_ASSERT(m->m_len >= sizeof (struct ip6_hdr), 610120585Ssam ("first mbuf too short, len %u", m->m_len)); 611105197Ssam ip6 = mtod(m, struct ip6_hdr *); 612105197Ssam hlen = sizeof(struct ip6_hdr); 613105197Ssam if (m->m_len > hlen) { 614151967Sandre MGETHDR(mh, M_DONTWAIT, MT_DATA); 615105197Ssam if (!mh) { 616105197Ssam m_freem(m); 617105197Ssam return NULL; 618105197Ssam } 619108466Ssam M_MOVE_PKTHDR(mh, m); 620105197Ssam MH_ALIGN(mh, hlen); 621105197Ssam m->m_len -= hlen; 622105197Ssam m->m_data += hlen; 623105197Ssam mh->m_next = m; 624105197Ssam m = mh; 625105197Ssam m->m_len = hlen; 626105197Ssam bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen); 627105197Ssam } else if (m->m_len < hlen) { 628105197Ssam m = m_pullup(m, hlen); 629105197Ssam if (!m) 630105197Ssam return NULL; 631105197Ssam } 632105197Ssam return m; 633105197Ssam} 634105197Ssam 635105197Ssam/* 636105197Ssam * IPsec output logic for IPv6, transport mode. 637105197Ssam */ 638105197Ssamint 639105197Ssamipsec6_output_trans( 640105197Ssam struct ipsec_output_state *state, 641105197Ssam u_char *nexthdrp, 642105197Ssam struct mbuf *mprev, 643105197Ssam struct secpolicy *sp, 644105197Ssam int flags, 645105197Ssam int *tun) 646105197Ssam{ 647105197Ssam struct ipsecrequest *isr; 648105197Ssam struct secasindex saidx; 649105197Ssam int error = 0; 650105197Ssam struct mbuf *m; 651105197Ssam 652120585Ssam IPSEC_ASSERT(state != NULL, ("null state")); 653120585Ssam IPSEC_ASSERT(state->m != NULL, ("null m")); 654120585Ssam IPSEC_ASSERT(nexthdrp != NULL, ("null nexthdrp")); 655120585Ssam IPSEC_ASSERT(mprev != NULL, ("null mprev")); 656120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 657120585Ssam IPSEC_ASSERT(tun != NULL, ("null tun")); 658105197Ssam 659105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 660170122Sbz printf("%s: applied SP\n", __func__); 661105197Ssam kdebug_secpolicy(sp)); 662105197Ssam 663105197Ssam isr = sp->req; 664105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 665105197Ssam /* the rest will be handled by ipsec6_output_tunnel() */ 666105197Ssam *tun = 1; /* need tunnel-mode processing */ 667105197Ssam return 0; 668105197Ssam } 669105197Ssam 670105197Ssam *tun = 0; 671105197Ssam m = state->m; 672105197Ssam 673171133Sgnn IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 674105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 675105197Ssam if (isr == NULL) { 676177175Sbz if (error != 0) { 677105197Ssam#ifdef notdef 678177175Sbz /* XXX should notification be done for all errors ? */ 679177175Sbz /* 680177175Sbz * Notify the fact that the packet is discarded 681177175Sbz * to ourselves. I believe this is better than 682177175Sbz * just silently discarding. (jinmei@kame.net) 683177175Sbz * XXX: should we restrict the error to TCP packets? 684177175Sbz * XXX: should we directly notify sockets via 685177175Sbz * pfctlinputs? 686177175Sbz */ 687177175Sbz icmp6_error(m, ICMP6_DST_UNREACH, 688177175Sbz ICMP6_DST_UNREACH_ADMIN, 0); 689177175Sbz m = NULL; /* NB: icmp6_error frees mbuf */ 690105197Ssam#endif 691177175Sbz goto bad; 692177175Sbz } 693177175Sbz return EJUSTRETURN; 694105197Ssam } 695105197Ssam 696171133Sgnn error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 697171133Sgnn sizeof (struct ip6_hdr), 698171133Sgnn offsetof(struct ip6_hdr, 699171133Sgnn ip6_nxt)); 700171133Sgnn IPSECREQUEST_UNLOCK(isr); 701171133Sgnn return error; 702105197Ssambad: 703171133Sgnn if (isr) 704171133Sgnn IPSECREQUEST_UNLOCK(isr); 705105197Ssam if (m) 706105197Ssam m_freem(m); 707105197Ssam state->m = NULL; 708105197Ssam return error; 709105197Ssam} 710105197Ssam 711105197Ssamstatic int 712105197Ssamipsec6_encapsulate(struct mbuf *m, struct secasvar *sav) 713105197Ssam{ 714105197Ssam struct ip6_hdr *oip6; 715105197Ssam struct ip6_hdr *ip6; 716105197Ssam size_t plen; 717105197Ssam 718105197Ssam /* can't tunnel between different AFs */ 719105197Ssam if (sav->sah->saidx.src.sa.sa_family != AF_INET6 || 720105197Ssam sav->sah->saidx.dst.sa.sa_family != AF_INET6) { 721105197Ssam m_freem(m); 722105197Ssam return EINVAL; 723105197Ssam } 724171133Sgnn IPSEC_ASSERT(m->m_len == sizeof (struct ip6_hdr), 725120585Ssam ("mbuf wrong size; len %u", m->m_len)); 726105197Ssam 727105197Ssam 728105197Ssam /* 729105197Ssam * grow the mbuf to accomodate the new IPv6 header. 730105197Ssam */ 731105197Ssam plen = m->m_pkthdr.len; 732105197Ssam if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) { 733105197Ssam struct mbuf *n; 734111119Simp MGET(n, M_DONTWAIT, MT_DATA); 735105197Ssam if (!n) { 736105197Ssam m_freem(m); 737105197Ssam return ENOBUFS; 738105197Ssam } 739105197Ssam n->m_len = sizeof(struct ip6_hdr); 740105197Ssam n->m_next = m->m_next; 741105197Ssam m->m_next = n; 742105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 743105197Ssam oip6 = mtod(n, struct ip6_hdr *); 744105197Ssam } else { 745105197Ssam m->m_next->m_len += sizeof(struct ip6_hdr); 746105197Ssam m->m_next->m_data -= sizeof(struct ip6_hdr); 747105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 748105197Ssam oip6 = mtod(m->m_next, struct ip6_hdr *); 749105197Ssam } 750105197Ssam ip6 = mtod(m, struct ip6_hdr *); 751113076Sdes bcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr)); 752105197Ssam 753105197Ssam /* Fake link-local scope-class addresses */ 754105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) 755105197Ssam oip6->ip6_src.s6_addr16[1] = 0; 756105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) 757105197Ssam oip6->ip6_dst.s6_addr16[1] = 0; 758105197Ssam 759105197Ssam /* construct new IPv6 header. see RFC 2401 5.1.2.2 */ 760105197Ssam /* ECN consideration. */ 761181803Sbz ip6_ecn_ingress(V_ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow); 762105197Ssam if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr)) 763105197Ssam ip6->ip6_plen = htons(plen); 764105197Ssam else { 765105197Ssam /* ip6->ip6_plen will be updated in ip6_output() */ 766105197Ssam } 767105197Ssam ip6->ip6_nxt = IPPROTO_IPV6; 768171133Sgnn ip6->ip6_src = sav->sah->saidx.src.sin6.sin6_addr; 769171133Sgnn ip6->ip6_dst = sav->sah->saidx.dst.sin6.sin6_addr; 770105197Ssam ip6->ip6_hlim = IPV6_DEFHLIM; 771105197Ssam 772105197Ssam /* XXX Should ip6_src be updated later ? */ 773105197Ssam 774105197Ssam return 0; 775105197Ssam} 776105197Ssam 777105197Ssam/* 778105197Ssam * IPsec output logic for IPv6, tunnel mode. 779105197Ssam */ 780105197Ssamint 781105197Ssamipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int flags) 782105197Ssam{ 783105197Ssam struct ip6_hdr *ip6; 784105197Ssam struct ipsecrequest *isr; 785105197Ssam struct secasindex saidx; 786105197Ssam int error; 787213836Sbz struct sockaddr_in6 *dst6; 788105197Ssam struct mbuf *m; 789105197Ssam 790120585Ssam IPSEC_ASSERT(state != NULL, ("null state")); 791120585Ssam IPSEC_ASSERT(state->m != NULL, ("null m")); 792120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 793105197Ssam 794105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 795170122Sbz printf("%s: applied SP\n", __func__); 796105197Ssam kdebug_secpolicy(sp)); 797105197Ssam 798105197Ssam m = state->m; 799105197Ssam /* 800105197Ssam * transport mode ipsec (before the 1st tunnel mode) is already 801105197Ssam * processed by ipsec6_output_trans(). 802105197Ssam */ 803105197Ssam for (isr = sp->req; isr; isr = isr->next) { 804105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 805105197Ssam break; 806105197Ssam } 807170123Sbz 808170123Sbz IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 809105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 810177175Sbz if (isr == NULL) { 811177175Sbz if (error != 0) 812177175Sbz goto bad; 813177175Sbz return EJUSTRETURN; 814177175Sbz } 815105197Ssam 816174054Sbz#ifdef DEV_ENC 817181627Svanhu encif->if_opackets++; 818181627Svanhu encif->if_obytes += m->m_pkthdr.len; 819181627Svanhu 820174054Sbz /* pass the mbuf to enc0 for bpf processing */ 821174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 822174054Sbz /* pass the mbuf to enc0 for packet filtering */ 823174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 824174054Sbz goto bad; 825174054Sbz#endif 826174054Sbz 827105197Ssam /* 828105197Ssam * There may be the case that SA status will be changed when 829105197Ssam * we are refering to one. So calling splsoftnet(). 830105197Ssam */ 831105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 832105197Ssam /* 833105197Ssam * build IPsec tunnel. 834105197Ssam */ 835105197Ssam /* XXX should be processed with other familiy */ 836105197Ssam if (isr->sav->sah->saidx.src.sa.sa_family != AF_INET6) { 837120585Ssam ipseclog((LOG_ERR, "%s: family mismatched between " 838120585Ssam "inner and outer, spi=%u\n", __func__, 839105197Ssam ntohl(isr->sav->spi))); 840252692Sae IPSEC6STAT_INC(ips_out_inval); 841105197Ssam error = EAFNOSUPPORT; 842105197Ssam goto bad; 843105197Ssam } 844105197Ssam 845105197Ssam m = ipsec6_splithdr(m); 846105197Ssam if (!m) { 847252692Sae IPSEC6STAT_INC(ips_out_nomem); 848105197Ssam error = ENOMEM; 849105197Ssam goto bad; 850105197Ssam } 851105197Ssam error = ipsec6_encapsulate(m, isr->sav); 852105197Ssam if (error) { 853105197Ssam m = NULL; 854105197Ssam goto bad; 855105197Ssam } 856105197Ssam ip6 = mtod(m, struct ip6_hdr *); 857105197Ssam 858214250Sbz state->ro = 859214250Sbz (struct route *)&isr->sav->sah->route_cache.sin6_route; 860105197Ssam state->dst = (struct sockaddr *)&state->ro->ro_dst; 861105197Ssam dst6 = (struct sockaddr_in6 *)state->dst; 862105197Ssam if (state->ro->ro_rt 863105197Ssam && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 864105197Ssam || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { 865105197Ssam RTFREE(state->ro->ro_rt); 866105197Ssam state->ro->ro_rt = NULL; 867105197Ssam } 868187936Sbz if (state->ro->ro_rt == NULL) { 869105197Ssam bzero(dst6, sizeof(*dst6)); 870105197Ssam dst6->sin6_family = AF_INET6; 871105197Ssam dst6->sin6_len = sizeof(*dst6); 872105197Ssam dst6->sin6_addr = ip6->ip6_dst; 873232292Sbz rtalloc_ign_fib(state->ro, 0UL, M_GETFIB(m)); 874105197Ssam } 875187936Sbz if (state->ro->ro_rt == NULL) { 876250044Sae IP6STAT_INC(ip6s_noroute); 877252692Sae IPSEC6STAT_INC(ips_out_noroute); 878105197Ssam error = EHOSTUNREACH; 879105197Ssam goto bad; 880105197Ssam } 881105197Ssam 882105197Ssam /* adjust state->dst if tunnel endpoint is offlink */ 883213837Sbz if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) 884105197Ssam state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; 885105197Ssam } 886105197Ssam 887105197Ssam m = ipsec6_splithdr(m); 888105197Ssam if (!m) { 889252692Sae IPSEC6STAT_INC(ips_out_nomem); 890105197Ssam error = ENOMEM; 891105197Ssam goto bad; 892105197Ssam } 893105197Ssam ip6 = mtod(m, struct ip6_hdr *); 894174054Sbz 895174054Sbz#ifdef DEV_ENC 896174054Sbz /* pass the mbuf to enc0 for bpf processing */ 897174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_AFTER); 898174054Sbz /* pass the mbuf to enc0 for packet filtering */ 899174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 900174054Sbz goto bad; 901174054Sbz#endif 902174054Sbz 903170123Sbz error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 904105197Ssam sizeof (struct ip6_hdr), 905105197Ssam offsetof(struct ip6_hdr, ip6_nxt)); 906170123Sbz IPSECREQUEST_UNLOCK(isr); 907170123Sbz return error; 908105197Ssambad: 909170123Sbz if (isr) 910170123Sbz IPSECREQUEST_UNLOCK(isr); 911105197Ssam if (m) 912105197Ssam m_freem(m); 913105197Ssam state->m = NULL; 914105197Ssam return error; 915105197Ssam} 916105197Ssam#endif /*INET6*/ 917