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$ 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> 48195699Srwatson#include <net/vnet.h> 49105197Ssam 50105197Ssam#include <netinet/in.h> 51105197Ssam#include <netinet/in_systm.h> 52105197Ssam#include <netinet/ip.h> 53105197Ssam#include <netinet/ip_var.h> 54105197Ssam#include <netinet/in_var.h> 55105197Ssam#include <netinet/ip_ecn.h> 56105197Ssam#ifdef INET6 57105197Ssam#include <netinet6/ip6_ecn.h> 58105197Ssam#endif 59105197Ssam 60105197Ssam#include <netinet/ip6.h> 61105197Ssam#ifdef INET6 62105197Ssam#include <netinet6/ip6_var.h> 63105197Ssam#endif 64105197Ssam#include <netinet/in_pcb.h> 65105197Ssam#ifdef INET6 66105197Ssam#include <netinet/icmp6.h> 67105197Ssam#endif 68105197Ssam 69105197Ssam#include <netipsec/ipsec.h> 70105197Ssam#ifdef INET6 71105197Ssam#include <netipsec/ipsec6.h> 72105197Ssam#endif 73105197Ssam#include <netipsec/ah_var.h> 74105197Ssam#include <netipsec/esp_var.h> 75105197Ssam#include <netipsec/ipcomp_var.h> 76105197Ssam 77105197Ssam#include <netipsec/xform.h> 78105197Ssam 79105197Ssam#include <netipsec/key.h> 80105197Ssam#include <netipsec/keydb.h> 81105197Ssam#include <netipsec/key_debug.h> 82105197Ssam 83105197Ssam#include <machine/in_cksum.h> 84105197Ssam 85194062Svanhu#ifdef IPSEC_NAT_T 86194062Svanhu#include <netinet/udp.h> 87194062Svanhu#endif 88194062Svanhu 89181627Svanhu#ifdef DEV_ENC 90181627Svanhu#include <net/if_enc.h> 91181627Svanhu#endif 92181627Svanhu 93181627Svanhu 94105197Ssamint 95105197Ssamipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) 96105197Ssam{ 97105197Ssam struct tdb_ident *tdbi; 98105197Ssam struct m_tag *mtag; 99105197Ssam struct secasvar *sav; 100105197Ssam struct secasindex *saidx; 101105197Ssam int error; 102105197Ssam 103120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 104120585Ssam IPSEC_ASSERT(isr != NULL, ("null ISR")); 105105197Ssam sav = isr->sav; 106120585Ssam IPSEC_ASSERT(sav != NULL, ("null SA")); 107120585Ssam IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); 108105197Ssam 109105197Ssam saidx = &sav->sah->saidx; 110105197Ssam switch (saidx->dst.sa.sa_family) { 111105197Ssam#ifdef INET 112105197Ssam case AF_INET: 113105197Ssam /* Fix the header length, for AH processing. */ 114105197Ssam mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 115105197Ssam break; 116105197Ssam#endif /* INET */ 117105197Ssam#ifdef INET6 118105197Ssam case AF_INET6: 119105197Ssam /* Fix the header length, for AH processing. */ 120105197Ssam if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 121105197Ssam error = ENXIO; 122105197Ssam goto bad; 123105197Ssam } 124105197Ssam if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 125105197Ssam /* No jumbogram support. */ 126105197Ssam error = ENXIO; /*?*/ 127105197Ssam goto bad; 128105197Ssam } 129105197Ssam mtod(m, struct ip6_hdr *)->ip6_plen = 130105197Ssam htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 131105197Ssam break; 132105197Ssam#endif /* INET6 */ 133105197Ssam default: 134120585Ssam DPRINTF(("%s: unknown protocol family %u\n", __func__, 135105197Ssam saidx->dst.sa.sa_family)); 136105197Ssam error = ENXIO; 137105197Ssam goto bad; 138105197Ssam } 139105197Ssam 140105197Ssam /* 141105197Ssam * Add a record of what we've done or what needs to be done to the 142105197Ssam * packet. 143105197Ssam */ 144105197Ssam mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 145105197Ssam sizeof(struct tdb_ident), M_NOWAIT); 146105197Ssam if (mtag == NULL) { 147120585Ssam DPRINTF(("%s: could not get packet tag\n", __func__)); 148105197Ssam error = ENOMEM; 149105197Ssam goto bad; 150105197Ssam } 151105197Ssam 152105197Ssam tdbi = (struct tdb_ident *)(mtag + 1); 153105197Ssam tdbi->dst = saidx->dst; 154105197Ssam tdbi->proto = saidx->proto; 155105197Ssam tdbi->spi = sav->spi; 156105197Ssam m_tag_prepend(m, mtag); 157105197Ssam 158105197Ssam /* 159105197Ssam * If there's another (bundled) SA to apply, do so. 160105197Ssam * Note that this puts a burden on the kernel stack size. 161105197Ssam * If this is a problem we'll need to introduce a queue 162105197Ssam * to set the packet on so we can unwind the stack before 163105197Ssam * doing further processing. 164105197Ssam */ 165105197Ssam if (isr->next) { 166238700Sbz /* XXX-BZ currently only support same AF bundles. */ 167221129Sbz switch (saidx->dst.sa.sa_family) { 168221129Sbz#ifdef INET 169221129Sbz case AF_INET: 170274755Sae IPSECSTAT_INC(ips_out_bundlesa); 171221129Sbz return ipsec4_process_packet(m, isr->next, 0, 0); 172221129Sbz /* NOTREACHED */ 173221129Sbz#endif 174221129Sbz#ifdef notyet 175221129Sbz#ifdef INET6 176221129Sbz case AF_INET6: 177221129Sbz /* XXX */ 178274755Sae IPSEC6STAT_INC(ips_out_bundlesa); 179274132Sae return ipsec6_process_packet(m, isr->next); 180221129Sbz /* NOTREACHED */ 181221129Sbz#endif /* INET6 */ 182221129Sbz#endif 183221129Sbz default: 184221129Sbz DPRINTF(("%s: unknown protocol family %u\n", __func__, 185221129Sbz saidx->dst.sa.sa_family)); 186221129Sbz error = ENXIO; 187221129Sbz goto bad; 188221129Sbz } 189105197Ssam } 190117056Ssam key_sa_recordxfer(sav, m); /* record data transfer */ 191105197Ssam 192105197Ssam /* 193105197Ssam * We're done with IPsec processing, transmit the packet using the 194105197Ssam * appropriate network protocol (IP or IPv6). SPD lookup will be 195105197Ssam * performed again there. 196105197Ssam */ 197105197Ssam switch (saidx->dst.sa.sa_family) { 198105197Ssam#ifdef INET 199105197Ssam case AF_INET: 200194062Svanhu#ifdef IPSEC_NAT_T 201194062Svanhu /* 202194062Svanhu * If NAT-T is enabled, now that all IPsec processing is done 203194062Svanhu * insert UDP encapsulation header after IP header. 204194062Svanhu */ 205194062Svanhu if (sav->natt_type) { 206241919Sglebius struct ip *ip = mtod(m, struct ip *); 207194062Svanhu const int hlen = (ip->ip_hl << 2); 208194062Svanhu int size, off; 209194062Svanhu struct mbuf *mi; 210194062Svanhu struct udphdr *udp; 211194062Svanhu 212194062Svanhu size = sizeof(struct udphdr); 213194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 214194062Svanhu /* 215194062Svanhu * draft-ietf-ipsec-nat-t-ike-0[01].txt and 216194062Svanhu * draft-ietf-ipsec-udp-encaps-(00/)01.txt, 217194062Svanhu * ignoring possible AH mode 218194062Svanhu * non-IKE marker + non-ESP marker 219194062Svanhu * from draft-ietf-ipsec-udp-encaps-00.txt. 220194062Svanhu */ 221194062Svanhu size += sizeof(u_int64_t); 222194062Svanhu } 223194062Svanhu mi = m_makespace(m, hlen, size, &off); 224194062Svanhu if (mi == NULL) { 225194062Svanhu DPRINTF(("%s: m_makespace for udphdr failed\n", 226194062Svanhu __func__)); 227194062Svanhu error = ENOBUFS; 228194062Svanhu goto bad; 229194062Svanhu } 230194062Svanhu 231194062Svanhu udp = (struct udphdr *)(mtod(mi, caddr_t) + off); 232194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 233194062Svanhu udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); 234194062Svanhu else 235194062Svanhu udp->uh_sport = 236194062Svanhu KEY_PORTFROMSADDR(&sav->sah->saidx.src); 237194062Svanhu udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); 238194062Svanhu udp->uh_sum = 0; 239194062Svanhu udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 240241919Sglebius ip->ip_len = htons(m->m_pkthdr.len); 241194062Svanhu ip->ip_p = IPPROTO_UDP; 242194062Svanhu 243194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 244194062Svanhu *(u_int64_t *)(udp + 1) = 0; 245194062Svanhu } 246194062Svanhu#endif /* IPSEC_NAT_T */ 247194062Svanhu 248105197Ssam return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 249105197Ssam#endif /* INET */ 250105197Ssam#ifdef INET6 251105197Ssam case AF_INET6: 252105197Ssam /* 253105197Ssam * We don't need massage, IPv6 header fields are always in 254105197Ssam * net endian. 255105197Ssam */ 256105197Ssam return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 257105197Ssam#endif /* INET6 */ 258105197Ssam } 259105197Ssam panic("ipsec_process_done"); 260105197Ssambad: 261105197Ssam m_freem(m); 262105197Ssam return (error); 263105197Ssam} 264105197Ssam 265105197Ssamstatic struct ipsecrequest * 266105197Ssamipsec_nextisr( 267105197Ssam struct mbuf *m, 268105197Ssam struct ipsecrequest *isr, 269105197Ssam int af, 270105197Ssam struct secasindex *saidx, 271105197Ssam int *error 272105197Ssam) 273105197Ssam{ 274252028Sae#define IPSEC_OSTAT(name) do { \ 275252028Sae if (isr->saidx.proto == IPPROTO_ESP) \ 276252028Sae ESPSTAT_INC(esps_##name); \ 277252028Sae else if (isr->saidx.proto == IPPROTO_AH)\ 278252028Sae AHSTAT_INC(ahs_##name); \ 279252028Sae else \ 280252028Sae IPCOMPSTAT_INC(ipcomps_##name); \ 281252028Sae} while (0) 282105197Ssam struct secasvar *sav; 283105197Ssam 284120585Ssam IPSECREQUEST_LOCK_ASSERT(isr); 285120585Ssam 286120585Ssam IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 287120585Ssam ("invalid address family %u", af)); 288105197Ssamagain: 289105197Ssam /* 290105197Ssam * Craft SA index to search for proper SA. Note that 291105197Ssam * we only fillin unspecified SA peers for transport 292105197Ssam * mode; for tunnel mode they must already be filled in. 293105197Ssam */ 294105197Ssam *saidx = isr->saidx; 295105197Ssam if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 296105197Ssam /* Fillin unspecified SA peers only for transport mode */ 297105197Ssam if (af == AF_INET) { 298105197Ssam struct sockaddr_in *sin; 299105197Ssam struct ip *ip = mtod(m, struct ip *); 300105197Ssam 301105197Ssam if (saidx->src.sa.sa_len == 0) { 302105197Ssam sin = &saidx->src.sin; 303105197Ssam sin->sin_len = sizeof(*sin); 304105197Ssam sin->sin_family = AF_INET; 305105197Ssam sin->sin_port = IPSEC_PORT_ANY; 306105197Ssam sin->sin_addr = ip->ip_src; 307105197Ssam } 308105197Ssam if (saidx->dst.sa.sa_len == 0) { 309105197Ssam sin = &saidx->dst.sin; 310105197Ssam sin->sin_len = sizeof(*sin); 311105197Ssam sin->sin_family = AF_INET; 312105197Ssam sin->sin_port = IPSEC_PORT_ANY; 313105197Ssam sin->sin_addr = ip->ip_dst; 314105197Ssam } 315105197Ssam } else { 316105197Ssam struct sockaddr_in6 *sin6; 317105197Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 318105197Ssam 319105197Ssam if (saidx->src.sin6.sin6_len == 0) { 320105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->src; 321105197Ssam sin6->sin6_len = sizeof(*sin6); 322105197Ssam sin6->sin6_family = AF_INET6; 323105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 324105197Ssam sin6->sin6_addr = ip6->ip6_src; 325105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 326105197Ssam /* fix scope id for comparing SPD */ 327105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 328105197Ssam sin6->sin6_scope_id = 329105197Ssam ntohs(ip6->ip6_src.s6_addr16[1]); 330105197Ssam } 331105197Ssam } 332105197Ssam if (saidx->dst.sin6.sin6_len == 0) { 333105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->dst; 334105197Ssam sin6->sin6_len = sizeof(*sin6); 335105197Ssam sin6->sin6_family = AF_INET6; 336105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 337105197Ssam sin6->sin6_addr = ip6->ip6_dst; 338105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 339105197Ssam /* fix scope id for comparing SPD */ 340105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 341105197Ssam sin6->sin6_scope_id = 342105197Ssam ntohs(ip6->ip6_dst.s6_addr16[1]); 343105197Ssam } 344105197Ssam } 345105197Ssam } 346105197Ssam } 347105197Ssam 348105197Ssam /* 349105197Ssam * Lookup SA and validate it. 350105197Ssam */ 351105197Ssam *error = key_checkrequest(isr, saidx); 352105197Ssam if (*error != 0) { 353105197Ssam /* 354105197Ssam * IPsec processing is required, but no SA found. 355105197Ssam * I assume that key_acquire() had been called 356105197Ssam * to get/establish the SA. Here I discard 357105197Ssam * this packet because it is responsibility for 358105197Ssam * upper layer to retransmit the packet. 359105197Ssam */ 360274755Sae switch(af) { 361274755Sae case AF_INET: 362274755Sae IPSECSTAT_INC(ips_out_nosa); 363274755Sae break; 364274755Sae#ifdef INET6 365274755Sae case AF_INET6: 366274755Sae IPSEC6STAT_INC(ips_out_nosa); 367274755Sae break; 368274755Sae#endif 369274755Sae } 370105197Ssam goto bad; 371105197Ssam } 372105197Ssam sav = isr->sav; 373177175Sbz if (sav == NULL) { 374120585Ssam IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 375120585Ssam ("no SA found, but required; level %u", 376105197Ssam ipsec_get_reqlevel(isr))); 377120585Ssam IPSECREQUEST_UNLOCK(isr); 378105197Ssam isr = isr->next; 379177175Sbz /* 380177175Sbz * If isr is NULL, we found a 'use' policy w/o SA. 381177175Sbz * Return w/o error and w/o isr so we can drop out 382177175Sbz * and continue w/o IPsec processing. 383177175Sbz */ 384177175Sbz if (isr == NULL) 385105197Ssam return isr; 386120585Ssam IPSECREQUEST_LOCK(isr); 387105197Ssam goto again; 388105197Ssam } 389105197Ssam 390105197Ssam /* 391105197Ssam * Check system global policy controls. 392105197Ssam */ 393181803Sbz if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 394181803Sbz (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 395181803Sbz (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 396120585Ssam DPRINTF(("%s: IPsec outbound packet dropped due" 397120585Ssam " to policy (check your sysctls)\n", __func__)); 398252028Sae IPSEC_OSTAT(pdrops); 399105197Ssam *error = EHOSTUNREACH; 400105197Ssam goto bad; 401105197Ssam } 402105197Ssam 403105197Ssam /* 404105197Ssam * Sanity check the SA contents for the caller 405105197Ssam * before they invoke the xform output method. 406105197Ssam */ 407105197Ssam if (sav->tdb_xform == NULL) { 408120585Ssam DPRINTF(("%s: no transform for SA\n", __func__)); 409252028Sae IPSEC_OSTAT(noxform); 410105197Ssam *error = EHOSTUNREACH; 411105197Ssam goto bad; 412105197Ssam } 413105197Ssam return isr; 414105197Ssambad: 415120585Ssam IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 416120585Ssam IPSECREQUEST_UNLOCK(isr); 417105197Ssam return NULL; 418105197Ssam#undef IPSEC_OSTAT 419105197Ssam} 420105197Ssam 421105197Ssam#ifdef INET 422105197Ssam/* 423105197Ssam * IPsec output logic for IPv4. 424105197Ssam */ 425105197Ssamint 426105197Ssamipsec4_process_packet( 427105197Ssam struct mbuf *m, 428105197Ssam struct ipsecrequest *isr, 429105197Ssam int flags, 430105197Ssam int tunalready) 431105197Ssam{ 432105197Ssam struct secasindex saidx; 433105197Ssam struct secasvar *sav; 434105197Ssam struct ip *ip; 435119643Ssam int error, i, off; 436105197Ssam 437120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 438120585Ssam IPSEC_ASSERT(isr != NULL, ("null isr")); 439105197Ssam 440120585Ssam IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 441105197Ssam 442105197Ssam isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 443177175Sbz if (isr == NULL) { 444177175Sbz if (error != 0) 445177175Sbz goto bad; 446177175Sbz return EJUSTRETURN; 447177175Sbz } 448105197Ssam 449105197Ssam sav = isr->sav; 450159965Sthompsa 451159965Sthompsa#ifdef DEV_ENC 452181627Svanhu encif->if_opackets++; 453181627Svanhu encif->if_obytes += m->m_pkthdr.len; 454181627Svanhu 455174054Sbz /* pass the mbuf to enc0 for bpf processing */ 456174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 457159965Sthompsa /* pass the mbuf to enc0 for packet filtering */ 458174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 459159965Sthompsa goto bad; 460159965Sthompsa#endif 461159965Sthompsa 462105197Ssam if (!tunalready) { 463105197Ssam union sockaddr_union *dst = &sav->sah->saidx.dst; 464105197Ssam int setdf; 465105197Ssam 466105197Ssam /* 467105197Ssam * Collect IP_DF state from the outer header. 468105197Ssam */ 469105197Ssam if (dst->sa.sa_family == AF_INET) { 470105197Ssam if (m->m_len < sizeof (struct ip) && 471105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 472105197Ssam error = ENOBUFS; 473105197Ssam goto bad; 474105197Ssam } 475105197Ssam ip = mtod(m, struct ip *); 476105197Ssam /* Honor system-wide control of how to handle IP_DF */ 477181803Sbz switch (V_ip4_ipsec_dfbit) { 478105197Ssam case 0: /* clear in outer header */ 479105197Ssam case 1: /* set in outer header */ 480181803Sbz setdf = V_ip4_ipsec_dfbit; 481105197Ssam break; 482105197Ssam default: /* propagate to outer header */ 483105197Ssam setdf = ntohs(ip->ip_off & IP_DF); 484105197Ssam break; 485105197Ssam } 486105197Ssam } else { 487105197Ssam ip = NULL; /* keep compiler happy */ 488105197Ssam setdf = 0; 489105197Ssam } 490105197Ssam /* Do the appropriate encapsulation, if necessary */ 491105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 492105197Ssam dst->sa.sa_family != AF_INET || /* PF mismatch */ 493105197Ssam#if 0 494105197Ssam (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ 495105197Ssam sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ 496105197Ssam#endif 497105197Ssam (dst->sa.sa_family == AF_INET && /* Proxy */ 498105197Ssam dst->sin.sin_addr.s_addr != INADDR_ANY && 499105197Ssam dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 500105197Ssam struct mbuf *mp; 501105197Ssam 502105197Ssam /* Fix IPv4 header checksum and length */ 503105197Ssam if (m->m_len < sizeof (struct ip) && 504105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 505105197Ssam error = ENOBUFS; 506105197Ssam goto bad; 507105197Ssam } 508105197Ssam ip = mtod(m, struct ip *); 509274132Sae if (ip->ip_v == IPVERSION) { 510274132Sae ip->ip_len = htons(m->m_pkthdr.len); 511274132Sae ip->ip_sum = 0; 512274132Sae ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 513274132Sae } 514105197Ssam 515105197Ssam /* Encapsulate the packet */ 516105197Ssam error = ipip_output(m, isr, &mp, 0, 0); 517105197Ssam if (mp == NULL && !error) { 518105197Ssam /* Should never happen. */ 519120585Ssam DPRINTF(("%s: ipip_output returns no mbuf and " 520120585Ssam "no error!", __func__)); 521105197Ssam error = EFAULT; 522105197Ssam } 523105197Ssam if (error) { 524124765Ssam if (mp) { 525124765Ssam /* XXX: Should never happen! */ 526105197Ssam m_freem(mp); 527124765Ssam } 528124765Ssam m = NULL; /* ipip_output() already freed it */ 529105197Ssam goto bad; 530105197Ssam } 531105197Ssam m = mp, mp = NULL; 532105197Ssam /* 533105197Ssam * ipip_output clears IP_DF in the new header. If 534105197Ssam * we need to propagate IP_DF from the outer header, 535105197Ssam * then we have to do it here. 536105197Ssam * 537105197Ssam * XXX shouldn't assume what ipip_output does. 538105197Ssam */ 539105197Ssam if (dst->sa.sa_family == AF_INET && setdf) { 540105197Ssam if (m->m_len < sizeof (struct ip) && 541105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 542105197Ssam error = ENOBUFS; 543105197Ssam goto bad; 544105197Ssam } 545105197Ssam ip = mtod(m, struct ip *); 546105197Ssam ip->ip_off = ntohs(ip->ip_off); 547105197Ssam ip->ip_off |= IP_DF; 548105197Ssam ip->ip_off = htons(ip->ip_off); 549105197Ssam } 550105197Ssam } 551105197Ssam } 552105197Ssam 553159965Sthompsa#ifdef DEV_ENC 554159965Sthompsa /* pass the mbuf to enc0 for bpf processing */ 555274132Sae ipsec_bpf(m, sav, sav->sah->saidx.dst.sa.sa_family, ENC_OUT|ENC_AFTER); 556174054Sbz /* pass the mbuf to enc0 for packet filtering */ 557174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 558174054Sbz goto bad; 559159965Sthompsa#endif 560159965Sthompsa 561105197Ssam /* 562105197Ssam * Dispatch to the appropriate IPsec transform logic. The 563105197Ssam * packet will be returned for transmission after crypto 564105197Ssam * processing, etc. are completed. For encapsulation we 565105197Ssam * bypass this call because of the explicit call done above 566105197Ssam * (necessary to deal with IP_DF handling for IPv4). 567105197Ssam * 568105197Ssam * NB: m & sav are ``passed to caller'' who's reponsible for 569105197Ssam * for reclaiming their resources. 570105197Ssam */ 571105197Ssam if (sav->tdb_xform->xf_type != XF_IP4) { 572274132Sae union sockaddr_union *dst = &sav->sah->saidx.dst; 573274132Sae switch(dst->sa.sa_family) { 574274132Sae case AF_INET: 575274132Sae ip = mtod(m, struct ip *); 576274132Sae i = ip->ip_hl << 2; 577274132Sae off = offsetof(struct ip, ip_p); 578274132Sae break; 579274132Sae#ifdef INET6 580274132Sae case AF_INET6: 581274132Sae i = sizeof(struct ip6_hdr); 582274132Sae off = offsetof(struct ip6_hdr, ip6_nxt); 583274132Sae break; 584274132Sae#endif /* INET6 */ 585274132Sae default: 586274132Sae DPRINTF(("%s: unsupported protocol family %u\n", 587274132Sae __func__, dst->sa.sa_family)); 588274132Sae error = EPFNOSUPPORT; 589274132Sae IPSECSTAT_INC(ips_out_inval); 590274132Sae goto bad; 591274132Sae } 592105197Ssam error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 593105197Ssam } else { 594105197Ssam error = ipsec_process_done(m, isr); 595105197Ssam } 596120585Ssam IPSECREQUEST_UNLOCK(isr); 597105197Ssam return error; 598105197Ssambad: 599120585Ssam if (isr) 600120585Ssam IPSECREQUEST_UNLOCK(isr); 601105197Ssam if (m) 602105197Ssam m_freem(m); 603105197Ssam return error; 604105197Ssam} 605105197Ssam#endif 606105197Ssam 607274132Sae 608105197Ssam#ifdef INET6 609105197Ssamstatic int 610274132Saein6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia) 611105197Ssam{ 612274132Sae struct in6_addr ia2; 613105197Ssam 614274132Sae memcpy(&ia2, &sa->sin6_addr, sizeof(ia2)); 615274132Sae if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) 616274132Sae ia2.s6_addr16[1] = htons(sa->sin6_scope_id); 617105197Ssam 618274132Sae return IN6_ARE_ADDR_EQUAL(ia, &ia2); 619105197Ssam} 620105197Ssam 621105197Ssam/* 622274132Sae * IPsec output logic for IPv6. 623105197Ssam */ 624105197Ssamint 625274132Saeipsec6_process_packet( 626274132Sae struct mbuf *m, 627274132Sae struct ipsecrequest *isr 628274132Sae ) 629105197Ssam{ 630274132Sae struct secasindex saidx; 631274132Sae struct secasvar *sav; 632105197Ssam struct ip6_hdr *ip6; 633274132Sae int error, i, off; 634274132Sae union sockaddr_union *dst; 635105197Ssam 636274132Sae IPSEC_ASSERT(m != NULL, ("ipsec6_process_packet: null mbuf")); 637274132Sae IPSEC_ASSERT(isr != NULL, ("ipsec6_process_packet: null isr")); 638105197Ssam 639274132Sae IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 640105197Ssam 641105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 642177175Sbz if (isr == NULL) { 643177175Sbz if (error != 0) 644177175Sbz goto bad; 645274132Sae return EJUSTRETURN; 646177175Sbz } 647105197Ssam 648274132Sae sav = isr->sav; 649274132Sae dst = &sav->sah->saidx.dst; 650274132Sae 651274755Sae ip6 = mtod(m, struct ip6_hdr *); 652274755Sae ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); 653174054Sbz#ifdef DEV_ENC 654181627Svanhu encif->if_opackets++; 655181627Svanhu encif->if_obytes += m->m_pkthdr.len; 656181627Svanhu 657174054Sbz /* pass the mbuf to enc0 for bpf processing */ 658174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 659174054Sbz /* pass the mbuf to enc0 for packet filtering */ 660174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 661174054Sbz goto bad; 662274132Sae#endif /* DEV_ENC */ 663174054Sbz 664274132Sae /* Do the appropriate encapsulation, if necessary */ 665274132Sae if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 666274132Sae dst->sa.sa_family != AF_INET6 || /* PF mismatch */ 667274132Sae ((dst->sa.sa_family == AF_INET6) && 668274132Sae (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) && 669274132Sae (!in6_sa_equal_addrwithscope(&dst->sin6, 670274132Sae &ip6->ip6_dst)))) { 671274132Sae struct mbuf *mp; 672274132Sae 673274132Sae /* Fix IPv6 header payload length. */ 674274132Sae if (m->m_len < sizeof(struct ip6_hdr)) 675274132Sae if ((m = m_pullup(m,sizeof(struct ip6_hdr))) == NULL) { 676274132Sae error = ENOBUFS; 677274132Sae goto bad; 678274132Sae } 679274132Sae 680274132Sae if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { 681274132Sae /* No jumbogram support. */ 682274132Sae error = ENXIO; /*XXX*/ 683105197Ssam goto bad; 684105197Ssam } 685105197Ssam 686274132Sae /* Encapsulate the packet */ 687274132Sae error = ipip_output(m, isr, &mp, 0, 0); 688274132Sae if (mp == NULL && !error) { 689274132Sae /* Should never happen. */ 690274132Sae DPRINTF(("ipsec6_process_packet: ipip_output " 691274132Sae "returns no mbuf and no error!")); 692274132Sae error = EFAULT; 693105197Ssam goto bad; 694105197Ssam } 695274132Sae 696105197Ssam if (error) { 697274132Sae if (mp) { 698274132Sae /* XXX: Should never happen! */ 699274132Sae m_freem(mp); 700274132Sae } 701274132Sae m = NULL; /* ipip_output() already freed it */ 702105197Ssam goto bad; 703105197Ssam } 704105197Ssam 705274132Sae m = mp; 706274132Sae mp = NULL; 707105197Ssam } 708105197Ssam 709174054Sbz#ifdef DEV_ENC 710274132Sae ipsec_bpf(m, isr->sav, dst->sa.sa_family, ENC_OUT|ENC_AFTER); 711174054Sbz /* pass the mbuf to enc0 for packet filtering */ 712174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 713174054Sbz goto bad; 714274132Sae#endif /* DEV_ENC */ 715174054Sbz 716274132Sae switch(dst->sa.sa_family) { 717274132Sae#ifdef INET 718274132Sae case AF_INET: 719274132Sae { 720274132Sae struct ip *ip; 721274132Sae ip = mtod(m, struct ip *); 722274132Sae i = ip->ip_hl << 2; 723274132Sae off = offsetof(struct ip, ip_p); 724274132Sae } 725274132Sae break; 726274132Sae#endif /* AF_INET */ 727274132Sae case AF_INET6: 728274132Sae i = sizeof(struct ip6_hdr); 729274132Sae off = offsetof(struct ip6_hdr, ip6_nxt); 730274132Sae break; 731274132Sae default: 732274132Sae DPRINTF(("%s: unsupported protocol family %u\n", 733274132Sae __func__, dst->sa.sa_family)); 734274132Sae error = EPFNOSUPPORT; 735274132Sae IPSEC6STAT_INC(ips_out_inval); 736274132Sae goto bad; 737274132Sae } 738274132Sae error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 739170123Sbz IPSECREQUEST_UNLOCK(isr); 740170123Sbz return error; 741105197Ssambad: 742274132Sae 743170123Sbz if (isr) 744170123Sbz IPSECREQUEST_UNLOCK(isr); 745105197Ssam if (m) 746105197Ssam m_freem(m); 747105197Ssam return error; 748105197Ssam} 749105197Ssam#endif /*INET6*/ 750