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> 47*76039bc8SGleb Smirnoff #include <net/if_var.h> 48b28cd334SBjoern A. Zeeb #include <net/pfil.h> 4988768458SSam Leffler #include <net/route.h> 50eddfbb76SRobert Watson #include <net/vnet.h> 5188768458SSam Leffler 5288768458SSam Leffler #include <netinet/in.h> 5388768458SSam Leffler #include <netinet/in_systm.h> 5488768458SSam Leffler #include <netinet/ip.h> 5588768458SSam Leffler #include <netinet/ip_var.h> 5688768458SSam Leffler #include <netinet/in_var.h> 5788768458SSam Leffler #include <netinet/ip_ecn.h> 5888768458SSam Leffler #ifdef INET6 5988768458SSam Leffler #include <netinet6/ip6_ecn.h> 6088768458SSam Leffler #endif 6188768458SSam Leffler 6288768458SSam Leffler #include <netinet/ip6.h> 6388768458SSam Leffler #ifdef INET6 6488768458SSam Leffler #include <netinet6/ip6_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")); 10788768458SSam Leffler sav = isr->sav; 1089ffa9677SSam Leffler IPSEC_ASSERT(sav != NULL, ("null SA")); 1099ffa9677SSam Leffler IPSEC_ASSERT(sav->sah != NULL, ("null SAH")); 11088768458SSam Leffler 11188768458SSam Leffler saidx = &sav->sah->saidx; 11288768458SSam Leffler switch (saidx->dst.sa.sa_family) { 11388768458SSam Leffler #ifdef INET 11488768458SSam Leffler case AF_INET: 11588768458SSam Leffler /* Fix the header length, for AH processing. */ 11688768458SSam Leffler mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 11788768458SSam Leffler break; 11888768458SSam Leffler #endif /* INET */ 11988768458SSam Leffler #ifdef INET6 12088768458SSam Leffler case AF_INET6: 12188768458SSam Leffler /* Fix the header length, for AH processing. */ 12288768458SSam Leffler if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) { 12388768458SSam Leffler error = ENXIO; 12488768458SSam Leffler goto bad; 12588768458SSam Leffler } 12688768458SSam Leffler if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) { 12788768458SSam Leffler /* No jumbogram support. */ 12888768458SSam Leffler error = ENXIO; /*?*/ 12988768458SSam Leffler goto bad; 13088768458SSam Leffler } 13188768458SSam Leffler mtod(m, struct ip6_hdr *)->ip6_plen = 13288768458SSam Leffler htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 13388768458SSam Leffler break; 13488768458SSam Leffler #endif /* INET6 */ 13588768458SSam Leffler default: 1369ffa9677SSam Leffler DPRINTF(("%s: unknown protocol family %u\n", __func__, 13788768458SSam Leffler saidx->dst.sa.sa_family)); 13888768458SSam Leffler error = ENXIO; 13988768458SSam Leffler goto bad; 14088768458SSam Leffler } 14188768458SSam Leffler 14288768458SSam Leffler /* 14388768458SSam Leffler * Add a record of what we've done or what needs to be done to the 14488768458SSam Leffler * packet. 14588768458SSam Leffler */ 14688768458SSam Leffler mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 14788768458SSam Leffler sizeof(struct tdb_ident), M_NOWAIT); 14888768458SSam Leffler if (mtag == NULL) { 1499ffa9677SSam Leffler DPRINTF(("%s: could not get packet tag\n", __func__)); 15088768458SSam Leffler error = ENOMEM; 15188768458SSam Leffler goto bad; 15288768458SSam Leffler } 15388768458SSam Leffler 15488768458SSam Leffler tdbi = (struct tdb_ident *)(mtag + 1); 15588768458SSam Leffler tdbi->dst = saidx->dst; 15688768458SSam Leffler tdbi->proto = saidx->proto; 15788768458SSam Leffler tdbi->spi = sav->spi; 15888768458SSam Leffler m_tag_prepend(m, mtag); 15988768458SSam Leffler 16088768458SSam Leffler /* 16188768458SSam Leffler * If there's another (bundled) SA to apply, do so. 16288768458SSam Leffler * Note that this puts a burden on the kernel stack size. 16388768458SSam Leffler * If this is a problem we'll need to introduce a queue 16488768458SSam Leffler * to set the packet on so we can unwind the stack before 16588768458SSam Leffler * doing further processing. 16688768458SSam Leffler */ 16788768458SSam Leffler if (isr->next) { 1686659296cSAndrey V. Elsukov IPSECSTAT_INC(ips_out_bundlesa); 169174b0d41SBjoern A. Zeeb /* XXX-BZ currently only support same AF bundles. */ 170db178eb8SBjoern A. Zeeb switch (saidx->dst.sa.sa_family) { 171db178eb8SBjoern A. Zeeb #ifdef INET 172db178eb8SBjoern A. Zeeb case AF_INET: 17388768458SSam Leffler return ipsec4_process_packet(m, isr->next, 0, 0); 174db178eb8SBjoern A. Zeeb /* NOTREACHED */ 175db178eb8SBjoern A. Zeeb #endif 176db178eb8SBjoern A. Zeeb #ifdef notyet 177db178eb8SBjoern A. Zeeb #ifdef INET6 178db178eb8SBjoern A. Zeeb case AF_INET6: 179db178eb8SBjoern A. Zeeb /* XXX */ 180db178eb8SBjoern A. Zeeb ipsec6_output_trans() 181db178eb8SBjoern A. Zeeb ipsec6_output_tunnel() 182db178eb8SBjoern A. Zeeb /* NOTREACHED */ 183db178eb8SBjoern A. Zeeb #endif /* INET6 */ 184db178eb8SBjoern A. Zeeb #endif 185db178eb8SBjoern A. Zeeb default: 186db178eb8SBjoern A. Zeeb DPRINTF(("%s: unknown protocol family %u\n", __func__, 187db178eb8SBjoern A. Zeeb saidx->dst.sa.sa_family)); 188db178eb8SBjoern A. Zeeb error = ENXIO; 189db178eb8SBjoern A. Zeeb goto bad; 190db178eb8SBjoern A. Zeeb } 19188768458SSam Leffler } 1925d1dbc0eSSam Leffler key_sa_recordxfer(sav, m); /* record data transfer */ 19388768458SSam Leffler 19488768458SSam Leffler /* 19588768458SSam Leffler * We're done with IPsec processing, transmit the packet using the 19688768458SSam Leffler * appropriate network protocol (IP or IPv6). SPD lookup will be 19788768458SSam Leffler * performed again there. 19888768458SSam Leffler */ 19988768458SSam Leffler switch (saidx->dst.sa.sa_family) { 20088768458SSam Leffler #ifdef INET 20188768458SSam Leffler case AF_INET: 2027b495c44SVANHULLEBUS Yvan #ifdef IPSEC_NAT_T 2037b495c44SVANHULLEBUS Yvan /* 2047b495c44SVANHULLEBUS Yvan * If NAT-T is enabled, now that all IPsec processing is done 2057b495c44SVANHULLEBUS Yvan * insert UDP encapsulation header after IP header. 2067b495c44SVANHULLEBUS Yvan */ 2077b495c44SVANHULLEBUS Yvan if (sav->natt_type) { 20820472bceSGleb Smirnoff struct ip *ip = mtod(m, struct ip *); 2097b495c44SVANHULLEBUS Yvan #ifdef _IP_VHL 2107b495c44SVANHULLEBUS Yvan const int hlen = IP_VHL_HL(ip->ip_vhl); 2117b495c44SVANHULLEBUS Yvan #else 2127b495c44SVANHULLEBUS Yvan const int hlen = (ip->ip_hl << 2); 2137b495c44SVANHULLEBUS Yvan #endif 2147b495c44SVANHULLEBUS Yvan int size, off; 2157b495c44SVANHULLEBUS Yvan struct mbuf *mi; 2167b495c44SVANHULLEBUS Yvan struct udphdr *udp; 2177b495c44SVANHULLEBUS Yvan 2187b495c44SVANHULLEBUS Yvan size = sizeof(struct udphdr); 2197b495c44SVANHULLEBUS Yvan if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) { 2207b495c44SVANHULLEBUS Yvan /* 2217b495c44SVANHULLEBUS Yvan * draft-ietf-ipsec-nat-t-ike-0[01].txt and 2227b495c44SVANHULLEBUS Yvan * draft-ietf-ipsec-udp-encaps-(00/)01.txt, 2237b495c44SVANHULLEBUS Yvan * ignoring possible AH mode 2247b495c44SVANHULLEBUS Yvan * non-IKE marker + non-ESP marker 2257b495c44SVANHULLEBUS Yvan * from draft-ietf-ipsec-udp-encaps-00.txt. 2267b495c44SVANHULLEBUS Yvan */ 2277b495c44SVANHULLEBUS Yvan size += sizeof(u_int64_t); 2287b495c44SVANHULLEBUS Yvan } 2297b495c44SVANHULLEBUS Yvan mi = m_makespace(m, hlen, size, &off); 2307b495c44SVANHULLEBUS Yvan if (mi == NULL) { 2317b495c44SVANHULLEBUS Yvan DPRINTF(("%s: m_makespace for udphdr failed\n", 2327b495c44SVANHULLEBUS Yvan __func__)); 2337b495c44SVANHULLEBUS Yvan error = ENOBUFS; 2347b495c44SVANHULLEBUS Yvan goto bad; 2357b495c44SVANHULLEBUS Yvan } 2367b495c44SVANHULLEBUS Yvan 2377b495c44SVANHULLEBUS Yvan udp = (struct udphdr *)(mtod(mi, caddr_t) + off); 2387b495c44SVANHULLEBUS Yvan if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 2397b495c44SVANHULLEBUS Yvan udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT); 2407b495c44SVANHULLEBUS Yvan else 2417b495c44SVANHULLEBUS Yvan udp->uh_sport = 2427b495c44SVANHULLEBUS Yvan KEY_PORTFROMSADDR(&sav->sah->saidx.src); 2437b495c44SVANHULLEBUS Yvan udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst); 2447b495c44SVANHULLEBUS Yvan udp->uh_sum = 0; 2457b495c44SVANHULLEBUS Yvan udp->uh_ulen = htons(m->m_pkthdr.len - hlen); 24620472bceSGleb Smirnoff ip->ip_len = htons(m->m_pkthdr.len); 2477b495c44SVANHULLEBUS Yvan ip->ip_p = IPPROTO_UDP; 2487b495c44SVANHULLEBUS Yvan 2497b495c44SVANHULLEBUS Yvan if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) 2507b495c44SVANHULLEBUS Yvan *(u_int64_t *)(udp + 1) = 0; 2517b495c44SVANHULLEBUS Yvan } 2527b495c44SVANHULLEBUS Yvan #endif /* IPSEC_NAT_T */ 2537b495c44SVANHULLEBUS Yvan 25488768458SSam Leffler return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL); 25588768458SSam Leffler #endif /* INET */ 25688768458SSam Leffler #ifdef INET6 25788768458SSam Leffler case AF_INET6: 25888768458SSam Leffler /* 25988768458SSam Leffler * We don't need massage, IPv6 header fields are always in 26088768458SSam Leffler * net endian. 26188768458SSam Leffler */ 26288768458SSam Leffler return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 26388768458SSam Leffler #endif /* INET6 */ 26488768458SSam Leffler } 26588768458SSam Leffler panic("ipsec_process_done"); 26688768458SSam Leffler bad: 26788768458SSam Leffler m_freem(m); 26888768458SSam Leffler return (error); 26988768458SSam Leffler } 27088768458SSam Leffler 27188768458SSam Leffler static struct ipsecrequest * 27288768458SSam Leffler ipsec_nextisr( 27388768458SSam Leffler struct mbuf *m, 27488768458SSam Leffler struct ipsecrequest *isr, 27588768458SSam Leffler int af, 27688768458SSam Leffler struct secasindex *saidx, 27788768458SSam Leffler int *error 27888768458SSam Leffler ) 27988768458SSam Leffler { 280a04d64d8SAndrey V. Elsukov #define IPSEC_OSTAT(name) do { \ 281a04d64d8SAndrey V. Elsukov if (isr->saidx.proto == IPPROTO_ESP) \ 282a04d64d8SAndrey V. Elsukov ESPSTAT_INC(esps_##name); \ 283a04d64d8SAndrey V. Elsukov else if (isr->saidx.proto == IPPROTO_AH)\ 284a04d64d8SAndrey V. Elsukov AHSTAT_INC(ahs_##name); \ 285a04d64d8SAndrey V. Elsukov else \ 286a04d64d8SAndrey V. Elsukov IPCOMPSTAT_INC(ipcomps_##name); \ 287a04d64d8SAndrey V. Elsukov } while (0) 28888768458SSam Leffler struct secasvar *sav; 28988768458SSam Leffler 2909ffa9677SSam Leffler IPSECREQUEST_LOCK_ASSERT(isr); 2919ffa9677SSam Leffler 2929ffa9677SSam Leffler IPSEC_ASSERT(af == AF_INET || af == AF_INET6, 2939ffa9677SSam Leffler ("invalid address family %u", af)); 29488768458SSam Leffler again: 29588768458SSam Leffler /* 29688768458SSam Leffler * Craft SA index to search for proper SA. Note that 29788768458SSam Leffler * we only fillin unspecified SA peers for transport 29888768458SSam Leffler * mode; for tunnel mode they must already be filled in. 29988768458SSam Leffler */ 30088768458SSam Leffler *saidx = isr->saidx; 30188768458SSam Leffler if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) { 30288768458SSam Leffler /* Fillin unspecified SA peers only for transport mode */ 30388768458SSam Leffler if (af == AF_INET) { 30488768458SSam Leffler struct sockaddr_in *sin; 30588768458SSam Leffler struct ip *ip = mtod(m, struct ip *); 30688768458SSam Leffler 30788768458SSam Leffler if (saidx->src.sa.sa_len == 0) { 30888768458SSam Leffler sin = &saidx->src.sin; 30988768458SSam Leffler sin->sin_len = sizeof(*sin); 31088768458SSam Leffler sin->sin_family = AF_INET; 31188768458SSam Leffler sin->sin_port = IPSEC_PORT_ANY; 31288768458SSam Leffler sin->sin_addr = ip->ip_src; 31388768458SSam Leffler } 31488768458SSam Leffler if (saidx->dst.sa.sa_len == 0) { 31588768458SSam Leffler sin = &saidx->dst.sin; 31688768458SSam Leffler sin->sin_len = sizeof(*sin); 31788768458SSam Leffler sin->sin_family = AF_INET; 31888768458SSam Leffler sin->sin_port = IPSEC_PORT_ANY; 31988768458SSam Leffler sin->sin_addr = ip->ip_dst; 32088768458SSam Leffler } 32188768458SSam Leffler } else { 32288768458SSam Leffler struct sockaddr_in6 *sin6; 32388768458SSam Leffler struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 32488768458SSam Leffler 32588768458SSam Leffler if (saidx->src.sin6.sin6_len == 0) { 32688768458SSam Leffler sin6 = (struct sockaddr_in6 *)&saidx->src; 32788768458SSam Leffler sin6->sin6_len = sizeof(*sin6); 32888768458SSam Leffler sin6->sin6_family = AF_INET6; 32988768458SSam Leffler sin6->sin6_port = IPSEC_PORT_ANY; 33088768458SSam Leffler sin6->sin6_addr = ip6->ip6_src; 33188768458SSam Leffler if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 33288768458SSam Leffler /* fix scope id for comparing SPD */ 33388768458SSam Leffler sin6->sin6_addr.s6_addr16[1] = 0; 33488768458SSam Leffler sin6->sin6_scope_id = 33588768458SSam Leffler ntohs(ip6->ip6_src.s6_addr16[1]); 33688768458SSam Leffler } 33788768458SSam Leffler } 33888768458SSam Leffler if (saidx->dst.sin6.sin6_len == 0) { 33988768458SSam Leffler sin6 = (struct sockaddr_in6 *)&saidx->dst; 34088768458SSam Leffler sin6->sin6_len = sizeof(*sin6); 34188768458SSam Leffler sin6->sin6_family = AF_INET6; 34288768458SSam Leffler sin6->sin6_port = IPSEC_PORT_ANY; 34388768458SSam Leffler sin6->sin6_addr = ip6->ip6_dst; 34488768458SSam Leffler if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 34588768458SSam Leffler /* fix scope id for comparing SPD */ 34688768458SSam Leffler sin6->sin6_addr.s6_addr16[1] = 0; 34788768458SSam Leffler sin6->sin6_scope_id = 34888768458SSam Leffler ntohs(ip6->ip6_dst.s6_addr16[1]); 34988768458SSam Leffler } 35088768458SSam Leffler } 35188768458SSam Leffler } 35288768458SSam Leffler } 35388768458SSam Leffler 35488768458SSam Leffler /* 35588768458SSam Leffler * Lookup SA and validate it. 35688768458SSam Leffler */ 35788768458SSam Leffler *error = key_checkrequest(isr, saidx); 35888768458SSam Leffler if (*error != 0) { 35988768458SSam Leffler /* 36088768458SSam Leffler * IPsec processing is required, but no SA found. 36188768458SSam Leffler * I assume that key_acquire() had been called 36288768458SSam Leffler * to get/establish the SA. Here I discard 36388768458SSam Leffler * this packet because it is responsibility for 36488768458SSam Leffler * upper layer to retransmit the packet. 36588768458SSam Leffler */ 3666659296cSAndrey V. Elsukov IPSECSTAT_INC(ips_out_nosa); 36788768458SSam Leffler goto bad; 36888768458SSam Leffler } 36988768458SSam Leffler sav = isr->sav; 3709e3bdedeSBjoern A. Zeeb if (sav == NULL) { 3719ffa9677SSam Leffler IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE, 3729ffa9677SSam Leffler ("no SA found, but required; level %u", 37388768458SSam Leffler ipsec_get_reqlevel(isr))); 3749ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 37588768458SSam Leffler isr = isr->next; 3769e3bdedeSBjoern A. Zeeb /* 3779e3bdedeSBjoern A. Zeeb * If isr is NULL, we found a 'use' policy w/o SA. 3789e3bdedeSBjoern A. Zeeb * Return w/o error and w/o isr so we can drop out 3799e3bdedeSBjoern A. Zeeb * and continue w/o IPsec processing. 3809e3bdedeSBjoern A. Zeeb */ 3819e3bdedeSBjoern A. Zeeb if (isr == NULL) 38288768458SSam Leffler return isr; 3839ffa9677SSam Leffler IPSECREQUEST_LOCK(isr); 38488768458SSam Leffler goto again; 38588768458SSam Leffler } 38688768458SSam Leffler 38788768458SSam Leffler /* 38888768458SSam Leffler * Check system global policy controls. 38988768458SSam Leffler */ 390603724d3SBjoern A. Zeeb if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) || 391603724d3SBjoern A. Zeeb (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) || 392603724d3SBjoern A. Zeeb (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) { 3939ffa9677SSam Leffler DPRINTF(("%s: IPsec outbound packet dropped due" 3949ffa9677SSam Leffler " to policy (check your sysctls)\n", __func__)); 395a04d64d8SAndrey V. Elsukov IPSEC_OSTAT(pdrops); 39688768458SSam Leffler *error = EHOSTUNREACH; 39788768458SSam Leffler goto bad; 39888768458SSam Leffler } 39988768458SSam Leffler 40088768458SSam Leffler /* 40188768458SSam Leffler * Sanity check the SA contents for the caller 40288768458SSam Leffler * before they invoke the xform output method. 40388768458SSam Leffler */ 40488768458SSam Leffler if (sav->tdb_xform == NULL) { 4059ffa9677SSam Leffler DPRINTF(("%s: no transform for SA\n", __func__)); 406a04d64d8SAndrey V. Elsukov IPSEC_OSTAT(noxform); 40788768458SSam Leffler *error = EHOSTUNREACH; 40888768458SSam Leffler goto bad; 40988768458SSam Leffler } 41088768458SSam Leffler return isr; 41188768458SSam Leffler bad: 4129ffa9677SSam Leffler IPSEC_ASSERT(*error != 0, ("error return w/ no error code")); 4139ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 41488768458SSam Leffler return NULL; 41588768458SSam Leffler #undef IPSEC_OSTAT 41688768458SSam Leffler } 41788768458SSam Leffler 41888768458SSam Leffler #ifdef INET 41988768458SSam Leffler /* 42088768458SSam Leffler * IPsec output logic for IPv4. 42188768458SSam Leffler */ 42288768458SSam Leffler int 42388768458SSam Leffler ipsec4_process_packet( 42488768458SSam Leffler struct mbuf *m, 42588768458SSam Leffler struct ipsecrequest *isr, 42688768458SSam Leffler int flags, 42788768458SSam Leffler int tunalready) 42888768458SSam Leffler { 42988768458SSam Leffler struct secasindex saidx; 43088768458SSam Leffler struct secasvar *sav; 43188768458SSam Leffler struct ip *ip; 4326464079fSSam Leffler int error, i, off; 43388768458SSam Leffler 4349ffa9677SSam Leffler IPSEC_ASSERT(m != NULL, ("null mbuf")); 4359ffa9677SSam Leffler IPSEC_ASSERT(isr != NULL, ("null isr")); 43688768458SSam Leffler 4379ffa9677SSam Leffler IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 43888768458SSam Leffler 43988768458SSam Leffler isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error); 4409e3bdedeSBjoern A. Zeeb if (isr == NULL) { 4419e3bdedeSBjoern A. Zeeb if (error != 0) 44288768458SSam Leffler goto bad; 4439e3bdedeSBjoern A. Zeeb return EJUSTRETURN; 4449e3bdedeSBjoern A. Zeeb } 44588768458SSam Leffler 44688768458SSam Leffler sav = isr->sav; 447bdea400fSAndrew Thompson 448bdea400fSAndrew Thompson #ifdef DEV_ENC 44997c2a697SVANHULLEBUS Yvan encif->if_opackets++; 45097c2a697SVANHULLEBUS Yvan encif->if_obytes += m->m_pkthdr.len; 45197c2a697SVANHULLEBUS Yvan 45219ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for bpf processing */ 45319ad9831SBjoern A. Zeeb ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_BEFORE); 454bdea400fSAndrew Thompson /* pass the mbuf to enc0 for packet filtering */ 45519ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 456bdea400fSAndrew Thompson goto bad; 457bdea400fSAndrew Thompson #endif 458bdea400fSAndrew Thompson 45988768458SSam Leffler if (!tunalready) { 46088768458SSam Leffler union sockaddr_union *dst = &sav->sah->saidx.dst; 46188768458SSam Leffler int setdf; 46288768458SSam Leffler 46388768458SSam Leffler /* 46488768458SSam Leffler * Collect IP_DF state from the outer header. 46588768458SSam Leffler */ 46688768458SSam Leffler if (dst->sa.sa_family == AF_INET) { 46788768458SSam Leffler if (m->m_len < sizeof (struct ip) && 46888768458SSam Leffler (m = m_pullup(m, sizeof (struct ip))) == NULL) { 46988768458SSam Leffler error = ENOBUFS; 47088768458SSam Leffler goto bad; 47188768458SSam Leffler } 47288768458SSam Leffler ip = mtod(m, struct ip *); 47388768458SSam Leffler /* Honor system-wide control of how to handle IP_DF */ 474603724d3SBjoern A. Zeeb switch (V_ip4_ipsec_dfbit) { 47588768458SSam Leffler case 0: /* clear in outer header */ 47688768458SSam Leffler case 1: /* set in outer header */ 477603724d3SBjoern A. Zeeb setdf = V_ip4_ipsec_dfbit; 47888768458SSam Leffler break; 47988768458SSam Leffler default: /* propagate to outer header */ 48088768458SSam Leffler setdf = ntohs(ip->ip_off & IP_DF); 48188768458SSam Leffler break; 48288768458SSam Leffler } 48388768458SSam Leffler } else { 48488768458SSam Leffler ip = NULL; /* keep compiler happy */ 48588768458SSam Leffler setdf = 0; 48688768458SSam Leffler } 48788768458SSam Leffler /* Do the appropriate encapsulation, if necessary */ 48888768458SSam Leffler if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */ 48988768458SSam Leffler dst->sa.sa_family != AF_INET || /* PF mismatch */ 49088768458SSam Leffler #if 0 49188768458SSam Leffler (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */ 49288768458SSam Leffler sav->tdb_xform->xf_type == XF_IP4 || /* ditto */ 49388768458SSam Leffler #endif 49488768458SSam Leffler (dst->sa.sa_family == AF_INET && /* Proxy */ 49588768458SSam Leffler dst->sin.sin_addr.s_addr != INADDR_ANY && 49688768458SSam Leffler dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) { 49788768458SSam Leffler struct mbuf *mp; 49888768458SSam Leffler 49988768458SSam Leffler /* Fix IPv4 header checksum and length */ 50088768458SSam Leffler if (m->m_len < sizeof (struct ip) && 50188768458SSam Leffler (m = m_pullup(m, sizeof (struct ip))) == NULL) { 50288768458SSam Leffler error = ENOBUFS; 50388768458SSam Leffler goto bad; 50488768458SSam Leffler } 50588768458SSam Leffler ip = mtod(m, struct ip *); 50688768458SSam Leffler ip->ip_len = htons(m->m_pkthdr.len); 50788768458SSam Leffler ip->ip_sum = 0; 50888768458SSam Leffler #ifdef _IP_VHL 50988768458SSam Leffler if (ip->ip_vhl == IP_VHL_BORING) 51088768458SSam Leffler ip->ip_sum = in_cksum_hdr(ip); 51188768458SSam Leffler else 51288768458SSam Leffler ip->ip_sum = in_cksum(m, 51388768458SSam Leffler _IP_VHL_HL(ip->ip_vhl) << 2); 51488768458SSam Leffler #else 51588768458SSam Leffler ip->ip_sum = in_cksum(m, ip->ip_hl << 2); 51688768458SSam Leffler #endif 51788768458SSam Leffler 51888768458SSam Leffler /* Encapsulate the packet */ 51988768458SSam Leffler error = ipip_output(m, isr, &mp, 0, 0); 52088768458SSam Leffler if (mp == NULL && !error) { 52188768458SSam Leffler /* Should never happen. */ 5229ffa9677SSam Leffler DPRINTF(("%s: ipip_output returns no mbuf and " 5239ffa9677SSam Leffler "no error!", __func__)); 52488768458SSam Leffler error = EFAULT; 52588768458SSam Leffler } 52688768458SSam Leffler if (error) { 527b5b47bc6SSam Leffler if (mp) { 528b5b47bc6SSam Leffler /* XXX: Should never happen! */ 52988768458SSam Leffler m_freem(mp); 530b5b47bc6SSam Leffler } 531b5b47bc6SSam Leffler m = NULL; /* ipip_output() already freed it */ 53288768458SSam Leffler goto bad; 53388768458SSam Leffler } 53488768458SSam Leffler m = mp, mp = NULL; 53588768458SSam Leffler /* 53688768458SSam Leffler * ipip_output clears IP_DF in the new header. If 53788768458SSam Leffler * we need to propagate IP_DF from the outer header, 53888768458SSam Leffler * then we have to do it here. 53988768458SSam Leffler * 54088768458SSam Leffler * XXX shouldn't assume what ipip_output does. 54188768458SSam Leffler */ 54288768458SSam Leffler if (dst->sa.sa_family == AF_INET && setdf) { 54388768458SSam Leffler if (m->m_len < sizeof (struct ip) && 54488768458SSam Leffler (m = m_pullup(m, sizeof (struct ip))) == NULL) { 54588768458SSam Leffler error = ENOBUFS; 54688768458SSam Leffler goto bad; 54788768458SSam Leffler } 54888768458SSam Leffler ip = mtod(m, struct ip *); 54988768458SSam Leffler ip->ip_off = ntohs(ip->ip_off); 55088768458SSam Leffler ip->ip_off |= IP_DF; 55188768458SSam Leffler ip->ip_off = htons(ip->ip_off); 55288768458SSam Leffler } 55388768458SSam Leffler } 55488768458SSam Leffler } 55588768458SSam Leffler 556bdea400fSAndrew Thompson #ifdef DEV_ENC 557bdea400fSAndrew Thompson /* pass the mbuf to enc0 for bpf processing */ 55819ad9831SBjoern A. Zeeb ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_AFTER); 55919ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for packet filtering */ 56019ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 56119ad9831SBjoern A. Zeeb goto bad; 562bdea400fSAndrew Thompson #endif 563bdea400fSAndrew Thompson 56488768458SSam Leffler /* 56588768458SSam Leffler * Dispatch to the appropriate IPsec transform logic. The 56688768458SSam Leffler * packet will be returned for transmission after crypto 56788768458SSam Leffler * processing, etc. are completed. For encapsulation we 56888768458SSam Leffler * bypass this call because of the explicit call done above 56988768458SSam Leffler * (necessary to deal with IP_DF handling for IPv4). 57088768458SSam Leffler * 57188768458SSam Leffler * NB: m & sav are ``passed to caller'' who's reponsible for 57288768458SSam Leffler * for reclaiming their resources. 57388768458SSam Leffler */ 57488768458SSam Leffler if (sav->tdb_xform->xf_type != XF_IP4) { 57588768458SSam Leffler ip = mtod(m, struct ip *); 57688768458SSam Leffler i = ip->ip_hl << 2; 57788768458SSam Leffler off = offsetof(struct ip, ip_p); 57888768458SSam Leffler error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off); 57988768458SSam Leffler } else { 58088768458SSam Leffler error = ipsec_process_done(m, isr); 58188768458SSam Leffler } 5829ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 58388768458SSam Leffler return error; 58488768458SSam Leffler bad: 5859ffa9677SSam Leffler if (isr) 5869ffa9677SSam Leffler IPSECREQUEST_UNLOCK(isr); 58788768458SSam Leffler if (m) 58888768458SSam Leffler m_freem(m); 58988768458SSam Leffler return error; 59088768458SSam Leffler } 59188768458SSam Leffler #endif 59288768458SSam Leffler 59388768458SSam Leffler #ifdef INET6 59488768458SSam Leffler /* 59588768458SSam Leffler * Chop IP6 header from the payload. 59688768458SSam Leffler */ 59788768458SSam Leffler static struct mbuf * 59888768458SSam Leffler ipsec6_splithdr(struct mbuf *m) 59988768458SSam Leffler { 60088768458SSam Leffler struct mbuf *mh; 60188768458SSam Leffler struct ip6_hdr *ip6; 60288768458SSam Leffler int hlen; 60388768458SSam Leffler 6049ffa9677SSam Leffler IPSEC_ASSERT(m->m_len >= sizeof (struct ip6_hdr), 6059ffa9677SSam Leffler ("first mbuf too short, len %u", m->m_len)); 60688768458SSam Leffler ip6 = mtod(m, struct ip6_hdr *); 60788768458SSam Leffler hlen = sizeof(struct ip6_hdr); 60888768458SSam Leffler if (m->m_len > hlen) { 609eb1b1807SGleb Smirnoff MGETHDR(mh, M_NOWAIT, MT_DATA); 61088768458SSam Leffler if (!mh) { 61188768458SSam Leffler m_freem(m); 61288768458SSam Leffler return NULL; 61388768458SSam Leffler } 6149967cafcSSam Leffler M_MOVE_PKTHDR(mh, m); 61588768458SSam Leffler MH_ALIGN(mh, hlen); 61688768458SSam Leffler m->m_len -= hlen; 61788768458SSam Leffler m->m_data += hlen; 61888768458SSam Leffler mh->m_next = m; 61988768458SSam Leffler m = mh; 62088768458SSam Leffler m->m_len = hlen; 62188768458SSam Leffler bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen); 62288768458SSam Leffler } else if (m->m_len < hlen) { 62388768458SSam Leffler m = m_pullup(m, hlen); 62488768458SSam Leffler if (!m) 62588768458SSam Leffler return NULL; 62688768458SSam Leffler } 62788768458SSam Leffler return m; 62888768458SSam Leffler } 62988768458SSam Leffler 63088768458SSam Leffler /* 63188768458SSam Leffler * IPsec output logic for IPv6, transport mode. 63288768458SSam Leffler */ 63388768458SSam Leffler int 63488768458SSam Leffler ipsec6_output_trans( 63588768458SSam Leffler struct ipsec_output_state *state, 63688768458SSam Leffler u_char *nexthdrp, 63788768458SSam Leffler struct mbuf *mprev, 63888768458SSam Leffler struct secpolicy *sp, 63988768458SSam Leffler int flags, 64088768458SSam Leffler int *tun) 64188768458SSam Leffler { 64288768458SSam Leffler struct ipsecrequest *isr; 64388768458SSam Leffler struct secasindex saidx; 64488768458SSam Leffler int error = 0; 64588768458SSam Leffler struct mbuf *m; 64688768458SSam Leffler 6479ffa9677SSam Leffler IPSEC_ASSERT(state != NULL, ("null state")); 6489ffa9677SSam Leffler IPSEC_ASSERT(state->m != NULL, ("null m")); 6499ffa9677SSam Leffler IPSEC_ASSERT(nexthdrp != NULL, ("null nexthdrp")); 6509ffa9677SSam Leffler IPSEC_ASSERT(mprev != NULL, ("null mprev")); 6519ffa9677SSam Leffler IPSEC_ASSERT(sp != NULL, ("null sp")); 6529ffa9677SSam Leffler IPSEC_ASSERT(tun != NULL, ("null tun")); 65388768458SSam Leffler 65488768458SSam Leffler KEYDEBUG(KEYDEBUG_IPSEC_DATA, 65549c407e3SBjoern A. Zeeb printf("%s: applied SP\n", __func__); 65688768458SSam Leffler kdebug_secpolicy(sp)); 65788768458SSam Leffler 65888768458SSam Leffler isr = sp->req; 65988768458SSam Leffler if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 66088768458SSam Leffler /* the rest will be handled by ipsec6_output_tunnel() */ 66188768458SSam Leffler *tun = 1; /* need tunnel-mode processing */ 66288768458SSam Leffler return 0; 66388768458SSam Leffler } 66488768458SSam Leffler 66588768458SSam Leffler *tun = 0; 66688768458SSam Leffler m = state->m; 66788768458SSam Leffler 6682cb64cb2SGeorge V. Neville-Neil IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 66988768458SSam Leffler isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 67088768458SSam Leffler if (isr == NULL) { 6719e3bdedeSBjoern A. Zeeb if (error != 0) { 67288768458SSam Leffler #ifdef notdef 67388768458SSam Leffler /* XXX should notification be done for all errors ? */ 67488768458SSam Leffler /* 67588768458SSam Leffler * Notify the fact that the packet is discarded 67688768458SSam Leffler * to ourselves. I believe this is better than 67788768458SSam Leffler * just silently discarding. (jinmei@kame.net) 67888768458SSam Leffler * XXX: should we restrict the error to TCP packets? 67988768458SSam Leffler * XXX: should we directly notify sockets via 68088768458SSam Leffler * pfctlinputs? 68188768458SSam Leffler */ 68288768458SSam Leffler icmp6_error(m, ICMP6_DST_UNREACH, 68388768458SSam Leffler ICMP6_DST_UNREACH_ADMIN, 0); 68488768458SSam Leffler m = NULL; /* NB: icmp6_error frees mbuf */ 68588768458SSam Leffler #endif 68688768458SSam Leffler goto bad; 68788768458SSam Leffler } 6889e3bdedeSBjoern A. Zeeb return EJUSTRETURN; 6899e3bdedeSBjoern A. Zeeb } 69088768458SSam Leffler 6912cb64cb2SGeorge V. Neville-Neil error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 69288768458SSam Leffler sizeof (struct ip6_hdr), 6932cb64cb2SGeorge V. Neville-Neil offsetof(struct ip6_hdr, 6942cb64cb2SGeorge V. Neville-Neil ip6_nxt)); 6952cb64cb2SGeorge V. Neville-Neil IPSECREQUEST_UNLOCK(isr); 6962cb64cb2SGeorge V. Neville-Neil return error; 69788768458SSam Leffler bad: 6982cb64cb2SGeorge V. Neville-Neil if (isr) 6992cb64cb2SGeorge V. Neville-Neil IPSECREQUEST_UNLOCK(isr); 70088768458SSam Leffler if (m) 70188768458SSam Leffler m_freem(m); 70288768458SSam Leffler state->m = NULL; 70388768458SSam Leffler return error; 70488768458SSam Leffler } 70588768458SSam Leffler 70688768458SSam Leffler static int 70788768458SSam Leffler ipsec6_encapsulate(struct mbuf *m, struct secasvar *sav) 70888768458SSam Leffler { 70988768458SSam Leffler struct ip6_hdr *oip6; 71088768458SSam Leffler struct ip6_hdr *ip6; 71188768458SSam Leffler size_t plen; 71288768458SSam Leffler 71388768458SSam Leffler /* can't tunnel between different AFs */ 71488768458SSam Leffler if (sav->sah->saidx.src.sa.sa_family != AF_INET6 || 71588768458SSam Leffler sav->sah->saidx.dst.sa.sa_family != AF_INET6) { 71688768458SSam Leffler m_freem(m); 71788768458SSam Leffler return EINVAL; 71888768458SSam Leffler } 7192cb64cb2SGeorge V. Neville-Neil IPSEC_ASSERT(m->m_len == sizeof (struct ip6_hdr), 7209ffa9677SSam Leffler ("mbuf wrong size; len %u", m->m_len)); 72188768458SSam Leffler 72288768458SSam Leffler 72388768458SSam Leffler /* 72488768458SSam Leffler * grow the mbuf to accomodate the new IPv6 header. 72588768458SSam Leffler */ 72688768458SSam Leffler plen = m->m_pkthdr.len; 72788768458SSam Leffler if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) { 72888768458SSam Leffler struct mbuf *n; 729eb1b1807SGleb Smirnoff MGET(n, M_NOWAIT, MT_DATA); 73088768458SSam Leffler if (!n) { 73188768458SSam Leffler m_freem(m); 73288768458SSam Leffler return ENOBUFS; 73388768458SSam Leffler } 73488768458SSam Leffler n->m_len = sizeof(struct ip6_hdr); 73588768458SSam Leffler n->m_next = m->m_next; 73688768458SSam Leffler m->m_next = n; 73788768458SSam Leffler m->m_pkthdr.len += sizeof(struct ip6_hdr); 73888768458SSam Leffler oip6 = mtod(n, struct ip6_hdr *); 73988768458SSam Leffler } else { 74088768458SSam Leffler m->m_next->m_len += sizeof(struct ip6_hdr); 74188768458SSam Leffler m->m_next->m_data -= sizeof(struct ip6_hdr); 74288768458SSam Leffler m->m_pkthdr.len += sizeof(struct ip6_hdr); 74388768458SSam Leffler oip6 = mtod(m->m_next, struct ip6_hdr *); 74488768458SSam Leffler } 74588768458SSam Leffler ip6 = mtod(m, struct ip6_hdr *); 746e1fec84aSDag-Erling Smørgrav bcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr)); 74788768458SSam Leffler 74888768458SSam Leffler /* Fake link-local scope-class addresses */ 74988768458SSam Leffler if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) 75088768458SSam Leffler oip6->ip6_src.s6_addr16[1] = 0; 75188768458SSam Leffler if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) 75288768458SSam Leffler oip6->ip6_dst.s6_addr16[1] = 0; 75388768458SSam Leffler 75488768458SSam Leffler /* construct new IPv6 header. see RFC 2401 5.1.2.2 */ 75588768458SSam Leffler /* ECN consideration. */ 756603724d3SBjoern A. Zeeb ip6_ecn_ingress(V_ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow); 75788768458SSam Leffler if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr)) 75888768458SSam Leffler ip6->ip6_plen = htons(plen); 75988768458SSam Leffler else { 76088768458SSam Leffler /* ip6->ip6_plen will be updated in ip6_output() */ 76188768458SSam Leffler } 76288768458SSam Leffler ip6->ip6_nxt = IPPROTO_IPV6; 7632cb64cb2SGeorge V. Neville-Neil ip6->ip6_src = sav->sah->saidx.src.sin6.sin6_addr; 7642cb64cb2SGeorge V. Neville-Neil ip6->ip6_dst = sav->sah->saidx.dst.sin6.sin6_addr; 76588768458SSam Leffler ip6->ip6_hlim = IPV6_DEFHLIM; 76688768458SSam Leffler 76788768458SSam Leffler /* XXX Should ip6_src be updated later ? */ 76888768458SSam Leffler 76988768458SSam Leffler return 0; 77088768458SSam Leffler } 77188768458SSam Leffler 77288768458SSam Leffler /* 77388768458SSam Leffler * IPsec output logic for IPv6, tunnel mode. 77488768458SSam Leffler */ 77588768458SSam Leffler int 77688768458SSam Leffler ipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int flags) 77788768458SSam Leffler { 77888768458SSam Leffler struct ip6_hdr *ip6; 77988768458SSam Leffler struct ipsecrequest *isr; 78088768458SSam Leffler struct secasindex saidx; 78188768458SSam Leffler int error; 78288768458SSam Leffler struct sockaddr_in6 *dst6; 78388768458SSam Leffler struct mbuf *m; 78488768458SSam Leffler 7859ffa9677SSam Leffler IPSEC_ASSERT(state != NULL, ("null state")); 7869ffa9677SSam Leffler IPSEC_ASSERT(state->m != NULL, ("null m")); 7879ffa9677SSam Leffler IPSEC_ASSERT(sp != NULL, ("null sp")); 78888768458SSam Leffler 78988768458SSam Leffler KEYDEBUG(KEYDEBUG_IPSEC_DATA, 79049c407e3SBjoern A. Zeeb printf("%s: applied SP\n", __func__); 79188768458SSam Leffler kdebug_secpolicy(sp)); 79288768458SSam Leffler 79388768458SSam Leffler m = state->m; 79488768458SSam Leffler /* 79588768458SSam Leffler * transport mode ipsec (before the 1st tunnel mode) is already 79688768458SSam Leffler * processed by ipsec6_output_trans(). 79788768458SSam Leffler */ 79888768458SSam Leffler for (isr = sp->req; isr; isr = isr->next) { 79988768458SSam Leffler if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 80088768458SSam Leffler break; 80188768458SSam Leffler } 802923e1044SBjoern A. Zeeb 803923e1044SBjoern A. Zeeb IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */ 80488768458SSam Leffler isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error); 8059e3bdedeSBjoern A. Zeeb if (isr == NULL) { 8069e3bdedeSBjoern A. Zeeb if (error != 0) 80788768458SSam Leffler goto bad; 8089e3bdedeSBjoern A. Zeeb return EJUSTRETURN; 8099e3bdedeSBjoern A. Zeeb } 81088768458SSam Leffler 81119ad9831SBjoern A. Zeeb #ifdef DEV_ENC 81297c2a697SVANHULLEBUS Yvan encif->if_opackets++; 81397c2a697SVANHULLEBUS Yvan encif->if_obytes += m->m_pkthdr.len; 81497c2a697SVANHULLEBUS Yvan 81519ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for bpf processing */ 81619ad9831SBjoern A. Zeeb ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_BEFORE); 81719ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for packet filtering */ 81819ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0) 81919ad9831SBjoern A. Zeeb goto bad; 82019ad9831SBjoern A. Zeeb #endif 82119ad9831SBjoern A. Zeeb 82288768458SSam Leffler /* 82388768458SSam Leffler * There may be the case that SA status will be changed when 82488768458SSam Leffler * we are refering to one. So calling splsoftnet(). 82588768458SSam Leffler */ 82688768458SSam Leffler if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 82788768458SSam Leffler /* 82888768458SSam Leffler * build IPsec tunnel. 82988768458SSam Leffler */ 83088768458SSam Leffler /* XXX should be processed with other familiy */ 83188768458SSam Leffler if (isr->sav->sah->saidx.src.sa.sa_family != AF_INET6) { 8329ffa9677SSam Leffler ipseclog((LOG_ERR, "%s: family mismatched between " 8339ffa9677SSam Leffler "inner and outer, spi=%u\n", __func__, 83488768458SSam Leffler ntohl(isr->sav->spi))); 8356659296cSAndrey V. Elsukov IPSEC6STAT_INC(ips_out_inval); 83688768458SSam Leffler error = EAFNOSUPPORT; 83788768458SSam Leffler goto bad; 83888768458SSam Leffler } 83988768458SSam Leffler 84088768458SSam Leffler m = ipsec6_splithdr(m); 84188768458SSam Leffler if (!m) { 8426659296cSAndrey V. Elsukov IPSEC6STAT_INC(ips_out_nomem); 84388768458SSam Leffler error = ENOMEM; 84488768458SSam Leffler goto bad; 84588768458SSam Leffler } 84688768458SSam Leffler error = ipsec6_encapsulate(m, isr->sav); 84788768458SSam Leffler if (error) { 84888768458SSam Leffler m = NULL; 84988768458SSam Leffler goto bad; 85088768458SSam Leffler } 85188768458SSam Leffler ip6 = mtod(m, struct ip6_hdr *); 85288768458SSam Leffler 8534a85b5e2SBjoern A. Zeeb state->ro = 8544a85b5e2SBjoern A. Zeeb (struct route *)&isr->sav->sah->route_cache.sin6_route; 85588768458SSam Leffler state->dst = (struct sockaddr *)&state->ro->ro_dst; 85688768458SSam Leffler dst6 = (struct sockaddr_in6 *)state->dst; 85788768458SSam Leffler if (state->ro->ro_rt 85888768458SSam Leffler && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0 85988768458SSam Leffler || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) { 86088768458SSam Leffler RTFREE(state->ro->ro_rt); 86188768458SSam Leffler state->ro->ro_rt = NULL; 86288768458SSam Leffler } 86389d18518SBjoern A. Zeeb if (state->ro->ro_rt == NULL) { 86488768458SSam Leffler bzero(dst6, sizeof(*dst6)); 86588768458SSam Leffler dst6->sin6_family = AF_INET6; 86688768458SSam Leffler dst6->sin6_len = sizeof(*dst6); 86788768458SSam Leffler dst6->sin6_addr = ip6->ip6_dst; 86881d5d46bSBjoern A. Zeeb rtalloc_ign_fib(state->ro, 0UL, M_GETFIB(m)); 86988768458SSam Leffler } 87089d18518SBjoern A. Zeeb if (state->ro->ro_rt == NULL) { 8719cb8d207SAndrey V. Elsukov IP6STAT_INC(ip6s_noroute); 8726659296cSAndrey V. Elsukov IPSEC6STAT_INC(ips_out_noroute); 87388768458SSam Leffler error = EHOSTUNREACH; 87488768458SSam Leffler goto bad; 87588768458SSam Leffler } 87688768458SSam Leffler 87788768458SSam Leffler /* adjust state->dst if tunnel endpoint is offlink */ 878acf456a0SBjoern A. Zeeb if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) 87988768458SSam Leffler state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway; 88088768458SSam Leffler } 88188768458SSam Leffler 88288768458SSam Leffler m = ipsec6_splithdr(m); 88388768458SSam Leffler if (!m) { 8846659296cSAndrey V. Elsukov IPSEC6STAT_INC(ips_out_nomem); 88588768458SSam Leffler error = ENOMEM; 88688768458SSam Leffler goto bad; 88788768458SSam Leffler } 88888768458SSam Leffler ip6 = mtod(m, struct ip6_hdr *); 88919ad9831SBjoern A. Zeeb 89019ad9831SBjoern A. Zeeb #ifdef DEV_ENC 89119ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for bpf processing */ 89219ad9831SBjoern A. Zeeb ipsec_bpf(m, isr->sav, AF_INET6, ENC_OUT|ENC_AFTER); 89319ad9831SBjoern A. Zeeb /* pass the mbuf to enc0 for packet filtering */ 89419ad9831SBjoern A. Zeeb if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0) 89519ad9831SBjoern A. Zeeb goto bad; 89619ad9831SBjoern A. Zeeb #endif 89719ad9831SBjoern A. Zeeb 898923e1044SBjoern A. Zeeb error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL, 89988768458SSam Leffler sizeof (struct ip6_hdr), 90088768458SSam Leffler offsetof(struct ip6_hdr, ip6_nxt)); 901923e1044SBjoern A. Zeeb IPSECREQUEST_UNLOCK(isr); 902923e1044SBjoern A. Zeeb return error; 90388768458SSam Leffler bad: 904923e1044SBjoern A. Zeeb if (isr) 905923e1044SBjoern A. Zeeb IPSECREQUEST_UNLOCK(isr); 90688768458SSam Leffler if (m) 90788768458SSam Leffler m_freem(m); 90888768458SSam Leffler state->m = NULL; 90988768458SSam Leffler return error; 91088768458SSam Leffler } 91188768458SSam Leffler #endif /*INET6*/ 912