1aaea26efSSam Leffler /*- 2aaea26efSSam Leffler * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting 3aaea26efSSam Leffler * All rights reserved. 4aaea26efSSam Leffler * 5aaea26efSSam Leffler * Redistribution and use in source and binary forms, with or without 6aaea26efSSam Leffler * modification, are permitted provided that the following conditions 7aaea26efSSam Leffler * are met: 8aaea26efSSam Leffler * 1. Redistributions of source code must retain the above copyright 9aaea26efSSam Leffler * notice, this list of conditions and the following disclaimer. 10aaea26efSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 11aaea26efSSam Leffler * notice, this list of conditions and the following disclaimer in the 12aaea26efSSam Leffler * documentation and/or other materials provided with the distribution. 13aaea26efSSam Leffler * 14aaea26efSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15aaea26efSSam Leffler * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16aaea26efSSam Leffler * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17aaea26efSSam Leffler * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18aaea26efSSam Leffler * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19aaea26efSSam Leffler * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20aaea26efSSam Leffler * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21aaea26efSSam Leffler * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22aaea26efSSam Leffler * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23aaea26efSSam Leffler * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24aaea26efSSam Leffler * SUCH DAMAGE. 25aaea26efSSam Leffler * 26aaea26efSSam Leffler * $FreeBSD$ 27aaea26efSSam Leffler */ 2888768458SSam Leffler 2988768458SSam Leffler /* 3088768458SSam Leffler * IPsec output processing. 3188768458SSam Leffler */ 3288768458SSam Leffler #include "opt_inet.h" 3388768458SSam Leffler #include "opt_inet6.h" 3488768458SSam Leffler #include "opt_ipsec.h" 35bdea400fSAndrew Thompson #include "opt_enc.h" 3688768458SSam Leffler 3788768458SSam Leffler #include <sys/param.h> 3888768458SSam Leffler #include <sys/systm.h> 3988768458SSam Leffler #include <sys/mbuf.h> 4088768458SSam Leffler #include <sys/domain.h> 4188768458SSam Leffler #include <sys/protosw.h> 4288768458SSam Leffler #include <sys/socket.h> 4388768458SSam Leffler #include <sys/errno.h> 4488768458SSam Leffler #include <sys/syslog.h> 4588768458SSam Leffler 4688768458SSam Leffler #include <net/if.h> 4776039bc8SGleb Smirnoff #include <net/if_var.h> 48b28cd334SBjoern A. Zeeb #include <net/pfil.h> 49eddfbb76SRobert Watson #include <net/vnet.h> 5088768458SSam Leffler 5188768458SSam Leffler #include <netinet/in.h> 5288768458SSam Leffler #include <netinet/in_systm.h> 5388768458SSam Leffler #include <netinet/ip.h> 5488768458SSam Leffler #include <netinet/ip_var.h> 5588768458SSam Leffler #include <netinet/in_var.h> 5688768458SSam Leffler #include <netinet/ip_ecn.h> 5788768458SSam Leffler #ifdef INET6 5888768458SSam Leffler #include <netinet6/ip6_ecn.h> 5988768458SSam Leffler #endif 6088768458SSam Leffler 6188768458SSam Leffler #include <netinet/ip6.h> 6288768458SSam Leffler #ifdef INET6 6388768458SSam Leffler #include <netinet6/ip6_var.h> 6461f37615SAndrey V. Elsukov #include <netinet6/scope6_var.h> 6588768458SSam Leffler #endif 6688768458SSam Leffler #include <netinet/in_pcb.h> 6788768458SSam Leffler #ifdef INET6 6888768458SSam Leffler #include <netinet/icmp6.h> 6988768458SSam Leffler #endif 7088768458SSam Leffler 7188768458SSam Leffler #include <netipsec/ipsec.h> 7288768458SSam Leffler #ifdef INET6 7388768458SSam Leffler #include <netipsec/ipsec6.h> 7488768458SSam Leffler #endif 7588768458SSam Leffler #include <netipsec/ah_var.h> 7688768458SSam Leffler #include <netipsec/esp_var.h> 7788768458SSam Leffler #include <netipsec/ipcomp_var.h> 7888768458SSam Leffler 7988768458SSam Leffler #include <netipsec/xform.h> 8088768458SSam Leffler 8188768458SSam Leffler #include <netipsec/key.h> 8288768458SSam Leffler #include <netipsec/keydb.h> 8388768458SSam Leffler #include <netipsec/key_debug.h> 8488768458SSam Leffler 8588768458SSam Leffler #include <machine/in_cksum.h> 8688768458SSam Leffler 877b495c44SVANHULLEBUS Yvan #ifdef IPSEC_NAT_T 887b495c44SVANHULLEBUS Yvan #include <netinet/udp.h> 897b495c44SVANHULLEBUS Yvan #endif 907b495c44SVANHULLEBUS Yvan 9197c2a697SVANHULLEBUS Yvan #ifdef DEV_ENC 9297c2a697SVANHULLEBUS Yvan #include <net/if_enc.h> 9397c2a697SVANHULLEBUS Yvan #endif 9497c2a697SVANHULLEBUS Yvan 9597c2a697SVANHULLEBUS Yvan 9688768458SSam Leffler int 9788768458SSam Leffler ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr) 9888768458SSam Leffler { 9988768458SSam Leffler struct tdb_ident *tdbi; 10088768458SSam Leffler struct m_tag *mtag; 10188768458SSam Leffler struct secasvar *sav; 10288768458SSam Leffler struct secasindex *saidx; 10388768458SSam Leffler int error; 10488768458SSam Leffler 1059ffa9677SSam Leffler IPSEC_ASSERT(m != NULL, ("null mbuf")); 1069ffa9677SSam Leffler IPSEC_ASSERT(isr != NULL, ("null ISR")); 1073d80e82dSAndrey V. Elsukov IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp")); 10888768458SSam Leffler sav = isr->sav; 1099ffa9677SSam Leffler IPSEC_ASSERT(sav != NULL, ("null SA")); 1109ffa9677SSam Leffler IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); 11188768458SSam Leffler 11288768458SSam Leffler saidx = &sav->sah->saidx; 11388768458SSam Leffler switch (saidx->dst.sa.sa_family) { 11488768458SSam Leffler #ifdef INET 11588768458SSam Leffler case AF_INET: 11688768458SSam Leffler /* Fix the header length, for AH processing. */ 11788768458SSam Leffler mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 11888768458SSam Leffler break; 11988768458SSam Leffler #endif /* INET */ 12088768458SSam Leffler #ifdef INET6 12188768458SSam Leffler case AF_INET6: 12288768458SSam Leffler /* Fix the header length, for AH processing. */ 12388768458SSam Leffler if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 12488768458SSam Leffler error = ENXIO; 12588768458SSam Leffler goto bad; 12688768458SSam Leffler } 12788768458SSam Leffler if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 12888768458SSam Leffler /* No jumbogram support. */ 12988768458SSam Leffler error = ENXIO; /*?*/ 13088768458SSam Leffler goto bad; 13188768458SSam Leffler } 13288768458SSam Leffler mtod(m, struct ip6_hdr *)->ip6_plen = 13388768458SSam Leffler htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 13488768458SSam Leffler break; 13588768458SSam Leffler #endif /* INET6 */ 13688768458SSam Leffler default: 1379ffa9677SSam Leffler DPRINTF(("%s: unknown protocol family %u\n", __func__, 13888768458SSam Leffler saidx->dst.sa.sa_family)); 13988768458SSam Leffler error = ENXIO; 14088768458SSam Leffler goto bad; 14188768458SSam Leffler } 14288768458SSam Leffler 14388768458SSam Leffler /* 14488768458SSam Leffler * Add a record of what we've done or what needs to be done to the 14588768458SSam Leffler * packet. 14688768458SSam Leffler */ 14788768458SSam Leffler mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 14888768458SSam Leffler sizeof(struct tdb_ident), M_NOWAIT); 14988768458SSam Leffler if (mtag == NULL) { 1509ffa9677SSam Leffler DPRINTF(("%s: could not get packet tag\n", __func__)); 15188768458SSam Leffler error = ENOMEM; 15288768458SSam Leffler goto bad; 15388768458SSam Leffler } 15488768458SSam Leffler 15588768458SSam Leffler tdbi = (struct tdb_ident *)(mtag + 1); 15688768458SSam Leffler tdbi->dst = saidx->dst; 15788768458SSam Leffler tdbi->proto = saidx->proto; 15888768458SSam Leffler tdbi->spi = sav->spi; 15988768458SSam Leffler m_tag_prepend(m, mtag); 16088768458SSam Leffler 161*59959de5SErmal Luçi key_sa_recordxfer(sav, m); /* record data transfer */ 162*59959de5SErmal Luçi 16388768458SSam Leffler /* 16488768458SSam Leffler * If there's another (bundled) SA to apply, do so. 16588768458SSam Leffler * Note that this puts a burden on the kernel stack size. 16688768458SSam Leffler * If this is a problem we'll need to introduce a queue 16788768458SSam Leffler * to set the packet on so we can unwind the stack before 16888768458SSam Leffler * doing further processing. 1693d80e82dSAndrey V. Elsukov * 1703d80e82dSAndrey V. Elsukov * If ipsec[46]_process_packet() will successfully queue 1713d80e82dSAndrey V. Elsukov * the request, we need to take additional reference to SP, 1723d80e82dSAndrey V. Elsukov * because xform callback will release reference. 17388768458SSam Leffler */ 17488768458SSam Leffler if (isr->next) { 175174b0d41SBjoern A. Zeeb /* XXX-BZ currently only support same AF bundles. */ 176db178eb8SBjoern A. Zeeb switch (saidx->dst.sa.sa_family) { 177db178eb8SBjoern A. Zeeb #ifdef INET 178db178eb8SBjoern A. Zeeb case AF_INET: 179f9d8f665SAndrey V. Elsukov IPSECSTAT_INC(ips_out_bundlesa); 1803d80e82dSAndrey V. Elsukov key_addref(isr->sp); 1813d80e82dSAndrey V. Elsukov error = ipsec4_process_packet(m, isr->next); 1823d80e82dSAndrey V. Elsukov if (error != 0) 1833d80e82dSAndrey V. Elsukov KEY_FREESP(&isr->sp); 1843d80e82dSAndrey V. Elsukov return (error); 185db178eb8SBjoern A. Zeeb /* NOTREACHED */ 186db178eb8SBjoern A. Zeeb #endif 187db178eb8SBjoern A. Zeeb #ifdef notyet 188db178eb8SBjoern A. Zeeb #ifdef INET6 189db178eb8SBjoern A. Zeeb case AF_INET6: 190db178eb8SBjoern A. Zeeb /* XXX */ 191f9d8f665SAndrey V. Elsukov IPSEC6STAT_INC(ips_out_bundlesa); 1923d80e82dSAndrey V. Elsukov key_addref(isr->sp); 1933d80e82dSAndrey V. Elsukov error = ipsec6_process_packet(m, isr->next); 1943d80e82dSAndrey V. Elsukov if (error != 0) 1953d80e82dSAndrey V. Elsukov KEY_FREESP(&isr->sp); 1963d80e82dSAndrey V. Elsukov return (error); 197db178eb8SBjoern A. Zeeb /* NOTREACHED */ 198db178eb8SBjoern A. Zeeb #endif /* INET6 */ 199db178eb8SBjoern A. Zeeb #endif 200db178eb8SBjoern A. Zeeb default: 201db178eb8SBjoern A. Zeeb DPRINTF(("%s: unknown protocol family %u\n", __func__, 202db178eb8SBjoern A. Zeeb saidx->dst.sa.sa_family)); 203db178eb8SBjoern A. Zeeb error = ENXIO; 204db178eb8SBjoern A. Zeeb goto bad; 205db178eb8SBjoern A. Zeeb } 20688768458SSam Leffler } 20788768458SSam Leffler 20888768458SSam Leffler /* 20988768458SSam Leffler * We're done with IPsec processing, transmit the packet using the 2106508929bSAndrey V. Elsukov * appropriate network protocol (IP or IPv6). 21188768458SSam Leffler */ 21288768458SSam Leffler switch (saidx->dst.sa.sa_family) { 21388768458SSam Leffler #ifdef INET 21488768458SSam Leffler case AF_INET: 2157b495c44SVANHULLEBUS Yvan #ifdef IPSEC_NAT_T 2167b495c44SVANHULLEBUS Yvan /* 2177b495c44SVANHULLEBUS Yvan * If NAT-T is enabled, now that all IPsec processing is done 2187b495c44SVANHULLEBUS Yvan * insert UDP encapsulation header after IP header. 2197b495c44SVANHULLEBUS Yvan */ 2207b495c44SVANHULLEBUS Yvan if (sav->natt_type) { 22120472bceSGleb Smirnoff struct ip *ip = mtod(m, struct ip *); 2227b495c44SVANHULLEBUS Yvan const int hlen = (ip->ip_hl << 2); 2237b495c44SVANHULLEBUS Yvan int size, off; 2247b495c44SVANHULLEBUS Yvan struct mbuf *mi; 2257b495c44SVANHULLEBUS Yvan struct udphdr *udp; 2267b495c44SVANHULLEBUS Yvan 2277b495c44SVANHULLEBUS Yvan size = sizeof(struct udphdr); 2287b495c44SVANHULLEBUS Yvan if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 2297b495c44SVANHULLEBUS Yvan /* 2307b495c44SVANHULLEBUS Yvan * draft-ietf-ipsec-nat-t-ike-0[01].txt and 2317b495c44SVANHULLEBUS Yvan * draft-ietf-ipsec-udp-encaps-(00/)01.txt, 2327b495c44SVANHULLEBUS Yvan * ignoring possible AH mode 2337b495c44SVANHULLEBUS Yvan * non-IKE marker + non-ESP marker 2347b495c44SVANHULLEBUS Yvan * from draft-ietf-ipsec-udp-encaps-00.txt. 2357b495c44SVANHULLEBUS Yvan */ 2367b495c44SVANHULLEBUS Yvan size += sizeof(u_int64_t); 2377b495c44SVANHULLEBUS Yvan } 2387b495c44SVANHULLEBUS Yvan mi = m_makespace(m, hlen, size, &off); 2397b495c44SVANHULLEBUS Yvan if (mi == NULL) { 2407b495c44SVANHULLEBUS Yvan DPRINTF(("%s: m_makespace for udphdr failed\n", 2417b495c44SVANHULLEBUS Yvan __func__)); 2427b495c44SVANHULLEBUS Yvan error = ENOBUFS; 2437b495c44SVANHULLEBUS Yvan goto bad; 2447b495c44SVANHULLEBUS Yvan } 2457b495c44SVANHULLEBUS Yvan 2467b495c44SVANHULLEBUS Yvan udp = (struct udphdr *)(mtod(mi, caddr_t) + off); 2477b495c44SVANHULLEBUS Yvan if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 2487b495c44SVANHULLEBUS Yvan udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); 2497b495c44SVANHULLEBUS Yvan else 2507b495c44SVANHULLEBUS Yvan udp->uh_sport = 2517b495c44SVANHULLEBUS Yvan KEY_PORTFROMSADDR(&sav->sah->saidx.src); 2527b495c44SVANHULLEBUS Yvan udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); 2537b495c44SVANHULLEBUS Yvan udp->uh_sum = 0; 2547b495c44SVANHULLEBUS Yvan udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 25520472bceSGleb Smirnoff ip->ip_len = htons(m->m_pkthdr.len); 2567b495c44SVANHULLEBUS Yvan ip->ip_p = IPPROTO_UDP; 2577b495c44SVANHULLEBUS Yvan 2587b495c44SVANHULLEBUS Yvan if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 2597b495c44SVANHULLEBUS Yvan *(u_int64_t *)(udp + 1) = 0; 2607b495c44SVANHULLEBUS Yvan } 2617b495c44SVANHULLEBUS Yvan #endif /* IPSEC_NAT_T */ 2627b495c44SVANHULLEBUS Yvan 26388768458SSam Leffler return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 26488768458SSam Leffler #endif /* INET */ 26588768458SSam Leffler #ifdef INET6 26688768458SSam Leffler case AF_INET6: 26788768458SSam Leffler /* 26888768458SSam Leffler * We don't need massage, IPv6 header fields are always in 26988768458SSam Leffler * net endian. 27088768458SSam Leffler */ 27188768458SSam Leffler return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 27288768458SSam Leffler #endif /* INET6 */ 27388768458SSam Leffler } 27488768458SSam Leffler panic("ipsec_process_done"); 27588768458SSam Leffler bad: 27688768458SSam Leffler m_freem(m); 27788768458SSam Leffler return (error); 27888768458SSam Leffler } 27988768458SSam Leffler 28088768458SSam Leffler static struct ipsecrequest * 28188768458SSam Leffler ipsec_nextisr( 28288768458SSam Leffler struct mbuf *m, 28388768458SSam Leffler struct ipsecrequest *isr, 28488768458SSam Leffler int af, 28588768458SSam Leffler struct secasindex *saidx, 28688768458SSam Leffler int *error 28788768458SSam Leffler ) 28888768458SSam Leffler { 289a04d64d8SAndrey V. Elsukov #define IPSEC_OSTAT(name) do { \ 290a04d64d8SAndrey V. Elsukov if (isr->saidx.proto == IPPROTO_ESP) \ 291a04d64d8SAndrey V. Elsukov ESPSTAT_INC(esps_##name); \ 292a04d64d8SAndrey V. Elsukov else if (isr->saidx.proto == IPPROTO_AH)\ 293a04d64d8SAndrey V. Elsukov AHSTAT_INC(ahs_##name); \ 294a04d64d8SAndrey V. Elsukov else \ 295a04d64d8SAndrey V. Elsukov IPCOMPSTAT_INC(ipcomps_##name); \ 296a04d64d8SAndrey V. Elsukov } while (0) 29788768458SSam Leffler struct secasvar *sav; 29888768458SSam Leffler 2999ffa9677SSam Leffler IPSECREQUEST_LOCK_ASSERT(isr); 3009ffa9677SSam Leffler 3019ffa9677SSam Leffler IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 3029ffa9677SSam Leffler ("invalid address family %u", af)); 30388768458SSam Leffler again: 30488768458SSam Leffler /* 30588768458SSam Leffler * Craft SA index to search for proper SA. Note that 30688768458SSam Leffler * we only fillin unspecified SA peers for transport 30788768458SSam Leffler * mode; for tunnel mode they must already be filled in. 30888768458SSam Leffler */ 30988768458SSam Leffler *saidx = isr->saidx; 31088768458SSam Leffler if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 31188768458SSam Leffler /* Fillin unspecified SA peers only for transport mode */ 31288768458SSam Leffler if (af == AF_INET) { 31388768458SSam Leffler struct sockaddr_in *sin; 31488768458SSam Leffler struct ip *ip = mtod(m, struct ip *); 31588768458SSam Leffler 31688768458SSam Leffler if (saidx->src.sa.sa_len == 0) { 31788768458SSam Leffler sin = &saidx->src.sin; 31888768458SSam Leffler sin->sin_len = sizeof(*sin); 31988768458SSam Leffler sin->sin_family = AF_INET; 32088768458SSam Leffler sin->sin_port = IPSEC_PORT_ANY; 32188768458SSam Leffler sin->sin_addr = ip->ip_src; 32288768458SSam Leffler } 32388768458SSam Leffler if (saidx->dst.sa.sa_len == 0) { 32488768458SSam Leffler sin = &saidx->dst.sin; 32588768458SSam Leffler sin->sin_len = sizeof(*sin); 32688768458SSam Leffler sin->sin_family = AF_INET; 32788768458SSam Leffler sin->sin_port = IPSEC_PORT_ANY; 32888768458SSam Leffler sin->sin_addr = ip->ip_dst; 32988768458SSam Leffler } 33088768458SSam Leffler } else { 33188768458SSam Leffler struct sockaddr_in6 *sin6; 33288768458SSam Leffler struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 33388768458SSam Leffler 33488768458SSam Leffler if (saidx->src.sin6.sin6_len == 0) { 33588768458SSam Leffler sin6 = (struct sockaddr_in6 *)&saidx->src; 33688768458SSam Leffler sin6->sin6_len = sizeof(*sin6); 33788768458SSam Leffler sin6->sin6_family = AF_INET6; 33888768458SSam Leffler sin6->sin6_port = IPSEC_PORT_ANY; 33988768458SSam Leffler sin6->sin6_addr = ip6->ip6_src; 34088768458SSam Leffler if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 34188768458SSam Leffler /* fix scope id for comparing SPD */ 34288768458SSam Leffler sin6->sin6_addr.s6_addr16[1] = 0; 34388768458SSam Leffler sin6->sin6_scope_id = 34488768458SSam Leffler ntohs(ip6->ip6_src.s6_addr16[1]); 34588768458SSam Leffler } 34688768458SSam Leffler } 34788768458SSam Leffler if (saidx->dst.sin6.sin6_len == 0) { 34888768458SSam Leffler sin6 = (struct sockaddr_in6 *)&saidx->dst; 34988768458SSam Leffler sin6->sin6_len = sizeof(*sin6); 35088768458SSam Leffler sin6->sin6_family = AF_INET6; 35188768458SSam Leffler sin6->sin6_port = IPSEC_PORT_ANY; 35288768458SSam Leffler sin6->sin6_addr = ip6->ip6_dst; 35388768458SSam Leffler if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 35488768458SSam Leffler /* fix scope id for comparing SPD */ 35588768458SSam Leffler sin6->sin6_addr.s6_addr16[1] = 0; 35688768458SSam Leffler sin6->sin6_scope_id = 35788768458SSam Leffler ntohs(ip6->ip6_dst.s6_addr16[1]); 35888768458SSam Leffler } 35988768458SSam Leffler } 36088768458SSam Leffler } 36188768458SSam Leffler } 36288768458SSam Leffler 36388768458SSam Leffler /* 36488768458SSam Leffler * Lookup SA and validate it. 36588768458SSam Leffler */ 36688768458SSam Leffler *error = key_checkrequest(isr, saidx); 36788768458SSam Leffler if (*error != 0) { 36888768458SSam Leffler /* 36988768458SSam Leffler * IPsec processing is required, but no SA found. 37088768458SSam Leffler * I assume that key_acquire() had been called 37188768458SSam Leffler * to get/establish the SA. Here I discard 37288768458SSam Leffler * this packet because it is responsibility for 37388768458SSam Leffler * upper layer to retransmit the packet. 37488768458SSam Leffler */ 375f3c93842SAndrey V. Elsukov switch(af) { 376f3c93842SAndrey V. Elsukov case AF_INET: 3776659296cSAndrey V. Elsukov IPSECSTAT_INC(ips_out_nosa); 378f3c93842SAndrey V. Elsukov break; 379f3c93842SAndrey V. Elsukov #ifdef INET6 380f3c93842SAndrey V. Elsukov case AF_INET6: 381f3c93842SAndrey V. Elsukov IPSEC6STAT_INC(ips_out_nosa); 382f3c93842SAndrey V. Elsukov break; 383f3c93842SAndrey V. Elsukov #endif 384f3c93842SAndrey V. Elsukov } 38588768458SSam Leffler goto bad; 38688768458SSam Leffler } 38788768458SSam Leffler sav = isr->sav; 3889e3bdedeSBjoern A. Zeeb if (sav == NULL) { 3899ffa9677SSam Leffler IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 3909ffa9677SSam Leffler ("no SA found, but required; level %u", 39188768458SSam Leffler ipsec_get_reqlevel(isr))); 3929ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 39388768458SSam Leffler isr = isr->next; 3949e3bdedeSBjoern A. Zeeb /* 3959e3bdedeSBjoern A. Zeeb * If isr is NULL, we found a 'use' policy w/o SA. 3969e3bdedeSBjoern A. Zeeb * Return w/o error and w/o isr so we can drop out 3979e3bdedeSBjoern A. Zeeb * and continue w/o IPsec processing. 3989e3bdedeSBjoern A. Zeeb */ 3999e3bdedeSBjoern A. Zeeb if (isr == NULL) 40088768458SSam Leffler return isr; 4019ffa9677SSam Leffler IPSECREQUEST_LOCK(isr); 40288768458SSam Leffler goto again; 40388768458SSam Leffler } 40488768458SSam Leffler 40588768458SSam Leffler /* 40688768458SSam Leffler * Check system global policy controls. 40788768458SSam Leffler */ 408603724d3SBjoern A. Zeeb if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 409603724d3SBjoern A. Zeeb (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 410603724d3SBjoern A. Zeeb (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 4119ffa9677SSam Leffler DPRINTF(("%s: IPsec outbound packet dropped due" 4129ffa9677SSam Leffler " to policy (check your sysctls)\n", __func__)); 413a04d64d8SAndrey V. Elsukov IPSEC_OSTAT(pdrops); 41488768458SSam Leffler *error = EHOSTUNREACH; 41588768458SSam Leffler goto bad; 41688768458SSam Leffler } 41788768458SSam Leffler 41888768458SSam Leffler /* 41988768458SSam Leffler * Sanity check the SA contents for the caller 42088768458SSam Leffler * before they invoke the xform output method. 42188768458SSam Leffler */ 42288768458SSam Leffler if (sav->tdb_xform == NULL) { 4239ffa9677SSam Leffler DPRINTF(("%s: no transform for SA\n", __func__)); 424a04d64d8SAndrey V. Elsukov IPSEC_OSTAT(noxform); 42588768458SSam Leffler *error = EHOSTUNREACH; 42688768458SSam Leffler goto bad; 42788768458SSam Leffler } 42888768458SSam Leffler return isr; 42988768458SSam Leffler bad: 4309ffa9677SSam Leffler IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 4319ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 43288768458SSam Leffler return NULL; 43388768458SSam Leffler #undef IPSEC_OSTAT 43488768458SSam Leffler } 43588768458SSam Leffler 43661f37615SAndrey V. Elsukov static int 43761f37615SAndrey V. Elsukov ipsec_encap(struct mbuf **mp, struct secasindex *saidx) 43861f37615SAndrey V. Elsukov { 43961f37615SAndrey V. Elsukov #ifdef INET6 44061f37615SAndrey V. Elsukov struct ip6_hdr *ip6; 44161f37615SAndrey V. Elsukov #endif 44261f37615SAndrey V. Elsukov struct ip *ip; 44361f37615SAndrey V. Elsukov int setdf; 44461f37615SAndrey V. Elsukov uint8_t itos, proto; 44561f37615SAndrey V. Elsukov 44661f37615SAndrey V. Elsukov ip = mtod(*mp, struct ip *); 44761f37615SAndrey V. Elsukov switch (ip->ip_v) { 44861f37615SAndrey V. Elsukov #ifdef INET 44961f37615SAndrey V. Elsukov case IPVERSION: 45061f37615SAndrey V. Elsukov proto = IPPROTO_IPIP; 45161f37615SAndrey V. Elsukov /* 45261f37615SAndrey V. Elsukov * Collect IP_DF state from the inner header 45361f37615SAndrey V. Elsukov * and honor system-wide control of how to handle it. 45461f37615SAndrey V. Elsukov */ 45561f37615SAndrey V. Elsukov switch (V_ip4_ipsec_dfbit) { 45661f37615SAndrey V. Elsukov case 0: /* clear in outer header */ 45761f37615SAndrey V. Elsukov case 1: /* set in outer header */ 45861f37615SAndrey V. Elsukov setdf = V_ip4_ipsec_dfbit; 45961f37615SAndrey V. Elsukov break; 46061f37615SAndrey V. Elsukov default:/* propagate to outer header */ 46161f37615SAndrey V. Elsukov setdf = (ip->ip_off & ntohs(IP_DF)) != 0; 46261f37615SAndrey V. Elsukov } 46361f37615SAndrey V. Elsukov itos = ip->ip_tos; 46461f37615SAndrey V. Elsukov break; 46561f37615SAndrey V. Elsukov #endif 46661f37615SAndrey V. Elsukov #ifdef INET6 46761f37615SAndrey V. Elsukov case (IPV6_VERSION >> 4): 46861f37615SAndrey V. Elsukov proto = IPPROTO_IPV6; 46961f37615SAndrey V. Elsukov ip6 = mtod(*mp, struct ip6_hdr *); 47061f37615SAndrey V. Elsukov itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 47161f37615SAndrey V. Elsukov setdf = V_ip4_ipsec_dfbit ? 1: 0; 47261f37615SAndrey V. Elsukov /* scoped address handling */ 47361f37615SAndrey V. Elsukov in6_clearscope(&ip6->ip6_src); 47461f37615SAndrey V. Elsukov in6_clearscope(&ip6->ip6_dst); 47561f37615SAndrey V. Elsukov break; 47661f37615SAndrey V. Elsukov #endif 47761f37615SAndrey V. Elsukov default: 47861f37615SAndrey V. Elsukov return (EAFNOSUPPORT); 47961f37615SAndrey V. Elsukov } 48061f37615SAndrey V. Elsukov switch (saidx->dst.sa.sa_family) { 48161f37615SAndrey V. Elsukov #ifdef INET 48261f37615SAndrey V. Elsukov case AF_INET: 48361f37615SAndrey V. Elsukov if (saidx->src.sa.sa_family != AF_INET || 48461f37615SAndrey V. Elsukov saidx->src.sin.sin_addr.s_addr == INADDR_ANY || 48561f37615SAndrey V. Elsukov saidx->dst.sin.sin_addr.s_addr == INADDR_ANY) 48661f37615SAndrey V. Elsukov return (EINVAL); 48761f37615SAndrey V. Elsukov M_PREPEND(*mp, sizeof(struct ip), M_NOWAIT); 48861f37615SAndrey V. Elsukov if (*mp == NULL) 48961f37615SAndrey V. Elsukov return (ENOBUFS); 49061f37615SAndrey V. Elsukov ip = mtod(*mp, struct ip *); 49161f37615SAndrey V. Elsukov ip->ip_v = IPVERSION; 49261f37615SAndrey V. Elsukov ip->ip_hl = sizeof(struct ip) >> 2; 49361f37615SAndrey V. Elsukov ip->ip_p = proto; 49461f37615SAndrey V. Elsukov ip->ip_len = htons((*mp)->m_pkthdr.len); 49561f37615SAndrey V. Elsukov ip->ip_ttl = V_ip_defttl; 49661f37615SAndrey V. Elsukov ip->ip_sum = 0; 49761f37615SAndrey V. Elsukov ip->ip_off = setdf ? htons(IP_DF): 0; 49861f37615SAndrey V. Elsukov ip->ip_src = saidx->src.sin.sin_addr; 49961f37615SAndrey V. Elsukov ip->ip_dst = saidx->dst.sin.sin_addr; 50061f37615SAndrey V. Elsukov ip_ecn_ingress(V_ip4_ipsec_ecn, &ip->ip_tos, &itos); 50161f37615SAndrey V. Elsukov ip_fillid(ip); 50261f37615SAndrey V. Elsukov break; 50361f37615SAndrey V. Elsukov #endif /* INET */ 50461f37615SAndrey V. Elsukov #ifdef INET6 50561f37615SAndrey V. Elsukov case AF_INET6: 50661f37615SAndrey V. Elsukov if (saidx->src.sa.sa_family != AF_INET6 || 50761f37615SAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr) || 50861f37615SAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr)) 50961f37615SAndrey V. Elsukov return (EINVAL); 51061f37615SAndrey V. Elsukov M_PREPEND(*mp, sizeof(struct ip6_hdr), M_NOWAIT); 51161f37615SAndrey V. Elsukov if (*mp == NULL) 51261f37615SAndrey V. Elsukov return (ENOBUFS); 51361f37615SAndrey V. Elsukov ip6 = mtod(*mp, struct ip6_hdr *); 51461f37615SAndrey V. Elsukov ip6->ip6_flow = 0; 51561f37615SAndrey V. Elsukov ip6->ip6_vfc = IPV6_VERSION; 51661f37615SAndrey V. Elsukov ip6->ip6_hlim = V_ip6_defhlim; 51761f37615SAndrey V. Elsukov ip6->ip6_nxt = proto; 51861f37615SAndrey V. Elsukov ip6->ip6_dst = saidx->dst.sin6.sin6_addr; 5191ae800e7SAndrey V. Elsukov /* For link-local address embed scope zone id */ 5201ae800e7SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) 5211ae800e7SAndrey V. Elsukov ip6->ip6_dst.s6_addr16[1] = 5221ae800e7SAndrey V. Elsukov htons(saidx->dst.sin6.sin6_scope_id & 0xffff); 52361f37615SAndrey V. Elsukov ip6->ip6_src = saidx->src.sin6.sin6_addr; 5241ae800e7SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) 5251ae800e7SAndrey V. Elsukov ip6->ip6_src.s6_addr16[1] = 5261ae800e7SAndrey V. Elsukov htons(saidx->src.sin6.sin6_scope_id & 0xffff); 52761f37615SAndrey V. Elsukov ip6->ip6_plen = htons((*mp)->m_pkthdr.len - sizeof(*ip6)); 52861f37615SAndrey V. Elsukov ip_ecn_ingress(V_ip6_ipsec_ecn, &proto, &itos); 52961f37615SAndrey V. Elsukov ip6->ip6_flow |= htonl((uint32_t)proto << 20); 53061f37615SAndrey V. Elsukov break; 53161f37615SAndrey V. Elsukov #endif /* INET6 */ 53261f37615SAndrey V. Elsukov default: 53361f37615SAndrey V. Elsukov return (EAFNOSUPPORT); 53461f37615SAndrey V. Elsukov } 53561f37615SAndrey V. Elsukov return (0); 53661f37615SAndrey V. Elsukov } 53761f37615SAndrey V. Elsukov 53888768458SSam Leffler #ifdef INET 53988768458SSam Leffler /* 54088768458SSam Leffler * IPsec output logic for IPv4. 54188768458SSam Leffler */ 54288768458SSam Leffler int 543619764beSAndrey V. Elsukov ipsec4_process_packet(struct mbuf *m, struct ipsecrequest *isr) 54488768458SSam Leffler { 545962ac6c7SAndrey V. Elsukov char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN]; 546619764beSAndrey V. Elsukov union sockaddr_union *dst; 54788768458SSam Leffler struct secasindex saidx; 54888768458SSam Leffler struct secasvar *sav; 54988768458SSam Leffler struct ip *ip; 55061f37615SAndrey V. Elsukov int error, i, off; 55188768458SSam Leffler 5529ffa9677SSam Leffler IPSEC_ASSERT(m != NULL, ("null mbuf")); 5539ffa9677SSam Leffler IPSEC_ASSERT(isr != NULL, ("null isr")); 55488768458SSam Leffler 5559ffa9677SSam Leffler IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 55688768458SSam Leffler 55788768458SSam Leffler isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 5589e3bdedeSBjoern A. Zeeb if (isr == NULL) { 5599e3bdedeSBjoern A. Zeeb if (error != 0) 56088768458SSam Leffler goto bad; 5619e3bdedeSBjoern A. Zeeb return EJUSTRETURN; 5629e3bdedeSBjoern A. Zeeb } 56388768458SSam Leffler 56488768458SSam Leffler sav = isr->sav; 565619764beSAndrey V. Elsukov if (m->m_len < sizeof(struct ip) && 566619764beSAndrey V. Elsukov (m = m_pullup(m, sizeof (struct ip))) == NULL) { 567619764beSAndrey V. Elsukov error = ENOBUFS; 568619764beSAndrey V. Elsukov goto bad; 569619764beSAndrey V. Elsukov } 570619764beSAndrey V. Elsukov ip = mtod(m, struct ip *); 571619764beSAndrey V. Elsukov dst = &sav->sah->saidx.dst; 572bdea400fSAndrew Thompson #ifdef DEV_ENC 5736ff8af1cSGleb Smirnoff if_inc_counter(encif, IFCOUNTER_OPACKETS, 1); 5746ff8af1cSGleb Smirnoff if_inc_counter(encif, IFCOUNTER_OBYTES, m->m_pkthdr.len); 57597c2a697SVANHULLEBUS Yvan 57619ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for bpf processing */ 57719ad9831SBjoern A. Zeeb ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 578bdea400fSAndrew Thompson /* pass the mbuf to enc0 for packet filtering */ 57919ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 580bdea400fSAndrew Thompson goto bad; 581574fde00SAndrey V. Elsukov ip = mtod(m, struct ip *); 582bdea400fSAndrew Thompson #endif 58388768458SSam Leffler /* Do the appropriate encapsulation, if necessary */ 58488768458SSam Leffler if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 58588768458SSam Leffler dst->sa.sa_family != AF_INET || /* PF mismatch */ 58688768458SSam Leffler (dst->sa.sa_family == AF_INET && /* Proxy */ 58788768458SSam Leffler dst->sin.sin_addr.s_addr != INADDR_ANY && 58888768458SSam Leffler dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 58988768458SSam Leffler /* Fix IPv4 header checksum and length */ 59088768458SSam Leffler ip->ip_len = htons(m->m_pkthdr.len); 59188768458SSam Leffler ip->ip_sum = 0; 59288768458SSam Leffler ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 59361f37615SAndrey V. Elsukov error = ipsec_encap(&m, &sav->sah->saidx); 594619764beSAndrey V. Elsukov if (error != 0) { 59561f37615SAndrey V. Elsukov DPRINTF(("%s: encapsulation for SA %s->%s " 59661f37615SAndrey V. Elsukov "SPI 0x%08x failed with error %d\n", __func__, 597962ac6c7SAndrey V. Elsukov ipsec_address(&sav->sah->saidx.src, sbuf, 598962ac6c7SAndrey V. Elsukov sizeof(sbuf)), 599962ac6c7SAndrey V. Elsukov ipsec_address(&sav->sah->saidx.dst, dbuf, 600962ac6c7SAndrey V. Elsukov sizeof(dbuf)), ntohl(sav->spi), error)); 60188768458SSam Leffler goto bad; 60288768458SSam Leffler } 60388768458SSam Leffler } 604bdea400fSAndrew Thompson #ifdef DEV_ENC 605bdea400fSAndrew Thompson /* pass the mbuf to enc0 for bpf processing */ 606aaf2cfc0SVANHULLEBUS Yvan ipsec_bpf(m, sav, sav->sah->saidx.dst.sa.sa_family, ENC_OUT|ENC_AFTER); 60719ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for packet filtering */ 60819ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 60919ad9831SBjoern A. Zeeb goto bad; 610bdea400fSAndrew Thompson #endif 611bdea400fSAndrew Thompson 61288768458SSam Leffler /* 61388768458SSam Leffler * Dispatch to the appropriate IPsec transform logic. The 61488768458SSam Leffler * packet will be returned for transmission after crypto 61561f37615SAndrey V. Elsukov * processing, etc. are completed. 61688768458SSam Leffler * 61788768458SSam Leffler * NB: m & sav are ``passed to caller'' who's reponsible for 61888768458SSam Leffler * for reclaiming their resources. 61988768458SSam Leffler */ 620aaf2cfc0SVANHULLEBUS Yvan switch(dst->sa.sa_family) { 621aaf2cfc0SVANHULLEBUS Yvan case AF_INET: 62288768458SSam Leffler ip = mtod(m, struct ip *); 62388768458SSam Leffler i = ip->ip_hl << 2; 62488768458SSam Leffler off = offsetof(struct ip, ip_p); 625aaf2cfc0SVANHULLEBUS Yvan break; 626aaf2cfc0SVANHULLEBUS Yvan #ifdef INET6 627aaf2cfc0SVANHULLEBUS Yvan case AF_INET6: 628aaf2cfc0SVANHULLEBUS Yvan i = sizeof(struct ip6_hdr); 629aaf2cfc0SVANHULLEBUS Yvan off = offsetof(struct ip6_hdr, ip6_nxt); 630aaf2cfc0SVANHULLEBUS Yvan break; 631aaf2cfc0SVANHULLEBUS Yvan #endif /* INET6 */ 632aaf2cfc0SVANHULLEBUS Yvan default: 633aaf2cfc0SVANHULLEBUS Yvan DPRINTF(("%s: unsupported protocol family %u\n", 634aaf2cfc0SVANHULLEBUS Yvan __func__, dst->sa.sa_family)); 635aaf2cfc0SVANHULLEBUS Yvan error = EPFNOSUPPORT; 6366d120f90SBjoern A. Zeeb IPSECSTAT_INC(ips_out_inval); 637aaf2cfc0SVANHULLEBUS Yvan goto bad; 638aaf2cfc0SVANHULLEBUS Yvan } 63988768458SSam Leffler error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 6409ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 64161f37615SAndrey V. Elsukov return (error); 64288768458SSam Leffler bad: 6439ffa9677SSam Leffler if (isr) 6449ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 64588768458SSam Leffler if (m) 64688768458SSam Leffler m_freem(m); 64788768458SSam Leffler return error; 64888768458SSam Leffler } 64988768458SSam Leffler #endif 65088768458SSam Leffler 651aaf2cfc0SVANHULLEBUS Yvan 65288768458SSam Leffler #ifdef INET6 65388768458SSam Leffler static int 654aaf2cfc0SVANHULLEBUS Yvan in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia) 65588768458SSam Leffler { 656aaf2cfc0SVANHULLEBUS Yvan struct in6_addr ia2; 65788768458SSam Leffler 658aaf2cfc0SVANHULLEBUS Yvan memcpy(&ia2, &sa->sin6_addr, sizeof(ia2)); 659aaf2cfc0SVANHULLEBUS Yvan if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) 660aaf2cfc0SVANHULLEBUS Yvan ia2.s6_addr16[1] = htons(sa->sin6_scope_id); 66188768458SSam Leffler 662aaf2cfc0SVANHULLEBUS Yvan return IN6_ARE_ADDR_EQUAL(ia, &ia2); 66388768458SSam Leffler } 66488768458SSam Leffler 66588768458SSam Leffler /* 666aaf2cfc0SVANHULLEBUS Yvan * IPsec output logic for IPv6. 66788768458SSam Leffler */ 66888768458SSam Leffler int 669962ac6c7SAndrey V. Elsukov ipsec6_process_packet(struct mbuf *m, struct ipsecrequest *isr) 67088768458SSam Leffler { 671962ac6c7SAndrey V. Elsukov char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN]; 67288768458SSam Leffler struct secasindex saidx; 673aaf2cfc0SVANHULLEBUS Yvan struct secasvar *sav; 674aaf2cfc0SVANHULLEBUS Yvan struct ip6_hdr *ip6; 675aaf2cfc0SVANHULLEBUS Yvan int error, i, off; 676aaf2cfc0SVANHULLEBUS Yvan union sockaddr_union *dst; 67788768458SSam Leffler 678aaf2cfc0SVANHULLEBUS Yvan IPSEC_ASSERT(m != NULL, ("ipsec6_process_packet: null mbuf")); 679aaf2cfc0SVANHULLEBUS Yvan IPSEC_ASSERT(isr != NULL, ("ipsec6_process_packet: null isr")); 680923e1044SBjoern A. Zeeb 681923e1044SBjoern A. Zeeb IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 682aaf2cfc0SVANHULLEBUS Yvan 68388768458SSam Leffler isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 6849e3bdedeSBjoern A. Zeeb if (isr == NULL) { 6859e3bdedeSBjoern A. Zeeb if (error != 0) 68688768458SSam Leffler goto bad; 6879e3bdedeSBjoern A. Zeeb return EJUSTRETURN; 6889e3bdedeSBjoern A. Zeeb } 689aaf2cfc0SVANHULLEBUS Yvan sav = isr->sav; 690aaf2cfc0SVANHULLEBUS Yvan dst = &sav->sah->saidx.dst; 691aaf2cfc0SVANHULLEBUS Yvan 69267fd1727SAndrey V. Elsukov ip6 = mtod(m, struct ip6_hdr *); 69367fd1727SAndrey V. Elsukov ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); 69419ad9831SBjoern A. Zeeb #ifdef DEV_ENC 6956ff8af1cSGleb Smirnoff if_inc_counter(encif, IFCOUNTER_OPACKETS, 1); 6966ff8af1cSGleb Smirnoff if_inc_counter(encif, IFCOUNTER_OBYTES, m->m_pkthdr.len); 69797c2a697SVANHULLEBUS Yvan 69819ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for bpf processing */ 69919ad9831SBjoern A. Zeeb ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 70019ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for packet filtering */ 70119ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 70219ad9831SBjoern A. Zeeb goto bad; 703574fde00SAndrey V. Elsukov ip6 = mtod(m, struct ip6_hdr *); 704aaf2cfc0SVANHULLEBUS Yvan #endif /* DEV_ENC */ 70519ad9831SBjoern A. Zeeb 706aaf2cfc0SVANHULLEBUS Yvan /* Do the appropriate encapsulation, if necessary */ 707aaf2cfc0SVANHULLEBUS Yvan if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 708aaf2cfc0SVANHULLEBUS Yvan dst->sa.sa_family != AF_INET6 || /* PF mismatch */ 709aaf2cfc0SVANHULLEBUS Yvan ((dst->sa.sa_family == AF_INET6) && 710aaf2cfc0SVANHULLEBUS Yvan (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) && 711aaf2cfc0SVANHULLEBUS Yvan (!in6_sa_equal_addrwithscope(&dst->sin6, 712aaf2cfc0SVANHULLEBUS Yvan &ip6->ip6_dst)))) { 713aaf2cfc0SVANHULLEBUS Yvan if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { 714aaf2cfc0SVANHULLEBUS Yvan /* No jumbogram support. */ 715aaf2cfc0SVANHULLEBUS Yvan error = ENXIO; /*XXX*/ 71688768458SSam Leffler goto bad; 71788768458SSam Leffler } 71861f37615SAndrey V. Elsukov error = ipsec_encap(&m, &sav->sah->saidx); 71961f37615SAndrey V. Elsukov if (error != 0) { 72061f37615SAndrey V. Elsukov DPRINTF(("%s: encapsulation for SA %s->%s " 72161f37615SAndrey V. Elsukov "SPI 0x%08x failed with error %d\n", __func__, 722962ac6c7SAndrey V. Elsukov ipsec_address(&sav->sah->saidx.src, sbuf, 723962ac6c7SAndrey V. Elsukov sizeof(sbuf)), 724962ac6c7SAndrey V. Elsukov ipsec_address(&sav->sah->saidx.dst, dbuf, 725962ac6c7SAndrey V. Elsukov sizeof(dbuf)), ntohl(sav->spi), error)); 726aaf2cfc0SVANHULLEBUS Yvan goto bad; 727aaf2cfc0SVANHULLEBUS Yvan } 72888768458SSam Leffler } 72988768458SSam Leffler 73019ad9831SBjoern A. Zeeb #ifdef DEV_ENC 731aaf2cfc0SVANHULLEBUS Yvan ipsec_bpf(m, isr->sav, dst->sa.sa_family, ENC_OUT|ENC_AFTER); 73219ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for packet filtering */ 73319ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 73419ad9831SBjoern A. Zeeb goto bad; 735aaf2cfc0SVANHULLEBUS Yvan #endif /* DEV_ENC */ 73619ad9831SBjoern A. Zeeb 737aaf2cfc0SVANHULLEBUS Yvan switch(dst->sa.sa_family) { 738aaf2cfc0SVANHULLEBUS Yvan #ifdef INET 739aaf2cfc0SVANHULLEBUS Yvan case AF_INET: 740aaf2cfc0SVANHULLEBUS Yvan { 741aaf2cfc0SVANHULLEBUS Yvan struct ip *ip; 742aaf2cfc0SVANHULLEBUS Yvan ip = mtod(m, struct ip *); 743aaf2cfc0SVANHULLEBUS Yvan i = ip->ip_hl << 2; 744aaf2cfc0SVANHULLEBUS Yvan off = offsetof(struct ip, ip_p); 745aaf2cfc0SVANHULLEBUS Yvan } 746aaf2cfc0SVANHULLEBUS Yvan break; 747aaf2cfc0SVANHULLEBUS Yvan #endif /* AF_INET */ 748aaf2cfc0SVANHULLEBUS Yvan case AF_INET6: 749aaf2cfc0SVANHULLEBUS Yvan i = sizeof(struct ip6_hdr); 750aaf2cfc0SVANHULLEBUS Yvan off = offsetof(struct ip6_hdr, ip6_nxt); 751aaf2cfc0SVANHULLEBUS Yvan break; 752aaf2cfc0SVANHULLEBUS Yvan default: 753aaf2cfc0SVANHULLEBUS Yvan DPRINTF(("%s: unsupported protocol family %u\n", 754aaf2cfc0SVANHULLEBUS Yvan __func__, dst->sa.sa_family)); 755aaf2cfc0SVANHULLEBUS Yvan error = EPFNOSUPPORT; 756aaf2cfc0SVANHULLEBUS Yvan IPSEC6STAT_INC(ips_out_inval); 757aaf2cfc0SVANHULLEBUS Yvan goto bad; 758aaf2cfc0SVANHULLEBUS Yvan } 759aaf2cfc0SVANHULLEBUS Yvan error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 760923e1044SBjoern A. Zeeb IPSECREQUEST_UNLOCK(isr); 761923e1044SBjoern A. Zeeb return error; 76288768458SSam Leffler bad: 763aaf2cfc0SVANHULLEBUS Yvan 764923e1044SBjoern A. Zeeb if (isr) 765923e1044SBjoern A. Zeeb IPSECREQUEST_UNLOCK(isr); 76688768458SSam Leffler if (m) 76788768458SSam Leffler m_freem(m); 76888768458SSam Leffler return error; 76988768458SSam Leffler } 77088768458SSam Leffler #endif /*INET6*/ 771