ipsec_output.c revision 195699
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 195699 2009-07-14 22:48:30Z rwatson $ 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> 45181803Sbz#include <sys/vimage.h> 46105197Ssam 47105197Ssam#include <net/if.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) { 168181803Sbz V_ipsec4stat.ips_out_bundlesa++; 169105197Ssam return ipsec4_process_packet(m, isr->next, 0, 0); 170105197Ssam } 171117056Ssam key_sa_recordxfer(sav, m); /* record data transfer */ 172105197Ssam 173105197Ssam /* 174105197Ssam * We're done with IPsec processing, transmit the packet using the 175105197Ssam * appropriate network protocol (IP or IPv6). SPD lookup will be 176105197Ssam * performed again there. 177105197Ssam */ 178105197Ssam switch (saidx->dst.sa.sa_family) { 179105197Ssam#ifdef INET 180105197Ssam struct ip *ip; 181105197Ssam case AF_INET: 182105197Ssam ip = mtod(m, struct ip *); 183105197Ssam ip->ip_len = ntohs(ip->ip_len); 184105197Ssam ip->ip_off = ntohs(ip->ip_off); 185105197Ssam 186194062Svanhu#ifdef IPSEC_NAT_T 187194062Svanhu /* 188194062Svanhu * If NAT-T is enabled, now that all IPsec processing is done 189194062Svanhu * insert UDP encapsulation header after IP header. 190194062Svanhu */ 191194062Svanhu if (sav->natt_type) { 192194062Svanhu#ifdef _IP_VHL 193194062Svanhu const int hlen = IP_VHL_HL(ip->ip_vhl); 194194062Svanhu#else 195194062Svanhu const int hlen = (ip->ip_hl << 2); 196194062Svanhu#endif 197194062Svanhu int size, off; 198194062Svanhu struct mbuf *mi; 199194062Svanhu struct udphdr *udp; 200194062Svanhu 201194062Svanhu size = sizeof(struct udphdr); 202194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 203194062Svanhu /* 204194062Svanhu * draft-ietf-ipsec-nat-t-ike-0[01].txt and 205194062Svanhu * draft-ietf-ipsec-udp-encaps-(00/)01.txt, 206194062Svanhu * ignoring possible AH mode 207194062Svanhu * non-IKE marker + non-ESP marker 208194062Svanhu * from draft-ietf-ipsec-udp-encaps-00.txt. 209194062Svanhu */ 210194062Svanhu size += sizeof(u_int64_t); 211194062Svanhu } 212194062Svanhu mi = m_makespace(m, hlen, size, &off); 213194062Svanhu if (mi == NULL) { 214194062Svanhu DPRINTF(("%s: m_makespace for udphdr failed\n", 215194062Svanhu __func__)); 216194062Svanhu error = ENOBUFS; 217194062Svanhu goto bad; 218194062Svanhu } 219194062Svanhu 220194062Svanhu udp = (struct udphdr *)(mtod(mi, caddr_t) + off); 221194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 222194062Svanhu udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); 223194062Svanhu else 224194062Svanhu udp->uh_sport = 225194062Svanhu KEY_PORTFROMSADDR(&sav->sah->saidx.src); 226194062Svanhu udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); 227194062Svanhu udp->uh_sum = 0; 228194062Svanhu udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 229194062Svanhu ip->ip_len = m->m_pkthdr.len; 230194062Svanhu ip->ip_p = IPPROTO_UDP; 231194062Svanhu 232194062Svanhu if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 233194062Svanhu *(u_int64_t *)(udp + 1) = 0; 234194062Svanhu } 235194062Svanhu#endif /* IPSEC_NAT_T */ 236194062Svanhu 237105197Ssam return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 238105197Ssam#endif /* INET */ 239105197Ssam#ifdef INET6 240105197Ssam case AF_INET6: 241105197Ssam /* 242105197Ssam * We don't need massage, IPv6 header fields are always in 243105197Ssam * net endian. 244105197Ssam */ 245105197Ssam return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 246105197Ssam#endif /* INET6 */ 247105197Ssam } 248105197Ssam panic("ipsec_process_done"); 249105197Ssambad: 250105197Ssam m_freem(m); 251105197Ssam KEY_FREESAV(&sav); 252105197Ssam return (error); 253105197Ssam} 254105197Ssam 255105197Ssamstatic struct ipsecrequest * 256105197Ssamipsec_nextisr( 257105197Ssam struct mbuf *m, 258105197Ssam struct ipsecrequest *isr, 259105197Ssam int af, 260105197Ssam struct secasindex *saidx, 261105197Ssam int *error 262105197Ssam) 263105197Ssam{ 264105197Ssam#define IPSEC_OSTAT(x,y,z) (isr->saidx.proto == IPPROTO_ESP ? (x)++ : \ 265105197Ssam isr->saidx.proto == IPPROTO_AH ? (y)++ : (z)++) 266105197Ssam struct secasvar *sav; 267105197Ssam 268120585Ssam IPSECREQUEST_LOCK_ASSERT(isr); 269120585Ssam 270120585Ssam IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 271120585Ssam ("invalid address family %u", af)); 272105197Ssamagain: 273105197Ssam /* 274105197Ssam * Craft SA index to search for proper SA. Note that 275105197Ssam * we only fillin unspecified SA peers for transport 276105197Ssam * mode; for tunnel mode they must already be filled in. 277105197Ssam */ 278105197Ssam *saidx = isr->saidx; 279105197Ssam if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 280105197Ssam /* Fillin unspecified SA peers only for transport mode */ 281105197Ssam if (af == AF_INET) { 282105197Ssam struct sockaddr_in *sin; 283105197Ssam struct ip *ip = mtod(m, struct ip *); 284105197Ssam 285105197Ssam if (saidx->src.sa.sa_len == 0) { 286105197Ssam sin = &saidx->src.sin; 287105197Ssam sin->sin_len = sizeof(*sin); 288105197Ssam sin->sin_family = AF_INET; 289105197Ssam sin->sin_port = IPSEC_PORT_ANY; 290105197Ssam sin->sin_addr = ip->ip_src; 291105197Ssam } 292105197Ssam if (saidx->dst.sa.sa_len == 0) { 293105197Ssam sin = &saidx->dst.sin; 294105197Ssam sin->sin_len = sizeof(*sin); 295105197Ssam sin->sin_family = AF_INET; 296105197Ssam sin->sin_port = IPSEC_PORT_ANY; 297105197Ssam sin->sin_addr = ip->ip_dst; 298105197Ssam } 299105197Ssam } else { 300105197Ssam struct sockaddr_in6 *sin6; 301105197Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 302105197Ssam 303105197Ssam if (saidx->src.sin6.sin6_len == 0) { 304105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->src; 305105197Ssam sin6->sin6_len = sizeof(*sin6); 306105197Ssam sin6->sin6_family = AF_INET6; 307105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 308105197Ssam sin6->sin6_addr = ip6->ip6_src; 309105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 310105197Ssam /* fix scope id for comparing SPD */ 311105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 312105197Ssam sin6->sin6_scope_id = 313105197Ssam ntohs(ip6->ip6_src.s6_addr16[1]); 314105197Ssam } 315105197Ssam } 316105197Ssam if (saidx->dst.sin6.sin6_len == 0) { 317105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->dst; 318105197Ssam sin6->sin6_len = sizeof(*sin6); 319105197Ssam sin6->sin6_family = AF_INET6; 320105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 321105197Ssam sin6->sin6_addr = ip6->ip6_dst; 322105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 323105197Ssam /* fix scope id for comparing SPD */ 324105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 325105197Ssam sin6->sin6_scope_id = 326105197Ssam ntohs(ip6->ip6_dst.s6_addr16[1]); 327105197Ssam } 328105197Ssam } 329105197Ssam } 330105197Ssam } 331105197Ssam 332105197Ssam /* 333105197Ssam * Lookup SA and validate it. 334105197Ssam */ 335105197Ssam *error = key_checkrequest(isr, saidx); 336105197Ssam if (*error != 0) { 337105197Ssam /* 338105197Ssam * IPsec processing is required, but no SA found. 339105197Ssam * I assume that key_acquire() had been called 340105197Ssam * to get/establish the SA. Here I discard 341105197Ssam * this packet because it is responsibility for 342105197Ssam * upper layer to retransmit the packet. 343105197Ssam */ 344181803Sbz V_ipsec4stat.ips_out_nosa++; 345105197Ssam goto bad; 346105197Ssam } 347105197Ssam sav = isr->sav; 348177175Sbz if (sav == NULL) { 349120585Ssam IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 350120585Ssam ("no SA found, but required; level %u", 351105197Ssam ipsec_get_reqlevel(isr))); 352120585Ssam IPSECREQUEST_UNLOCK(isr); 353105197Ssam isr = isr->next; 354177175Sbz /* 355177175Sbz * If isr is NULL, we found a 'use' policy w/o SA. 356177175Sbz * Return w/o error and w/o isr so we can drop out 357177175Sbz * and continue w/o IPsec processing. 358177175Sbz */ 359177175Sbz if (isr == NULL) 360105197Ssam return isr; 361120585Ssam IPSECREQUEST_LOCK(isr); 362105197Ssam goto again; 363105197Ssam } 364105197Ssam 365105197Ssam /* 366105197Ssam * Check system global policy controls. 367105197Ssam */ 368181803Sbz if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 369181803Sbz (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 370181803Sbz (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 371120585Ssam DPRINTF(("%s: IPsec outbound packet dropped due" 372120585Ssam " to policy (check your sysctls)\n", __func__)); 373181803Sbz IPSEC_OSTAT(V_espstat.esps_pdrops, V_ahstat.ahs_pdrops, 374181803Sbz V_ipcompstat.ipcomps_pdrops); 375105197Ssam *error = EHOSTUNREACH; 376105197Ssam goto bad; 377105197Ssam } 378105197Ssam 379105197Ssam /* 380105197Ssam * Sanity check the SA contents for the caller 381105197Ssam * before they invoke the xform output method. 382105197Ssam */ 383105197Ssam if (sav->tdb_xform == NULL) { 384120585Ssam DPRINTF(("%s: no transform for SA\n", __func__)); 385181803Sbz IPSEC_OSTAT(V_espstat.esps_noxform, V_ahstat.ahs_noxform, 386181803Sbz V_ipcompstat.ipcomps_noxform); 387105197Ssam *error = EHOSTUNREACH; 388105197Ssam goto bad; 389105197Ssam } 390105197Ssam return isr; 391105197Ssambad: 392120585Ssam IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 393120585Ssam IPSECREQUEST_UNLOCK(isr); 394105197Ssam return NULL; 395105197Ssam#undef IPSEC_OSTAT 396105197Ssam} 397105197Ssam 398105197Ssam#ifdef INET 399105197Ssam/* 400105197Ssam * IPsec output logic for IPv4. 401105197Ssam */ 402105197Ssamint 403105197Ssamipsec4_process_packet( 404105197Ssam struct mbuf *m, 405105197Ssam struct ipsecrequest *isr, 406105197Ssam int flags, 407105197Ssam int tunalready) 408105197Ssam{ 409105197Ssam struct secasindex saidx; 410105197Ssam struct secasvar *sav; 411105197Ssam struct ip *ip; 412119643Ssam int error, i, off; 413105197Ssam 414120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 415120585Ssam IPSEC_ASSERT(isr != NULL, ("null isr")); 416105197Ssam 417120585Ssam IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 418105197Ssam 419105197Ssam isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 420177175Sbz if (isr == NULL) { 421177175Sbz if (error != 0) 422177175Sbz goto bad; 423177175Sbz return EJUSTRETURN; 424177175Sbz } 425105197Ssam 426105197Ssam sav = isr->sav; 427159965Sthompsa 428159965Sthompsa#ifdef DEV_ENC 429181627Svanhu encif->if_opackets++; 430181627Svanhu encif->if_obytes += m->m_pkthdr.len; 431181627Svanhu 432174054Sbz /* pass the mbuf to enc0 for bpf processing */ 433174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 434159965Sthompsa /* pass the mbuf to enc0 for packet filtering */ 435174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 436159965Sthompsa goto bad; 437159965Sthompsa#endif 438159965Sthompsa 439105197Ssam if (!tunalready) { 440105197Ssam union sockaddr_union *dst = &sav->sah->saidx.dst; 441105197Ssam int setdf; 442105197Ssam 443105197Ssam /* 444105197Ssam * Collect IP_DF state from the outer header. 445105197Ssam */ 446105197Ssam if (dst->sa.sa_family == AF_INET) { 447105197Ssam if (m->m_len < sizeof (struct ip) && 448105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 449105197Ssam error = ENOBUFS; 450105197Ssam goto bad; 451105197Ssam } 452105197Ssam ip = mtod(m, struct ip *); 453105197Ssam /* Honor system-wide control of how to handle IP_DF */ 454181803Sbz switch (V_ip4_ipsec_dfbit) { 455105197Ssam case 0: /* clear in outer header */ 456105197Ssam case 1: /* set in outer header */ 457181803Sbz setdf = V_ip4_ipsec_dfbit; 458105197Ssam break; 459105197Ssam default: /* propagate to outer header */ 460105197Ssam setdf = ntohs(ip->ip_off & IP_DF); 461105197Ssam break; 462105197Ssam } 463105197Ssam } else { 464105197Ssam ip = NULL; /* keep compiler happy */ 465105197Ssam setdf = 0; 466105197Ssam } 467105197Ssam /* Do the appropriate encapsulation, if necessary */ 468105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 469105197Ssam dst->sa.sa_family != AF_INET || /* PF mismatch */ 470105197Ssam#if 0 471105197Ssam (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ 472105197Ssam sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ 473105197Ssam#endif 474105197Ssam (dst->sa.sa_family == AF_INET && /* Proxy */ 475105197Ssam dst->sin.sin_addr.s_addr != INADDR_ANY && 476105197Ssam dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 477105197Ssam struct mbuf *mp; 478105197Ssam 479105197Ssam /* Fix IPv4 header checksum and length */ 480105197Ssam if (m->m_len < sizeof (struct ip) && 481105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 482105197Ssam error = ENOBUFS; 483105197Ssam goto bad; 484105197Ssam } 485105197Ssam ip = mtod(m, struct ip *); 486105197Ssam ip->ip_len = htons(m->m_pkthdr.len); 487105197Ssam ip->ip_sum = 0; 488105197Ssam#ifdef _IP_VHL 489105197Ssam if (ip->ip_vhl == IP_VHL_BORING) 490105197Ssam ip->ip_sum = in_cksum_hdr(ip); 491105197Ssam else 492105197Ssam ip->ip_sum = in_cksum(m, 493105197Ssam _IP_VHL_HL(ip->ip_vhl) << 2); 494105197Ssam#else 495105197Ssam ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 496105197Ssam#endif 497105197Ssam 498105197Ssam /* Encapsulate the packet */ 499105197Ssam error = ipip_output(m, isr, &mp, 0, 0); 500105197Ssam if (mp == NULL && !error) { 501105197Ssam /* Should never happen. */ 502120585Ssam DPRINTF(("%s: ipip_output returns no mbuf and " 503120585Ssam "no error!", __func__)); 504105197Ssam error = EFAULT; 505105197Ssam } 506105197Ssam if (error) { 507124765Ssam if (mp) { 508124765Ssam /* XXX: Should never happen! */ 509105197Ssam m_freem(mp); 510124765Ssam } 511124765Ssam m = NULL; /* ipip_output() already freed it */ 512105197Ssam goto bad; 513105197Ssam } 514105197Ssam m = mp, mp = NULL; 515105197Ssam /* 516105197Ssam * ipip_output clears IP_DF in the new header. If 517105197Ssam * we need to propagate IP_DF from the outer header, 518105197Ssam * then we have to do it here. 519105197Ssam * 520105197Ssam * XXX shouldn't assume what ipip_output does. 521105197Ssam */ 522105197Ssam if (dst->sa.sa_family == AF_INET && setdf) { 523105197Ssam if (m->m_len < sizeof (struct ip) && 524105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 525105197Ssam error = ENOBUFS; 526105197Ssam goto bad; 527105197Ssam } 528105197Ssam ip = mtod(m, struct ip *); 529105197Ssam ip->ip_off = ntohs(ip->ip_off); 530105197Ssam ip->ip_off |= IP_DF; 531105197Ssam ip->ip_off = htons(ip->ip_off); 532105197Ssam } 533105197Ssam } 534105197Ssam } 535105197Ssam 536159965Sthompsa#ifdef DEV_ENC 537159965Sthompsa /* pass the mbuf to enc0 for bpf processing */ 538174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_AFTER); 539174054Sbz /* pass the mbuf to enc0 for packet filtering */ 540174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 541174054Sbz goto bad; 542159965Sthompsa#endif 543159965Sthompsa 544105197Ssam /* 545105197Ssam * Dispatch to the appropriate IPsec transform logic. The 546105197Ssam * packet will be returned for transmission after crypto 547105197Ssam * processing, etc. are completed. For encapsulation we 548105197Ssam * bypass this call because of the explicit call done above 549105197Ssam * (necessary to deal with IP_DF handling for IPv4). 550105197Ssam * 551105197Ssam * NB: m & sav are ``passed to caller'' who's reponsible for 552105197Ssam * for reclaiming their resources. 553105197Ssam */ 554105197Ssam if (sav->tdb_xform->xf_type != XF_IP4) { 555105197Ssam ip = mtod(m, struct ip *); 556105197Ssam i = ip->ip_hl << 2; 557105197Ssam off = offsetof(struct ip, ip_p); 558105197Ssam error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 559105197Ssam } else { 560105197Ssam error = ipsec_process_done(m, isr); 561105197Ssam } 562120585Ssam IPSECREQUEST_UNLOCK(isr); 563105197Ssam return error; 564105197Ssambad: 565120585Ssam if (isr) 566120585Ssam IPSECREQUEST_UNLOCK(isr); 567105197Ssam if (m) 568105197Ssam m_freem(m); 569105197Ssam return error; 570105197Ssam} 571105197Ssam#endif 572105197Ssam 573105197Ssam#ifdef INET6 574105197Ssam/* 575105197Ssam * Chop IP6 header from the payload. 576105197Ssam */ 577105197Ssamstatic struct mbuf * 578105197Ssamipsec6_splithdr(struct mbuf *m) 579105197Ssam{ 580105197Ssam struct mbuf *mh; 581105197Ssam struct ip6_hdr *ip6; 582105197Ssam int hlen; 583105197Ssam 584120585Ssam IPSEC_ASSERT(m->m_len >= sizeof (struct ip6_hdr), 585120585Ssam ("first mbuf too short, len %u", m->m_len)); 586105197Ssam ip6 = mtod(m, struct ip6_hdr *); 587105197Ssam hlen = sizeof(struct ip6_hdr); 588105197Ssam if (m->m_len > hlen) { 589151967Sandre MGETHDR(mh, M_DONTWAIT, MT_DATA); 590105197Ssam if (!mh) { 591105197Ssam m_freem(m); 592105197Ssam return NULL; 593105197Ssam } 594108466Ssam M_MOVE_PKTHDR(mh, m); 595105197Ssam MH_ALIGN(mh, hlen); 596105197Ssam m->m_len -= hlen; 597105197Ssam m->m_data += hlen; 598105197Ssam mh->m_next = m; 599105197Ssam m = mh; 600105197Ssam m->m_len = hlen; 601105197Ssam bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen); 602105197Ssam } else if (m->m_len < hlen) { 603105197Ssam m = m_pullup(m, hlen); 604105197Ssam if (!m) 605105197Ssam return NULL; 606105197Ssam } 607105197Ssam return m; 608105197Ssam} 609105197Ssam 610105197Ssam/* 611105197Ssam * IPsec output logic for IPv6, transport mode. 612105197Ssam */ 613105197Ssamint 614105197Ssamipsec6_output_trans( 615105197Ssam struct ipsec_output_state *state, 616105197Ssam u_char *nexthdrp, 617105197Ssam struct mbuf *mprev, 618105197Ssam struct secpolicy *sp, 619105197Ssam int flags, 620105197Ssam int *tun) 621105197Ssam{ 622105197Ssam struct ipsecrequest *isr; 623105197Ssam struct secasindex saidx; 624105197Ssam int error = 0; 625105197Ssam struct mbuf *m; 626105197Ssam 627120585Ssam IPSEC_ASSERT(state != NULL, ("null state")); 628120585Ssam IPSEC_ASSERT(state->m != NULL, ("null m")); 629120585Ssam IPSEC_ASSERT(nexthdrp != NULL, ("null nexthdrp")); 630120585Ssam IPSEC_ASSERT(mprev != NULL, ("null mprev")); 631120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 632120585Ssam IPSEC_ASSERT(tun != NULL, ("null tun")); 633105197Ssam 634105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 635170122Sbz printf("%s: applied SP\n", __func__); 636105197Ssam kdebug_secpolicy(sp)); 637105197Ssam 638105197Ssam isr = sp->req; 639105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 640105197Ssam /* the rest will be handled by ipsec6_output_tunnel() */ 641105197Ssam *tun = 1; /* need tunnel-mode processing */ 642105197Ssam return 0; 643105197Ssam } 644105197Ssam 645105197Ssam *tun = 0; 646105197Ssam m = state->m; 647105197Ssam 648171133Sgnn IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 649105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 650105197Ssam if (isr == NULL) { 651177175Sbz if (error != 0) { 652105197Ssam#ifdef notdef 653177175Sbz /* XXX should notification be done for all errors ? */ 654177175Sbz /* 655177175Sbz * Notify the fact that the packet is discarded 656177175Sbz * to ourselves. I believe this is better than 657177175Sbz * just silently discarding. (jinmei@kame.net) 658177175Sbz * XXX: should we restrict the error to TCP packets? 659177175Sbz * XXX: should we directly notify sockets via 660177175Sbz * pfctlinputs? 661177175Sbz */ 662177175Sbz icmp6_error(m, ICMP6_DST_UNREACH, 663177175Sbz ICMP6_DST_UNREACH_ADMIN, 0); 664177175Sbz m = NULL; /* NB: icmp6_error frees mbuf */ 665105197Ssam#endif 666177175Sbz goto bad; 667177175Sbz } 668177175Sbz return EJUSTRETURN; 669105197Ssam } 670105197Ssam 671171133Sgnn error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 672171133Sgnn sizeof (struct ip6_hdr), 673171133Sgnn offsetof(struct ip6_hdr, 674171133Sgnn ip6_nxt)); 675171133Sgnn IPSECREQUEST_UNLOCK(isr); 676171133Sgnn return error; 677105197Ssambad: 678171133Sgnn if (isr) 679171133Sgnn IPSECREQUEST_UNLOCK(isr); 680105197Ssam if (m) 681105197Ssam m_freem(m); 682105197Ssam state->m = NULL; 683105197Ssam return error; 684105197Ssam} 685105197Ssam 686105197Ssamstatic int 687105197Ssamipsec6_encapsulate(struct mbuf *m, struct secasvar *sav) 688105197Ssam{ 689105197Ssam struct ip6_hdr *oip6; 690105197Ssam struct ip6_hdr *ip6; 691105197Ssam size_t plen; 692105197Ssam 693105197Ssam /* can't tunnel between different AFs */ 694105197Ssam if (sav->sah->saidx.src.sa.sa_family != AF_INET6 || 695105197Ssam sav->sah->saidx.dst.sa.sa_family != AF_INET6) { 696105197Ssam m_freem(m); 697105197Ssam return EINVAL; 698105197Ssam } 699171133Sgnn IPSEC_ASSERT(m->m_len == sizeof (struct ip6_hdr), 700120585Ssam ("mbuf wrong size; len %u", m->m_len)); 701105197Ssam 702105197Ssam 703105197Ssam /* 704105197Ssam * grow the mbuf to accomodate the new IPv6 header. 705105197Ssam */ 706105197Ssam plen = m->m_pkthdr.len; 707105197Ssam if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) { 708105197Ssam struct mbuf *n; 709111119Simp MGET(n, M_DONTWAIT, MT_DATA); 710105197Ssam if (!n) { 711105197Ssam m_freem(m); 712105197Ssam return ENOBUFS; 713105197Ssam } 714105197Ssam n->m_len = sizeof(struct ip6_hdr); 715105197Ssam n->m_next = m->m_next; 716105197Ssam m->m_next = n; 717105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 718105197Ssam oip6 = mtod(n, struct ip6_hdr *); 719105197Ssam } else { 720105197Ssam m->m_next->m_len += sizeof(struct ip6_hdr); 721105197Ssam m->m_next->m_data -= sizeof(struct ip6_hdr); 722105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 723105197Ssam oip6 = mtod(m->m_next, struct ip6_hdr *); 724105197Ssam } 725105197Ssam ip6 = mtod(m, struct ip6_hdr *); 726113076Sdes bcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr)); 727105197Ssam 728105197Ssam /* Fake link-local scope-class addresses */ 729105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) 730105197Ssam oip6->ip6_src.s6_addr16[1] = 0; 731105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) 732105197Ssam oip6->ip6_dst.s6_addr16[1] = 0; 733105197Ssam 734105197Ssam /* construct new IPv6 header. see RFC 2401 5.1.2.2 */ 735105197Ssam /* ECN consideration. */ 736181803Sbz ip6_ecn_ingress(V_ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow); 737105197Ssam if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr)) 738105197Ssam ip6->ip6_plen = htons(plen); 739105197Ssam else { 740105197Ssam /* ip6->ip6_plen will be updated in ip6_output() */ 741105197Ssam } 742105197Ssam ip6->ip6_nxt = IPPROTO_IPV6; 743171133Sgnn ip6->ip6_src = sav->sah->saidx.src.sin6.sin6_addr; 744171133Sgnn ip6->ip6_dst = sav->sah->saidx.dst.sin6.sin6_addr; 745105197Ssam ip6->ip6_hlim = IPV6_DEFHLIM; 746105197Ssam 747105197Ssam /* XXX Should ip6_src be updated later ? */ 748105197Ssam 749105197Ssam return 0; 750105197Ssam} 751105197Ssam 752105197Ssam/* 753105197Ssam * IPsec output logic for IPv6, tunnel mode. 754105197Ssam */ 755105197Ssamint 756105197Ssamipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int flags) 757105197Ssam{ 758105197Ssam struct ip6_hdr *ip6; 759105197Ssam struct ipsecrequest *isr; 760105197Ssam struct secasindex saidx; 761105197Ssam int error; 762105197Ssam struct sockaddr_in6* dst6; 763105197Ssam struct mbuf *m; 764105197Ssam 765120585Ssam IPSEC_ASSERT(state != NULL, ("null state")); 766120585Ssam IPSEC_ASSERT(state->m != NULL, ("null m")); 767120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 768105197Ssam 769105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 770170122Sbz printf("%s: applied SP\n", __func__); 771105197Ssam kdebug_secpolicy(sp)); 772105197Ssam 773105197Ssam m = state->m; 774105197Ssam /* 775105197Ssam * transport mode ipsec (before the 1st tunnel mode) is already 776105197Ssam * processed by ipsec6_output_trans(). 777105197Ssam */ 778105197Ssam for (isr = sp->req; isr; isr = isr->next) { 779105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 780105197Ssam break; 781105197Ssam } 782170123Sbz 783170123Sbz IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 784105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 785177175Sbz if (isr == NULL) { 786177175Sbz if (error != 0) 787177175Sbz goto bad; 788177175Sbz return EJUSTRETURN; 789177175Sbz } 790105197Ssam 791174054Sbz#ifdef DEV_ENC 792181627Svanhu encif->if_opackets++; 793181627Svanhu encif->if_obytes += m->m_pkthdr.len; 794181627Svanhu 795174054Sbz /* pass the mbuf to enc0 for bpf processing */ 796174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 797174054Sbz /* pass the mbuf to enc0 for packet filtering */ 798174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 799174054Sbz goto bad; 800174054Sbz#endif 801174054Sbz 802105197Ssam /* 803105197Ssam * There may be the case that SA status will be changed when 804105197Ssam * we are refering to one. So calling splsoftnet(). 805105197Ssam */ 806105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 807105197Ssam /* 808105197Ssam * build IPsec tunnel. 809105197Ssam */ 810105197Ssam /* XXX should be processed with other familiy */ 811105197Ssam if (isr->sav->sah->saidx.src.sa.sa_family != AF_INET6) { 812120585Ssam ipseclog((LOG_ERR, "%s: family mismatched between " 813120585Ssam "inner and outer, spi=%u\n", __func__, 814105197Ssam ntohl(isr->sav->spi))); 815181803Sbz V_ipsec6stat.ips_out_inval++; 816105197Ssam error = EAFNOSUPPORT; 817105197Ssam goto bad; 818105197Ssam } 819105197Ssam 820105197Ssam m = ipsec6_splithdr(m); 821105197Ssam if (!m) { 822181803Sbz V_ipsec6stat.ips_out_nomem++; 823105197Ssam error = ENOMEM; 824105197Ssam goto bad; 825105197Ssam } 826105197Ssam error = ipsec6_encapsulate(m, isr->sav); 827105197Ssam if (error) { 828105197Ssam m = NULL; 829105197Ssam goto bad; 830105197Ssam } 831105197Ssam ip6 = mtod(m, struct ip6_hdr *); 832105197Ssam 833105197Ssam state->ro = &isr->sav->sah->sa_route; 834105197Ssam state->dst = (struct sockaddr *)&state->ro->ro_dst; 835105197Ssam dst6 = (struct sockaddr_in6 *)state->dst; 836105197Ssam if (state->ro->ro_rt 837105197Ssam && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 838105197Ssam || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { 839105197Ssam RTFREE(state->ro->ro_rt); 840105197Ssam state->ro->ro_rt = NULL; 841105197Ssam } 842187936Sbz if (state->ro->ro_rt == NULL) { 843105197Ssam bzero(dst6, sizeof(*dst6)); 844105197Ssam dst6->sin6_family = AF_INET6; 845105197Ssam dst6->sin6_len = sizeof(*dst6); 846105197Ssam dst6->sin6_addr = ip6->ip6_dst; 847105197Ssam rtalloc(state->ro); 848105197Ssam } 849187936Sbz if (state->ro->ro_rt == NULL) { 850181803Sbz V_ip6stat.ip6s_noroute++; 851181803Sbz V_ipsec6stat.ips_out_noroute++; 852105197Ssam error = EHOSTUNREACH; 853105197Ssam goto bad; 854105197Ssam } 855105197Ssam 856105197Ssam /* adjust state->dst if tunnel endpoint is offlink */ 857105197Ssam if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) { 858105197Ssam state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; 859105197Ssam dst6 = (struct sockaddr_in6 *)state->dst; 860105197Ssam } 861105197Ssam } 862105197Ssam 863105197Ssam m = ipsec6_splithdr(m); 864105197Ssam if (!m) { 865181803Sbz V_ipsec6stat.ips_out_nomem++; 866105197Ssam error = ENOMEM; 867105197Ssam goto bad; 868105197Ssam } 869105197Ssam ip6 = mtod(m, struct ip6_hdr *); 870174054Sbz 871174054Sbz#ifdef DEV_ENC 872174054Sbz /* pass the mbuf to enc0 for bpf processing */ 873174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_AFTER); 874174054Sbz /* pass the mbuf to enc0 for packet filtering */ 875174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 876174054Sbz goto bad; 877174054Sbz#endif 878174054Sbz 879170123Sbz error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 880105197Ssam sizeof (struct ip6_hdr), 881105197Ssam offsetof(struct ip6_hdr, ip6_nxt)); 882170123Sbz IPSECREQUEST_UNLOCK(isr); 883170123Sbz return error; 884105197Ssambad: 885170123Sbz if (isr) 886170123Sbz IPSECREQUEST_UNLOCK(isr); 887105197Ssam if (m) 888105197Ssam m_freem(m); 889105197Ssam state->m = NULL; 890105197Ssam return error; 891105197Ssam} 892105197Ssam#endif /*INET6*/ 893