ipsec_output.c revision 181803
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 181803 2008-08-17 23:27:27Z bz $ 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> 50105197Ssam 51105197Ssam#include <netinet/in.h> 52105197Ssam#include <netinet/in_systm.h> 53105197Ssam#include <netinet/ip.h> 54105197Ssam#include <netinet/ip_var.h> 55105197Ssam#include <netinet/in_var.h> 56105197Ssam#include <netinet/ip_ecn.h> 57105197Ssam#ifdef INET6 58105197Ssam#include <netinet6/ip6_ecn.h> 59105197Ssam#endif 60105197Ssam 61105197Ssam#include <netinet/ip6.h> 62105197Ssam#ifdef INET6 63105197Ssam#include <netinet6/ip6_var.h> 64105197Ssam#endif 65105197Ssam#include <netinet/in_pcb.h> 66105197Ssam#ifdef INET6 67105197Ssam#include <netinet/icmp6.h> 68105197Ssam#endif 69105197Ssam 70105197Ssam#include <netipsec/ipsec.h> 71105197Ssam#ifdef INET6 72105197Ssam#include <netipsec/ipsec6.h> 73105197Ssam#endif 74105197Ssam#include <netipsec/ah_var.h> 75105197Ssam#include <netipsec/esp_var.h> 76105197Ssam#include <netipsec/ipcomp_var.h> 77105197Ssam 78105197Ssam#include <netipsec/xform.h> 79105197Ssam 80105197Ssam#include <netipsec/key.h> 81105197Ssam#include <netipsec/keydb.h> 82105197Ssam#include <netipsec/key_debug.h> 83105197Ssam 84105197Ssam#include <machine/in_cksum.h> 85105197Ssam 86181627Svanhu#ifdef DEV_ENC 87181627Svanhu#include <net/if_enc.h> 88181627Svanhu#endif 89181627Svanhu 90181627Svanhu 91105197Ssamint 92105197Ssamipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) 93105197Ssam{ 94105197Ssam struct tdb_ident *tdbi; 95105197Ssam struct m_tag *mtag; 96105197Ssam struct secasvar *sav; 97105197Ssam struct secasindex *saidx; 98105197Ssam int error; 99105197Ssam 100120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 101120585Ssam IPSEC_ASSERT(isr != NULL, ("null ISR")); 102105197Ssam sav = isr->sav; 103120585Ssam IPSEC_ASSERT(sav != NULL, ("null SA")); 104120585Ssam IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); 105105197Ssam 106105197Ssam saidx = &sav->sah->saidx; 107105197Ssam switch (saidx->dst.sa.sa_family) { 108105197Ssam#ifdef INET 109105197Ssam case AF_INET: 110105197Ssam /* Fix the header length, for AH processing. */ 111105197Ssam mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 112105197Ssam break; 113105197Ssam#endif /* INET */ 114105197Ssam#ifdef INET6 115105197Ssam case AF_INET6: 116105197Ssam /* Fix the header length, for AH processing. */ 117105197Ssam if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 118105197Ssam error = ENXIO; 119105197Ssam goto bad; 120105197Ssam } 121105197Ssam if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 122105197Ssam /* No jumbogram support. */ 123105197Ssam error = ENXIO; /*?*/ 124105197Ssam goto bad; 125105197Ssam } 126105197Ssam mtod(m, struct ip6_hdr *)->ip6_plen = 127105197Ssam htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 128105197Ssam break; 129105197Ssam#endif /* INET6 */ 130105197Ssam default: 131120585Ssam DPRINTF(("%s: unknown protocol family %u\n", __func__, 132105197Ssam saidx->dst.sa.sa_family)); 133105197Ssam error = ENXIO; 134105197Ssam goto bad; 135105197Ssam } 136105197Ssam 137105197Ssam /* 138105197Ssam * Add a record of what we've done or what needs to be done to the 139105197Ssam * packet. 140105197Ssam */ 141105197Ssam mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 142105197Ssam sizeof(struct tdb_ident), M_NOWAIT); 143105197Ssam if (mtag == NULL) { 144120585Ssam DPRINTF(("%s: could not get packet tag\n", __func__)); 145105197Ssam error = ENOMEM; 146105197Ssam goto bad; 147105197Ssam } 148105197Ssam 149105197Ssam tdbi = (struct tdb_ident *)(mtag + 1); 150105197Ssam tdbi->dst = saidx->dst; 151105197Ssam tdbi->proto = saidx->proto; 152105197Ssam tdbi->spi = sav->spi; 153105197Ssam m_tag_prepend(m, mtag); 154105197Ssam 155105197Ssam /* 156105197Ssam * If there's another (bundled) SA to apply, do so. 157105197Ssam * Note that this puts a burden on the kernel stack size. 158105197Ssam * If this is a problem we'll need to introduce a queue 159105197Ssam * to set the packet on so we can unwind the stack before 160105197Ssam * doing further processing. 161105197Ssam */ 162105197Ssam if (isr->next) { 163181803Sbz V_ipsec4stat.ips_out_bundlesa++; 164105197Ssam return ipsec4_process_packet(m, isr->next, 0, 0); 165105197Ssam } 166117056Ssam key_sa_recordxfer(sav, m); /* record data transfer */ 167105197Ssam 168105197Ssam /* 169105197Ssam * We're done with IPsec processing, transmit the packet using the 170105197Ssam * appropriate network protocol (IP or IPv6). SPD lookup will be 171105197Ssam * performed again there. 172105197Ssam */ 173105197Ssam switch (saidx->dst.sa.sa_family) { 174105197Ssam#ifdef INET 175105197Ssam struct ip *ip; 176105197Ssam case AF_INET: 177105197Ssam ip = mtod(m, struct ip *); 178105197Ssam ip->ip_len = ntohs(ip->ip_len); 179105197Ssam ip->ip_off = ntohs(ip->ip_off); 180105197Ssam 181105197Ssam return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 182105197Ssam#endif /* INET */ 183105197Ssam#ifdef INET6 184105197Ssam case AF_INET6: 185105197Ssam /* 186105197Ssam * We don't need massage, IPv6 header fields are always in 187105197Ssam * net endian. 188105197Ssam */ 189105197Ssam return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 190105197Ssam#endif /* INET6 */ 191105197Ssam } 192105197Ssam panic("ipsec_process_done"); 193105197Ssambad: 194105197Ssam m_freem(m); 195105197Ssam KEY_FREESAV(&sav); 196105197Ssam return (error); 197105197Ssam} 198105197Ssam 199105197Ssamstatic struct ipsecrequest * 200105197Ssamipsec_nextisr( 201105197Ssam struct mbuf *m, 202105197Ssam struct ipsecrequest *isr, 203105197Ssam int af, 204105197Ssam struct secasindex *saidx, 205105197Ssam int *error 206105197Ssam) 207105197Ssam{ 208105197Ssam#define IPSEC_OSTAT(x,y,z) (isr->saidx.proto == IPPROTO_ESP ? (x)++ : \ 209105197Ssam isr->saidx.proto == IPPROTO_AH ? (y)++ : (z)++) 210105197Ssam struct secasvar *sav; 211105197Ssam 212120585Ssam IPSECREQUEST_LOCK_ASSERT(isr); 213120585Ssam 214120585Ssam IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 215120585Ssam ("invalid address family %u", af)); 216105197Ssamagain: 217105197Ssam /* 218105197Ssam * Craft SA index to search for proper SA. Note that 219105197Ssam * we only fillin unspecified SA peers for transport 220105197Ssam * mode; for tunnel mode they must already be filled in. 221105197Ssam */ 222105197Ssam *saidx = isr->saidx; 223105197Ssam if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 224105197Ssam /* Fillin unspecified SA peers only for transport mode */ 225105197Ssam if (af == AF_INET) { 226105197Ssam struct sockaddr_in *sin; 227105197Ssam struct ip *ip = mtod(m, struct ip *); 228105197Ssam 229105197Ssam if (saidx->src.sa.sa_len == 0) { 230105197Ssam sin = &saidx->src.sin; 231105197Ssam sin->sin_len = sizeof(*sin); 232105197Ssam sin->sin_family = AF_INET; 233105197Ssam sin->sin_port = IPSEC_PORT_ANY; 234105197Ssam sin->sin_addr = ip->ip_src; 235105197Ssam } 236105197Ssam if (saidx->dst.sa.sa_len == 0) { 237105197Ssam sin = &saidx->dst.sin; 238105197Ssam sin->sin_len = sizeof(*sin); 239105197Ssam sin->sin_family = AF_INET; 240105197Ssam sin->sin_port = IPSEC_PORT_ANY; 241105197Ssam sin->sin_addr = ip->ip_dst; 242105197Ssam } 243105197Ssam } else { 244105197Ssam struct sockaddr_in6 *sin6; 245105197Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 246105197Ssam 247105197Ssam if (saidx->src.sin6.sin6_len == 0) { 248105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->src; 249105197Ssam sin6->sin6_len = sizeof(*sin6); 250105197Ssam sin6->sin6_family = AF_INET6; 251105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 252105197Ssam sin6->sin6_addr = ip6->ip6_src; 253105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 254105197Ssam /* fix scope id for comparing SPD */ 255105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 256105197Ssam sin6->sin6_scope_id = 257105197Ssam ntohs(ip6->ip6_src.s6_addr16[1]); 258105197Ssam } 259105197Ssam } 260105197Ssam if (saidx->dst.sin6.sin6_len == 0) { 261105197Ssam sin6 = (struct sockaddr_in6 *)&saidx->dst; 262105197Ssam sin6->sin6_len = sizeof(*sin6); 263105197Ssam sin6->sin6_family = AF_INET6; 264105197Ssam sin6->sin6_port = IPSEC_PORT_ANY; 265105197Ssam sin6->sin6_addr = ip6->ip6_dst; 266105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 267105197Ssam /* fix scope id for comparing SPD */ 268105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 269105197Ssam sin6->sin6_scope_id = 270105197Ssam ntohs(ip6->ip6_dst.s6_addr16[1]); 271105197Ssam } 272105197Ssam } 273105197Ssam } 274105197Ssam } 275105197Ssam 276105197Ssam /* 277105197Ssam * Lookup SA and validate it. 278105197Ssam */ 279105197Ssam *error = key_checkrequest(isr, saidx); 280105197Ssam if (*error != 0) { 281105197Ssam /* 282105197Ssam * IPsec processing is required, but no SA found. 283105197Ssam * I assume that key_acquire() had been called 284105197Ssam * to get/establish the SA. Here I discard 285105197Ssam * this packet because it is responsibility for 286105197Ssam * upper layer to retransmit the packet. 287105197Ssam */ 288181803Sbz V_ipsec4stat.ips_out_nosa++; 289105197Ssam goto bad; 290105197Ssam } 291105197Ssam sav = isr->sav; 292177175Sbz if (sav == NULL) { 293120585Ssam IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 294120585Ssam ("no SA found, but required; level %u", 295105197Ssam ipsec_get_reqlevel(isr))); 296120585Ssam IPSECREQUEST_UNLOCK(isr); 297105197Ssam isr = isr->next; 298177175Sbz /* 299177175Sbz * If isr is NULL, we found a 'use' policy w/o SA. 300177175Sbz * Return w/o error and w/o isr so we can drop out 301177175Sbz * and continue w/o IPsec processing. 302177175Sbz */ 303177175Sbz if (isr == NULL) 304105197Ssam return isr; 305120585Ssam IPSECREQUEST_LOCK(isr); 306105197Ssam goto again; 307105197Ssam } 308105197Ssam 309105197Ssam /* 310105197Ssam * Check system global policy controls. 311105197Ssam */ 312181803Sbz if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 313181803Sbz (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 314181803Sbz (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 315120585Ssam DPRINTF(("%s: IPsec outbound packet dropped due" 316120585Ssam " to policy (check your sysctls)\n", __func__)); 317181803Sbz IPSEC_OSTAT(V_espstat.esps_pdrops, V_ahstat.ahs_pdrops, 318181803Sbz V_ipcompstat.ipcomps_pdrops); 319105197Ssam *error = EHOSTUNREACH; 320105197Ssam goto bad; 321105197Ssam } 322105197Ssam 323105197Ssam /* 324105197Ssam * Sanity check the SA contents for the caller 325105197Ssam * before they invoke the xform output method. 326105197Ssam */ 327105197Ssam if (sav->tdb_xform == NULL) { 328120585Ssam DPRINTF(("%s: no transform for SA\n", __func__)); 329181803Sbz IPSEC_OSTAT(V_espstat.esps_noxform, V_ahstat.ahs_noxform, 330181803Sbz V_ipcompstat.ipcomps_noxform); 331105197Ssam *error = EHOSTUNREACH; 332105197Ssam goto bad; 333105197Ssam } 334105197Ssam return isr; 335105197Ssambad: 336120585Ssam IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 337120585Ssam IPSECREQUEST_UNLOCK(isr); 338105197Ssam return NULL; 339105197Ssam#undef IPSEC_OSTAT 340105197Ssam} 341105197Ssam 342105197Ssam#ifdef INET 343105197Ssam/* 344105197Ssam * IPsec output logic for IPv4. 345105197Ssam */ 346105197Ssamint 347105197Ssamipsec4_process_packet( 348105197Ssam struct mbuf *m, 349105197Ssam struct ipsecrequest *isr, 350105197Ssam int flags, 351105197Ssam int tunalready) 352105197Ssam{ 353105197Ssam struct secasindex saidx; 354105197Ssam struct secasvar *sav; 355105197Ssam struct ip *ip; 356119643Ssam int error, i, off; 357105197Ssam 358120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 359120585Ssam IPSEC_ASSERT(isr != NULL, ("null isr")); 360105197Ssam 361120585Ssam IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 362105197Ssam 363105197Ssam isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 364177175Sbz if (isr == NULL) { 365177175Sbz if (error != 0) 366177175Sbz goto bad; 367177175Sbz return EJUSTRETURN; 368177175Sbz } 369105197Ssam 370105197Ssam sav = isr->sav; 371159965Sthompsa 372159965Sthompsa#ifdef DEV_ENC 373181627Svanhu encif->if_opackets++; 374181627Svanhu encif->if_obytes += m->m_pkthdr.len; 375181627Svanhu 376174054Sbz /* pass the mbuf to enc0 for bpf processing */ 377174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 378159965Sthompsa /* pass the mbuf to enc0 for packet filtering */ 379174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 380159965Sthompsa goto bad; 381159965Sthompsa#endif 382159965Sthompsa 383105197Ssam if (!tunalready) { 384105197Ssam union sockaddr_union *dst = &sav->sah->saidx.dst; 385105197Ssam int setdf; 386105197Ssam 387105197Ssam /* 388105197Ssam * Collect IP_DF state from the outer header. 389105197Ssam */ 390105197Ssam if (dst->sa.sa_family == AF_INET) { 391105197Ssam if (m->m_len < sizeof (struct ip) && 392105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 393105197Ssam error = ENOBUFS; 394105197Ssam goto bad; 395105197Ssam } 396105197Ssam ip = mtod(m, struct ip *); 397105197Ssam /* Honor system-wide control of how to handle IP_DF */ 398181803Sbz switch (V_ip4_ipsec_dfbit) { 399105197Ssam case 0: /* clear in outer header */ 400105197Ssam case 1: /* set in outer header */ 401181803Sbz setdf = V_ip4_ipsec_dfbit; 402105197Ssam break; 403105197Ssam default: /* propagate to outer header */ 404105197Ssam setdf = ntohs(ip->ip_off & IP_DF); 405105197Ssam break; 406105197Ssam } 407105197Ssam } else { 408105197Ssam ip = NULL; /* keep compiler happy */ 409105197Ssam setdf = 0; 410105197Ssam } 411105197Ssam /* Do the appropriate encapsulation, if necessary */ 412105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 413105197Ssam dst->sa.sa_family != AF_INET || /* PF mismatch */ 414105197Ssam#if 0 415105197Ssam (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ 416105197Ssam sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ 417105197Ssam#endif 418105197Ssam (dst->sa.sa_family == AF_INET && /* Proxy */ 419105197Ssam dst->sin.sin_addr.s_addr != INADDR_ANY && 420105197Ssam dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 421105197Ssam struct mbuf *mp; 422105197Ssam 423105197Ssam /* Fix IPv4 header checksum and length */ 424105197Ssam if (m->m_len < sizeof (struct ip) && 425105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 426105197Ssam error = ENOBUFS; 427105197Ssam goto bad; 428105197Ssam } 429105197Ssam ip = mtod(m, struct ip *); 430105197Ssam ip->ip_len = htons(m->m_pkthdr.len); 431105197Ssam ip->ip_sum = 0; 432105197Ssam#ifdef _IP_VHL 433105197Ssam if (ip->ip_vhl == IP_VHL_BORING) 434105197Ssam ip->ip_sum = in_cksum_hdr(ip); 435105197Ssam else 436105197Ssam ip->ip_sum = in_cksum(m, 437105197Ssam _IP_VHL_HL(ip->ip_vhl) << 2); 438105197Ssam#else 439105197Ssam ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 440105197Ssam#endif 441105197Ssam 442105197Ssam /* Encapsulate the packet */ 443105197Ssam error = ipip_output(m, isr, &mp, 0, 0); 444105197Ssam if (mp == NULL && !error) { 445105197Ssam /* Should never happen. */ 446120585Ssam DPRINTF(("%s: ipip_output returns no mbuf and " 447120585Ssam "no error!", __func__)); 448105197Ssam error = EFAULT; 449105197Ssam } 450105197Ssam if (error) { 451124765Ssam if (mp) { 452124765Ssam /* XXX: Should never happen! */ 453105197Ssam m_freem(mp); 454124765Ssam } 455124765Ssam m = NULL; /* ipip_output() already freed it */ 456105197Ssam goto bad; 457105197Ssam } 458105197Ssam m = mp, mp = NULL; 459105197Ssam /* 460105197Ssam * ipip_output clears IP_DF in the new header. If 461105197Ssam * we need to propagate IP_DF from the outer header, 462105197Ssam * then we have to do it here. 463105197Ssam * 464105197Ssam * XXX shouldn't assume what ipip_output does. 465105197Ssam */ 466105197Ssam if (dst->sa.sa_family == AF_INET && setdf) { 467105197Ssam if (m->m_len < sizeof (struct ip) && 468105197Ssam (m = m_pullup(m, sizeof (struct ip))) == NULL) { 469105197Ssam error = ENOBUFS; 470105197Ssam goto bad; 471105197Ssam } 472105197Ssam ip = mtod(m, struct ip *); 473105197Ssam ip->ip_off = ntohs(ip->ip_off); 474105197Ssam ip->ip_off |= IP_DF; 475105197Ssam ip->ip_off = htons(ip->ip_off); 476105197Ssam } 477105197Ssam } 478105197Ssam } 479105197Ssam 480159965Sthompsa#ifdef DEV_ENC 481159965Sthompsa /* pass the mbuf to enc0 for bpf processing */ 482174054Sbz ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_AFTER); 483174054Sbz /* pass the mbuf to enc0 for packet filtering */ 484174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 485174054Sbz goto bad; 486159965Sthompsa#endif 487159965Sthompsa 488105197Ssam /* 489105197Ssam * Dispatch to the appropriate IPsec transform logic. The 490105197Ssam * packet will be returned for transmission after crypto 491105197Ssam * processing, etc. are completed. For encapsulation we 492105197Ssam * bypass this call because of the explicit call done above 493105197Ssam * (necessary to deal with IP_DF handling for IPv4). 494105197Ssam * 495105197Ssam * NB: m & sav are ``passed to caller'' who's reponsible for 496105197Ssam * for reclaiming their resources. 497105197Ssam */ 498105197Ssam if (sav->tdb_xform->xf_type != XF_IP4) { 499105197Ssam ip = mtod(m, struct ip *); 500105197Ssam i = ip->ip_hl << 2; 501105197Ssam off = offsetof(struct ip, ip_p); 502105197Ssam error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 503105197Ssam } else { 504105197Ssam error = ipsec_process_done(m, isr); 505105197Ssam } 506120585Ssam IPSECREQUEST_UNLOCK(isr); 507105197Ssam return error; 508105197Ssambad: 509120585Ssam if (isr) 510120585Ssam IPSECREQUEST_UNLOCK(isr); 511105197Ssam if (m) 512105197Ssam m_freem(m); 513105197Ssam return error; 514105197Ssam} 515105197Ssam#endif 516105197Ssam 517105197Ssam#ifdef INET6 518105197Ssam/* 519105197Ssam * Chop IP6 header from the payload. 520105197Ssam */ 521105197Ssamstatic struct mbuf * 522105197Ssamipsec6_splithdr(struct mbuf *m) 523105197Ssam{ 524105197Ssam struct mbuf *mh; 525105197Ssam struct ip6_hdr *ip6; 526105197Ssam int hlen; 527105197Ssam 528120585Ssam IPSEC_ASSERT(m->m_len >= sizeof (struct ip6_hdr), 529120585Ssam ("first mbuf too short, len %u", m->m_len)); 530105197Ssam ip6 = mtod(m, struct ip6_hdr *); 531105197Ssam hlen = sizeof(struct ip6_hdr); 532105197Ssam if (m->m_len > hlen) { 533151967Sandre MGETHDR(mh, M_DONTWAIT, MT_DATA); 534105197Ssam if (!mh) { 535105197Ssam m_freem(m); 536105197Ssam return NULL; 537105197Ssam } 538108466Ssam M_MOVE_PKTHDR(mh, m); 539105197Ssam MH_ALIGN(mh, hlen); 540105197Ssam m->m_len -= hlen; 541105197Ssam m->m_data += hlen; 542105197Ssam mh->m_next = m; 543105197Ssam m = mh; 544105197Ssam m->m_len = hlen; 545105197Ssam bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen); 546105197Ssam } else if (m->m_len < hlen) { 547105197Ssam m = m_pullup(m, hlen); 548105197Ssam if (!m) 549105197Ssam return NULL; 550105197Ssam } 551105197Ssam return m; 552105197Ssam} 553105197Ssam 554105197Ssam/* 555105197Ssam * IPsec output logic for IPv6, transport mode. 556105197Ssam */ 557105197Ssamint 558105197Ssamipsec6_output_trans( 559105197Ssam struct ipsec_output_state *state, 560105197Ssam u_char *nexthdrp, 561105197Ssam struct mbuf *mprev, 562105197Ssam struct secpolicy *sp, 563105197Ssam int flags, 564105197Ssam int *tun) 565105197Ssam{ 566105197Ssam struct ipsecrequest *isr; 567105197Ssam struct secasindex saidx; 568105197Ssam int error = 0; 569105197Ssam struct mbuf *m; 570105197Ssam 571120585Ssam IPSEC_ASSERT(state != NULL, ("null state")); 572120585Ssam IPSEC_ASSERT(state->m != NULL, ("null m")); 573120585Ssam IPSEC_ASSERT(nexthdrp != NULL, ("null nexthdrp")); 574120585Ssam IPSEC_ASSERT(mprev != NULL, ("null mprev")); 575120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 576120585Ssam IPSEC_ASSERT(tun != NULL, ("null tun")); 577105197Ssam 578105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 579170122Sbz printf("%s: applied SP\n", __func__); 580105197Ssam kdebug_secpolicy(sp)); 581105197Ssam 582105197Ssam isr = sp->req; 583105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 584105197Ssam /* the rest will be handled by ipsec6_output_tunnel() */ 585105197Ssam *tun = 1; /* need tunnel-mode processing */ 586105197Ssam return 0; 587105197Ssam } 588105197Ssam 589105197Ssam *tun = 0; 590105197Ssam m = state->m; 591105197Ssam 592171133Sgnn IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 593105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 594105197Ssam if (isr == NULL) { 595177175Sbz if (error != 0) { 596105197Ssam#ifdef notdef 597177175Sbz /* XXX should notification be done for all errors ? */ 598177175Sbz /* 599177175Sbz * Notify the fact that the packet is discarded 600177175Sbz * to ourselves. I believe this is better than 601177175Sbz * just silently discarding. (jinmei@kame.net) 602177175Sbz * XXX: should we restrict the error to TCP packets? 603177175Sbz * XXX: should we directly notify sockets via 604177175Sbz * pfctlinputs? 605177175Sbz */ 606177175Sbz icmp6_error(m, ICMP6_DST_UNREACH, 607177175Sbz ICMP6_DST_UNREACH_ADMIN, 0); 608177175Sbz m = NULL; /* NB: icmp6_error frees mbuf */ 609105197Ssam#endif 610177175Sbz goto bad; 611177175Sbz } 612177175Sbz return EJUSTRETURN; 613105197Ssam } 614105197Ssam 615171133Sgnn error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 616171133Sgnn sizeof (struct ip6_hdr), 617171133Sgnn offsetof(struct ip6_hdr, 618171133Sgnn ip6_nxt)); 619171133Sgnn IPSECREQUEST_UNLOCK(isr); 620171133Sgnn return error; 621105197Ssambad: 622171133Sgnn if (isr) 623171133Sgnn IPSECREQUEST_UNLOCK(isr); 624105197Ssam if (m) 625105197Ssam m_freem(m); 626105197Ssam state->m = NULL; 627105197Ssam return error; 628105197Ssam} 629105197Ssam 630105197Ssamstatic int 631105197Ssamipsec6_encapsulate(struct mbuf *m, struct secasvar *sav) 632105197Ssam{ 633105197Ssam struct ip6_hdr *oip6; 634105197Ssam struct ip6_hdr *ip6; 635105197Ssam size_t plen; 636105197Ssam 637105197Ssam /* can't tunnel between different AFs */ 638105197Ssam if (sav->sah->saidx.src.sa.sa_family != AF_INET6 || 639105197Ssam sav->sah->saidx.dst.sa.sa_family != AF_INET6) { 640105197Ssam m_freem(m); 641105197Ssam return EINVAL; 642105197Ssam } 643171133Sgnn IPSEC_ASSERT(m->m_len == sizeof (struct ip6_hdr), 644120585Ssam ("mbuf wrong size; len %u", m->m_len)); 645105197Ssam 646105197Ssam 647105197Ssam /* 648105197Ssam * grow the mbuf to accomodate the new IPv6 header. 649105197Ssam */ 650105197Ssam plen = m->m_pkthdr.len; 651105197Ssam if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) { 652105197Ssam struct mbuf *n; 653111119Simp MGET(n, M_DONTWAIT, MT_DATA); 654105197Ssam if (!n) { 655105197Ssam m_freem(m); 656105197Ssam return ENOBUFS; 657105197Ssam } 658105197Ssam n->m_len = sizeof(struct ip6_hdr); 659105197Ssam n->m_next = m->m_next; 660105197Ssam m->m_next = n; 661105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 662105197Ssam oip6 = mtod(n, struct ip6_hdr *); 663105197Ssam } else { 664105197Ssam m->m_next->m_len += sizeof(struct ip6_hdr); 665105197Ssam m->m_next->m_data -= sizeof(struct ip6_hdr); 666105197Ssam m->m_pkthdr.len += sizeof(struct ip6_hdr); 667105197Ssam oip6 = mtod(m->m_next, struct ip6_hdr *); 668105197Ssam } 669105197Ssam ip6 = mtod(m, struct ip6_hdr *); 670113076Sdes bcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr)); 671105197Ssam 672105197Ssam /* Fake link-local scope-class addresses */ 673105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) 674105197Ssam oip6->ip6_src.s6_addr16[1] = 0; 675105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) 676105197Ssam oip6->ip6_dst.s6_addr16[1] = 0; 677105197Ssam 678105197Ssam /* construct new IPv6 header. see RFC 2401 5.1.2.2 */ 679105197Ssam /* ECN consideration. */ 680181803Sbz ip6_ecn_ingress(V_ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow); 681105197Ssam if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr)) 682105197Ssam ip6->ip6_plen = htons(plen); 683105197Ssam else { 684105197Ssam /* ip6->ip6_plen will be updated in ip6_output() */ 685105197Ssam } 686105197Ssam ip6->ip6_nxt = IPPROTO_IPV6; 687171133Sgnn ip6->ip6_src = sav->sah->saidx.src.sin6.sin6_addr; 688171133Sgnn ip6->ip6_dst = sav->sah->saidx.dst.sin6.sin6_addr; 689105197Ssam ip6->ip6_hlim = IPV6_DEFHLIM; 690105197Ssam 691105197Ssam /* XXX Should ip6_src be updated later ? */ 692105197Ssam 693105197Ssam return 0; 694105197Ssam} 695105197Ssam 696105197Ssam/* 697105197Ssam * IPsec output logic for IPv6, tunnel mode. 698105197Ssam */ 699105197Ssamint 700105197Ssamipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int flags) 701105197Ssam{ 702105197Ssam struct ip6_hdr *ip6; 703105197Ssam struct ipsecrequest *isr; 704105197Ssam struct secasindex saidx; 705105197Ssam int error; 706105197Ssam struct sockaddr_in6* dst6; 707105197Ssam struct mbuf *m; 708105197Ssam 709120585Ssam IPSEC_ASSERT(state != NULL, ("null state")); 710120585Ssam IPSEC_ASSERT(state->m != NULL, ("null m")); 711120585Ssam IPSEC_ASSERT(sp != NULL, ("null sp")); 712105197Ssam 713105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 714170122Sbz printf("%s: applied SP\n", __func__); 715105197Ssam kdebug_secpolicy(sp)); 716105197Ssam 717105197Ssam m = state->m; 718105197Ssam /* 719105197Ssam * transport mode ipsec (before the 1st tunnel mode) is already 720105197Ssam * processed by ipsec6_output_trans(). 721105197Ssam */ 722105197Ssam for (isr = sp->req; isr; isr = isr->next) { 723105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 724105197Ssam break; 725105197Ssam } 726170123Sbz 727170123Sbz IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 728105197Ssam isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 729177175Sbz if (isr == NULL) { 730177175Sbz if (error != 0) 731177175Sbz goto bad; 732177175Sbz return EJUSTRETURN; 733177175Sbz } 734105197Ssam 735174054Sbz#ifdef DEV_ENC 736181627Svanhu encif->if_opackets++; 737181627Svanhu encif->if_obytes += m->m_pkthdr.len; 738181627Svanhu 739174054Sbz /* pass the mbuf to enc0 for bpf processing */ 740174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 741174054Sbz /* pass the mbuf to enc0 for packet filtering */ 742174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 743174054Sbz goto bad; 744174054Sbz#endif 745174054Sbz 746105197Ssam /* 747105197Ssam * There may be the case that SA status will be changed when 748105197Ssam * we are refering to one. So calling splsoftnet(). 749105197Ssam */ 750105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 751105197Ssam /* 752105197Ssam * build IPsec tunnel. 753105197Ssam */ 754105197Ssam /* XXX should be processed with other familiy */ 755105197Ssam if (isr->sav->sah->saidx.src.sa.sa_family != AF_INET6) { 756120585Ssam ipseclog((LOG_ERR, "%s: family mismatched between " 757120585Ssam "inner and outer, spi=%u\n", __func__, 758105197Ssam ntohl(isr->sav->spi))); 759181803Sbz V_ipsec6stat.ips_out_inval++; 760105197Ssam error = EAFNOSUPPORT; 761105197Ssam goto bad; 762105197Ssam } 763105197Ssam 764105197Ssam m = ipsec6_splithdr(m); 765105197Ssam if (!m) { 766181803Sbz V_ipsec6stat.ips_out_nomem++; 767105197Ssam error = ENOMEM; 768105197Ssam goto bad; 769105197Ssam } 770105197Ssam error = ipsec6_encapsulate(m, isr->sav); 771105197Ssam if (error) { 772105197Ssam m = NULL; 773105197Ssam goto bad; 774105197Ssam } 775105197Ssam ip6 = mtod(m, struct ip6_hdr *); 776105197Ssam 777105197Ssam state->ro = &isr->sav->sah->sa_route; 778105197Ssam state->dst = (struct sockaddr *)&state->ro->ro_dst; 779105197Ssam dst6 = (struct sockaddr_in6 *)state->dst; 780105197Ssam if (state->ro->ro_rt 781105197Ssam && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 782105197Ssam || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { 783105197Ssam RTFREE(state->ro->ro_rt); 784105197Ssam state->ro->ro_rt = NULL; 785105197Ssam } 786105197Ssam if (state->ro->ro_rt == 0) { 787105197Ssam bzero(dst6, sizeof(*dst6)); 788105197Ssam dst6->sin6_family = AF_INET6; 789105197Ssam dst6->sin6_len = sizeof(*dst6); 790105197Ssam dst6->sin6_addr = ip6->ip6_dst; 791105197Ssam rtalloc(state->ro); 792105197Ssam } 793105197Ssam if (state->ro->ro_rt == 0) { 794181803Sbz V_ip6stat.ip6s_noroute++; 795181803Sbz V_ipsec6stat.ips_out_noroute++; 796105197Ssam error = EHOSTUNREACH; 797105197Ssam goto bad; 798105197Ssam } 799105197Ssam 800105197Ssam /* adjust state->dst if tunnel endpoint is offlink */ 801105197Ssam if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) { 802105197Ssam state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; 803105197Ssam dst6 = (struct sockaddr_in6 *)state->dst; 804105197Ssam } 805105197Ssam } 806105197Ssam 807105197Ssam m = ipsec6_splithdr(m); 808105197Ssam if (!m) { 809181803Sbz V_ipsec6stat.ips_out_nomem++; 810105197Ssam error = ENOMEM; 811105197Ssam goto bad; 812105197Ssam } 813105197Ssam ip6 = mtod(m, struct ip6_hdr *); 814174054Sbz 815174054Sbz#ifdef DEV_ENC 816174054Sbz /* pass the mbuf to enc0 for bpf processing */ 817174054Sbz ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_AFTER); 818174054Sbz /* pass the mbuf to enc0 for packet filtering */ 819174054Sbz if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 820174054Sbz goto bad; 821174054Sbz#endif 822174054Sbz 823170123Sbz error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 824105197Ssam sizeof (struct ip6_hdr), 825105197Ssam offsetof(struct ip6_hdr, ip6_nxt)); 826170123Sbz IPSECREQUEST_UNLOCK(isr); 827170123Sbz return error; 828105197Ssambad: 829170123Sbz if (isr) 830170123Sbz IPSECREQUEST_UNLOCK(isr); 831105197Ssam if (m) 832105197Ssam m_freem(m); 833105197Ssam state->m = NULL; 834105197Ssam return error; 835105197Ssam} 836105197Ssam#endif /*INET6*/ 837