ipsec_output.c revision 282132
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 282132 2015-04-28 09:29:28Z 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> 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> 64281692Sae#include <netinet6/scope6_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")); 107282046Sae IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp")); 108105197Ssam sav = isr->sav; 109120585Ssam IPSEC_ASSERT(sav != NULL, ("null SA")); 110120585Ssam IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); 111105197Ssam 112105197Ssam saidx = &sav->sah->saidx; 113105197Ssam switch (saidx->dst.sa.sa_family) { 114105197Ssam#ifdef INET 115105197Ssam case AF_INET: 116105197Ssam /* Fix the header length, for AH processing. */ 117105197Ssam mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 118105197Ssam break; 119105197Ssam#endif /* INET */ 120105197Ssam#ifdef INET6 121105197Ssam case AF_INET6: 122105197Ssam /* Fix the header length, for AH processing. */ 123105197Ssam if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 124105197Ssam error = ENXIO; 125105197Ssam goto bad; 126105197Ssam } 127105197Ssam if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 128105197Ssam /* No jumbogram support. */ 129105197Ssam error = ENXIO; /*?*/ 130105197Ssam goto bad; 131105197Ssam } 132105197Ssam mtod(m, struct ip6_hdr *)->ip6_plen = 133105197Ssam htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 134105197Ssam break; 135105197Ssam#endif /* INET6 */ 136105197Ssam default: 137120585Ssam DPRINTF(("%s: unknown protocol family %u\n", __func__, 138105197Ssam saidx->dst.sa.sa_family)); 139105197Ssam error = ENXIO; 140105197Ssam goto bad; 141105197Ssam } 142105197Ssam 143105197Ssam /* 144105197Ssam * Add a record of what we've done or what needs to be done to the 145105197Ssam * packet. 146105197Ssam */ 147105197Ssam mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 148105197Ssam sizeof(struct tdb_ident), M_NOWAIT); 149105197Ssam if (mtag == NULL) { 150120585Ssam DPRINTF(("%s: could not get packet tag\n", __func__)); 151105197Ssam error = ENOMEM; 152105197Ssam goto bad; 153105197Ssam } 154105197Ssam 155105197Ssam tdbi = (struct tdb_ident *)(mtag + 1); 156105197Ssam tdbi->dst = saidx->dst; 157105197Ssam tdbi->proto = saidx->proto; 158105197Ssam tdbi->spi = sav->spi; 159105197Ssam m_tag_prepend(m, mtag); 160105197Ssam 161105197Ssam /* 162105197Ssam * If there's another (bundled) SA to apply, do so. 163105197Ssam * Note that this puts a burden on the kernel stack size. 164105197Ssam * If this is a problem we'll need to introduce a queue 165105197Ssam * to set the packet on so we can unwind the stack before 166105197Ssam * doing further processing. 167282046Sae * 168282046Sae * If ipsec[46]_process_packet() will successfully queue 169282046Sae * the request, we need to take additional reference to SP, 170282046Sae * because xform callback will release reference. 171105197Ssam */ 172105197Ssam if (isr->next) { 173238700Sbz /* XXX-BZ currently only support same AF bundles. */ 174221129Sbz switch (saidx->dst.sa.sa_family) { 175221129Sbz#ifdef INET 176221129Sbz case AF_INET: 177274467Sae IPSECSTAT_INC(ips_out_bundlesa); 178282046Sae key_addref(isr->sp); 179282046Sae error = ipsec4_process_packet(m, isr->next); 180282046Sae if (error != 0) 181282046Sae KEY_FREESP(&isr->sp); 182282046Sae return (error); 183221129Sbz /* NOTREACHED */ 184221129Sbz#endif 185221129Sbz#ifdef notyet 186221129Sbz#ifdef INET6 187221129Sbz case AF_INET6: 188221129Sbz /* XXX */ 189274467Sae IPSEC6STAT_INC(ips_out_bundlesa); 190282046Sae key_addref(isr->sp); 191282046Sae error = ipsec6_process_packet(m, isr->next); 192282046Sae if (error != 0) 193282046Sae KEY_FREESP(&isr->sp); 194282046Sae return (error); 195221129Sbz /* NOTREACHED */ 196221129Sbz#endif /* INET6 */ 197221129Sbz#endif 198221129Sbz default: 199221129Sbz DPRINTF(("%s: unknown protocol family %u\n", __func__, 200221129Sbz saidx->dst.sa.sa_family)); 201221129Sbz error = ENXIO; 202221129Sbz goto bad; 203221129Sbz } 204105197Ssam } 205117056Ssam key_sa_recordxfer(sav, m); /* record data transfer */ 206105197Ssam 207105197Ssam /* 208105197Ssam * We're done with IPsec processing, transmit the packet using the 209105197Ssam * appropriate network protocol (IP or IPv6). SPD lookup will be 210105197Ssam * performed again there. 211105197Ssam */ 212105197Ssam switch (saidx->dst.sa.sa_family) { 213105197Ssam#ifdef INET 214105197Ssam case AF_INET: 215194062Svanhu#ifdef IPSEC_NAT_T 216194062Svanhu /* 217194062Svanhu * If NAT-T is enabled, now that all IPsec processing is done 218194062Svanhu * insert UDP encapsulation header after IP header. 219194062Svanhu */ 220194062Svanhu if (sav->natt_type) { 221241919Sglebius struct ip *ip = mtod(m, struct ip *); 222194062Svanhu const int hlen = (ip->ip_hl << 2); 223194062Svanhu int size, off; 224194062Svanhu struct mbuf *mi; 225194062Svanhu struct udphdr *udp; 226194062Svanhu 227194062Svanhu size = sizeof(struct udphdr); 228194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 229194062Svanhu /* 230194062Svanhu * draft-ietf-ipsec-nat-t-ike-0[01].txt and 231194062Svanhu * draft-ietf-ipsec-udp-encaps-(00/)01.txt, 232194062Svanhu * ignoring possible AH mode 233194062Svanhu * non-IKE marker + non-ESP marker 234194062Svanhu * from draft-ietf-ipsec-udp-encaps-00.txt. 235194062Svanhu */ 236194062Svanhu size += sizeof(u_int64_t); 237194062Svanhu } 238194062Svanhu mi = m_makespace(m, hlen, size, &off); 239194062Svanhu if (mi == NULL) { 240194062Svanhu DPRINTF(("%s: m_makespace for udphdr failed\n", 241194062Svanhu __func__)); 242194062Svanhu error = ENOBUFS; 243194062Svanhu goto bad; 244194062Svanhu } 245194062Svanhu 246194062Svanhu udp = (struct udphdr *)(mtod(mi, caddr_t) + off); 247194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 248194062Svanhu udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); 249194062Svanhu else 250194062Svanhu udp->uh_sport = 251194062Svanhu KEY_PORTFROMSADDR(&sav->sah->saidx.src); 252194062Svanhu udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); 253194062Svanhu udp->uh_sum = 0; 254194062Svanhu udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 255241919Sglebius ip->ip_len = htons(m->m_pkthdr.len); 256194062Svanhu ip->ip_p = IPPROTO_UDP; 257194062Svanhu 258194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 259194062Svanhu *(u_int64_t *)(udp + 1) = 0; 260194062Svanhu } 261194062Svanhu#endif /* IPSEC_NAT_T */ 262194062Svanhu 263105197Ssam return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 264105197Ssam#endif /* INET */ 265105197Ssam#ifdef INET6 266105197Ssam case AF_INET6: 267105197Ssam /* 268105197Ssam * We don't need massage, IPv6 header fields are always in 269105197Ssam * net endian. 270105197Ssam */ 271105197Ssam return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 272105197Ssam#endif /* INET6 */ 273105197Ssam } 274105197Ssam panic("ipsec_process_done"); 275105197Ssambad: 276105197Ssam m_freem(m); 277105197Ssam return (error); 278105197Ssam} 279105197Ssam 280105197Ssamstatic struct ipsecrequest * 281105197Ssamipsec_nextisr( 282105197Ssam struct mbuf *m, 283105197Ssam struct ipsecrequest *isr, 284105197Ssam int af, 285105197Ssam struct secasindex *saidx, 286105197Ssam int *error 287105197Ssam) 288105197Ssam{ 289252028Sae#define IPSEC_OSTAT(name) do { \ 290252028Sae if (isr->saidx.proto == IPPROTO_ESP) \ 291252028Sae ESPSTAT_INC(esps_##name); \ 292252028Sae else if (isr->saidx.proto == IPPROTO_AH)\ 293252028Sae AHSTAT_INC(ahs_##name); \ 294252028Sae else \ 295252028Sae IPCOMPSTAT_INC(ipcomps_##name); \ 296252028Sae} while (0) 297105197Ssam struct secasvar *sav; 298105197Ssam 299120585Ssam IPSECREQUEST_LOCK_ASSERT(isr); 300120585Ssam 301120585Ssam IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 302120585Ssam ("invalid address family %u", af)); 303105197Ssamagain: 304105197Ssam /* 305105197Ssam * Craft SA index to search for proper SA. Note that 306105197Ssam * we only fillin unspecified SA peers for transport 307105197Ssam * mode; for tunnel mode they must already be filled in. 308105197Ssam */ 309105197Ssam *saidx = isr->saidx; 310105197Ssam if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 311105197Ssam /* Fillin unspecified SA peers only for transport mode */ 312105197Ssam if (af == AF_INET) { 313105197Ssam struct sockaddr_in *sin; 314105197Ssam struct ip *ip = mtod(m, struct ip *); 315105197Ssam 316105197Ssam if (saidx->src.sa.sa_len == 0) { 317105197Ssam sin = &saidx->src.sin; 318105197Ssam sin->sin_len = sizeof(*sin); 319105197Ssam sin->sin_family = AF_INET; 320105197Ssam sin->sin_port = IPSEC_PORT_ANY; 321105197Ssam sin->sin_addr = ip->ip_src; 322105197Ssam } 323105197Ssam if (saidx->dst.sa.sa_len == 0) { 324105197Ssam sin = &saidx->dst.sin; 325105197Ssam sin->sin_len = sizeof(*sin); 326105197Ssam sin->sin_family = AF_INET; 327105197Ssam sin->sin_port = IPSEC_PORT_ANY; 328105197Ssam sin->sin_addr = ip->ip_dst; 329105197Ssam } 330105197Ssam } else { 331105197Ssam struct sockaddr_in6 *sin6; 332105197Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 333105197Ssam 334105197Ssam if (saidx->src.sin6.sin6_len == 0) { 335105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->src; 336105197Ssam sin6->sin6_len = sizeof(*sin6); 337105197Ssam sin6->sin6_family = AF_INET6; 338105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 339105197Ssam sin6->sin6_addr = ip6->ip6_src; 340105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 341105197Ssam /* fix scope id for comparing SPD */ 342105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 343105197Ssam sin6->sin6_scope_id = 344105197Ssam ntohs(ip6->ip6_src.s6_addr16[1]); 345105197Ssam } 346105197Ssam } 347105197Ssam if (saidx->dst.sin6.sin6_len == 0) { 348105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->dst; 349105197Ssam sin6->sin6_len = sizeof(*sin6); 350105197Ssam sin6->sin6_family = AF_INET6; 351105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 352105197Ssam sin6->sin6_addr = ip6->ip6_dst; 353105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 354105197Ssam /* fix scope id for comparing SPD */ 355105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 356105197Ssam sin6->sin6_scope_id = 357105197Ssam ntohs(ip6->ip6_dst.s6_addr16[1]); 358105197Ssam } 359105197Ssam } 360105197Ssam } 361105197Ssam } 362105197Ssam 363105197Ssam /* 364105197Ssam * Lookup SA and validate it. 365105197Ssam */ 366105197Ssam *error = key_checkrequest(isr, saidx); 367105197Ssam if (*error != 0) { 368105197Ssam /* 369105197Ssam * IPsec processing is required, but no SA found. 370105197Ssam * I assume that key_acquire() had been called 371105197Ssam * to get/establish the SA. Here I discard 372105197Ssam * this packet because it is responsibility for 373105197Ssam * upper layer to retransmit the packet. 374105197Ssam */ 375274434Sae switch(af) { 376274434Sae case AF_INET: 377274434Sae IPSECSTAT_INC(ips_out_nosa); 378274434Sae break; 379274434Sae#ifdef INET6 380274434Sae case AF_INET6: 381274434Sae IPSEC6STAT_INC(ips_out_nosa); 382274434Sae break; 383274434Sae#endif 384274434Sae } 385105197Ssam goto bad; 386105197Ssam } 387105197Ssam sav = isr->sav; 388177175Sbz if (sav == NULL) { 389120585Ssam IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 390120585Ssam ("no SA found, but required; level %u", 391105197Ssam ipsec_get_reqlevel(isr))); 392120585Ssam IPSECREQUEST_UNLOCK(isr); 393105197Ssam isr = isr->next; 394177175Sbz /* 395177175Sbz * If isr is NULL, we found a 'use' policy w/o SA. 396177175Sbz * Return w/o error and w/o isr so we can drop out 397177175Sbz * and continue w/o IPsec processing. 398177175Sbz */ 399177175Sbz if (isr == NULL) 400105197Ssam return isr; 401120585Ssam IPSECREQUEST_LOCK(isr); 402105197Ssam goto again; 403105197Ssam } 404105197Ssam 405105197Ssam /* 406105197Ssam * Check system global policy controls. 407105197Ssam */ 408181803Sbz if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 409181803Sbz (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 410181803Sbz (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 411120585Ssam DPRINTF(("%s: IPsec outbound packet dropped due" 412120585Ssam " to policy (check your sysctls)\n", __func__)); 413252028Sae IPSEC_OSTAT(pdrops); 414105197Ssam *error = EHOSTUNREACH; 415105197Ssam goto bad; 416105197Ssam } 417105197Ssam 418105197Ssam /* 419105197Ssam * Sanity check the SA contents for the caller 420105197Ssam * before they invoke the xform output method. 421105197Ssam */ 422105197Ssam if (sav->tdb_xform == NULL) { 423120585Ssam DPRINTF(("%s: no transform for SA\n", __func__)); 424252028Sae IPSEC_OSTAT(noxform); 425105197Ssam *error = EHOSTUNREACH; 426105197Ssam goto bad; 427105197Ssam } 428105197Ssam return isr; 429105197Ssambad: 430120585Ssam IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 431120585Ssam IPSECREQUEST_UNLOCK(isr); 432105197Ssam return NULL; 433105197Ssam#undef IPSEC_OSTAT 434105197Ssam} 435105197Ssam 436281692Saestatic int 437281692Saeipsec_encap(struct mbuf **mp, struct secasindex *saidx) 438281692Sae{ 439281692Sae#ifdef INET6 440281692Sae struct ip6_hdr *ip6; 441281692Sae#endif 442281692Sae struct ip *ip; 443281692Sae int setdf; 444281692Sae uint8_t itos, proto; 445281692Sae 446281692Sae ip = mtod(*mp, struct ip *); 447281692Sae switch (ip->ip_v) { 448105197Ssam#ifdef INET 449281692Sae case IPVERSION: 450281692Sae proto = IPPROTO_IPIP; 451281692Sae /* 452281692Sae * Collect IP_DF state from the inner header 453281692Sae * and honor system-wide control of how to handle it. 454281692Sae */ 455281692Sae switch (V_ip4_ipsec_dfbit) { 456281692Sae case 0: /* clear in outer header */ 457281692Sae case 1: /* set in outer header */ 458281692Sae setdf = V_ip4_ipsec_dfbit; 459281692Sae break; 460281692Sae default:/* propagate to outer header */ 461281692Sae setdf = (ip->ip_off & ntohs(IP_DF)) != 0; 462281692Sae } 463281692Sae itos = ip->ip_tos; 464281692Sae break; 465281692Sae#endif 466281692Sae#ifdef INET6 467281692Sae case (IPV6_VERSION >> 4): 468281692Sae proto = IPPROTO_IPV6; 469281692Sae ip6 = mtod(*mp, struct ip6_hdr *); 470281692Sae itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 471281692Sae setdf = V_ip4_ipsec_dfbit ? 1: 0; 472281692Sae /* scoped address handling */ 473281692Sae in6_clearscope(&ip6->ip6_src); 474281692Sae in6_clearscope(&ip6->ip6_dst); 475281692Sae break; 476281692Sae#endif 477281692Sae default: 478281692Sae return (EAFNOSUPPORT); 479281692Sae } 480281692Sae switch (saidx->dst.sa.sa_family) { 481281692Sae#ifdef INET 482281692Sae case AF_INET: 483281692Sae if (saidx->src.sa.sa_family != AF_INET || 484281692Sae saidx->src.sin.sin_addr.s_addr == INADDR_ANY || 485281692Sae saidx->dst.sin.sin_addr.s_addr == INADDR_ANY) 486281692Sae return (EINVAL); 487281692Sae M_PREPEND(*mp, sizeof(struct ip), M_NOWAIT); 488281692Sae if (*mp == NULL) 489281692Sae return (ENOBUFS); 490281692Sae ip = mtod(*mp, struct ip *); 491281692Sae ip->ip_v = IPVERSION; 492281692Sae ip->ip_hl = sizeof(struct ip) >> 2; 493281692Sae ip->ip_p = proto; 494281692Sae ip->ip_len = htons((*mp)->m_pkthdr.len); 495281692Sae ip->ip_ttl = V_ip_defttl; 496281692Sae ip->ip_sum = 0; 497281692Sae ip->ip_off = setdf ? htons(IP_DF): 0; 498281692Sae ip->ip_src = saidx->src.sin.sin_addr; 499281692Sae ip->ip_dst = saidx->dst.sin.sin_addr; 500281692Sae ip_ecn_ingress(V_ip4_ipsec_ecn, &ip->ip_tos, &itos); 501281692Sae ip_fillid(ip); 502281692Sae break; 503281692Sae#endif /* INET */ 504281692Sae#ifdef INET6 505281692Sae case AF_INET6: 506281692Sae if (saidx->src.sa.sa_family != AF_INET6 || 507281692Sae IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr) || 508281692Sae IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr)) 509281692Sae return (EINVAL); 510281692Sae M_PREPEND(*mp, sizeof(struct ip6_hdr), M_NOWAIT); 511281692Sae if (*mp == NULL) 512281692Sae return (ENOBUFS); 513281692Sae ip6 = mtod(*mp, struct ip6_hdr *); 514281692Sae ip6->ip6_flow = 0; 515281692Sae ip6->ip6_vfc = IPV6_VERSION; 516281692Sae ip6->ip6_hlim = V_ip6_defhlim; 517281692Sae ip6->ip6_nxt = proto; 518281692Sae ip6->ip6_dst = saidx->dst.sin6.sin6_addr; 519281693Sae /* For link-local address embed scope zone id */ 520281693Sae if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) 521281693Sae ip6->ip6_dst.s6_addr16[1] = 522281693Sae htons(saidx->dst.sin6.sin6_scope_id & 0xffff); 523281692Sae ip6->ip6_src = saidx->src.sin6.sin6_addr; 524281693Sae if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) 525281693Sae ip6->ip6_src.s6_addr16[1] = 526281693Sae htons(saidx->src.sin6.sin6_scope_id & 0xffff); 527281692Sae ip6->ip6_plen = htons((*mp)->m_pkthdr.len - sizeof(*ip6)); 528281692Sae ip_ecn_ingress(V_ip6_ipsec_ecn, &proto, &itos); 529281692Sae ip6->ip6_flow |= htonl((uint32_t)proto << 20); 530281692Sae break; 531281692Sae#endif /* INET6 */ 532281692Sae default: 533281692Sae return (EAFNOSUPPORT); 534281692Sae } 535281692Sae return (0); 536281692Sae} 537281692Sae 538281692Sae#ifdef INET 539105197Ssam/* 540105197Ssam * IPsec output logic for IPv4. 541105197Ssam */ 542105197Ssamint 543275708Saeipsec4_process_packet(struct mbuf *m, struct ipsecrequest *isr) 544105197Ssam{ 545281695Sae char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN]; 546275708Sae union sockaddr_union *dst; 547105197Ssam struct secasindex saidx; 548105197Ssam struct secasvar *sav; 549105197Ssam struct ip *ip; 550281692Sae int error, i, off; 551105197Ssam 552120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 553120585Ssam IPSEC_ASSERT(isr != NULL, ("null isr")); 554105197Ssam 555120585Ssam IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 556105197Ssam 557105197Ssam isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 558177175Sbz if (isr == NULL) { 559177175Sbz if (error != 0) 560177175Sbz goto bad; 561177175Sbz return EJUSTRETURN; 562177175Sbz } 563105197Ssam 564105197Ssam sav = isr->sav; 565275708Sae if (m->m_len < sizeof(struct ip) && 566275708Sae (m = m_pullup(m, sizeof (struct ip))) == NULL) { 567275708Sae error = ENOBUFS; 568275708Sae goto bad; 569275708Sae } 570275708Sae ip = mtod(m, struct ip *); 571275708Sae dst = &sav->sah->saidx.dst; 572159965Sthompsa#ifdef DEV_ENC 573271862Sglebius if_inc_counter(encif, IFCOUNTER_OPACKETS, 1); 574271862Sglebius if_inc_counter(encif, IFCOUNTER_OBYTES, m->m_pkthdr.len); 575181627Svanhu 576174054Sbz /* pass the mbuf to enc0 for bpf processing */ 577174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 578159965Sthompsa /* pass the mbuf to enc0 for packet filtering */ 579174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 580159965Sthompsa goto bad; 581282132Sae ip = mtod(m, struct ip *); 582159965Sthompsa#endif 583275708Sae /* Do the appropriate encapsulation, if necessary */ 584275708Sae if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 585275708Sae dst->sa.sa_family != AF_INET || /* PF mismatch */ 586275708Sae (dst->sa.sa_family == AF_INET && /* Proxy */ 587275708Sae dst->sin.sin_addr.s_addr != INADDR_ANY && 588275708Sae dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 589275708Sae /* Fix IPv4 header checksum and length */ 590275708Sae ip->ip_len = htons(m->m_pkthdr.len); 591275708Sae ip->ip_sum = 0; 592275708Sae ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 593281692Sae error = ipsec_encap(&m, &sav->sah->saidx); 594275708Sae if (error != 0) { 595281692Sae DPRINTF(("%s: encapsulation for SA %s->%s " 596281692Sae "SPI 0x%08x failed with error %d\n", __func__, 597281695Sae ipsec_address(&sav->sah->saidx.src, sbuf, 598281695Sae sizeof(sbuf)), 599281695Sae ipsec_address(&sav->sah->saidx.dst, dbuf, 600281695Sae sizeof(dbuf)), ntohl(sav->spi), error)); 601275708Sae goto bad; 602275708Sae } 603105197Ssam } 604159965Sthompsa#ifdef DEV_ENC 605159965Sthompsa /* pass the mbuf to enc0 for bpf processing */ 606266800Svanhu ipsec_bpf(m, sav, sav->sah->saidx.dst.sa.sa_family, ENC_OUT|ENC_AFTER); 607174054Sbz /* pass the mbuf to enc0 for packet filtering */ 608174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 609174054Sbz goto bad; 610159965Sthompsa#endif 611159965Sthompsa 612105197Ssam /* 613105197Ssam * Dispatch to the appropriate IPsec transform logic. The 614105197Ssam * packet will be returned for transmission after crypto 615281692Sae * processing, etc. are completed. 616105197Ssam * 617105197Ssam * NB: m & sav are ``passed to caller'' who's reponsible for 618105197Ssam * for reclaiming their resources. 619105197Ssam */ 620281692Sae switch(dst->sa.sa_family) { 621281692Sae case AF_INET: 622281692Sae ip = mtod(m, struct ip *); 623281692Sae i = ip->ip_hl << 2; 624281692Sae off = offsetof(struct ip, ip_p); 625281692Sae break; 626266800Svanhu#ifdef INET6 627281692Sae case AF_INET6: 628281692Sae i = sizeof(struct ip6_hdr); 629281692Sae off = offsetof(struct ip6_hdr, ip6_nxt); 630281692Sae break; 631266800Svanhu#endif /* INET6 */ 632281692Sae default: 633266800Svanhu DPRINTF(("%s: unsupported protocol family %u\n", 634281692Sae __func__, dst->sa.sa_family)); 635281692Sae error = EPFNOSUPPORT; 636281692Sae IPSECSTAT_INC(ips_out_inval); 637281692Sae goto bad; 638105197Ssam } 639281692Sae error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 640120585Ssam IPSECREQUEST_UNLOCK(isr); 641281692Sae return (error); 642105197Ssambad: 643120585Ssam if (isr) 644120585Ssam IPSECREQUEST_UNLOCK(isr); 645105197Ssam if (m) 646105197Ssam m_freem(m); 647105197Ssam return error; 648105197Ssam} 649105197Ssam#endif 650105197Ssam 651266800Svanhu 652105197Ssam#ifdef INET6 653105197Ssamstatic int 654266800Svanhuin6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia) 655105197Ssam{ 656266800Svanhu struct in6_addr ia2; 657105197Ssam 658266800Svanhu memcpy(&ia2, &sa->sin6_addr, sizeof(ia2)); 659266800Svanhu if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) 660266800Svanhu ia2.s6_addr16[1] = htons(sa->sin6_scope_id); 661105197Ssam 662266800Svanhu return IN6_ARE_ADDR_EQUAL(ia, &ia2); 663105197Ssam} 664105197Ssam 665105197Ssam/* 666266800Svanhu * IPsec output logic for IPv6. 667105197Ssam */ 668105197Ssamint 669281695Saeipsec6_process_packet(struct mbuf *m, struct ipsecrequest *isr) 670105197Ssam{ 671281695Sae char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN]; 672266800Svanhu struct secasindex saidx; 673266800Svanhu struct secasvar *sav; 674105197Ssam struct ip6_hdr *ip6; 675266800Svanhu int error, i, off; 676266800Svanhu union sockaddr_union *dst; 677105197Ssam 678266800Svanhu IPSEC_ASSERT(m != NULL, ("ipsec6_process_packet: null mbuf")); 679266800Svanhu IPSEC_ASSERT(isr != NULL, ("ipsec6_process_packet: null isr")); 680105197Ssam 681266800Svanhu IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 682105197Ssam 683105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 684177175Sbz if (isr == NULL) { 685177175Sbz if (error != 0) 686177175Sbz goto bad; 687266800Svanhu return EJUSTRETURN; 688177175Sbz } 689266800Svanhu sav = isr->sav; 690266800Svanhu dst = &sav->sah->saidx.dst; 691266800Svanhu 692274454Sae ip6 = mtod(m, struct ip6_hdr *); 693274454Sae ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); 694174054Sbz#ifdef DEV_ENC 695271862Sglebius if_inc_counter(encif, IFCOUNTER_OPACKETS, 1); 696271862Sglebius if_inc_counter(encif, IFCOUNTER_OBYTES, m->m_pkthdr.len); 697181627Svanhu 698174054Sbz /* pass the mbuf to enc0 for bpf processing */ 699174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 700174054Sbz /* pass the mbuf to enc0 for packet filtering */ 701174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 702174054Sbz goto bad; 703282132Sae ip6 = mtod(m, struct ip6_hdr *); 704266800Svanhu#endif /* DEV_ENC */ 705174054Sbz 706266800Svanhu /* Do the appropriate encapsulation, if necessary */ 707266800Svanhu if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 708266800Svanhu dst->sa.sa_family != AF_INET6 || /* PF mismatch */ 709266800Svanhu ((dst->sa.sa_family == AF_INET6) && 710266800Svanhu (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) && 711266800Svanhu (!in6_sa_equal_addrwithscope(&dst->sin6, 712266800Svanhu &ip6->ip6_dst)))) { 713266800Svanhu if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { 714266800Svanhu /* No jumbogram support. */ 715266800Svanhu error = ENXIO; /*XXX*/ 716105197Ssam goto bad; 717105197Ssam } 718281692Sae error = ipsec_encap(&m, &sav->sah->saidx); 719281692Sae if (error != 0) { 720281692Sae DPRINTF(("%s: encapsulation for SA %s->%s " 721281692Sae "SPI 0x%08x failed with error %d\n", __func__, 722281695Sae ipsec_address(&sav->sah->saidx.src, sbuf, 723281695Sae sizeof(sbuf)), 724281695Sae ipsec_address(&sav->sah->saidx.dst, dbuf, 725281695Sae sizeof(dbuf)), ntohl(sav->spi), error)); 726105197Ssam goto bad; 727105197Ssam } 728105197Ssam } 729105197Ssam 730174054Sbz#ifdef DEV_ENC 731266800Svanhu ipsec_bpf(m, isr->sav, dst->sa.sa_family, ENC_OUT|ENC_AFTER); 732174054Sbz /* pass the mbuf to enc0 for packet filtering */ 733174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 734174054Sbz goto bad; 735266800Svanhu#endif /* DEV_ENC */ 736174054Sbz 737266800Svanhu switch(dst->sa.sa_family) { 738266800Svanhu#ifdef INET 739266800Svanhu case AF_INET: 740266800Svanhu { 741266800Svanhu struct ip *ip; 742266800Svanhu ip = mtod(m, struct ip *); 743266800Svanhu i = ip->ip_hl << 2; 744266800Svanhu off = offsetof(struct ip, ip_p); 745266800Svanhu } 746266800Svanhu break; 747266800Svanhu#endif /* AF_INET */ 748266800Svanhu case AF_INET6: 749266800Svanhu i = sizeof(struct ip6_hdr); 750266800Svanhu off = offsetof(struct ip6_hdr, ip6_nxt); 751266800Svanhu break; 752266800Svanhu default: 753266800Svanhu DPRINTF(("%s: unsupported protocol family %u\n", 754266800Svanhu __func__, dst->sa.sa_family)); 755266800Svanhu error = EPFNOSUPPORT; 756266800Svanhu IPSEC6STAT_INC(ips_out_inval); 757266800Svanhu goto bad; 758266800Svanhu } 759266800Svanhu error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 760170123Sbz IPSECREQUEST_UNLOCK(isr); 761170123Sbz return error; 762105197Ssambad: 763266800Svanhu 764170123Sbz if (isr) 765170123Sbz IPSECREQUEST_UNLOCK(isr); 766105197Ssam if (m) 767105197Ssam m_freem(m); 768105197Ssam return error; 769105197Ssam} 770266822Sbz#endif /*INET6*/ 771