xref: /netbsd-src/sys/netipsec/ipsec_output.c (revision 481d3881954fd794ca5f2d880b68c53a5db8620e)
1*481d3881Srin /*	$NetBSD: ipsec_output.c,v 1.87 2024/07/05 04:31:54 rin Exp $	*/
2fdbf515aSthorpej 
379002044Smaxv /*
4fdbf515aSthorpej  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
5fdbf515aSthorpej  * All rights reserved.
6fdbf515aSthorpej  *
7fdbf515aSthorpej  * Redistribution and use in source and binary forms, with or without
8fdbf515aSthorpej  * modification, are permitted provided that the following conditions
9fdbf515aSthorpej  * are met:
10fdbf515aSthorpej  * 1. Redistributions of source code must retain the above copyright
11fdbf515aSthorpej  *    notice, this list of conditions and the following disclaimer.
12fdbf515aSthorpej  * 2. Redistributions in binary form must reproduce the above copyright
13fdbf515aSthorpej  *    notice, this list of conditions and the following disclaimer in the
14fdbf515aSthorpej  *    documentation and/or other materials provided with the distribution.
15fdbf515aSthorpej  *
16fdbf515aSthorpej  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17fdbf515aSthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18fdbf515aSthorpej  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19fdbf515aSthorpej  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20fdbf515aSthorpej  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21fdbf515aSthorpej  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22fdbf515aSthorpej  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23fdbf515aSthorpej  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24fdbf515aSthorpej  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25fdbf515aSthorpej  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26fdbf515aSthorpej  * SUCH DAMAGE.
27fdbf515aSthorpej  *
28e2c8a664Smaxv  * $FreeBSD: sys/netipsec/ipsec_output.c,v 1.3.2.2 2003/03/28 20:32:53 sam Exp $
29fdbf515aSthorpej  */
3074029031Sjonathan 
3174029031Sjonathan #include <sys/cdefs.h>
32*481d3881Srin __KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.87 2024/07/05 04:31:54 rin Exp $");
3374029031Sjonathan 
3480d40a78Sozaki-r #if defined(_KERNEL_OPT)
3574029031Sjonathan #include "opt_inet.h"
36e1c9808fSozaki-r #include "opt_net_mpsafe.h"
3780d40a78Sozaki-r #endif
3874029031Sjonathan 
3974029031Sjonathan #include <sys/param.h>
4074029031Sjonathan #include <sys/systm.h>
4174029031Sjonathan #include <sys/mbuf.h>
4274029031Sjonathan #include <sys/domain.h>
4374029031Sjonathan #include <sys/protosw.h>
4474029031Sjonathan #include <sys/socket.h>
4574029031Sjonathan #include <sys/errno.h>
4674029031Sjonathan #include <sys/syslog.h>
4774029031Sjonathan 
4874029031Sjonathan #include <net/if.h>
4974029031Sjonathan #include <net/route.h>
5074029031Sjonathan 
5174029031Sjonathan #include <netinet/in.h>
5274029031Sjonathan #include <netinet/in_systm.h>
5374029031Sjonathan #include <netinet/ip.h>
5474029031Sjonathan #include <netinet/ip_var.h>
5574029031Sjonathan #include <netinet/in_var.h>
5674029031Sjonathan #include <netinet/ip_ecn.h>
5774029031Sjonathan 
5874029031Sjonathan #include <netinet/ip6.h>
5974029031Sjonathan #ifdef INET6
6074029031Sjonathan #include <netinet6/ip6_var.h>
6174029031Sjonathan #endif
6274029031Sjonathan #include <netinet/in_pcb.h>
6374029031Sjonathan #ifdef INET6
6474029031Sjonathan #include <netinet/icmp6.h>
6574029031Sjonathan #endif
664ddfe916Sdegroote #include <netinet/udp.h>
6774029031Sjonathan 
6874029031Sjonathan #include <netipsec/ipsec.h>
6985b3ba5bSjonathan #include <netipsec/ipsec_var.h>
70caf49ea5Sthorpej #include <netipsec/ipsec_private.h>
7174029031Sjonathan #ifdef INET6
7274029031Sjonathan #include <netipsec/ipsec6.h>
7374029031Sjonathan #endif
7474029031Sjonathan #include <netipsec/ah_var.h>
7574029031Sjonathan #include <netipsec/esp_var.h>
7674029031Sjonathan #include <netipsec/ipcomp_var.h>
7774029031Sjonathan 
7874029031Sjonathan #include <netipsec/xform.h>
7974029031Sjonathan 
809355900eStls #include <netipsec/key.h>
819355900eStls #include <netipsec/keydb.h>
829355900eStls #include <netipsec/key_debug.h>
8374029031Sjonathan 
8464e64168Sozaki-r static percpu_t *ipsec_rtcache_percpu __cacheline_aligned;
8561e79ba3Sdegroote 
8661e79ba3Sdegroote /*
8761e79ba3Sdegroote  * Add a IPSEC_OUT_DONE tag to mark that we have finished the ipsec processing
8861e79ba3Sdegroote  * It will be used by ip{,6}_output to check if we have already or not
8961e79ba3Sdegroote  * processed this packet.
9061e79ba3Sdegroote  */
9161e79ba3Sdegroote static int
ipsec_register_done(struct mbuf * m,int * error)9261e79ba3Sdegroote ipsec_register_done(struct mbuf *m, int *error)
9361e79ba3Sdegroote {
9461e79ba3Sdegroote 	struct m_tag *mtag;
9561e79ba3Sdegroote 
9661e79ba3Sdegroote 	mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, 0, M_NOWAIT);
9761e79ba3Sdegroote 	if (mtag == NULL) {
98290dc492Sozaki-r 		IPSECLOG(LOG_DEBUG, "could not get packet tag\n");
9961e79ba3Sdegroote 		*error = ENOMEM;
10061e79ba3Sdegroote 		return -1;
10161e79ba3Sdegroote 	}
10261e79ba3Sdegroote 
10361e79ba3Sdegroote 	m_tag_prepend(m, mtag);
10461e79ba3Sdegroote 	return 0;
10561e79ba3Sdegroote }
10661e79ba3Sdegroote 
10755718e80Sdegroote static int
ipsec_reinject_ipstack(struct mbuf * m,int af,int flags)108c535599fSknakahara ipsec_reinject_ipstack(struct mbuf *m, int af, int flags)
10955718e80Sdegroote {
110f26d4ea4Sozaki-r 	int rv = -1;
11164e64168Sozaki-r 	struct route *ro;
11255718e80Sdegroote 
113f26d4ea4Sozaki-r 	KASSERT(af == AF_INET || af == AF_INET6);
114f26d4ea4Sozaki-r 
115cead3b88Sozaki-r 	KERNEL_LOCK_UNLESS_NET_MPSAFE();
116e524fb36Sozaki-r 	ro = rtcache_percpu_getref(ipsec_rtcache_percpu);
117f26d4ea4Sozaki-r 	switch (af) {
118f26d4ea4Sozaki-r #ifdef INET
119f26d4ea4Sozaki-r 	case AF_INET:
12064e64168Sozaki-r 		rv = ip_output(m, NULL, ro, IP_RAWOUTPUT|IP_NOIPNEWID,
1217f3d4048Splunky 		    NULL, NULL);
122f26d4ea4Sozaki-r 		break;
123e1c9808fSozaki-r #endif
12455718e80Sdegroote #ifdef INET6
12555718e80Sdegroote 	case AF_INET6:
12655718e80Sdegroote 		/*
12755718e80Sdegroote 		 * We don't need massage, IPv6 header fields are always in
12855718e80Sdegroote 		 * net endian.
12955718e80Sdegroote 		 */
130c535599fSknakahara 		rv = ip6_output(m, NULL, ro, flags, NULL, NULL, NULL);
131f26d4ea4Sozaki-r 		break;
132f26d4ea4Sozaki-r #endif
133f26d4ea4Sozaki-r 	}
134e524fb36Sozaki-r 	rtcache_percpu_putref(ipsec_rtcache_percpu);
135cead3b88Sozaki-r 	KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
13655718e80Sdegroote 
137f26d4ea4Sozaki-r 	return rv;
13855718e80Sdegroote }
13955718e80Sdegroote 
14074029031Sjonathan int
ipsec_process_done(struct mbuf * m,const struct ipsecrequest * isr,struct secasvar * sav,int flags)14183c2b87aSozaki-r ipsec_process_done(struct mbuf *m, const struct ipsecrequest *isr,
142c535599fSknakahara     struct secasvar *sav, int flags)
14374029031Sjonathan {
14474029031Sjonathan 	struct secasindex *saidx;
14574029031Sjonathan 	int error;
1464ddfe916Sdegroote #ifdef INET
1474ddfe916Sdegroote 	struct ip *ip;
14879002044Smaxv #endif
1494ddfe916Sdegroote #ifdef INET6
1504ddfe916Sdegroote 	struct ip6_hdr *ip6;
15179002044Smaxv #endif
1524ddfe916Sdegroote 	struct mbuf *mo;
1534ddfe916Sdegroote 	struct udphdr *udp = NULL;
154890dda53Sknakahara 	int hlen, roff, iphlen;
15574029031Sjonathan 
1562620e166Sozaki-r 	KASSERT(m != NULL);
1572620e166Sozaki-r 	KASSERT(isr != NULL);
1582620e166Sozaki-r 	KASSERT(sav != NULL);
15974029031Sjonathan 
16074029031Sjonathan 	saidx = &sav->sah->saidx;
1614ddfe916Sdegroote 
1624ddfe916Sdegroote 	if (sav->natt_type != 0) {
1634ddfe916Sdegroote 		hlen = sizeof(struct udphdr);
1644ddfe916Sdegroote 
165890dda53Sknakahara 		switch (saidx->dst.sa.sa_family) {
166890dda53Sknakahara #ifdef INET
167890dda53Sknakahara 		case AF_INET:
168890dda53Sknakahara 			ip = mtod(m, struct ip *);
1694ddfe916Sdegroote 			mo = m_makespace(m, sizeof(struct ip), hlen, &roff);
170890dda53Sknakahara 			iphlen = ip->ip_hl << 2;
171890dda53Sknakahara 			break;
172890dda53Sknakahara #endif
173890dda53Sknakahara #ifdef INET6
174890dda53Sknakahara 		case AF_INET6:
175890dda53Sknakahara 			ip6 = mtod(m, struct ip6_hdr *);
176890dda53Sknakahara 			mo = m_makespace(m, sizeof(struct ip6_hdr), hlen, &roff);
177890dda53Sknakahara 			iphlen = sizeof(*ip6);
178890dda53Sknakahara 			break;
179890dda53Sknakahara #endif
180890dda53Sknakahara 		default:
181890dda53Sknakahara 			IPSECLOG(LOG_DEBUG, "unknown protocol family %u\n",
182890dda53Sknakahara 			    saidx->dst.sa.sa_family);
183890dda53Sknakahara 			error = ENXIO;
184890dda53Sknakahara 			goto bad;
185890dda53Sknakahara 		}
186890dda53Sknakahara 
1874ddfe916Sdegroote 		if (mo == NULL) {
188dd8c81f5Sryo 			char buf[IPSEC_ADDRSTRLEN];
189290dc492Sozaki-r 			IPSECLOG(LOG_DEBUG,
190290dc492Sozaki-r 			    "failed to inject %u byte UDP for SA %s/%08lx\n",
191dd8c81f5Sryo 			    hlen, ipsec_address(&saidx->dst, buf, sizeof(buf)),
192290dc492Sozaki-r 			    (u_long)ntohl(sav->spi));
1934ddfe916Sdegroote 			error = ENOBUFS;
1944ddfe916Sdegroote 			goto bad;
1954ddfe916Sdegroote 		}
1964ddfe916Sdegroote 
1974ddfe916Sdegroote 		udp = (struct udphdr *)(mtod(mo, char *) + roff);
1984ddfe916Sdegroote 		udp->uh_sport = key_portfromsaddr(&saidx->src);
1994ddfe916Sdegroote 		udp->uh_dport = key_portfromsaddr(&saidx->dst);
2004ddfe916Sdegroote 		udp->uh_sum = 0;
201890dda53Sknakahara 		udp->uh_ulen = htons(m->m_pkthdr.len - iphlen);
2024ddfe916Sdegroote 	}
2034ddfe916Sdegroote 
2041e45b2f1Smaxv 	/*
2051e45b2f1Smaxv 	 * Fix the header length, for AH processing.
2061e45b2f1Smaxv 	 */
20774029031Sjonathan 	switch (saidx->dst.sa.sa_family) {
20874029031Sjonathan #ifdef INET
20974029031Sjonathan 	case AF_INET:
2104ddfe916Sdegroote 		ip = mtod(m, struct ip *);
2114ddfe916Sdegroote 		ip->ip_len = htons(m->m_pkthdr.len);
212890dda53Sknakahara 		/* IPv4 packet does not have to be set UDP checksum. */
2134ddfe916Sdegroote 		if (sav->natt_type != 0)
2144ddfe916Sdegroote 			ip->ip_p = IPPROTO_UDP;
21574029031Sjonathan 		break;
21679002044Smaxv #endif
21774029031Sjonathan #ifdef INET6
21874029031Sjonathan 	case AF_INET6:
21974029031Sjonathan 		if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) {
22074029031Sjonathan 			error = ENXIO;
22174029031Sjonathan 			goto bad;
22274029031Sjonathan 		}
22374029031Sjonathan 		if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
22474029031Sjonathan 			/* No jumbogram support. */
22574029031Sjonathan 			error = ENXIO;	/*?*/
22674029031Sjonathan 			goto bad;
22774029031Sjonathan 		}
2284ddfe916Sdegroote 		ip6 = mtod(m, struct ip6_hdr *);
2294ddfe916Sdegroote 		ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
230890dda53Sknakahara 		/* IPv6 packet should be set UDP checksum. */
231890dda53Sknakahara 		if (sav->natt_type != 0) {
2324ddfe916Sdegroote 			ip6->ip6_nxt = IPPROTO_UDP;
233890dda53Sknakahara 			ipsec6_udp_cksum(m);
234890dda53Sknakahara 		}
23574029031Sjonathan 		break;
23679002044Smaxv #endif
23774029031Sjonathan 	default:
238290dc492Sozaki-r 		IPSECLOG(LOG_DEBUG, "unknown protocol family %u\n",
239290dc492Sozaki-r 		    saidx->dst.sa.sa_family);
24074029031Sjonathan 		error = ENXIO;
24174029031Sjonathan 		goto bad;
24274029031Sjonathan 	}
24374029031Sjonathan 
244e790ebf1Sdrochner 	key_sa_recordxfer(sav, m);
245e790ebf1Sdrochner 
24674029031Sjonathan 	/*
24774029031Sjonathan 	 * If there's another (bundled) SA to apply, do so.
24874029031Sjonathan 	 * Note that this puts a burden on the kernel stack size.
24974029031Sjonathan 	 * If this is a problem we'll need to introduce a queue
25074029031Sjonathan 	 * to set the packet on so we can unwind the stack before
25174029031Sjonathan 	 * doing further processing.
25274029031Sjonathan 	 */
25374029031Sjonathan 	if (isr->next) {
254caf49ea5Sthorpej 		IPSEC_STATINC(IPSEC_STAT_OUT_BUNDLESA);
255e2211411Sdegroote 		switch (saidx->dst.sa.sa_family) {
256e2211411Sdegroote #ifdef INET
257e2211411Sdegroote 		case AF_INET:
258ea86b0a9Sozaki-r 			return ipsec4_process_packet(m, isr->next, NULL);
25979002044Smaxv #endif
260e2211411Sdegroote #ifdef INET6
261e2211411Sdegroote 		case AF_INET6:
262c535599fSknakahara 			return ipsec6_process_packet(m, isr->next, flags);
26379002044Smaxv #endif
264e2211411Sdegroote 		default:
265290dc492Sozaki-r 			IPSECLOG(LOG_DEBUG, "unknown protocol family %u\n",
266290dc492Sozaki-r 			    saidx->dst.sa.sa_family);
267e2211411Sdegroote 			error = ENXIO;
268e2211411Sdegroote 			goto bad;
269e2211411Sdegroote 		}
27074029031Sjonathan 	}
27174029031Sjonathan 
27274029031Sjonathan 	/*
2731e45b2f1Smaxv 	 * We're done with IPsec processing, mark the packet as processed,
2741e45b2f1Smaxv 	 * and transmit it using the appropriate network protocol
2751e45b2f1Smaxv 	 * (IPv4/IPv6).
27674029031Sjonathan 	 */
27761e79ba3Sdegroote 
27861e79ba3Sdegroote 	if (ipsec_register_done(m, &error) < 0)
27961e79ba3Sdegroote 		goto bad;
28061e79ba3Sdegroote 
281c535599fSknakahara 	return ipsec_reinject_ipstack(m, saidx->dst.sa.sa_family, flags);
28279002044Smaxv 
28374029031Sjonathan bad:
28474029031Sjonathan 	m_freem(m);
28579002044Smaxv 	return error;
28674029031Sjonathan }
28774029031Sjonathan 
288289fe532Sozaki-r static void
ipsec_fill_saidx_bymbuf(struct secasindex * saidx,const struct mbuf * m,const int af)289289fe532Sozaki-r ipsec_fill_saidx_bymbuf(struct secasindex *saidx, const struct mbuf *m,
290289fe532Sozaki-r     const int af)
29174029031Sjonathan {
29258c56020Sknakahara 	struct m_tag *mtag;
29358c56020Sknakahara 	u_int16_t natt_src = IPSEC_PORT_ANY;
29458c56020Sknakahara 	u_int16_t natt_dst = IPSEC_PORT_ANY;
29558c56020Sknakahara 
29658c56020Sknakahara 	/*
29758c56020Sknakahara 	 * For NAT-T enabled ipsecif(4), set NAT-T port numbers
29858c56020Sknakahara 	 * even if the saidx uses transport mode.
29958c56020Sknakahara 	 *
30058c56020Sknakahara 	 * See also ipsecif[46]_output().
30158c56020Sknakahara 	 */
30258c56020Sknakahara 	mtag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS);
30358c56020Sknakahara 	if (mtag) {
30458c56020Sknakahara 		u_int16_t *natt_ports;
30558c56020Sknakahara 
30658c56020Sknakahara 		natt_ports = (u_int16_t *)(mtag + 1);
30758c56020Sknakahara 		natt_src = natt_ports[1];
30858c56020Sknakahara 		natt_dst = natt_ports[0];
30958c56020Sknakahara 	}
310caf49ea5Sthorpej 
31174029031Sjonathan 	if (af == AF_INET) {
31274029031Sjonathan 		struct sockaddr_in *sin;
31374029031Sjonathan 		struct ip *ip = mtod(m, struct ip *);
31474029031Sjonathan 
31574029031Sjonathan 		if (saidx->src.sa.sa_len == 0) {
31674029031Sjonathan 			sin = &saidx->src.sin;
31774029031Sjonathan 			sin->sin_len = sizeof(*sin);
31874029031Sjonathan 			sin->sin_family = AF_INET;
31958c56020Sknakahara 			sin->sin_port = natt_src;
32074029031Sjonathan 			sin->sin_addr = ip->ip_src;
32174029031Sjonathan 		}
32274029031Sjonathan 		if (saidx->dst.sa.sa_len == 0) {
32374029031Sjonathan 			sin = &saidx->dst.sin;
32474029031Sjonathan 			sin->sin_len = sizeof(*sin);
32574029031Sjonathan 			sin->sin_family = AF_INET;
32658c56020Sknakahara 			sin->sin_port = natt_dst;
32774029031Sjonathan 			sin->sin_addr = ip->ip_dst;
32874029031Sjonathan 		}
32974029031Sjonathan 	} else {
33074029031Sjonathan 		struct sockaddr_in6 *sin6;
33174029031Sjonathan 		struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
33274029031Sjonathan 
33374029031Sjonathan 		if (saidx->src.sin6.sin6_len == 0) {
33474029031Sjonathan 			sin6 = (struct sockaddr_in6 *)&saidx->src;
33574029031Sjonathan 			sin6->sin6_len = sizeof(*sin6);
33674029031Sjonathan 			sin6->sin6_family = AF_INET6;
33758c56020Sknakahara 			sin6->sin6_port = natt_src;
33874029031Sjonathan 			sin6->sin6_addr = ip6->ip6_src;
33974029031Sjonathan 			if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
34074029031Sjonathan 				/* fix scope id for comparing SPD */
34174029031Sjonathan 				sin6->sin6_addr.s6_addr16[1] = 0;
34274029031Sjonathan 				sin6->sin6_scope_id =
34374029031Sjonathan 				    ntohs(ip6->ip6_src.s6_addr16[1]);
34474029031Sjonathan 			}
34574029031Sjonathan 		}
34674029031Sjonathan 		if (saidx->dst.sin6.sin6_len == 0) {
34774029031Sjonathan 			sin6 = (struct sockaddr_in6 *)&saidx->dst;
34874029031Sjonathan 			sin6->sin6_len = sizeof(*sin6);
34974029031Sjonathan 			sin6->sin6_family = AF_INET6;
35058c56020Sknakahara 			sin6->sin6_port = natt_dst;
35174029031Sjonathan 			sin6->sin6_addr = ip6->ip6_dst;
35274029031Sjonathan 			if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
35374029031Sjonathan 				/* fix scope id for comparing SPD */
35474029031Sjonathan 				sin6->sin6_addr.s6_addr16[1] = 0;
35574029031Sjonathan 				sin6->sin6_scope_id =
35674029031Sjonathan 				    ntohs(ip6->ip6_dst.s6_addr16[1]);
35774029031Sjonathan 			}
35874029031Sjonathan 		}
35974029031Sjonathan 	}
36074029031Sjonathan }
36174029031Sjonathan 
36213270c39Sozaki-r struct secasvar *
ipsec_lookup_sa(const struct ipsecrequest * isr,const struct mbuf * m)36313270c39Sozaki-r ipsec_lookup_sa(const struct ipsecrequest *isr, const struct mbuf *m)
36413270c39Sozaki-r {
36513270c39Sozaki-r 	struct secasindex saidx;
36613270c39Sozaki-r 
36713270c39Sozaki-r 	saidx = isr->saidx;
36813270c39Sozaki-r 	if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
36913270c39Sozaki-r 		/* Fillin unspecified SA peers only for transport mode */
37013270c39Sozaki-r 		ipsec_fill_saidx_bymbuf(&saidx, m, isr->saidx.dst.sa.sa_family);
37113270c39Sozaki-r 	}
37213270c39Sozaki-r 
37313270c39Sozaki-r 	return key_lookup_sa_bysaidx(&saidx);
37413270c39Sozaki-r }
37513270c39Sozaki-r 
37674029031Sjonathan /*
377289fe532Sozaki-r  * ipsec_nextisr can return :
378289fe532Sozaki-r  * - isr == NULL and error != 0 => something is bad : the packet must be
379289fe532Sozaki-r  *   discarded
380289fe532Sozaki-r  * - isr == NULL and error == 0 => no more rules to apply, ipsec processing
381289fe532Sozaki-r  *   is done, reinject it in ip stack
382289fe532Sozaki-r  * - isr != NULL (error == 0) => we need to apply one rule to the packet
383289fe532Sozaki-r  */
38483c2b87aSozaki-r static const struct ipsecrequest *
ipsec_nextisr(struct mbuf * m,const struct ipsecrequest * isr,int af,int * error,struct secasvar ** ret)38579002044Smaxv ipsec_nextisr(struct mbuf *m, const struct ipsecrequest *isr, int af,
38679002044Smaxv     int *error, struct secasvar **ret)
387289fe532Sozaki-r {
388289fe532Sozaki-r #define	IPSEC_OSTAT(type)						\
389289fe532Sozaki-r do {									\
390289fe532Sozaki-r 	switch (isr->saidx.proto) {					\
391289fe532Sozaki-r 	case IPPROTO_ESP:						\
392289fe532Sozaki-r 		ESP_STATINC(ESP_STAT_ ## type);				\
393289fe532Sozaki-r 		break;							\
394289fe532Sozaki-r 	case IPPROTO_AH:						\
395289fe532Sozaki-r 		AH_STATINC(AH_STAT_ ## type);				\
396289fe532Sozaki-r 		break;							\
397289fe532Sozaki-r 	default:							\
398289fe532Sozaki-r 		IPCOMP_STATINC(IPCOMP_STAT_ ## type);			\
399289fe532Sozaki-r 		break;							\
400289fe532Sozaki-r 	}								\
401289fe532Sozaki-r } while (/*CONSTCOND*/0)
402289fe532Sozaki-r 
403289fe532Sozaki-r 	struct secasvar *sav = NULL;
4047f4c0fa0Sozaki-r 	struct secasindex saidx;
405289fe532Sozaki-r 
406289fe532Sozaki-r 	KASSERTMSG(af == AF_INET || af == AF_INET6,
407289fe532Sozaki-r 	    "invalid address family %u", af);
408289fe532Sozaki-r again:
409289fe532Sozaki-r 	/*
410289fe532Sozaki-r 	 * Craft SA index to search for proper SA.  Note that
411289fe532Sozaki-r 	 * we only fillin unspecified SA peers for transport
412289fe532Sozaki-r 	 * mode; for tunnel mode they must already be filled in.
413289fe532Sozaki-r 	 */
4147f4c0fa0Sozaki-r 	saidx = isr->saidx;
415289fe532Sozaki-r 	if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
416289fe532Sozaki-r 		/* Fillin unspecified SA peers only for transport mode */
4177f4c0fa0Sozaki-r 		ipsec_fill_saidx_bymbuf(&saidx, m, af);
418289fe532Sozaki-r 	}
419289fe532Sozaki-r 
420289fe532Sozaki-r 	/*
42174029031Sjonathan 	 * Lookup SA and validate it.
42274029031Sjonathan 	 */
4237f4c0fa0Sozaki-r 	*error = key_checkrequest(isr, &saidx, &sav);
42474029031Sjonathan 	if (*error != 0) {
42574029031Sjonathan 		/*
42674029031Sjonathan 		 * IPsec processing is required, but no SA found.
42774029031Sjonathan 		 * I assume that key_acquire() had been called
42874029031Sjonathan 		 * to get/establish the SA. Here I discard
42974029031Sjonathan 		 * this packet because it is responsibility for
43074029031Sjonathan 		 * upper layer to retransmit the packet.
43174029031Sjonathan 		 */
432caf49ea5Sthorpej 		IPSEC_STATINC(IPSEC_STAT_OUT_NOSA);
43374029031Sjonathan 		goto bad;
43474029031Sjonathan 	}
43555718e80Sdegroote 	/* sav may be NULL here if we have an USE rule */
43655718e80Sdegroote 	if (sav == NULL) {
4372620e166Sozaki-r 		KASSERTMSG(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE,
4382620e166Sozaki-r 		    "no SA found, but required; level %u",
4392620e166Sozaki-r 		    ipsec_get_reqlevel(isr));
44074029031Sjonathan 		isr = isr->next;
44155718e80Sdegroote 		/*
4421e45b2f1Smaxv 		 * No more rules to apply, return NULL isr and no error.
4431e45b2f1Smaxv 		 * It can happen when the last rules are USE rules.
44479002044Smaxv 		 */
44574029031Sjonathan 		if (isr == NULL) {
44638b8f795Sozaki-r 			*ret = NULL;
44755718e80Sdegroote 			*error = 0;
44874029031Sjonathan 			return isr;
44974029031Sjonathan 		}
45074029031Sjonathan 		goto again;
45174029031Sjonathan 	}
45274029031Sjonathan 
45374029031Sjonathan 	/*
45474029031Sjonathan 	 * Check system global policy controls.
45574029031Sjonathan 	 */
45674029031Sjonathan 	if ((isr->saidx.proto == IPPROTO_ESP && !esp_enable) ||
45774029031Sjonathan 	    (isr->saidx.proto == IPPROTO_AH && !ah_enable) ||
45874029031Sjonathan 	    (isr->saidx.proto == IPPROTO_IPCOMP && !ipcomp_enable)) {
459290dc492Sozaki-r 		IPSECLOG(LOG_DEBUG, "IPsec outbound packet dropped due"
460290dc492Sozaki-r 		    " to policy (check your sysctls)\n");
461fc624c4aSozaki-r 		IPSEC_OSTAT(PDROPS);
46274029031Sjonathan 		*error = EHOSTUNREACH;
4638be5cabcSozaki-r 		KEY_SA_UNREF(&sav);
46474029031Sjonathan 		goto bad;
46574029031Sjonathan 	}
46674029031Sjonathan 
46774029031Sjonathan 	/*
46874029031Sjonathan 	 * Sanity check the SA contents for the caller
46974029031Sjonathan 	 * before they invoke the xform output method.
47074029031Sjonathan 	 */
471765c1e87Sozaki-r 	KASSERT(sav->tdb_xform != NULL);
47238b8f795Sozaki-r 	*ret = sav;
47374029031Sjonathan 	return isr;
47479002044Smaxv 
47574029031Sjonathan bad:
4762620e166Sozaki-r 	KASSERTMSG(*error != 0, "error return w/ no error code");
47774029031Sjonathan 	return NULL;
47874029031Sjonathan #undef IPSEC_OSTAT
47974029031Sjonathan }
48074029031Sjonathan 
48174029031Sjonathan #ifdef INET
48274029031Sjonathan /*
48374029031Sjonathan  * IPsec output logic for IPv4.
48474029031Sjonathan  */
48574029031Sjonathan int
ipsec4_process_packet(struct mbuf * m,const struct ipsecrequest * isr,u_long * mtu)48683c2b87aSozaki-r ipsec4_process_packet(struct mbuf *m, const struct ipsecrequest *isr,
487ea86b0a9Sozaki-r     u_long *mtu)
48874029031Sjonathan {
48938b8f795Sozaki-r 	struct secasvar *sav = NULL;
49074029031Sjonathan 	struct ip *ip;
491986909fbSozaki-r 	int error, i, off;
4929ceb805eSozaki-r 	union sockaddr_union *dst;
4939ceb805eSozaki-r 	int setdf;
49474029031Sjonathan 
4952620e166Sozaki-r 	KASSERT(m != NULL);
4966c5d24edSmaxv 	KASSERT(m->m_nextpkt == NULL);
4972620e166Sozaki-r 	KASSERT(isr != NULL);
49874029031Sjonathan 
49938b8f795Sozaki-r 	isr = ipsec_nextisr(m, isr, AF_INET, &error, &sav);
50055718e80Sdegroote 	if (isr == NULL) {
50155718e80Sdegroote 		if (error != 0) {
50274029031Sjonathan 			goto bad;
50355718e80Sdegroote 		} else {
50455718e80Sdegroote 			if (ipsec_register_done(m, &error) < 0)
50555718e80Sdegroote 				goto bad;
50655718e80Sdegroote 
507c535599fSknakahara 			return ipsec_reinject_ipstack(m, AF_INET, 0);
50855718e80Sdegroote 		}
50955718e80Sdegroote 	}
51038b8f795Sozaki-r 	KASSERT(sav != NULL);
51179002044Smaxv 
512052a8431Smaxv 	if (m->m_len < sizeof(struct ip) &&
513052a8431Smaxv 	    (m = m_pullup(m, sizeof(struct ip))) == NULL) {
514052a8431Smaxv 		error = ENOBUFS;
515052a8431Smaxv 		goto unrefsav;
516052a8431Smaxv 	}
517052a8431Smaxv 
518ea86b0a9Sozaki-r 	/*
519ea86b0a9Sozaki-r 	 * Check if we need to handle NAT-T fragmentation.
520ea86b0a9Sozaki-r 	 */
521ea86b0a9Sozaki-r 	if (isr == isr->sp->req) { /* Check only if called from ipsec4_output */
522ea86b0a9Sozaki-r 		KASSERT(mtu != NULL);
523ea86b0a9Sozaki-r 		ip = mtod(m, struct ip *);
524f645db7aSmaxv 		if (!(sav->natt_type & UDP_ENCAP_ESPINUDP)) {
525ea86b0a9Sozaki-r 			goto noneed;
526ea86b0a9Sozaki-r 		}
527ea86b0a9Sozaki-r 		if (ntohs(ip->ip_len) <= sav->esp_frag)
528ea86b0a9Sozaki-r 			goto noneed;
529ea86b0a9Sozaki-r 		*mtu = sav->esp_frag;
5308be5cabcSozaki-r 		KEY_SA_UNREF(&sav);
531ea86b0a9Sozaki-r 		return 0;
532ea86b0a9Sozaki-r 	}
533ea86b0a9Sozaki-r noneed:
5349ceb805eSozaki-r 	dst = &sav->sah->saidx.dst;
53574029031Sjonathan 
53674029031Sjonathan 	/*
53774029031Sjonathan 	 * Collect IP_DF state from the outer header.
53874029031Sjonathan 	 */
53974029031Sjonathan 	if (dst->sa.sa_family == AF_INET) {
54074029031Sjonathan 		ip = mtod(m, struct ip *);
54174029031Sjonathan 		/* Honor system-wide control of how to handle IP_DF */
54274029031Sjonathan 		switch (ip4_ipsec_dfbit) {
54374029031Sjonathan 		case 0:			/* clear in outer header */
54474029031Sjonathan 		case 1:			/* set in outer header */
54574029031Sjonathan 			setdf = ip4_ipsec_dfbit;
54674029031Sjonathan 			break;
54774029031Sjonathan 		default:		/* propagate to outer header */
5482ea4c766Sjonathan 			setdf = ip->ip_off;
5492ea4c766Sjonathan 			setdf = ntohs(setdf);
5502ea4c766Sjonathan 			setdf = htons(setdf & IP_DF);
55174029031Sjonathan 			break;
55274029031Sjonathan 		}
55374029031Sjonathan 	} else {
55474029031Sjonathan 		ip = NULL;		/* keep compiler happy */
55574029031Sjonathan 		setdf = 0;
55674029031Sjonathan 	}
55779002044Smaxv 
55874029031Sjonathan 	/* Do the appropriate encapsulation, if necessary */
55974029031Sjonathan 	if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
56074029031Sjonathan 	    dst->sa.sa_family != AF_INET ||	    /* PF mismatch */
56174029031Sjonathan 	    (dst->sa.sa_family == AF_INET &&	    /* Proxy */
56274029031Sjonathan 	     dst->sin.sin_addr.s_addr != INADDR_ANY &&
56374029031Sjonathan 	     dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
56474029031Sjonathan 		struct mbuf *mp;
56574029031Sjonathan 
56674029031Sjonathan 		/* Fix IPv4 header checksum and length */
56774029031Sjonathan 		ip = mtod(m, struct ip *);
56874029031Sjonathan 		ip->ip_len = htons(m->m_pkthdr.len);
56974029031Sjonathan 		ip->ip_sum = 0;
57074029031Sjonathan 		ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
57174029031Sjonathan 
57274029031Sjonathan 		/* Encapsulate the packet */
57320668b06Smaxv 		error = ipip_output(m, sav, &mp);
57474029031Sjonathan 		if (mp == NULL && !error) {
57574029031Sjonathan 			/* Should never happen. */
576290dc492Sozaki-r 			IPSECLOG(LOG_DEBUG,
577290dc492Sozaki-r 			    "ipip_output returns no mbuf and no error!");
57874029031Sjonathan 			error = EFAULT;
57974029031Sjonathan 		}
58074029031Sjonathan 		if (error) {
581f8d2d740Sscw 			if (mp) {
582f8d2d740Sscw 				/* XXX: Should never happen! */
58374029031Sjonathan 				m_freem(mp);
584f8d2d740Sscw 			}
585f8d2d740Sscw 			m = NULL; /* ipip_output() already freed it */
58638b8f795Sozaki-r 			goto unrefsav;
58774029031Sjonathan 		}
58874029031Sjonathan 		m = mp, mp = NULL;
58979002044Smaxv 
59074029031Sjonathan 		/*
59174029031Sjonathan 		 * ipip_output clears IP_DF in the new header.  If
59274029031Sjonathan 		 * we need to propagate IP_DF from the outer header,
59374029031Sjonathan 		 * then we have to do it here.
59474029031Sjonathan 		 *
59574029031Sjonathan 		 * XXX shouldn't assume what ipip_output does.
59674029031Sjonathan 		 */
59774029031Sjonathan 		if (dst->sa.sa_family == AF_INET && setdf) {
59874029031Sjonathan 			if (m->m_len < sizeof(struct ip) &&
59974029031Sjonathan 			    (m = m_pullup(m, sizeof(struct ip))) == NULL) {
60074029031Sjonathan 				error = ENOBUFS;
60138b8f795Sozaki-r 				goto unrefsav;
60274029031Sjonathan 			}
60374029031Sjonathan 			ip = mtod(m, struct ip *);
604ef67739aSozaki-r 			ip->ip_off |= htons(IP_DF);
60574029031Sjonathan 		}
60674029031Sjonathan 	}
60774029031Sjonathan 
60874029031Sjonathan 	/*
60974029031Sjonathan 	 * Dispatch to the appropriate IPsec transform logic.  The
61074029031Sjonathan 	 * packet will be returned for transmission after crypto
61174029031Sjonathan 	 * processing, etc. are completed.  For encapsulation we
61274029031Sjonathan 	 * bypass this call because of the explicit call done above
61374029031Sjonathan 	 * (necessary to deal with IP_DF handling for IPv4).
61474029031Sjonathan 	 *
61575d2abaeSandvar 	 * NB: m & sav are ``passed to caller'' who's responsible for
61674029031Sjonathan 	 *     for reclaiming their resources.
61774029031Sjonathan 	 */
61874029031Sjonathan 	if (sav->tdb_xform->xf_type != XF_IP4) {
619a46f4db6Sdrochner 		if (dst->sa.sa_family == AF_INET) {
62074029031Sjonathan 			ip = mtod(m, struct ip *);
62174029031Sjonathan 			i = ip->ip_hl << 2;
62274029031Sjonathan 			off = offsetof(struct ip, ip_p);
623a46f4db6Sdrochner 		} else {
624a46f4db6Sdrochner 			i = sizeof(struct ip6_hdr);
625a46f4db6Sdrochner 			off = offsetof(struct ip6_hdr, ip6_nxt);
626a46f4db6Sdrochner 		}
627c535599fSknakahara 		error = (*sav->tdb_xform->xf_output)(m, isr, sav, i, off, 0);
62874029031Sjonathan 	} else {
629c535599fSknakahara 		error = ipsec_process_done(m, isr, sav, 0);
63074029031Sjonathan 	}
6318be5cabcSozaki-r 	KEY_SA_UNREF(&sav);
63274029031Sjonathan 	return error;
63379002044Smaxv 
63438b8f795Sozaki-r unrefsav:
6358be5cabcSozaki-r 	KEY_SA_UNREF(&sav);
63674029031Sjonathan bad:
63774029031Sjonathan 	m_freem(m);
63874029031Sjonathan 	return error;
63974029031Sjonathan }
64074029031Sjonathan #endif
64174029031Sjonathan 
64274029031Sjonathan #ifdef INET6
643a7d0cc88Smaxv static int
compute_ipsec_pos(struct mbuf * m,int * i,int * off)644cf21c579Sdrochner compute_ipsec_pos(struct mbuf *m, int *i, int *off)
645cf21c579Sdrochner {
646cf21c579Sdrochner 	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
647cf21c579Sdrochner 	struct ip6_ext ip6e;
648cf21c579Sdrochner 	int dstopt = 0;
64979002044Smaxv 	int nxt;
650cf21c579Sdrochner 
651cf21c579Sdrochner 	*i = sizeof(struct ip6_hdr);
652cf21c579Sdrochner 	*off = offsetof(struct ip6_hdr, ip6_nxt);
653cf21c579Sdrochner 	nxt = ip6->ip6_nxt;
654cf21c579Sdrochner 
655cf21c579Sdrochner 	/*
656cf21c579Sdrochner 	 * chase mbuf chain to find the appropriate place to
657cf21c579Sdrochner 	 * put AH/ESP/IPcomp header.
658cf21c579Sdrochner 	 *     IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
659cf21c579Sdrochner 	 */
660a7d0cc88Smaxv 	while (1) {
661cf21c579Sdrochner 		switch (nxt) {
662cf21c579Sdrochner 		case IPPROTO_AH:
663cf21c579Sdrochner 		case IPPROTO_ESP:
664cf21c579Sdrochner 		case IPPROTO_IPCOMP:
665cf21c579Sdrochner 			/*
66656fc5b5dSmaxv 			 * We should not skip security header added
667cf21c579Sdrochner 			 * beforehand.
668cf21c579Sdrochner 			 */
669a7d0cc88Smaxv 			return 0;
670cf21c579Sdrochner 
671cf21c579Sdrochner 		case IPPROTO_HOPOPTS:
672cf21c579Sdrochner 		case IPPROTO_DSTOPTS:
673cf21c579Sdrochner 		case IPPROTO_ROUTING:
67456fc5b5dSmaxv 			if (*i + sizeof(ip6e) > m->m_pkthdr.len) {
67556fc5b5dSmaxv 				return EINVAL;
67656fc5b5dSmaxv 			}
67756fc5b5dSmaxv 
678cf21c579Sdrochner 			/*
67956fc5b5dSmaxv 			 * If we see 2nd destination option header,
680cf21c579Sdrochner 			 * we should stop there.
681cf21c579Sdrochner 			 */
682cf21c579Sdrochner 			if (nxt == IPPROTO_DSTOPTS && dstopt)
683a7d0cc88Smaxv 				return 0;
684cf21c579Sdrochner 
685cf21c579Sdrochner 			if (nxt == IPPROTO_DSTOPTS) {
686cf21c579Sdrochner 				/*
68756fc5b5dSmaxv 				 * Seen 1st or 2nd destination option.
688cf21c579Sdrochner 				 * next time we see one, it must be 2nd.
689cf21c579Sdrochner 				 */
690cf21c579Sdrochner 				dstopt = 1;
691cf21c579Sdrochner 			} else if (nxt == IPPROTO_ROUTING) {
692cf21c579Sdrochner 				/*
69356fc5b5dSmaxv 				 * If we see destination option next
694cf21c579Sdrochner 				 * time, it must be dest2.
695cf21c579Sdrochner 				 */
696cf21c579Sdrochner 				dstopt = 2;
697cf21c579Sdrochner 			}
698cf21c579Sdrochner 
699cf21c579Sdrochner 			/* skip this header */
700cf21c579Sdrochner 			m_copydata(m, *i, sizeof(ip6e), &ip6e);
701cf21c579Sdrochner 			nxt = ip6e.ip6e_nxt;
702cf21c579Sdrochner 			*off = *i + offsetof(struct ip6_ext, ip6e_nxt);
703cf21c579Sdrochner 			*i += (ip6e.ip6e_len + 1) << 3;
70456fc5b5dSmaxv 			if (*i > m->m_pkthdr.len) {
70556fc5b5dSmaxv 				return EINVAL;
70656fc5b5dSmaxv 			}
707cf21c579Sdrochner 			break;
708cf21c579Sdrochner 		default:
709a7d0cc88Smaxv 			return 0;
710cf21c579Sdrochner 		}
711a7d0cc88Smaxv 	}
712a7d0cc88Smaxv 
713a7d0cc88Smaxv 	return 0;
714cf21c579Sdrochner }
715cf21c579Sdrochner 
716813bea3eSdrochner static int
in6_sa_equal_addrwithscope(const struct sockaddr_in6 * sa,const struct in6_addr * ia)71779002044Smaxv in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa,
71879002044Smaxv     const struct in6_addr *ia)
719813bea3eSdrochner {
720813bea3eSdrochner 	struct in6_addr ia2;
721813bea3eSdrochner 
722813bea3eSdrochner 	memcpy(&ia2, &sa->sin6_addr, sizeof(ia2));
723813bea3eSdrochner 	if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr))
724813bea3eSdrochner 		ia2.s6_addr16[1] = htons(sa->sin6_scope_id);
725813bea3eSdrochner 
726813bea3eSdrochner 	return IN6_ARE_ADDR_EQUAL(ia, &ia2);
727813bea3eSdrochner }
728813bea3eSdrochner 
72974029031Sjonathan int
ipsec6_process_packet(struct mbuf * m,const struct ipsecrequest * isr,int flags)730c535599fSknakahara ipsec6_process_packet(struct mbuf *m, const struct ipsecrequest *isr, int flags)
73174029031Sjonathan {
73238b8f795Sozaki-r 	struct secasvar *sav = NULL;
733e2211411Sdegroote 	struct ip6_hdr *ip6;
734986909fbSozaki-r 	int error, i, off;
735e4ef78d8Sdrochner 	union sockaddr_union *dst;
73674029031Sjonathan 
7372620e166Sozaki-r 	KASSERT(m != NULL);
7386c5d24edSmaxv 	KASSERT(m->m_nextpkt == NULL);
7392620e166Sozaki-r 	KASSERT(isr != NULL);
74074029031Sjonathan 
74138b8f795Sozaki-r 	isr = ipsec_nextisr(m, isr, AF_INET6, &error, &sav);
74274029031Sjonathan 	if (isr == NULL) {
74355718e80Sdegroote 		if (error != 0) {
7447038ebdfSdrochner 			/* XXX Should we send a notification ? */
74574029031Sjonathan 			goto bad;
74655718e80Sdegroote 		} else {
74755718e80Sdegroote 			if (ipsec_register_done(m, &error) < 0)
74855718e80Sdegroote 				goto bad;
74955718e80Sdegroote 
750c535599fSknakahara 			return ipsec_reinject_ipstack(m, AF_INET6, flags);
75155718e80Sdegroote 		}
75274029031Sjonathan 	}
75374029031Sjonathan 
75438b8f795Sozaki-r 	KASSERT(sav != NULL);
755e4ef78d8Sdrochner 	dst = &sav->sah->saidx.dst;
756e2211411Sdegroote 
757589f503dSmaxv 	if (m->m_len < sizeof(struct ip6_hdr)) {
758589f503dSmaxv 		if ((m = m_pullup(m,sizeof(struct ip6_hdr))) == NULL) {
759589f503dSmaxv 			error = ENOBUFS;
760589f503dSmaxv 			goto unrefsav;
761589f503dSmaxv 		}
762589f503dSmaxv 	}
763589f503dSmaxv 	ip6 = mtod(m, struct ip6_hdr *);
76474029031Sjonathan 
765e2211411Sdegroote 	/* Do the appropriate encapsulation, if necessary */
766e2211411Sdegroote 	if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
76779002044Smaxv 	    dst->sa.sa_family != AF_INET6 ||        /* AF mismatch */
768e2211411Sdegroote 	    ((dst->sa.sa_family == AF_INET6) &&
769e2211411Sdegroote 	     (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
77079002044Smaxv 	     (!in6_sa_equal_addrwithscope(&dst->sin6, &ip6->ip6_dst)))) {
771e2211411Sdegroote 		struct mbuf *mp;
7727038ebdfSdrochner 
773e2211411Sdegroote 		if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
774e2211411Sdegroote 			/* No jumbogram support. */
77574f0dfc5Sozaki-r 			error = ENXIO;   /*XXX*/
77638b8f795Sozaki-r 			goto unrefsav;
77774029031Sjonathan 		}
7787038ebdfSdrochner 
779589f503dSmaxv 		/* Fix IPv6 header payload length. */
780e2211411Sdegroote 		ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
78174029031Sjonathan 
782e2211411Sdegroote 		/* Encapsulate the packet */
78320668b06Smaxv 		error = ipip_output(m, sav, &mp);
784e2211411Sdegroote 		if (mp == NULL && !error) {
785e2211411Sdegroote 			/* Should never happen. */
786290dc492Sozaki-r 			IPSECLOG(LOG_DEBUG,
787290dc492Sozaki-r 			    "ipip_output returns no mbuf and no error!");
788e2211411Sdegroote 			error = EFAULT;
78974029031Sjonathan 		}
79074029031Sjonathan 
79174029031Sjonathan 		if (error) {
792e2211411Sdegroote 			if (mp) {
793e2211411Sdegroote 				/* XXX: Should never happen! */
794e2211411Sdegroote 				m_freem(mp);
795e2211411Sdegroote 			}
796e2211411Sdegroote 			m = NULL; /* ipip_output() already freed it */
79738b8f795Sozaki-r 			goto unrefsav;
79874029031Sjonathan 		}
79974029031Sjonathan 
800e2211411Sdegroote 		m = mp;
801e2211411Sdegroote 		mp = NULL;
802eb04733cSjoerg 	}
80374029031Sjonathan 
804e4ef78d8Sdrochner 	if (dst->sa.sa_family == AF_INET) {
805e4ef78d8Sdrochner 		struct ip *ip;
806e4ef78d8Sdrochner 		ip = mtod(m, struct ip *);
807e4ef78d8Sdrochner 		i = ip->ip_hl << 2;
808e4ef78d8Sdrochner 		off = offsetof(struct ip, ip_p);
809e4ef78d8Sdrochner 	} else {
810a7d0cc88Smaxv 		error = compute_ipsec_pos(m, &i, &off);
811a7d0cc88Smaxv 		if (error)
812a7d0cc88Smaxv 			goto unrefsav;
81374029031Sjonathan 	}
814c535599fSknakahara 	error = (*sav->tdb_xform->xf_output)(m, isr, sav, i, off, flags);
8158be5cabcSozaki-r 	KEY_SA_UNREF(&sav);
816e2211411Sdegroote 	return error;
81779002044Smaxv 
81838b8f795Sozaki-r unrefsav:
8198be5cabcSozaki-r 	KEY_SA_UNREF(&sav);
82074029031Sjonathan bad:
82174029031Sjonathan 	m_freem(m);
82274029031Sjonathan 	return error;
82374029031Sjonathan }
82474029031Sjonathan #endif /* INET6 */
82564e64168Sozaki-r 
82664e64168Sozaki-r void
ipsec_output_init(void)82764e64168Sozaki-r ipsec_output_init(void)
82864e64168Sozaki-r {
82964e64168Sozaki-r 
830e524fb36Sozaki-r 	ipsec_rtcache_percpu = rtcache_percpu_alloc();
83164e64168Sozaki-r }
832