ipsec_output.c revision 274454
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: head/sys/netipsec/ipsec_output.c 274454 2014-11-12 22:51:30Z 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> 47257176Sglebius#include <net/if_var.h> 48171497Sbz#include <net/pfil.h> 49105197Ssam#include <net/route.h> 50195699Srwatson#include <net/vnet.h> 51105197Ssam 52105197Ssam#include <netinet/in.h> 53105197Ssam#include <netinet/in_systm.h> 54105197Ssam#include <netinet/ip.h> 55105197Ssam#include <netinet/ip_var.h> 56105197Ssam#include <netinet/in_var.h> 57105197Ssam#include <netinet/ip_ecn.h> 58105197Ssam#ifdef INET6 59105197Ssam#include <netinet6/ip6_ecn.h> 60105197Ssam#endif 61105197Ssam 62105197Ssam#include <netinet/ip6.h> 63105197Ssam#ifdef INET6 64105197Ssam#include <netinet6/ip6_var.h> 65105197Ssam#endif 66105197Ssam#include <netinet/in_pcb.h> 67105197Ssam#ifdef INET6 68105197Ssam#include <netinet/icmp6.h> 69105197Ssam#endif 70105197Ssam 71105197Ssam#include <netipsec/ipsec.h> 72105197Ssam#ifdef INET6 73105197Ssam#include <netipsec/ipsec6.h> 74105197Ssam#endif 75105197Ssam#include <netipsec/ah_var.h> 76105197Ssam#include <netipsec/esp_var.h> 77105197Ssam#include <netipsec/ipcomp_var.h> 78105197Ssam 79105197Ssam#include <netipsec/xform.h> 80105197Ssam 81105197Ssam#include <netipsec/key.h> 82105197Ssam#include <netipsec/keydb.h> 83105197Ssam#include <netipsec/key_debug.h> 84105197Ssam 85105197Ssam#include <machine/in_cksum.h> 86105197Ssam 87194062Svanhu#ifdef IPSEC_NAT_T 88194062Svanhu#include <netinet/udp.h> 89194062Svanhu#endif 90194062Svanhu 91181627Svanhu#ifdef DEV_ENC 92181627Svanhu#include <net/if_enc.h> 93181627Svanhu#endif 94181627Svanhu 95181627Svanhu 96105197Ssamint 97105197Ssamipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) 98105197Ssam{ 99105197Ssam struct tdb_ident *tdbi; 100105197Ssam struct m_tag *mtag; 101105197Ssam struct secasvar *sav; 102105197Ssam struct secasindex *saidx; 103105197Ssam int error; 104105197Ssam 105120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 106120585Ssam IPSEC_ASSERT(isr != NULL, ("null ISR")); 107105197Ssam sav = isr->sav; 108120585Ssam IPSEC_ASSERT(sav != NULL, ("null SA")); 109120585Ssam IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); 110105197Ssam 111105197Ssam saidx = &sav->sah->saidx; 112105197Ssam switch (saidx->dst.sa.sa_family) { 113105197Ssam#ifdef INET 114105197Ssam case AF_INET: 115105197Ssam /* Fix the header length, for AH processing. */ 116105197Ssam mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 117105197Ssam break; 118105197Ssam#endif /* INET */ 119105197Ssam#ifdef INET6 120105197Ssam case AF_INET6: 121105197Ssam /* Fix the header length, for AH processing. */ 122105197Ssam if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 123105197Ssam error = ENXIO; 124105197Ssam goto bad; 125105197Ssam } 126105197Ssam if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 127105197Ssam /* No jumbogram support. */ 128105197Ssam error = ENXIO; /*?*/ 129105197Ssam goto bad; 130105197Ssam } 131105197Ssam mtod(m, struct ip6_hdr *)->ip6_plen = 132105197Ssam htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 133105197Ssam break; 134105197Ssam#endif /* INET6 */ 135105197Ssam default: 136120585Ssam DPRINTF(("%s: unknown protocol family %u\n", __func__, 137105197Ssam saidx->dst.sa.sa_family)); 138105197Ssam error = ENXIO; 139105197Ssam goto bad; 140105197Ssam } 141105197Ssam 142105197Ssam /* 143105197Ssam * Add a record of what we've done or what needs to be done to the 144105197Ssam * packet. 145105197Ssam */ 146105197Ssam mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 147105197Ssam sizeof(struct tdb_ident), M_NOWAIT); 148105197Ssam if (mtag == NULL) { 149120585Ssam DPRINTF(("%s: could not get packet tag\n", __func__)); 150105197Ssam error = ENOMEM; 151105197Ssam goto bad; 152105197Ssam } 153105197Ssam 154105197Ssam tdbi = (struct tdb_ident *)(mtag + 1); 155105197Ssam tdbi->dst = saidx->dst; 156105197Ssam tdbi->proto = saidx->proto; 157105197Ssam tdbi->spi = sav->spi; 158105197Ssam m_tag_prepend(m, mtag); 159105197Ssam 160105197Ssam /* 161105197Ssam * If there's another (bundled) SA to apply, do so. 162105197Ssam * Note that this puts a burden on the kernel stack size. 163105197Ssam * If this is a problem we'll need to introduce a queue 164105197Ssam * to set the packet on so we can unwind the stack before 165105197Ssam * doing further processing. 166105197Ssam */ 167105197Ssam if (isr->next) { 168252026Sae IPSECSTAT_INC(ips_out_bundlesa); 169238700Sbz /* XXX-BZ currently only support same AF bundles. */ 170221129Sbz switch (saidx->dst.sa.sa_family) { 171221129Sbz#ifdef INET 172221129Sbz case AF_INET: 173221129Sbz return ipsec4_process_packet(m, isr->next, 0, 0); 174221129Sbz /* NOTREACHED */ 175221129Sbz#endif 176221129Sbz#ifdef notyet 177221129Sbz#ifdef INET6 178221129Sbz case AF_INET6: 179221129Sbz /* XXX */ 180266800Svanhu return ipsec6_process_packet(m, isr->next); 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 193105197Ssam /* 194105197Ssam * We're done with IPsec processing, transmit the packet using the 195105197Ssam * appropriate network protocol (IP or IPv6). SPD lookup will be 196105197Ssam * performed again there. 197105197Ssam */ 198105197Ssam switch (saidx->dst.sa.sa_family) { 199105197Ssam#ifdef INET 200105197Ssam case AF_INET: 201194062Svanhu#ifdef IPSEC_NAT_T 202194062Svanhu /* 203194062Svanhu * If NAT-T is enabled, now that all IPsec processing is done 204194062Svanhu * insert UDP encapsulation header after IP header. 205194062Svanhu */ 206194062Svanhu if (sav->natt_type) { 207241919Sglebius struct ip *ip = mtod(m, struct ip *); 208194062Svanhu const int hlen = (ip->ip_hl << 2); 209194062Svanhu int size, off; 210194062Svanhu struct mbuf *mi; 211194062Svanhu struct udphdr *udp; 212194062Svanhu 213194062Svanhu size = sizeof(struct udphdr); 214194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 215194062Svanhu /* 216194062Svanhu * draft-ietf-ipsec-nat-t-ike-0[01].txt and 217194062Svanhu * draft-ietf-ipsec-udp-encaps-(00/)01.txt, 218194062Svanhu * ignoring possible AH mode 219194062Svanhu * non-IKE marker + non-ESP marker 220194062Svanhu * from draft-ietf-ipsec-udp-encaps-00.txt. 221194062Svanhu */ 222194062Svanhu size += sizeof(u_int64_t); 223194062Svanhu } 224194062Svanhu mi = m_makespace(m, hlen, size, &off); 225194062Svanhu if (mi == NULL) { 226194062Svanhu DPRINTF(("%s: m_makespace for udphdr failed\n", 227194062Svanhu __func__)); 228194062Svanhu error = ENOBUFS; 229194062Svanhu goto bad; 230194062Svanhu } 231194062Svanhu 232194062Svanhu udp = (struct udphdr *)(mtod(mi, caddr_t) + off); 233194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 234194062Svanhu udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); 235194062Svanhu else 236194062Svanhu udp->uh_sport = 237194062Svanhu KEY_PORTFROMSADDR(&sav->sah->saidx.src); 238194062Svanhu udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); 239194062Svanhu udp->uh_sum = 0; 240194062Svanhu udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 241241919Sglebius ip->ip_len = htons(m->m_pkthdr.len); 242194062Svanhu ip->ip_p = IPPROTO_UDP; 243194062Svanhu 244194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 245194062Svanhu *(u_int64_t *)(udp + 1) = 0; 246194062Svanhu } 247194062Svanhu#endif /* IPSEC_NAT_T */ 248194062Svanhu 249105197Ssam return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 250105197Ssam#endif /* INET */ 251105197Ssam#ifdef INET6 252105197Ssam case AF_INET6: 253105197Ssam /* 254105197Ssam * We don't need massage, IPv6 header fields are always in 255105197Ssam * net endian. 256105197Ssam */ 257105197Ssam return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 258105197Ssam#endif /* INET6 */ 259105197Ssam } 260105197Ssam panic("ipsec_process_done"); 261105197Ssambad: 262105197Ssam m_freem(m); 263105197Ssam return (error); 264105197Ssam} 265105197Ssam 266105197Ssamstatic struct ipsecrequest * 267105197Ssamipsec_nextisr( 268105197Ssam struct mbuf *m, 269105197Ssam struct ipsecrequest *isr, 270105197Ssam int af, 271105197Ssam struct secasindex *saidx, 272105197Ssam int *error 273105197Ssam) 274105197Ssam{ 275252028Sae#define IPSEC_OSTAT(name) do { \ 276252028Sae if (isr->saidx.proto == IPPROTO_ESP) \ 277252028Sae ESPSTAT_INC(esps_##name); \ 278252028Sae else if (isr->saidx.proto == IPPROTO_AH)\ 279252028Sae AHSTAT_INC(ahs_##name); \ 280252028Sae else \ 281252028Sae IPCOMPSTAT_INC(ipcomps_##name); \ 282252028Sae} while (0) 283105197Ssam struct secasvar *sav; 284105197Ssam 285120585Ssam IPSECREQUEST_LOCK_ASSERT(isr); 286120585Ssam 287120585Ssam IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 288120585Ssam ("invalid address family %u", af)); 289105197Ssamagain: 290105197Ssam /* 291105197Ssam * Craft SA index to search for proper SA. Note that 292105197Ssam * we only fillin unspecified SA peers for transport 293105197Ssam * mode; for tunnel mode they must already be filled in. 294105197Ssam */ 295105197Ssam *saidx = isr->saidx; 296105197Ssam if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 297105197Ssam /* Fillin unspecified SA peers only for transport mode */ 298105197Ssam if (af == AF_INET) { 299105197Ssam struct sockaddr_in *sin; 300105197Ssam struct ip *ip = mtod(m, struct ip *); 301105197Ssam 302105197Ssam if (saidx->src.sa.sa_len == 0) { 303105197Ssam sin = &saidx->src.sin; 304105197Ssam sin->sin_len = sizeof(*sin); 305105197Ssam sin->sin_family = AF_INET; 306105197Ssam sin->sin_port = IPSEC_PORT_ANY; 307105197Ssam sin->sin_addr = ip->ip_src; 308105197Ssam } 309105197Ssam if (saidx->dst.sa.sa_len == 0) { 310105197Ssam sin = &saidx->dst.sin; 311105197Ssam sin->sin_len = sizeof(*sin); 312105197Ssam sin->sin_family = AF_INET; 313105197Ssam sin->sin_port = IPSEC_PORT_ANY; 314105197Ssam sin->sin_addr = ip->ip_dst; 315105197Ssam } 316105197Ssam } else { 317105197Ssam struct sockaddr_in6 *sin6; 318105197Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 319105197Ssam 320105197Ssam if (saidx->src.sin6.sin6_len == 0) { 321105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->src; 322105197Ssam sin6->sin6_len = sizeof(*sin6); 323105197Ssam sin6->sin6_family = AF_INET6; 324105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 325105197Ssam sin6->sin6_addr = ip6->ip6_src; 326105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 327105197Ssam /* fix scope id for comparing SPD */ 328105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 329105197Ssam sin6->sin6_scope_id = 330105197Ssam ntohs(ip6->ip6_src.s6_addr16[1]); 331105197Ssam } 332105197Ssam } 333105197Ssam if (saidx->dst.sin6.sin6_len == 0) { 334105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->dst; 335105197Ssam sin6->sin6_len = sizeof(*sin6); 336105197Ssam sin6->sin6_family = AF_INET6; 337105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 338105197Ssam sin6->sin6_addr = ip6->ip6_dst; 339105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 340105197Ssam /* fix scope id for comparing SPD */ 341105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 342105197Ssam sin6->sin6_scope_id = 343105197Ssam ntohs(ip6->ip6_dst.s6_addr16[1]); 344105197Ssam } 345105197Ssam } 346105197Ssam } 347105197Ssam } 348105197Ssam 349105197Ssam /* 350105197Ssam * Lookup SA and validate it. 351105197Ssam */ 352105197Ssam *error = key_checkrequest(isr, saidx); 353105197Ssam if (*error != 0) { 354105197Ssam /* 355105197Ssam * IPsec processing is required, but no SA found. 356105197Ssam * I assume that key_acquire() had been called 357105197Ssam * to get/establish the SA. Here I discard 358105197Ssam * this packet because it is responsibility for 359105197Ssam * upper layer to retransmit the packet. 360105197Ssam */ 361274434Sae switch(af) { 362274434Sae case AF_INET: 363274434Sae IPSECSTAT_INC(ips_out_nosa); 364274434Sae break; 365274434Sae#ifdef INET6 366274434Sae case AF_INET6: 367274434Sae IPSEC6STAT_INC(ips_out_nosa); 368274434Sae break; 369274434Sae#endif 370274434Sae } 371105197Ssam goto bad; 372105197Ssam } 373105197Ssam sav = isr->sav; 374177175Sbz if (sav == NULL) { 375120585Ssam IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 376120585Ssam ("no SA found, but required; level %u", 377105197Ssam ipsec_get_reqlevel(isr))); 378120585Ssam IPSECREQUEST_UNLOCK(isr); 379105197Ssam isr = isr->next; 380177175Sbz /* 381177175Sbz * If isr is NULL, we found a 'use' policy w/o SA. 382177175Sbz * Return w/o error and w/o isr so we can drop out 383177175Sbz * and continue w/o IPsec processing. 384177175Sbz */ 385177175Sbz if (isr == NULL) 386105197Ssam return isr; 387120585Ssam IPSECREQUEST_LOCK(isr); 388105197Ssam goto again; 389105197Ssam } 390105197Ssam 391105197Ssam /* 392105197Ssam * Check system global policy controls. 393105197Ssam */ 394181803Sbz if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 395181803Sbz (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 396181803Sbz (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 397120585Ssam DPRINTF(("%s: IPsec outbound packet dropped due" 398120585Ssam " to policy (check your sysctls)\n", __func__)); 399252028Sae IPSEC_OSTAT(pdrops); 400105197Ssam *error = EHOSTUNREACH; 401105197Ssam goto bad; 402105197Ssam } 403105197Ssam 404105197Ssam /* 405105197Ssam * Sanity check the SA contents for the caller 406105197Ssam * before they invoke the xform output method. 407105197Ssam */ 408105197Ssam if (sav->tdb_xform == NULL) { 409120585Ssam DPRINTF(("%s: no transform for SA\n", __func__)); 410252028Sae IPSEC_OSTAT(noxform); 411105197Ssam *error = EHOSTUNREACH; 412105197Ssam goto bad; 413105197Ssam } 414105197Ssam return isr; 415105197Ssambad: 416120585Ssam IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 417120585Ssam IPSECREQUEST_UNLOCK(isr); 418105197Ssam return NULL; 419105197Ssam#undef IPSEC_OSTAT 420105197Ssam} 421105197Ssam 422105197Ssam#ifdef INET 423105197Ssam/* 424105197Ssam * IPsec output logic for IPv4. 425105197Ssam */ 426105197Ssamint 427105197Ssamipsec4_process_packet( 428105197Ssam struct mbuf *m, 429105197Ssam struct ipsecrequest *isr, 430105197Ssam int flags, 431105197Ssam int tunalready) 432105197Ssam{ 433105197Ssam struct secasindex saidx; 434105197Ssam struct secasvar *sav; 435105197Ssam struct ip *ip; 436119643Ssam int error, i, off; 437105197Ssam 438120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 439120585Ssam IPSEC_ASSERT(isr != NULL, ("null isr")); 440105197Ssam 441120585Ssam IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 442105197Ssam 443105197Ssam isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 444177175Sbz if (isr == NULL) { 445177175Sbz if (error != 0) 446177175Sbz goto bad; 447177175Sbz return EJUSTRETURN; 448177175Sbz } 449105197Ssam 450105197Ssam sav = isr->sav; 451159965Sthompsa 452159965Sthompsa#ifdef DEV_ENC 453271862Sglebius if_inc_counter(encif, IFCOUNTER_OPACKETS, 1); 454271862Sglebius if_inc_counter(encif, IFCOUNTER_OBYTES, m->m_pkthdr.len); 455181627Svanhu 456174054Sbz /* pass the mbuf to enc0 for bpf processing */ 457174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 458159965Sthompsa /* pass the mbuf to enc0 for packet filtering */ 459174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 460159965Sthompsa goto bad; 461159965Sthompsa#endif 462159965Sthompsa 463105197Ssam if (!tunalready) { 464105197Ssam union sockaddr_union *dst = &sav->sah->saidx.dst; 465105197Ssam int setdf; 466105197Ssam 467105197Ssam /* 468105197Ssam * Collect IP_DF state from the outer header. 469105197Ssam */ 470105197Ssam if (dst->sa.sa_family == AF_INET) { 471105197Ssam if (m->m_len < sizeof (struct ip) && 472105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 473105197Ssam error = ENOBUFS; 474105197Ssam goto bad; 475105197Ssam } 476105197Ssam ip = mtod(m, struct ip *); 477105197Ssam /* Honor system-wide control of how to handle IP_DF */ 478181803Sbz switch (V_ip4_ipsec_dfbit) { 479105197Ssam case 0: /* clear in outer header */ 480105197Ssam case 1: /* set in outer header */ 481181803Sbz setdf = V_ip4_ipsec_dfbit; 482105197Ssam break; 483105197Ssam default: /* propagate to outer header */ 484105197Ssam setdf = ntohs(ip->ip_off & IP_DF); 485105197Ssam break; 486105197Ssam } 487105197Ssam } else { 488105197Ssam ip = NULL; /* keep compiler happy */ 489105197Ssam setdf = 0; 490105197Ssam } 491105197Ssam /* Do the appropriate encapsulation, if necessary */ 492105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 493105197Ssam dst->sa.sa_family != AF_INET || /* PF mismatch */ 494105197Ssam#if 0 495105197Ssam (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ 496105197Ssam sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ 497105197Ssam#endif 498105197Ssam (dst->sa.sa_family == AF_INET && /* Proxy */ 499105197Ssam dst->sin.sin_addr.s_addr != INADDR_ANY && 500105197Ssam dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 501105197Ssam struct mbuf *mp; 502105197Ssam 503105197Ssam /* Fix IPv4 header checksum and length */ 504105197Ssam if (m->m_len < sizeof (struct ip) && 505105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 506105197Ssam error = ENOBUFS; 507105197Ssam goto bad; 508105197Ssam } 509105197Ssam ip = mtod(m, struct ip *); 510268083Szec if (ip->ip_v == IPVERSION) { 511268083Szec ip->ip_len = htons(m->m_pkthdr.len); 512268083Szec ip->ip_sum = 0; 513268083Szec ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 514268083Szec } 515105197Ssam 516105197Ssam /* Encapsulate the packet */ 517105197Ssam error = ipip_output(m, isr, &mp, 0, 0); 518105197Ssam if (mp == NULL && !error) { 519105197Ssam /* Should never happen. */ 520120585Ssam DPRINTF(("%s: ipip_output returns no mbuf and " 521120585Ssam "no error!", __func__)); 522105197Ssam error = EFAULT; 523105197Ssam } 524105197Ssam if (error) { 525124765Ssam if (mp) { 526124765Ssam /* XXX: Should never happen! */ 527105197Ssam m_freem(mp); 528124765Ssam } 529124765Ssam m = NULL; /* ipip_output() already freed it */ 530105197Ssam goto bad; 531105197Ssam } 532105197Ssam m = mp, mp = NULL; 533105197Ssam /* 534105197Ssam * ipip_output clears IP_DF in the new header. If 535105197Ssam * we need to propagate IP_DF from the outer header, 536105197Ssam * then we have to do it here. 537105197Ssam * 538105197Ssam * XXX shouldn't assume what ipip_output does. 539105197Ssam */ 540105197Ssam if (dst->sa.sa_family == AF_INET && setdf) { 541105197Ssam if (m->m_len < sizeof (struct ip) && 542105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 543105197Ssam error = ENOBUFS; 544105197Ssam goto bad; 545105197Ssam } 546105197Ssam ip = mtod(m, struct ip *); 547105197Ssam ip->ip_off = ntohs(ip->ip_off); 548105197Ssam ip->ip_off |= IP_DF; 549105197Ssam ip->ip_off = htons(ip->ip_off); 550105197Ssam } 551105197Ssam } 552105197Ssam } 553105197Ssam 554159965Sthompsa#ifdef DEV_ENC 555159965Sthompsa /* pass the mbuf to enc0 for bpf processing */ 556266800Svanhu ipsec_bpf(m, sav, sav->sah->saidx.dst.sa.sa_family, ENC_OUT|ENC_AFTER); 557174054Sbz /* pass the mbuf to enc0 for packet filtering */ 558174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 559174054Sbz goto bad; 560159965Sthompsa#endif 561159965Sthompsa 562105197Ssam /* 563105197Ssam * Dispatch to the appropriate IPsec transform logic. The 564105197Ssam * packet will be returned for transmission after crypto 565105197Ssam * processing, etc. are completed. For encapsulation we 566105197Ssam * bypass this call because of the explicit call done above 567105197Ssam * (necessary to deal with IP_DF handling for IPv4). 568105197Ssam * 569105197Ssam * NB: m & sav are ``passed to caller'' who's reponsible for 570105197Ssam * for reclaiming their resources. 571105197Ssam */ 572105197Ssam if (sav->tdb_xform->xf_type != XF_IP4) { 573266800Svanhu union sockaddr_union *dst = &sav->sah->saidx.dst; 574266800Svanhu switch(dst->sa.sa_family) { 575266800Svanhu case AF_INET: 576266800Svanhu ip = mtod(m, struct ip *); 577266800Svanhu i = ip->ip_hl << 2; 578266800Svanhu off = offsetof(struct ip, ip_p); 579266800Svanhu break; 580266800Svanhu#ifdef INET6 581266800Svanhu case AF_INET6: 582266800Svanhu i = sizeof(struct ip6_hdr); 583266800Svanhu off = offsetof(struct ip6_hdr, ip6_nxt); 584266800Svanhu break; 585266800Svanhu#endif /* INET6 */ 586266800Svanhu default: 587266800Svanhu DPRINTF(("%s: unsupported protocol family %u\n", 588266800Svanhu __func__, dst->sa.sa_family)); 589266800Svanhu error = EPFNOSUPPORT; 590266822Sbz IPSECSTAT_INC(ips_out_inval); 591266800Svanhu goto bad; 592266800Svanhu } 593105197Ssam error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 594105197Ssam } else { 595105197Ssam error = ipsec_process_done(m, isr); 596105197Ssam } 597120585Ssam IPSECREQUEST_UNLOCK(isr); 598105197Ssam return error; 599105197Ssambad: 600120585Ssam if (isr) 601120585Ssam IPSECREQUEST_UNLOCK(isr); 602105197Ssam if (m) 603105197Ssam m_freem(m); 604105197Ssam return error; 605105197Ssam} 606105197Ssam#endif 607105197Ssam 608266800Svanhu 609105197Ssam#ifdef INET6 610105197Ssamstatic int 611266800Svanhuin6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia) 612105197Ssam{ 613266800Svanhu struct in6_addr ia2; 614105197Ssam 615266800Svanhu memcpy(&ia2, &sa->sin6_addr, sizeof(ia2)); 616266800Svanhu if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) 617266800Svanhu ia2.s6_addr16[1] = htons(sa->sin6_scope_id); 618105197Ssam 619266800Svanhu return IN6_ARE_ADDR_EQUAL(ia, &ia2); 620105197Ssam} 621105197Ssam 622105197Ssam/* 623266800Svanhu * IPsec output logic for IPv6. 624105197Ssam */ 625105197Ssamint 626266800Svanhuipsec6_process_packet( 627266800Svanhu struct mbuf *m, 628266800Svanhu struct ipsecrequest *isr 629266800Svanhu ) 630105197Ssam{ 631266800Svanhu struct secasindex saidx; 632266800Svanhu struct secasvar *sav; 633105197Ssam struct ip6_hdr *ip6; 634266800Svanhu int error, i, off; 635266800Svanhu union sockaddr_union *dst; 636105197Ssam 637266800Svanhu IPSEC_ASSERT(m != NULL, ("ipsec6_process_packet: null mbuf")); 638266800Svanhu IPSEC_ASSERT(isr != NULL, ("ipsec6_process_packet: null isr")); 639105197Ssam 640266800Svanhu IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 641105197Ssam 642105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 643177175Sbz if (isr == NULL) { 644177175Sbz if (error != 0) 645177175Sbz goto bad; 646266800Svanhu return EJUSTRETURN; 647177175Sbz } 648105197Ssam 649266800Svanhu sav = isr->sav; 650266800Svanhu dst = &sav->sah->saidx.dst; 651266800Svanhu 652274454Sae ip6 = mtod(m, struct ip6_hdr *); 653274454Sae ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); 654174054Sbz#ifdef DEV_ENC 655271862Sglebius if_inc_counter(encif, IFCOUNTER_OPACKETS, 1); 656271862Sglebius if_inc_counter(encif, IFCOUNTER_OBYTES, m->m_pkthdr.len); 657181627Svanhu 658174054Sbz /* pass the mbuf to enc0 for bpf processing */ 659174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 660174054Sbz /* pass the mbuf to enc0 for packet filtering */ 661174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 662174054Sbz goto bad; 663266800Svanhu#endif /* DEV_ENC */ 664174054Sbz 665266800Svanhu /* Do the appropriate encapsulation, if necessary */ 666266800Svanhu if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 667266800Svanhu dst->sa.sa_family != AF_INET6 || /* PF mismatch */ 668266800Svanhu ((dst->sa.sa_family == AF_INET6) && 669266800Svanhu (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) && 670266800Svanhu (!in6_sa_equal_addrwithscope(&dst->sin6, 671266800Svanhu &ip6->ip6_dst)))) { 672266800Svanhu struct mbuf *mp; 673266800Svanhu 674266800Svanhu /* Fix IPv6 header payload length. */ 675266800Svanhu if (m->m_len < sizeof(struct ip6_hdr)) 676266800Svanhu if ((m = m_pullup(m,sizeof(struct ip6_hdr))) == NULL) { 677266800Svanhu error = ENOBUFS; 678266800Svanhu goto bad; 679266800Svanhu } 680266800Svanhu 681266800Svanhu if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { 682266800Svanhu /* No jumbogram support. */ 683266800Svanhu error = ENXIO; /*XXX*/ 684105197Ssam goto bad; 685105197Ssam } 686105197Ssam 687266800Svanhu /* Encapsulate the packet */ 688266800Svanhu error = ipip_output(m, isr, &mp, 0, 0); 689266800Svanhu if (mp == NULL && !error) { 690266800Svanhu /* Should never happen. */ 691266800Svanhu DPRINTF(("ipsec6_process_packet: ipip_output " 692266800Svanhu "returns no mbuf and no error!")); 693266800Svanhu error = EFAULT; 694105197Ssam goto bad; 695105197Ssam } 696266800Svanhu 697105197Ssam if (error) { 698266800Svanhu if (mp) { 699266800Svanhu /* XXX: Should never happen! */ 700266800Svanhu m_freem(mp); 701266800Svanhu } 702266800Svanhu m = NULL; /* ipip_output() already freed it */ 703105197Ssam goto bad; 704105197Ssam } 705105197Ssam 706266800Svanhu m = mp; 707266800Svanhu mp = NULL; 708105197Ssam } 709105197Ssam 710174054Sbz#ifdef DEV_ENC 711266800Svanhu ipsec_bpf(m, isr->sav, dst->sa.sa_family, ENC_OUT|ENC_AFTER); 712174054Sbz /* pass the mbuf to enc0 for packet filtering */ 713174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 714174054Sbz goto bad; 715266800Svanhu#endif /* DEV_ENC */ 716174054Sbz 717266800Svanhu switch(dst->sa.sa_family) { 718266800Svanhu#ifdef INET 719266800Svanhu case AF_INET: 720266800Svanhu { 721266800Svanhu struct ip *ip; 722266800Svanhu ip = mtod(m, struct ip *); 723266800Svanhu i = ip->ip_hl << 2; 724266800Svanhu off = offsetof(struct ip, ip_p); 725266800Svanhu } 726266800Svanhu break; 727266800Svanhu#endif /* AF_INET */ 728266800Svanhu case AF_INET6: 729266800Svanhu i = sizeof(struct ip6_hdr); 730266800Svanhu off = offsetof(struct ip6_hdr, ip6_nxt); 731266800Svanhu break; 732266800Svanhu default: 733266800Svanhu DPRINTF(("%s: unsupported protocol family %u\n", 734266800Svanhu __func__, dst->sa.sa_family)); 735266800Svanhu error = EPFNOSUPPORT; 736266800Svanhu IPSEC6STAT_INC(ips_out_inval); 737266800Svanhu goto bad; 738266800Svanhu } 739266800Svanhu error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 740170123Sbz IPSECREQUEST_UNLOCK(isr); 741170123Sbz return error; 742105197Ssambad: 743266800Svanhu 744170123Sbz if (isr) 745170123Sbz IPSECREQUEST_UNLOCK(isr); 746105197Ssam if (m) 747105197Ssam m_freem(m); 748105197Ssam return error; 749105197Ssam} 750266822Sbz#endif /*INET6*/ 751