xref: /freebsd/sys/netipsec/ipsec_output.c (revision a16771de4c1e01b52318edfab315d0ba2dce0c65)
1aaea26efSSam Leffler /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3fe267a55SPedro F. Giffuni  *
4aaea26efSSam Leffler  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
5fcf59617SAndrey V. Elsukov  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
6aaea26efSSam Leffler  * All rights reserved.
7aaea26efSSam Leffler  *
8aaea26efSSam Leffler  * Redistribution and use in source and binary forms, with or without
9aaea26efSSam Leffler  * modification, are permitted provided that the following conditions
10aaea26efSSam Leffler  * are met:
11aaea26efSSam Leffler  * 1. Redistributions of source code must retain the above copyright
12aaea26efSSam Leffler  *    notice, this list of conditions and the following disclaimer.
13aaea26efSSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
14aaea26efSSam Leffler  *    notice, this list of conditions and the following disclaimer in the
15aaea26efSSam Leffler  *    documentation and/or other materials provided with the distribution.
16aaea26efSSam Leffler  *
17aaea26efSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18aaea26efSSam Leffler  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19aaea26efSSam Leffler  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20aaea26efSSam Leffler  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21aaea26efSSam Leffler  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22aaea26efSSam Leffler  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23aaea26efSSam Leffler  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24aaea26efSSam Leffler  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25aaea26efSSam Leffler  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26aaea26efSSam Leffler  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27aaea26efSSam Leffler  * SUCH DAMAGE.
28aaea26efSSam Leffler  *
29aaea26efSSam Leffler  * $FreeBSD$
30aaea26efSSam Leffler  */
3188768458SSam Leffler 
3288768458SSam Leffler /*
3388768458SSam Leffler  * IPsec output processing.
3488768458SSam Leffler  */
3588768458SSam Leffler #include "opt_inet.h"
3688768458SSam Leffler #include "opt_inet6.h"
3788768458SSam Leffler #include "opt_ipsec.h"
38fcf59617SAndrey V. Elsukov #include "opt_sctp.h"
3988768458SSam Leffler 
4088768458SSam Leffler #include <sys/param.h>
4188768458SSam Leffler #include <sys/systm.h>
4288768458SSam Leffler #include <sys/mbuf.h>
4388768458SSam Leffler #include <sys/domain.h>
4488768458SSam Leffler #include <sys/protosw.h>
4588768458SSam Leffler #include <sys/socket.h>
4688768458SSam Leffler #include <sys/errno.h>
47ef91a976SAndrey V. Elsukov #include <sys/hhook.h>
4888768458SSam Leffler #include <sys/syslog.h>
4988768458SSam Leffler 
5088768458SSam Leffler #include <net/if.h>
51ef91a976SAndrey V. Elsukov #include <net/if_enc.h>
5276039bc8SGleb Smirnoff #include <net/if_var.h>
53eddfbb76SRobert Watson #include <net/vnet.h>
5488768458SSam Leffler 
5588768458SSam Leffler #include <netinet/in.h>
5688768458SSam Leffler #include <netinet/in_systm.h>
5788768458SSam Leffler #include <netinet/ip.h>
5888768458SSam Leffler #include <netinet/ip_var.h>
5988768458SSam Leffler #include <netinet/in_var.h>
6088768458SSam Leffler #include <netinet/ip_ecn.h>
6188768458SSam Leffler #ifdef INET6
6288768458SSam Leffler #include <netinet6/ip6_ecn.h>
6388768458SSam Leffler #endif
646b66194bSKornel Duleba #include <netinet/ip_icmp.h>
656b66194bSKornel Duleba #include <netinet/tcp_var.h>
6688768458SSam Leffler 
6788768458SSam Leffler #include <netinet/ip6.h>
6888768458SSam Leffler #ifdef INET6
6988768458SSam Leffler #include <netinet6/ip6_var.h>
7061f37615SAndrey V. Elsukov #include <netinet6/scope6_var.h>
7188768458SSam Leffler #endif
7288768458SSam Leffler #include <netinet/in_pcb.h>
7388768458SSam Leffler #ifdef INET6
7488768458SSam Leffler #include <netinet/icmp6.h>
7588768458SSam Leffler #endif
7695033af9SMark Johnston #if defined(SCTP) || defined(SCTP_SUPPORT)
77fcf59617SAndrey V. Elsukov #include <netinet/sctp_crc32.h>
78fcf59617SAndrey V. Elsukov #endif
7988768458SSam Leffler 
80fcf59617SAndrey V. Elsukov #include <netinet/udp.h>
81fcf59617SAndrey V. Elsukov #include <netipsec/ah.h>
82fcf59617SAndrey V. Elsukov #include <netipsec/esp.h>
8388768458SSam Leffler #include <netipsec/ipsec.h>
8488768458SSam Leffler #ifdef INET6
8588768458SSam Leffler #include <netipsec/ipsec6.h>
8688768458SSam Leffler #endif
8788768458SSam Leffler #include <netipsec/ah_var.h>
8888768458SSam Leffler #include <netipsec/esp_var.h>
8988768458SSam Leffler #include <netipsec/ipcomp_var.h>
9088768458SSam Leffler 
9188768458SSam Leffler #include <netipsec/xform.h>
9288768458SSam Leffler 
9388768458SSam Leffler #include <netipsec/key.h>
9488768458SSam Leffler #include <netipsec/keydb.h>
9588768458SSam Leffler #include <netipsec/key_debug.h>
9688768458SSam Leffler 
9788768458SSam Leffler #include <machine/in_cksum.h>
9888768458SSam Leffler 
99fcf59617SAndrey V. Elsukov #define	IPSEC_OSTAT_INC(proto, name)	do {		\
100fcf59617SAndrey V. Elsukov 	if ((proto) == IPPROTO_ESP)	\
101fcf59617SAndrey V. Elsukov 		ESPSTAT_INC(esps_##name);	\
102fcf59617SAndrey V. Elsukov 	else if ((proto) == IPPROTO_AH)\
103fcf59617SAndrey V. Elsukov 		AHSTAT_INC(ahs_##name);		\
104fcf59617SAndrey V. Elsukov 	else					\
105fcf59617SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_##name);	\
106fcf59617SAndrey V. Elsukov } while (0)
107fcf59617SAndrey V. Elsukov 
108fcf59617SAndrey V. Elsukov static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx);
109fcf59617SAndrey V. Elsukov 
110fcf59617SAndrey V. Elsukov #ifdef INET
111fcf59617SAndrey V. Elsukov static struct secasvar *
112fcf59617SAndrey V. Elsukov ipsec4_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error)
113fcf59617SAndrey V. Elsukov {
114fcf59617SAndrey V. Elsukov 	struct secasindex *saidx, tmpsaidx;
115fcf59617SAndrey V. Elsukov 	struct ipsecrequest *isr;
116fcf59617SAndrey V. Elsukov 	struct sockaddr_in *sin;
117fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
118fcf59617SAndrey V. Elsukov 	struct ip *ip;
119fcf59617SAndrey V. Elsukov 
120fcf59617SAndrey V. Elsukov 	/*
121fcf59617SAndrey V. Elsukov 	 * Check system global policy controls.
122fcf59617SAndrey V. Elsukov 	 */
123fcf59617SAndrey V. Elsukov next:
124fcf59617SAndrey V. Elsukov 	isr = sp->req[*pidx];
125fcf59617SAndrey V. Elsukov 	if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) ||
126fcf59617SAndrey V. Elsukov 	    (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) ||
127fcf59617SAndrey V. Elsukov 	    (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
128fcf59617SAndrey V. Elsukov 		DPRINTF(("%s: IPsec outbound packet dropped due"
129fcf59617SAndrey V. Elsukov 			" to policy (check your sysctls)\n", __func__));
130fcf59617SAndrey V. Elsukov 		IPSEC_OSTAT_INC(isr->saidx.proto, pdrops);
131fcf59617SAndrey V. Elsukov 		*error = EHOSTUNREACH;
132fcf59617SAndrey V. Elsukov 		return (NULL);
133fcf59617SAndrey V. Elsukov 	}
134fcf59617SAndrey V. Elsukov 	/*
135fcf59617SAndrey V. Elsukov 	 * Craft SA index to search for proper SA.  Note that
136fcf59617SAndrey V. Elsukov 	 * we only initialize unspecified SA peers for transport
137fcf59617SAndrey V. Elsukov 	 * mode; for tunnel mode they must already be filled in.
138fcf59617SAndrey V. Elsukov 	 */
139fcf59617SAndrey V. Elsukov 	if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
140fcf59617SAndrey V. Elsukov 		saidx = &tmpsaidx;
141fcf59617SAndrey V. Elsukov 		*saidx = isr->saidx;
142fcf59617SAndrey V. Elsukov 		ip = mtod(m, struct ip *);
143fcf59617SAndrey V. Elsukov 		if (saidx->src.sa.sa_len == 0) {
144fcf59617SAndrey V. Elsukov 			sin = &saidx->src.sin;
145fcf59617SAndrey V. Elsukov 			sin->sin_len = sizeof(*sin);
146fcf59617SAndrey V. Elsukov 			sin->sin_family = AF_INET;
147fcf59617SAndrey V. Elsukov 			sin->sin_port = IPSEC_PORT_ANY;
148fcf59617SAndrey V. Elsukov 			sin->sin_addr = ip->ip_src;
149fcf59617SAndrey V. Elsukov 		}
150fcf59617SAndrey V. Elsukov 		if (saidx->dst.sa.sa_len == 0) {
151fcf59617SAndrey V. Elsukov 			sin = &saidx->dst.sin;
152fcf59617SAndrey V. Elsukov 			sin->sin_len = sizeof(*sin);
153fcf59617SAndrey V. Elsukov 			sin->sin_family = AF_INET;
154fcf59617SAndrey V. Elsukov 			sin->sin_port = IPSEC_PORT_ANY;
155fcf59617SAndrey V. Elsukov 			sin->sin_addr = ip->ip_dst;
156fcf59617SAndrey V. Elsukov 		}
157fcf59617SAndrey V. Elsukov 	} else
158fcf59617SAndrey V. Elsukov 		saidx = &sp->req[*pidx]->saidx;
159fcf59617SAndrey V. Elsukov 	/*
160fcf59617SAndrey V. Elsukov 	 * Lookup SA and validate it.
161fcf59617SAndrey V. Elsukov 	 */
162fcf59617SAndrey V. Elsukov 	sav = key_allocsa_policy(sp, saidx, error);
163fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
164fcf59617SAndrey V. Elsukov 		IPSECSTAT_INC(ips_out_nosa);
165fcf59617SAndrey V. Elsukov 		if (*error != 0)
166fcf59617SAndrey V. Elsukov 			return (NULL);
167fcf59617SAndrey V. Elsukov 		if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) {
168fcf59617SAndrey V. Elsukov 			/*
169fcf59617SAndrey V. Elsukov 			 * We have no SA and policy that doesn't require
170fcf59617SAndrey V. Elsukov 			 * this IPsec transform, thus we can continue w/o
171fcf59617SAndrey V. Elsukov 			 * IPsec processing, i.e. return EJUSTRETURN.
172fcf59617SAndrey V. Elsukov 			 * But first check if there is some bundled transform.
173fcf59617SAndrey V. Elsukov 			 */
174fcf59617SAndrey V. Elsukov 			if (sp->tcount > ++(*pidx))
175fcf59617SAndrey V. Elsukov 				goto next;
176fcf59617SAndrey V. Elsukov 			*error = EJUSTRETURN;
177fcf59617SAndrey V. Elsukov 		}
178fcf59617SAndrey V. Elsukov 		return (NULL);
179fcf59617SAndrey V. Elsukov 	}
180fcf59617SAndrey V. Elsukov 	IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform"));
181fcf59617SAndrey V. Elsukov 	return (sav);
182fcf59617SAndrey V. Elsukov }
183fcf59617SAndrey V. Elsukov 
184fcf59617SAndrey V. Elsukov /*
185fcf59617SAndrey V. Elsukov  * IPsec output logic for IPv4.
186fcf59617SAndrey V. Elsukov  */
187fcf59617SAndrey V. Elsukov static int
1881a01e0e7SAndrey V. Elsukov ipsec4_perform_request(struct mbuf *m, struct secpolicy *sp,
1891a01e0e7SAndrey V. Elsukov     struct inpcb *inp, u_int idx)
190fcf59617SAndrey V. Elsukov {
191fcf59617SAndrey V. Elsukov 	struct ipsec_ctx_data ctx;
192fcf59617SAndrey V. Elsukov 	union sockaddr_union *dst;
193fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
194fcf59617SAndrey V. Elsukov 	struct ip *ip;
195fcf59617SAndrey V. Elsukov 	int error, i, off;
196fcf59617SAndrey V. Elsukov 
197fcf59617SAndrey V. Elsukov 	IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
198fcf59617SAndrey V. Elsukov 
199fcf59617SAndrey V. Elsukov 	/*
200fcf59617SAndrey V. Elsukov 	 * We hold the reference to SP. Content of SP couldn't be changed.
201fcf59617SAndrey V. Elsukov 	 * Craft secasindex and do lookup for suitable SA.
202fcf59617SAndrey V. Elsukov 	 * Then do encapsulation if needed and call xform's output.
203fcf59617SAndrey V. Elsukov 	 * We need to store SP in the xform callback parameters.
204fcf59617SAndrey V. Elsukov 	 * In xform callback we will extract SP and it can be used to
205fcf59617SAndrey V. Elsukov 	 * determine next transform. At the end of transform we can
206fcf59617SAndrey V. Elsukov 	 * release reference to SP.
207fcf59617SAndrey V. Elsukov 	 */
208fcf59617SAndrey V. Elsukov 	sav = ipsec4_allocsa(m, sp, &idx, &error);
209fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
210fcf59617SAndrey V. Elsukov 		if (error == EJUSTRETURN) { /* No IPsec required */
211fcf59617SAndrey V. Elsukov 			key_freesp(&sp);
212fcf59617SAndrey V. Elsukov 			return (error);
213fcf59617SAndrey V. Elsukov 		}
214fcf59617SAndrey V. Elsukov 		goto bad;
215fcf59617SAndrey V. Elsukov 	}
216fcf59617SAndrey V. Elsukov 	/*
217fcf59617SAndrey V. Elsukov 	 * XXXAE: most likely ip_sum at this point is wrong.
218fcf59617SAndrey V. Elsukov 	 */
2191a01e0e7SAndrey V. Elsukov 	IPSEC_INIT_CTX(&ctx, &m, inp, sav, AF_INET, IPSEC_ENC_BEFORE);
220fcf59617SAndrey V. Elsukov 	if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
221fcf59617SAndrey V. Elsukov 		goto bad;
222fcf59617SAndrey V. Elsukov 
223fcf59617SAndrey V. Elsukov 	ip = mtod(m, struct ip *);
224fcf59617SAndrey V. Elsukov 	dst = &sav->sah->saidx.dst;
225fcf59617SAndrey V. Elsukov 	/* Do the appropriate encapsulation, if necessary */
226fcf59617SAndrey V. Elsukov 	if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
227fcf59617SAndrey V. Elsukov 	    dst->sa.sa_family != AF_INET ||	    /* PF mismatch */
228fcf59617SAndrey V. Elsukov 	    (dst->sa.sa_family == AF_INET &&	    /* Proxy */
229fcf59617SAndrey V. Elsukov 	     dst->sin.sin_addr.s_addr != INADDR_ANY &&
230fcf59617SAndrey V. Elsukov 	     dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
231fcf59617SAndrey V. Elsukov 		/* Fix IPv4 header checksum and length */
232fcf59617SAndrey V. Elsukov 		ip->ip_len = htons(m->m_pkthdr.len);
233fcf59617SAndrey V. Elsukov 		ip->ip_sum = 0;
234fcf59617SAndrey V. Elsukov 		ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
235fcf59617SAndrey V. Elsukov 		error = ipsec_encap(&m, &sav->sah->saidx);
236fcf59617SAndrey V. Elsukov 		if (error != 0) {
2377f1f6591SAndrey V. Elsukov 			DPRINTF(("%s: encapsulation for SPI 0x%08x failed "
2387f1f6591SAndrey V. Elsukov 			    "with error %d\n", __func__, ntohl(sav->spi),
2397f1f6591SAndrey V. Elsukov 			    error));
240fcf59617SAndrey V. Elsukov 			/* XXXAE: IPSEC_OSTAT_INC(tunnel); */
241fcf59617SAndrey V. Elsukov 			goto bad;
242fcf59617SAndrey V. Elsukov 		}
2431a01e0e7SAndrey V. Elsukov 		inp = NULL;
244fcf59617SAndrey V. Elsukov 	}
245fcf59617SAndrey V. Elsukov 
2461a01e0e7SAndrey V. Elsukov 	IPSEC_INIT_CTX(&ctx, &m, inp, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
247fcf59617SAndrey V. Elsukov 	if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
248fcf59617SAndrey V. Elsukov 		goto bad;
249fcf59617SAndrey V. Elsukov 
250fcf59617SAndrey V. Elsukov 	/*
251fcf59617SAndrey V. Elsukov 	 * Dispatch to the appropriate IPsec transform logic.  The
252fcf59617SAndrey V. Elsukov 	 * packet will be returned for transmission after crypto
253fcf59617SAndrey V. Elsukov 	 * processing, etc. are completed.
254fcf59617SAndrey V. Elsukov 	 *
255fcf59617SAndrey V. Elsukov 	 * NB: m & sav are ``passed to caller'' who's responsible for
256fcf59617SAndrey V. Elsukov 	 *     reclaiming their resources.
257fcf59617SAndrey V. Elsukov 	 */
258fcf59617SAndrey V. Elsukov 	switch(dst->sa.sa_family) {
259fcf59617SAndrey V. Elsukov 	case AF_INET:
260fcf59617SAndrey V. Elsukov 		ip = mtod(m, struct ip *);
261fcf59617SAndrey V. Elsukov 		i = ip->ip_hl << 2;
262fcf59617SAndrey V. Elsukov 		off = offsetof(struct ip, ip_p);
263fcf59617SAndrey V. Elsukov 		break;
264fcf59617SAndrey V. Elsukov #ifdef INET6
265fcf59617SAndrey V. Elsukov 	case AF_INET6:
266fcf59617SAndrey V. Elsukov 		i = sizeof(struct ip6_hdr);
267fcf59617SAndrey V. Elsukov 		off = offsetof(struct ip6_hdr, ip6_nxt);
268fcf59617SAndrey V. Elsukov 		break;
269fcf59617SAndrey V. Elsukov #endif /* INET6 */
270fcf59617SAndrey V. Elsukov 	default:
271fcf59617SAndrey V. Elsukov 		DPRINTF(("%s: unsupported protocol family %u\n",
272fcf59617SAndrey V. Elsukov 		    __func__, dst->sa.sa_family));
273fcf59617SAndrey V. Elsukov 		error = EPFNOSUPPORT;
274fcf59617SAndrey V. Elsukov 		IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf);
275fcf59617SAndrey V. Elsukov 		goto bad;
276fcf59617SAndrey V. Elsukov 	}
277fcf59617SAndrey V. Elsukov 	error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off);
278fcf59617SAndrey V. Elsukov 	return (error);
279fcf59617SAndrey V. Elsukov bad:
280fcf59617SAndrey V. Elsukov 	IPSECSTAT_INC(ips_out_inval);
281fcf59617SAndrey V. Elsukov 	if (m != NULL)
282fcf59617SAndrey V. Elsukov 		m_freem(m);
283fcf59617SAndrey V. Elsukov 	if (sav != NULL)
284fcf59617SAndrey V. Elsukov 		key_freesav(&sav);
285fcf59617SAndrey V. Elsukov 	key_freesp(&sp);
286fcf59617SAndrey V. Elsukov 	return (error);
287fcf59617SAndrey V. Elsukov }
2887b495c44SVANHULLEBUS Yvan 
28988768458SSam Leffler int
290fcf59617SAndrey V. Elsukov ipsec4_process_packet(struct mbuf *m, struct secpolicy *sp,
291fcf59617SAndrey V. Elsukov     struct inpcb *inp)
29288768458SSam Leffler {
293fcf59617SAndrey V. Elsukov 
2941a01e0e7SAndrey V. Elsukov 	return (ipsec4_perform_request(m, sp, inp, 0));
295fcf59617SAndrey V. Elsukov }
296fcf59617SAndrey V. Elsukov 
2976b66194bSKornel Duleba int
2986b66194bSKornel Duleba ipsec4_check_pmtu(struct mbuf *m, struct secpolicy *sp, int forwarding)
2996b66194bSKornel Duleba {
3006b66194bSKornel Duleba 	union sockaddr_union *dst;
3016b66194bSKornel Duleba 	struct in_conninfo inc;
3026b66194bSKornel Duleba 	struct secasvar *sav;
3036b66194bSKornel Duleba 	struct ip *ip;
3046b66194bSKornel Duleba 	size_t hlen, pmtu;
3056b66194bSKornel Duleba 	uint32_t idx;
3066b66194bSKornel Duleba 	int error;
3076b66194bSKornel Duleba 
3086b66194bSKornel Duleba 
3096b66194bSKornel Duleba 	/* Don't check PMTU if the frame won't have DF bit set. */
3106b66194bSKornel Duleba 	if (!V_ip4_ipsec_dfbit)
3116b66194bSKornel Duleba 		return (0);
3126b66194bSKornel Duleba 	if (V_ip4_ipsec_dfbit == 1)
3136b66194bSKornel Duleba 		goto setdf;
3146b66194bSKornel Duleba 
3156b66194bSKornel Duleba 	/* V_ip4_ipsec_dfbit > 1 - we will copy it from inner header. */
3166b66194bSKornel Duleba 	ip = mtod(m, struct ip *);
3176b66194bSKornel Duleba 	if (!(ip->ip_off & htons(IP_DF)))
3186b66194bSKornel Duleba 		return (0);
3196b66194bSKornel Duleba 
3206b66194bSKornel Duleba setdf:
3216b66194bSKornel Duleba 	idx = sp->tcount - 1;
3226b66194bSKornel Duleba 	sav = ipsec4_allocsa(m, sp, &idx, &error);
3236b66194bSKornel Duleba 	if (sav == NULL) {
3246b66194bSKornel Duleba 		key_freesp(&sp);
325*a16771deSKornel Duleba 		/*
326*a16771deSKornel Duleba 		 * No matching SA was found and SADB_ACQUIRE message was generated.
327*a16771deSKornel Duleba 		 * Since we have matched a SP to this packet drop it silently.
328*a16771deSKornel Duleba 		 */
329*a16771deSKornel Duleba 		if (error == 0)
330*a16771deSKornel Duleba 			error = EINPROGRESS;
3316b66194bSKornel Duleba 		if (error != EJUSTRETURN)
3326b66194bSKornel Duleba 			m_freem(m);
3336b66194bSKornel Duleba 
3346b66194bSKornel Duleba 		return (error);
3356b66194bSKornel Duleba 	}
3366b66194bSKornel Duleba 
3376b66194bSKornel Duleba 	dst = &sav->sah->saidx.dst;
3386b66194bSKornel Duleba 
3396b66194bSKornel Duleba 	/* Final header is not ipv4. */
3406b66194bSKornel Duleba 	if (dst->sa.sa_family != AF_INET) {
3416b66194bSKornel Duleba 		key_freesav(&sav);
3426b66194bSKornel Duleba 		return (0);
3436b66194bSKornel Duleba 	}
3446b66194bSKornel Duleba 
3456b66194bSKornel Duleba 	memset(&inc, 0, sizeof(inc));
3466b66194bSKornel Duleba 	inc.inc_faddr = satosin(&dst->sa)->sin_addr;
3476b66194bSKornel Duleba 	key_freesav(&sav);
3486b66194bSKornel Duleba 	pmtu = tcp_hc_getmtu(&inc);
3496b66194bSKornel Duleba 	/* No entry in hostcache. */
3506b66194bSKornel Duleba 	if (pmtu == 0)
3516b66194bSKornel Duleba 		return (0);
3526b66194bSKornel Duleba 
3536b66194bSKornel Duleba 	hlen = ipsec_hdrsiz_internal(sp);
3546b66194bSKornel Duleba 	if (m_length(m, NULL) + hlen > pmtu) {
3556b66194bSKornel Duleba 		/*
3566b66194bSKornel Duleba 		 * If we're forwarding generate ICMP message here,
3576b66194bSKornel Duleba 		 * so that it contains pmtu and not link mtu.
3586b66194bSKornel Duleba 		 * Set error to EINPROGRESS, in order for the frame
3596b66194bSKornel Duleba 		 * to be dropped silently.
3606b66194bSKornel Duleba 		 */
3616b66194bSKornel Duleba 		if (forwarding) {
3626b66194bSKornel Duleba 			if (pmtu > hlen)
3636b66194bSKornel Duleba 				icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
3646b66194bSKornel Duleba 				    0, pmtu - hlen);
3656b66194bSKornel Duleba 			else
3666b66194bSKornel Duleba 				m_freem(m);
3676b66194bSKornel Duleba 
3686b66194bSKornel Duleba 			key_freesp(&sp);
3696b66194bSKornel Duleba 			return (EINPROGRESS); /* Pretend that we consumed it. */
3706b66194bSKornel Duleba 		} else {
3716b66194bSKornel Duleba 			m_freem(m);
3726b66194bSKornel Duleba 			key_freesp(&sp);
3736b66194bSKornel Duleba 			return (EMSGSIZE);
3746b66194bSKornel Duleba 		}
3756b66194bSKornel Duleba 	}
3766b66194bSKornel Duleba 
3776b66194bSKornel Duleba 	return (0);
3786b66194bSKornel Duleba }
3796b66194bSKornel Duleba 
380fcf59617SAndrey V. Elsukov static int
381fcf59617SAndrey V. Elsukov ipsec4_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
382fcf59617SAndrey V. Elsukov {
383fcf59617SAndrey V. Elsukov 	struct secpolicy *sp;
38488768458SSam Leffler 	int error;
38588768458SSam Leffler 
386fcf59617SAndrey V. Elsukov 	/* Lookup for the corresponding outbound security policy */
38722bbefb2SAndrey V. Elsukov 	sp = ipsec4_checkpolicy(m, inp, &error, !forwarding);
388fcf59617SAndrey V. Elsukov 	if (sp == NULL) {
389fcf59617SAndrey V. Elsukov 		if (error == -EINVAL) {
390fcf59617SAndrey V. Elsukov 			/* Discarded by policy. */
391fcf59617SAndrey V. Elsukov 			m_freem(m);
392fcf59617SAndrey V. Elsukov 			return (EACCES);
393fcf59617SAndrey V. Elsukov 		}
394fcf59617SAndrey V. Elsukov 		return (0); /* No IPsec required. */
395fcf59617SAndrey V. Elsukov 	}
396fcf59617SAndrey V. Elsukov 
397fcf59617SAndrey V. Elsukov 	/*
398fcf59617SAndrey V. Elsukov 	 * Usually we have to have tunnel mode IPsec security policy
399fcf59617SAndrey V. Elsukov 	 * when we are forwarding a packet. Otherwise we could not handle
400fcf59617SAndrey V. Elsukov 	 * encrypted replies, because they are not destined for us. But
401fcf59617SAndrey V. Elsukov 	 * some users are doing source address translation for forwarded
402fcf59617SAndrey V. Elsukov 	 * packets, and thus, even if they are forwarded, the replies will
403fcf59617SAndrey V. Elsukov 	 * return back to us.
404fcf59617SAndrey V. Elsukov 	 */
405fcf59617SAndrey V. Elsukov 	if (!forwarding) {
406fcf59617SAndrey V. Elsukov 		/*
407fcf59617SAndrey V. Elsukov 		 * Do delayed checksums now because we send before
408fcf59617SAndrey V. Elsukov 		 * this is done in the normal processing path.
409fcf59617SAndrey V. Elsukov 		 */
410fcf59617SAndrey V. Elsukov 		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
4118e9313caSJohn Baldwin 			m = mb_unmapped_to_ext(m);
4128e9313caSJohn Baldwin 			if (m == NULL) {
4138e9313caSJohn Baldwin 				IPSECSTAT_INC(ips_out_nomem);
4148e9313caSJohn Baldwin 				key_freesp(&sp);
4158e9313caSJohn Baldwin 				return (ENOBUFS);
4168e9313caSJohn Baldwin 			}
417fcf59617SAndrey V. Elsukov 			in_delayed_cksum(m);
418fcf59617SAndrey V. Elsukov 			m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
419fcf59617SAndrey V. Elsukov 		}
42095033af9SMark Johnston #if defined(SCTP) || defined(SCTP_SUPPORT)
421fcf59617SAndrey V. Elsukov 		if (m->m_pkthdr.csum_flags & CSUM_SCTP) {
4228e9313caSJohn Baldwin 			struct ip *ip;
423fcf59617SAndrey V. Elsukov 
4248e9313caSJohn Baldwin 			m = mb_unmapped_to_ext(m);
4258e9313caSJohn Baldwin 			if (m == NULL) {
4268e9313caSJohn Baldwin 				IPSECSTAT_INC(ips_out_nomem);
4278e9313caSJohn Baldwin 				key_freesp(&sp);
4288e9313caSJohn Baldwin 				return (ENOBUFS);
4298e9313caSJohn Baldwin 			}
4308e9313caSJohn Baldwin 			ip = mtod(m, struct ip *);
431fcf59617SAndrey V. Elsukov 			sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2));
432fcf59617SAndrey V. Elsukov 			m->m_pkthdr.csum_flags &= ~CSUM_SCTP;
433fcf59617SAndrey V. Elsukov 		}
434fcf59617SAndrey V. Elsukov #endif
435fcf59617SAndrey V. Elsukov 	}
436fcf59617SAndrey V. Elsukov 	/* NB: callee frees mbuf and releases reference to SP */
4376b66194bSKornel Duleba 	error = ipsec4_check_pmtu(m, sp, forwarding);
4386b66194bSKornel Duleba 	if (error != 0) {
4396b66194bSKornel Duleba 		if (error == EJUSTRETURN)
4406b66194bSKornel Duleba 			return (0);
4416b66194bSKornel Duleba 
4426b66194bSKornel Duleba 		return (error);
4436b66194bSKornel Duleba 	}
4446b66194bSKornel Duleba 
445fcf59617SAndrey V. Elsukov 	error = ipsec4_process_packet(m, sp, inp);
446fcf59617SAndrey V. Elsukov 	if (error == EJUSTRETURN) {
447fcf59617SAndrey V. Elsukov 		/*
448fcf59617SAndrey V. Elsukov 		 * We had a SP with a level of 'use' and no SA. We
449fcf59617SAndrey V. Elsukov 		 * will just continue to process the packet without
450fcf59617SAndrey V. Elsukov 		 * IPsec processing and return without error.
451fcf59617SAndrey V. Elsukov 		 */
452fcf59617SAndrey V. Elsukov 		return (0);
453fcf59617SAndrey V. Elsukov 	}
454fcf59617SAndrey V. Elsukov 	if (error == 0)
455fcf59617SAndrey V. Elsukov 		return (EINPROGRESS); /* consumed by IPsec */
456fcf59617SAndrey V. Elsukov 	return (error);
457fcf59617SAndrey V. Elsukov }
458fcf59617SAndrey V. Elsukov 
459fcf59617SAndrey V. Elsukov /*
460fcf59617SAndrey V. Elsukov  * IPSEC_OUTPUT() method implementation for IPv4.
461fcf59617SAndrey V. Elsukov  * 0 - no IPsec handling needed
462fcf59617SAndrey V. Elsukov  * other values - mbuf consumed by IPsec.
463fcf59617SAndrey V. Elsukov  */
464fcf59617SAndrey V. Elsukov int
465fcf59617SAndrey V. Elsukov ipsec4_output(struct mbuf *m, struct inpcb *inp)
466fcf59617SAndrey V. Elsukov {
467fcf59617SAndrey V. Elsukov 
468fcf59617SAndrey V. Elsukov 	/*
469fcf59617SAndrey V. Elsukov 	 * If the packet is resubmitted to ip_output (e.g. after
470fcf59617SAndrey V. Elsukov 	 * AH, ESP, etc. processing), there will be a tag to bypass
471fcf59617SAndrey V. Elsukov 	 * the lookup and related policy checking.
472fcf59617SAndrey V. Elsukov 	 */
473fcf59617SAndrey V. Elsukov 	if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL)
474fcf59617SAndrey V. Elsukov 		return (0);
475fcf59617SAndrey V. Elsukov 
476fcf59617SAndrey V. Elsukov 	return (ipsec4_common_output(m, inp, 0));
477fcf59617SAndrey V. Elsukov }
478fcf59617SAndrey V. Elsukov 
479fcf59617SAndrey V. Elsukov /*
480fcf59617SAndrey V. Elsukov  * IPSEC_FORWARD() method implementation for IPv4.
481fcf59617SAndrey V. Elsukov  * 0 - no IPsec handling needed
482fcf59617SAndrey V. Elsukov  * other values - mbuf consumed by IPsec.
483fcf59617SAndrey V. Elsukov  */
484fcf59617SAndrey V. Elsukov int
485fcf59617SAndrey V. Elsukov ipsec4_forward(struct mbuf *m)
486fcf59617SAndrey V. Elsukov {
487fcf59617SAndrey V. Elsukov 
488fcf59617SAndrey V. Elsukov 	/*
489fcf59617SAndrey V. Elsukov 	 * Check if this packet has an active inbound SP and needs to be
490fcf59617SAndrey V. Elsukov 	 * dropped instead of forwarded.
491fcf59617SAndrey V. Elsukov 	 */
492fcf59617SAndrey V. Elsukov 	if (ipsec4_in_reject(m, NULL) != 0) {
493fcf59617SAndrey V. Elsukov 		m_freem(m);
494fcf59617SAndrey V. Elsukov 		return (EACCES);
495fcf59617SAndrey V. Elsukov 	}
496fcf59617SAndrey V. Elsukov 	return (ipsec4_common_output(m, NULL, 1));
497fcf59617SAndrey V. Elsukov }
498fcf59617SAndrey V. Elsukov #endif
499fcf59617SAndrey V. Elsukov 
500fcf59617SAndrey V. Elsukov #ifdef INET6
501fcf59617SAndrey V. Elsukov static int
502fcf59617SAndrey V. Elsukov in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa,
503fcf59617SAndrey V. Elsukov     const struct in6_addr *ia)
504fcf59617SAndrey V. Elsukov {
505fcf59617SAndrey V. Elsukov 	struct in6_addr ia2;
506fcf59617SAndrey V. Elsukov 
507fcf59617SAndrey V. Elsukov 	if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) {
508fcf59617SAndrey V. Elsukov 		memcpy(&ia2, &sa->sin6_addr, sizeof(ia2));
509fcf59617SAndrey V. Elsukov 		ia2.s6_addr16[1] = htons(sa->sin6_scope_id);
510fcf59617SAndrey V. Elsukov 		return (IN6_ARE_ADDR_EQUAL(ia, &ia2));
511fcf59617SAndrey V. Elsukov 	}
512fcf59617SAndrey V. Elsukov 	return (IN6_ARE_ADDR_EQUAL(&sa->sin6_addr, ia));
513fcf59617SAndrey V. Elsukov }
514fcf59617SAndrey V. Elsukov 
515fcf59617SAndrey V. Elsukov static struct secasvar *
516fcf59617SAndrey V. Elsukov ipsec6_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error)
517fcf59617SAndrey V. Elsukov {
518fcf59617SAndrey V. Elsukov 	struct secasindex *saidx, tmpsaidx;
519fcf59617SAndrey V. Elsukov 	struct ipsecrequest *isr;
520fcf59617SAndrey V. Elsukov 	struct sockaddr_in6 *sin6;
521fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
522fcf59617SAndrey V. Elsukov 	struct ip6_hdr *ip6;
523fcf59617SAndrey V. Elsukov 
524fcf59617SAndrey V. Elsukov 	/*
525fcf59617SAndrey V. Elsukov 	 * Check system global policy controls.
526fcf59617SAndrey V. Elsukov 	 */
527fcf59617SAndrey V. Elsukov next:
528fcf59617SAndrey V. Elsukov 	isr = sp->req[*pidx];
529fcf59617SAndrey V. Elsukov 	if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) ||
530fcf59617SAndrey V. Elsukov 	    (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) ||
531fcf59617SAndrey V. Elsukov 	    (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
532fcf59617SAndrey V. Elsukov 		DPRINTF(("%s: IPsec outbound packet dropped due"
533fcf59617SAndrey V. Elsukov 			" to policy (check your sysctls)\n", __func__));
534fcf59617SAndrey V. Elsukov 		IPSEC_OSTAT_INC(isr->saidx.proto, pdrops);
535fcf59617SAndrey V. Elsukov 		*error = EHOSTUNREACH;
536fcf59617SAndrey V. Elsukov 		return (NULL);
537fcf59617SAndrey V. Elsukov 	}
538fcf59617SAndrey V. Elsukov 	/*
539fcf59617SAndrey V. Elsukov 	 * Craft SA index to search for proper SA.  Note that
540fcf59617SAndrey V. Elsukov 	 * we only fillin unspecified SA peers for transport
541fcf59617SAndrey V. Elsukov 	 * mode; for tunnel mode they must already be filled in.
542fcf59617SAndrey V. Elsukov 	 */
543fcf59617SAndrey V. Elsukov 	if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
544fcf59617SAndrey V. Elsukov 		saidx = &tmpsaidx;
545fcf59617SAndrey V. Elsukov 		*saidx = isr->saidx;
546fcf59617SAndrey V. Elsukov 		ip6 = mtod(m, struct ip6_hdr *);
547fcf59617SAndrey V. Elsukov 		if (saidx->src.sin6.sin6_len == 0) {
548fcf59617SAndrey V. Elsukov 			sin6 = (struct sockaddr_in6 *)&saidx->src;
549fcf59617SAndrey V. Elsukov 			sin6->sin6_len = sizeof(*sin6);
550fcf59617SAndrey V. Elsukov 			sin6->sin6_family = AF_INET6;
551fcf59617SAndrey V. Elsukov 			sin6->sin6_port = IPSEC_PORT_ANY;
552fcf59617SAndrey V. Elsukov 			sin6->sin6_addr = ip6->ip6_src;
553fcf59617SAndrey V. Elsukov 			if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
554fcf59617SAndrey V. Elsukov 				/* fix scope id for comparing SPD */
555fcf59617SAndrey V. Elsukov 				sin6->sin6_addr.s6_addr16[1] = 0;
556fcf59617SAndrey V. Elsukov 				sin6->sin6_scope_id =
557fcf59617SAndrey V. Elsukov 				    ntohs(ip6->ip6_src.s6_addr16[1]);
558fcf59617SAndrey V. Elsukov 			}
559fcf59617SAndrey V. Elsukov 		}
560fcf59617SAndrey V. Elsukov 		if (saidx->dst.sin6.sin6_len == 0) {
561fcf59617SAndrey V. Elsukov 			sin6 = (struct sockaddr_in6 *)&saidx->dst;
562fcf59617SAndrey V. Elsukov 			sin6->sin6_len = sizeof(*sin6);
563fcf59617SAndrey V. Elsukov 			sin6->sin6_family = AF_INET6;
564fcf59617SAndrey V. Elsukov 			sin6->sin6_port = IPSEC_PORT_ANY;
565fcf59617SAndrey V. Elsukov 			sin6->sin6_addr = ip6->ip6_dst;
566fcf59617SAndrey V. Elsukov 			if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
567fcf59617SAndrey V. Elsukov 				/* fix scope id for comparing SPD */
568fcf59617SAndrey V. Elsukov 				sin6->sin6_addr.s6_addr16[1] = 0;
569fcf59617SAndrey V. Elsukov 				sin6->sin6_scope_id =
570fcf59617SAndrey V. Elsukov 				    ntohs(ip6->ip6_dst.s6_addr16[1]);
571fcf59617SAndrey V. Elsukov 			}
572fcf59617SAndrey V. Elsukov 		}
573fcf59617SAndrey V. Elsukov 	} else
574fcf59617SAndrey V. Elsukov 		saidx = &sp->req[*pidx]->saidx;
575fcf59617SAndrey V. Elsukov 	/*
576fcf59617SAndrey V. Elsukov 	 * Lookup SA and validate it.
577fcf59617SAndrey V. Elsukov 	 */
578fcf59617SAndrey V. Elsukov 	sav = key_allocsa_policy(sp, saidx, error);
579fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
580fcf59617SAndrey V. Elsukov 		IPSEC6STAT_INC(ips_out_nosa);
581fcf59617SAndrey V. Elsukov 		if (*error != 0)
582fcf59617SAndrey V. Elsukov 			return (NULL);
583fcf59617SAndrey V. Elsukov 		if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) {
584fcf59617SAndrey V. Elsukov 			/*
585fcf59617SAndrey V. Elsukov 			 * We have no SA and policy that doesn't require
586fcf59617SAndrey V. Elsukov 			 * this IPsec transform, thus we can continue w/o
587fcf59617SAndrey V. Elsukov 			 * IPsec processing, i.e. return EJUSTRETURN.
588fcf59617SAndrey V. Elsukov 			 * But first check if there is some bundled transform.
589fcf59617SAndrey V. Elsukov 			 */
590fcf59617SAndrey V. Elsukov 			if (sp->tcount > ++(*pidx))
591fcf59617SAndrey V. Elsukov 				goto next;
592fcf59617SAndrey V. Elsukov 			*error = EJUSTRETURN;
593fcf59617SAndrey V. Elsukov 		}
594fcf59617SAndrey V. Elsukov 		return (NULL);
595fcf59617SAndrey V. Elsukov 	}
596fcf59617SAndrey V. Elsukov 	IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform"));
597fcf59617SAndrey V. Elsukov 	return (sav);
598fcf59617SAndrey V. Elsukov }
599fcf59617SAndrey V. Elsukov 
600fcf59617SAndrey V. Elsukov /*
601fcf59617SAndrey V. Elsukov  * IPsec output logic for IPv6.
602fcf59617SAndrey V. Elsukov  */
603fcf59617SAndrey V. Elsukov static int
6041a01e0e7SAndrey V. Elsukov ipsec6_perform_request(struct mbuf *m, struct secpolicy *sp,
6051a01e0e7SAndrey V. Elsukov     struct inpcb *inp, u_int idx)
606fcf59617SAndrey V. Elsukov {
607fcf59617SAndrey V. Elsukov 	struct ipsec_ctx_data ctx;
608fcf59617SAndrey V. Elsukov 	union sockaddr_union *dst;
609fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
610fcf59617SAndrey V. Elsukov 	struct ip6_hdr *ip6;
611fcf59617SAndrey V. Elsukov 	int error, i, off;
612fcf59617SAndrey V. Elsukov 
613fcf59617SAndrey V. Elsukov 	IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
614fcf59617SAndrey V. Elsukov 
615fcf59617SAndrey V. Elsukov 	sav = ipsec6_allocsa(m, sp, &idx, &error);
616fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
617fcf59617SAndrey V. Elsukov 		if (error == EJUSTRETURN) { /* No IPsec required */
618fcf59617SAndrey V. Elsukov 			key_freesp(&sp);
619fcf59617SAndrey V. Elsukov 			return (error);
620fcf59617SAndrey V. Elsukov 		}
621fcf59617SAndrey V. Elsukov 		goto bad;
622fcf59617SAndrey V. Elsukov 	}
623fcf59617SAndrey V. Elsukov 
624fcf59617SAndrey V. Elsukov 	/* Fix IP length in case if it is not set yet. */
625fcf59617SAndrey V. Elsukov 	ip6 = mtod(m, struct ip6_hdr *);
626fcf59617SAndrey V. Elsukov 	ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
627fcf59617SAndrey V. Elsukov 
6281a01e0e7SAndrey V. Elsukov 	IPSEC_INIT_CTX(&ctx, &m, inp, sav, AF_INET6, IPSEC_ENC_BEFORE);
629fcf59617SAndrey V. Elsukov 	if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
630fcf59617SAndrey V. Elsukov 		goto bad;
631fcf59617SAndrey V. Elsukov 
632fcf59617SAndrey V. Elsukov 	ip6 = mtod(m, struct ip6_hdr *); /* pfil can change mbuf */
633fcf59617SAndrey V. Elsukov 	dst = &sav->sah->saidx.dst;
634fcf59617SAndrey V. Elsukov 
635fcf59617SAndrey V. Elsukov 	/* Do the appropriate encapsulation, if necessary */
636fcf59617SAndrey V. Elsukov 	if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
637fcf59617SAndrey V. Elsukov 	    dst->sa.sa_family != AF_INET6 ||        /* PF mismatch */
638fcf59617SAndrey V. Elsukov 	    ((dst->sa.sa_family == AF_INET6) &&
639fcf59617SAndrey V. Elsukov 	     (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
640fcf59617SAndrey V. Elsukov 	     (!in6_sa_equal_addrwithscope(&dst->sin6, &ip6->ip6_dst)))) {
641fcf59617SAndrey V. Elsukov 		if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
642fcf59617SAndrey V. Elsukov 			/* No jumbogram support. */
643fcf59617SAndrey V. Elsukov 			error = ENXIO;   /*XXX*/
644fcf59617SAndrey V. Elsukov 			goto bad;
645fcf59617SAndrey V. Elsukov 		}
646fcf59617SAndrey V. Elsukov 		error = ipsec_encap(&m, &sav->sah->saidx);
647fcf59617SAndrey V. Elsukov 		if (error != 0) {
6487f1f6591SAndrey V. Elsukov 			DPRINTF(("%s: encapsulation for SPI 0x%08x failed "
6497f1f6591SAndrey V. Elsukov 			    "with error %d\n", __func__, ntohl(sav->spi),
6507f1f6591SAndrey V. Elsukov 			    error));
651fcf59617SAndrey V. Elsukov 			/* XXXAE: IPSEC_OSTAT_INC(tunnel); */
652fcf59617SAndrey V. Elsukov 			goto bad;
653fcf59617SAndrey V. Elsukov 		}
6541a01e0e7SAndrey V. Elsukov 		inp = NULL;
655fcf59617SAndrey V. Elsukov 	}
656fcf59617SAndrey V. Elsukov 
6571a01e0e7SAndrey V. Elsukov 	IPSEC_INIT_CTX(&ctx, &m, inp, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
658fcf59617SAndrey V. Elsukov 	if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
659fcf59617SAndrey V. Elsukov 		goto bad;
660fcf59617SAndrey V. Elsukov 
661fcf59617SAndrey V. Elsukov 	switch(dst->sa.sa_family) {
662fcf59617SAndrey V. Elsukov #ifdef INET
663fcf59617SAndrey V. Elsukov 	case AF_INET:
664fcf59617SAndrey V. Elsukov 		{
665fcf59617SAndrey V. Elsukov 		struct ip *ip;
666fcf59617SAndrey V. Elsukov 		ip = mtod(m, struct ip *);
667fcf59617SAndrey V. Elsukov 		i = ip->ip_hl << 2;
668fcf59617SAndrey V. Elsukov 		off = offsetof(struct ip, ip_p);
669fcf59617SAndrey V. Elsukov 		}
670fcf59617SAndrey V. Elsukov 		break;
671fcf59617SAndrey V. Elsukov #endif /* AF_INET */
672fcf59617SAndrey V. Elsukov 	case AF_INET6:
673fcf59617SAndrey V. Elsukov 		i = sizeof(struct ip6_hdr);
674fcf59617SAndrey V. Elsukov 		off = offsetof(struct ip6_hdr, ip6_nxt);
675fcf59617SAndrey V. Elsukov 		break;
676fcf59617SAndrey V. Elsukov 	default:
677fcf59617SAndrey V. Elsukov 		DPRINTF(("%s: unsupported protocol family %u\n",
678fcf59617SAndrey V. Elsukov 				 __func__, dst->sa.sa_family));
679fcf59617SAndrey V. Elsukov 		error = EPFNOSUPPORT;
680fcf59617SAndrey V. Elsukov 		IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf);
681fcf59617SAndrey V. Elsukov 		goto bad;
682fcf59617SAndrey V. Elsukov 	}
683fcf59617SAndrey V. Elsukov 	error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off);
684fcf59617SAndrey V. Elsukov 	return (error);
685fcf59617SAndrey V. Elsukov bad:
686fcf59617SAndrey V. Elsukov 	IPSEC6STAT_INC(ips_out_inval);
687fcf59617SAndrey V. Elsukov 	if (m != NULL)
688fcf59617SAndrey V. Elsukov 		m_freem(m);
689fcf59617SAndrey V. Elsukov 	if (sav != NULL)
690fcf59617SAndrey V. Elsukov 		key_freesav(&sav);
691fcf59617SAndrey V. Elsukov 	key_freesp(&sp);
692fcf59617SAndrey V. Elsukov 	return (error);
693fcf59617SAndrey V. Elsukov }
694fcf59617SAndrey V. Elsukov 
695fcf59617SAndrey V. Elsukov int
696fcf59617SAndrey V. Elsukov ipsec6_process_packet(struct mbuf *m, struct secpolicy *sp,
697fcf59617SAndrey V. Elsukov     struct inpcb *inp)
698fcf59617SAndrey V. Elsukov {
699fcf59617SAndrey V. Elsukov 
7001a01e0e7SAndrey V. Elsukov 	return (ipsec6_perform_request(m, sp, inp, 0));
701fcf59617SAndrey V. Elsukov }
702fcf59617SAndrey V. Elsukov 
703fcf59617SAndrey V. Elsukov static int
704fcf59617SAndrey V. Elsukov ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
705fcf59617SAndrey V. Elsukov {
706fcf59617SAndrey V. Elsukov 	struct secpolicy *sp;
707fcf59617SAndrey V. Elsukov 	int error;
708fcf59617SAndrey V. Elsukov 
709fcf59617SAndrey V. Elsukov 	/* Lookup for the corresponding outbound security policy */
71022bbefb2SAndrey V. Elsukov 	sp = ipsec6_checkpolicy(m, inp, &error, !forwarding);
711fcf59617SAndrey V. Elsukov 	if (sp == NULL) {
712fcf59617SAndrey V. Elsukov 		if (error == -EINVAL) {
713fcf59617SAndrey V. Elsukov 			/* Discarded by policy. */
714fcf59617SAndrey V. Elsukov 			m_freem(m);
715fcf59617SAndrey V. Elsukov 			return (EACCES);
716fcf59617SAndrey V. Elsukov 		}
717fcf59617SAndrey V. Elsukov 		return (0); /* No IPsec required. */
718fcf59617SAndrey V. Elsukov 	}
719fcf59617SAndrey V. Elsukov 
720fcf59617SAndrey V. Elsukov 	if (!forwarding) {
721fcf59617SAndrey V. Elsukov 		/*
722fcf59617SAndrey V. Elsukov 		 * Do delayed checksums now because we send before
723fcf59617SAndrey V. Elsukov 		 * this is done in the normal processing path.
724fcf59617SAndrey V. Elsukov 		 */
725fcf59617SAndrey V. Elsukov 		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
7268e9313caSJohn Baldwin 			m = mb_unmapped_to_ext(m);
7278e9313caSJohn Baldwin 			if (m == NULL) {
7288e9313caSJohn Baldwin 				IPSEC6STAT_INC(ips_out_nomem);
7298e9313caSJohn Baldwin 				key_freesp(&sp);
7308e9313caSJohn Baldwin 				return (ENOBUFS);
7318e9313caSJohn Baldwin 			}
732fcf59617SAndrey V. Elsukov 			in6_delayed_cksum(m, m->m_pkthdr.len -
733fcf59617SAndrey V. Elsukov 			    sizeof(struct ip6_hdr), sizeof(struct ip6_hdr));
734fcf59617SAndrey V. Elsukov 			m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;
735fcf59617SAndrey V. Elsukov 		}
73695033af9SMark Johnston #if defined(SCTP) || defined(SCTP_SUPPORT)
737fcf59617SAndrey V. Elsukov 		if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) {
7388e9313caSJohn Baldwin 			m = mb_unmapped_to_ext(m);
7398e9313caSJohn Baldwin 			if (m == NULL) {
7408e9313caSJohn Baldwin 				IPSEC6STAT_INC(ips_out_nomem);
7418e9313caSJohn Baldwin 				key_freesp(&sp);
7428e9313caSJohn Baldwin 				return (ENOBUFS);
7438e9313caSJohn Baldwin 			}
744fcf59617SAndrey V. Elsukov 			sctp_delayed_cksum(m, sizeof(struct ip6_hdr));
745fcf59617SAndrey V. Elsukov 			m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6;
746fcf59617SAndrey V. Elsukov 		}
747fcf59617SAndrey V. Elsukov #endif
748fcf59617SAndrey V. Elsukov 	}
749fcf59617SAndrey V. Elsukov 	/* NB: callee frees mbuf and releases reference to SP */
750fcf59617SAndrey V. Elsukov 	error = ipsec6_process_packet(m, sp, inp);
751fcf59617SAndrey V. Elsukov 	if (error == EJUSTRETURN) {
752fcf59617SAndrey V. Elsukov 		/*
753fcf59617SAndrey V. Elsukov 		 * We had a SP with a level of 'use' and no SA. We
754fcf59617SAndrey V. Elsukov 		 * will just continue to process the packet without
755fcf59617SAndrey V. Elsukov 		 * IPsec processing and return without error.
756fcf59617SAndrey V. Elsukov 		 */
757fcf59617SAndrey V. Elsukov 		return (0);
758fcf59617SAndrey V. Elsukov 	}
759fcf59617SAndrey V. Elsukov 	if (error == 0)
760fcf59617SAndrey V. Elsukov 		return (EINPROGRESS); /* consumed by IPsec */
761fcf59617SAndrey V. Elsukov 	return (error);
762fcf59617SAndrey V. Elsukov }
763fcf59617SAndrey V. Elsukov 
764fcf59617SAndrey V. Elsukov /*
765fcf59617SAndrey V. Elsukov  * IPSEC_OUTPUT() method implementation for IPv6.
766fcf59617SAndrey V. Elsukov  * 0 - no IPsec handling needed
767fcf59617SAndrey V. Elsukov  * other values - mbuf consumed by IPsec.
768fcf59617SAndrey V. Elsukov  */
769fcf59617SAndrey V. Elsukov int
770fcf59617SAndrey V. Elsukov ipsec6_output(struct mbuf *m, struct inpcb *inp)
771fcf59617SAndrey V. Elsukov {
772fcf59617SAndrey V. Elsukov 
773fcf59617SAndrey V. Elsukov 	/*
774fcf59617SAndrey V. Elsukov 	 * If the packet is resubmitted to ip_output (e.g. after
775fcf59617SAndrey V. Elsukov 	 * AH, ESP, etc. processing), there will be a tag to bypass
776fcf59617SAndrey V. Elsukov 	 * the lookup and related policy checking.
777fcf59617SAndrey V. Elsukov 	 */
778fcf59617SAndrey V. Elsukov 	if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL)
779fcf59617SAndrey V. Elsukov 		return (0);
780fcf59617SAndrey V. Elsukov 
781fcf59617SAndrey V. Elsukov 	return (ipsec6_common_output(m, inp, 0));
782fcf59617SAndrey V. Elsukov }
783fcf59617SAndrey V. Elsukov 
784fcf59617SAndrey V. Elsukov /*
785fcf59617SAndrey V. Elsukov  * IPSEC_FORWARD() method implementation for IPv6.
786fcf59617SAndrey V. Elsukov  * 0 - no IPsec handling needed
787fcf59617SAndrey V. Elsukov  * other values - mbuf consumed by IPsec.
788fcf59617SAndrey V. Elsukov  */
789fcf59617SAndrey V. Elsukov int
790fcf59617SAndrey V. Elsukov ipsec6_forward(struct mbuf *m)
791fcf59617SAndrey V. Elsukov {
792fcf59617SAndrey V. Elsukov 
793fcf59617SAndrey V. Elsukov 	/*
794fcf59617SAndrey V. Elsukov 	 * Check if this packet has an active inbound SP and needs to be
795fcf59617SAndrey V. Elsukov 	 * dropped instead of forwarded.
796fcf59617SAndrey V. Elsukov 	 */
797fcf59617SAndrey V. Elsukov 	if (ipsec6_in_reject(m, NULL) != 0) {
798fcf59617SAndrey V. Elsukov 		m_freem(m);
799fcf59617SAndrey V. Elsukov 		return (EACCES);
800fcf59617SAndrey V. Elsukov 	}
801fcf59617SAndrey V. Elsukov 	return (ipsec6_common_output(m, NULL, 1));
802fcf59617SAndrey V. Elsukov }
803fcf59617SAndrey V. Elsukov #endif /* INET6 */
804fcf59617SAndrey V. Elsukov 
805fcf59617SAndrey V. Elsukov int
806fcf59617SAndrey V. Elsukov ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
807fcf59617SAndrey V. Elsukov     u_int idx)
808fcf59617SAndrey V. Elsukov {
809f82eb2a6SJohn Baldwin 	struct epoch_tracker et;
810fcf59617SAndrey V. Elsukov 	struct xform_history *xh;
811fcf59617SAndrey V. Elsukov 	struct secasindex *saidx;
812fcf59617SAndrey V. Elsukov 	struct m_tag *mtag;
813fcf59617SAndrey V. Elsukov 	int error;
81488768458SSam Leffler 
81588768458SSam Leffler 	saidx = &sav->sah->saidx;
81688768458SSam Leffler 	switch (saidx->dst.sa.sa_family) {
81788768458SSam Leffler #ifdef INET
81888768458SSam Leffler 	case AF_INET:
81988768458SSam Leffler 		/* Fix the header length, for AH processing. */
82088768458SSam Leffler 		mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
82188768458SSam Leffler 		break;
82288768458SSam Leffler #endif /* INET */
82388768458SSam Leffler #ifdef INET6
82488768458SSam Leffler 	case AF_INET6:
82588768458SSam Leffler 		/* Fix the header length, for AH processing. */
82688768458SSam Leffler 		if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) {
82788768458SSam Leffler 			error = ENXIO;
82888768458SSam Leffler 			goto bad;
82988768458SSam Leffler 		}
83088768458SSam Leffler 		if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) {
83188768458SSam Leffler 			/* No jumbogram support. */
83288768458SSam Leffler 			error = ENXIO;	/*?*/
83388768458SSam Leffler 			goto bad;
83488768458SSam Leffler 		}
83588768458SSam Leffler 		mtod(m, struct ip6_hdr *)->ip6_plen =
83688768458SSam Leffler 			htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
83788768458SSam Leffler 		break;
83888768458SSam Leffler #endif /* INET6 */
83988768458SSam Leffler 	default:
8409ffa9677SSam Leffler 		DPRINTF(("%s: unknown protocol family %u\n", __func__,
84188768458SSam Leffler 		    saidx->dst.sa.sa_family));
84288768458SSam Leffler 		error = ENXIO;
84388768458SSam Leffler 		goto bad;
84488768458SSam Leffler 	}
84588768458SSam Leffler 
84688768458SSam Leffler 	/*
847fcf59617SAndrey V. Elsukov 	 * Add a record of what we've done to the packet.
84888768458SSam Leffler 	 */
849fcf59617SAndrey V. Elsukov 	mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(*xh), M_NOWAIT);
85088768458SSam Leffler 	if (mtag == NULL) {
8519ffa9677SSam Leffler 		DPRINTF(("%s: could not get packet tag\n", __func__));
85288768458SSam Leffler 		error = ENOMEM;
85388768458SSam Leffler 		goto bad;
85488768458SSam Leffler 	}
85588768458SSam Leffler 
856fcf59617SAndrey V. Elsukov 	xh = (struct xform_history *)(mtag + 1);
857fcf59617SAndrey V. Elsukov 	xh->dst = saidx->dst;
858fcf59617SAndrey V. Elsukov 	xh->proto = saidx->proto;
859fcf59617SAndrey V. Elsukov 	xh->mode = saidx->mode;
860fcf59617SAndrey V. Elsukov 	xh->spi = sav->spi;
86188768458SSam Leffler 	m_tag_prepend(m, mtag);
86288768458SSam Leffler 
86359959de5SErmal Luçi 	key_sa_recordxfer(sav, m);		/* record data transfer */
86459959de5SErmal Luçi 
86588768458SSam Leffler 	/*
86688768458SSam Leffler 	 * If there's another (bundled) SA to apply, do so.
86788768458SSam Leffler 	 * Note that this puts a burden on the kernel stack size.
86888768458SSam Leffler 	 * If this is a problem we'll need to introduce a queue
86988768458SSam Leffler 	 * to set the packet on so we can unwind the stack before
87088768458SSam Leffler 	 * doing further processing.
87188768458SSam Leffler 	 */
872fcf59617SAndrey V. Elsukov 	if (++idx < sp->tcount) {
873db178eb8SBjoern A. Zeeb 		switch (saidx->dst.sa.sa_family) {
874db178eb8SBjoern A. Zeeb #ifdef INET
875db178eb8SBjoern A. Zeeb 		case AF_INET:
876fcf59617SAndrey V. Elsukov 			key_freesav(&sav);
877f9d8f665SAndrey V. Elsukov 			IPSECSTAT_INC(ips_out_bundlesa);
8781a01e0e7SAndrey V. Elsukov 			return (ipsec4_perform_request(m, sp, NULL, idx));
879db178eb8SBjoern A. Zeeb 			/* NOTREACHED */
880db178eb8SBjoern A. Zeeb #endif
881db178eb8SBjoern A. Zeeb #ifdef INET6
882db178eb8SBjoern A. Zeeb 		case AF_INET6:
883fcf59617SAndrey V. Elsukov 			key_freesav(&sav);
884f9d8f665SAndrey V. Elsukov 			IPSEC6STAT_INC(ips_out_bundlesa);
8851a01e0e7SAndrey V. Elsukov 			return (ipsec6_perform_request(m, sp, NULL, idx));
886db178eb8SBjoern A. Zeeb 			/* NOTREACHED */
887db178eb8SBjoern A. Zeeb #endif /* INET6 */
888db178eb8SBjoern A. Zeeb 		default:
889db178eb8SBjoern A. Zeeb 			DPRINTF(("%s: unknown protocol family %u\n", __func__,
890db178eb8SBjoern A. Zeeb 			    saidx->dst.sa.sa_family));
891fcf59617SAndrey V. Elsukov 			error = EPFNOSUPPORT;
892db178eb8SBjoern A. Zeeb 			goto bad;
893db178eb8SBjoern A. Zeeb 		}
89488768458SSam Leffler 	}
89588768458SSam Leffler 
896fcf59617SAndrey V. Elsukov 	key_freesp(&sp), sp = NULL;	/* Release reference to SP */
897fcf59617SAndrey V. Elsukov #ifdef INET
898fcf59617SAndrey V. Elsukov 	/*
899fcf59617SAndrey V. Elsukov 	 * Do UDP encapsulation if SA requires it.
900fcf59617SAndrey V. Elsukov 	 */
901fcf59617SAndrey V. Elsukov 	if (sav->natt != NULL) {
902fcf59617SAndrey V. Elsukov 		error = udp_ipsec_output(m, sav);
903fcf59617SAndrey V. Elsukov 		if (error != 0)
904fcf59617SAndrey V. Elsukov 			goto bad;
905fcf59617SAndrey V. Elsukov 	}
906fcf59617SAndrey V. Elsukov #endif /* INET */
90788768458SSam Leffler 	/*
90888768458SSam Leffler 	 * We're done with IPsec processing, transmit the packet using the
9096508929bSAndrey V. Elsukov 	 * appropriate network protocol (IP or IPv6).
91088768458SSam Leffler 	 */
911f82eb2a6SJohn Baldwin 	NET_EPOCH_ENTER(et);
91288768458SSam Leffler 	switch (saidx->dst.sa.sa_family) {
91388768458SSam Leffler #ifdef INET
91488768458SSam Leffler 	case AF_INET:
915fcf59617SAndrey V. Elsukov 		key_freesav(&sav);
916f82eb2a6SJohn Baldwin 		error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
917f82eb2a6SJohn Baldwin 		break;
91888768458SSam Leffler #endif /* INET */
91988768458SSam Leffler #ifdef INET6
92088768458SSam Leffler 	case AF_INET6:
921fcf59617SAndrey V. Elsukov 		key_freesav(&sav);
922f82eb2a6SJohn Baldwin 		error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
923f82eb2a6SJohn Baldwin 		break;
92488768458SSam Leffler #endif /* INET6 */
925f82eb2a6SJohn Baldwin 	default:
92688768458SSam Leffler 		panic("ipsec_process_done");
927f82eb2a6SJohn Baldwin 	}
928f82eb2a6SJohn Baldwin 	NET_EPOCH_EXIT(et);
929f82eb2a6SJohn Baldwin 	return (error);
93088768458SSam Leffler bad:
93188768458SSam Leffler 	m_freem(m);
932fcf59617SAndrey V. Elsukov 	key_freesav(&sav);
933fcf59617SAndrey V. Elsukov 	if (sp != NULL)
934fcf59617SAndrey V. Elsukov 		key_freesp(&sp);
93588768458SSam Leffler 	return (error);
93688768458SSam Leffler }
93788768458SSam Leffler 
938fcf59617SAndrey V. Elsukov /*
939fcf59617SAndrey V. Elsukov  * ipsec_prepend() is optimized version of M_PREPEND().
940fcf59617SAndrey V. Elsukov  * ipsec_encap() is called by IPsec output routine for tunnel mode SA.
941fcf59617SAndrey V. Elsukov  * It is expected that after IP encapsulation some IPsec transform will
942fcf59617SAndrey V. Elsukov  * be performed. Each IPsec transform inserts its variable length header
943fcf59617SAndrey V. Elsukov  * just after outer IP header using m_makespace(). If given mbuf has not
944fcf59617SAndrey V. Elsukov  * enough free space at the beginning, we allocate new mbuf and reserve
945fcf59617SAndrey V. Elsukov  * some space at the beginning and at the end.
946fcf59617SAndrey V. Elsukov  * This helps avoid allocating of new mbuf and data copying in m_makespace(),
947fcf59617SAndrey V. Elsukov  * we place outer header in the middle of mbuf's data with reserved leading
948fcf59617SAndrey V. Elsukov  * and trailing space:
949fcf59617SAndrey V. Elsukov  *	[ LEADINGSPACE ][ Outer IP header ][ TRAILINGSPACE ]
950fcf59617SAndrey V. Elsukov  * LEADINGSPACE will be used to add ethernet header, TRAILINGSPACE will
951fcf59617SAndrey V. Elsukov  * be used to inject AH/ESP/IPCOMP header.
952fcf59617SAndrey V. Elsukov  */
953fcf59617SAndrey V. Elsukov #define	IPSEC_TRAILINGSPACE	(sizeof(struct udphdr) +/* NAT-T */	\
954fcf59617SAndrey V. Elsukov     max(sizeof(struct newesp) + EALG_MAX_BLOCK_LEN,	/* ESP + IV */	\
955fcf59617SAndrey V. Elsukov 	sizeof(struct newah) + HASH_MAX_LEN		/* AH + ICV */))
956fcf59617SAndrey V. Elsukov static struct mbuf *
957fcf59617SAndrey V. Elsukov ipsec_prepend(struct mbuf *m, int len, int how)
95888768458SSam Leffler {
959fcf59617SAndrey V. Elsukov 	struct mbuf *n;
96088768458SSam Leffler 
961fcf59617SAndrey V. Elsukov 	M_ASSERTPKTHDR(m);
962fcf59617SAndrey V. Elsukov 	IPSEC_ASSERT(len < MHLEN, ("wrong length"));
963fcf59617SAndrey V. Elsukov 	if (M_LEADINGSPACE(m) >= len) {
964fcf59617SAndrey V. Elsukov 		/* No need to allocate new mbuf. */
965fcf59617SAndrey V. Elsukov 		m->m_data -= len;
966fcf59617SAndrey V. Elsukov 		m->m_len += len;
967fcf59617SAndrey V. Elsukov 		m->m_pkthdr.len += len;
968fcf59617SAndrey V. Elsukov 		return (m);
96988768458SSam Leffler 	}
970fcf59617SAndrey V. Elsukov 	n = m_gethdr(how, m->m_type);
971fcf59617SAndrey V. Elsukov 	if (n == NULL) {
972fcf59617SAndrey V. Elsukov 		m_freem(m);
973fcf59617SAndrey V. Elsukov 		return (NULL);
97488768458SSam Leffler 	}
975fcf59617SAndrey V. Elsukov 	m_move_pkthdr(n, m);
976fcf59617SAndrey V. Elsukov 	n->m_next = m;
977fcf59617SAndrey V. Elsukov 	if (len + IPSEC_TRAILINGSPACE < M_SIZE(n))
978fcf59617SAndrey V. Elsukov 		m_align(n, len + IPSEC_TRAILINGSPACE);
979fcf59617SAndrey V. Elsukov 	n->m_len = len;
980fcf59617SAndrey V. Elsukov 	n->m_pkthdr.len += len;
981fcf59617SAndrey V. Elsukov 	return (n);
98288768458SSam Leffler }
98388768458SSam Leffler 
98461f37615SAndrey V. Elsukov static int
98561f37615SAndrey V. Elsukov ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
98661f37615SAndrey V. Elsukov {
98761f37615SAndrey V. Elsukov #ifdef INET6
98861f37615SAndrey V. Elsukov 	struct ip6_hdr *ip6;
98961f37615SAndrey V. Elsukov #endif
99061f37615SAndrey V. Elsukov 	struct ip *ip;
99161f37615SAndrey V. Elsukov 	int setdf;
99261f37615SAndrey V. Elsukov 	uint8_t itos, proto;
99361f37615SAndrey V. Elsukov 
99461f37615SAndrey V. Elsukov 	ip = mtod(*mp, struct ip *);
99561f37615SAndrey V. Elsukov 	switch (ip->ip_v) {
99661f37615SAndrey V. Elsukov #ifdef INET
99761f37615SAndrey V. Elsukov 	case IPVERSION:
99861f37615SAndrey V. Elsukov 		proto = IPPROTO_IPIP;
99961f37615SAndrey V. Elsukov 		/*
100061f37615SAndrey V. Elsukov 		 * Collect IP_DF state from the inner header
100161f37615SAndrey V. Elsukov 		 * and honor system-wide control of how to handle it.
100261f37615SAndrey V. Elsukov 		 */
100361f37615SAndrey V. Elsukov 		switch (V_ip4_ipsec_dfbit) {
100461f37615SAndrey V. Elsukov 		case 0:	/* clear in outer header */
100561f37615SAndrey V. Elsukov 		case 1:	/* set in outer header */
100661f37615SAndrey V. Elsukov 			setdf = V_ip4_ipsec_dfbit;
100761f37615SAndrey V. Elsukov 			break;
100861f37615SAndrey V. Elsukov 		default:/* propagate to outer header */
10096f814d0eSAndrey V. Elsukov 			setdf = (ip->ip_off & htons(IP_DF)) != 0;
101061f37615SAndrey V. Elsukov 		}
101161f37615SAndrey V. Elsukov 		itos = ip->ip_tos;
101261f37615SAndrey V. Elsukov 		break;
101361f37615SAndrey V. Elsukov #endif
101461f37615SAndrey V. Elsukov #ifdef INET6
101561f37615SAndrey V. Elsukov 	case (IPV6_VERSION >> 4):
101661f37615SAndrey V. Elsukov 		proto = IPPROTO_IPV6;
101761f37615SAndrey V. Elsukov 		ip6 = mtod(*mp, struct ip6_hdr *);
101861f37615SAndrey V. Elsukov 		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
101961f37615SAndrey V. Elsukov 		setdf = V_ip4_ipsec_dfbit ? 1: 0;
102061f37615SAndrey V. Elsukov 		/* scoped address handling */
102161f37615SAndrey V. Elsukov 		in6_clearscope(&ip6->ip6_src);
102261f37615SAndrey V. Elsukov 		in6_clearscope(&ip6->ip6_dst);
102361f37615SAndrey V. Elsukov 		break;
102461f37615SAndrey V. Elsukov #endif
102561f37615SAndrey V. Elsukov 	default:
102661f37615SAndrey V. Elsukov 		return (EAFNOSUPPORT);
102761f37615SAndrey V. Elsukov 	}
102861f37615SAndrey V. Elsukov 	switch (saidx->dst.sa.sa_family) {
102961f37615SAndrey V. Elsukov #ifdef INET
103061f37615SAndrey V. Elsukov 	case AF_INET:
103161f37615SAndrey V. Elsukov 		if (saidx->src.sa.sa_family != AF_INET ||
103261f37615SAndrey V. Elsukov 		    saidx->src.sin.sin_addr.s_addr == INADDR_ANY ||
103361f37615SAndrey V. Elsukov 		    saidx->dst.sin.sin_addr.s_addr == INADDR_ANY)
103461f37615SAndrey V. Elsukov 			return (EINVAL);
1035fcf59617SAndrey V. Elsukov 		*mp = ipsec_prepend(*mp, sizeof(struct ip), M_NOWAIT);
103661f37615SAndrey V. Elsukov 		if (*mp == NULL)
103761f37615SAndrey V. Elsukov 			return (ENOBUFS);
103861f37615SAndrey V. Elsukov 		ip = mtod(*mp, struct ip *);
103961f37615SAndrey V. Elsukov 		ip->ip_v = IPVERSION;
104061f37615SAndrey V. Elsukov 		ip->ip_hl = sizeof(struct ip) >> 2;
104161f37615SAndrey V. Elsukov 		ip->ip_p = proto;
104261f37615SAndrey V. Elsukov 		ip->ip_len = htons((*mp)->m_pkthdr.len);
104361f37615SAndrey V. Elsukov 		ip->ip_ttl = V_ip_defttl;
104461f37615SAndrey V. Elsukov 		ip->ip_sum = 0;
104561f37615SAndrey V. Elsukov 		ip->ip_off = setdf ? htons(IP_DF): 0;
104661f37615SAndrey V. Elsukov 		ip->ip_src = saidx->src.sin.sin_addr;
104761f37615SAndrey V. Elsukov 		ip->ip_dst = saidx->dst.sin.sin_addr;
104861f37615SAndrey V. Elsukov 		ip_ecn_ingress(V_ip4_ipsec_ecn, &ip->ip_tos, &itos);
104961f37615SAndrey V. Elsukov 		ip_fillid(ip);
105061f37615SAndrey V. Elsukov 		break;
105161f37615SAndrey V. Elsukov #endif /* INET */
105261f37615SAndrey V. Elsukov #ifdef INET6
105361f37615SAndrey V. Elsukov 	case AF_INET6:
105461f37615SAndrey V. Elsukov 		if (saidx->src.sa.sa_family != AF_INET6 ||
105561f37615SAndrey V. Elsukov 		    IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr) ||
105661f37615SAndrey V. Elsukov 		    IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr))
105761f37615SAndrey V. Elsukov 			return (EINVAL);
1058fcf59617SAndrey V. Elsukov 		*mp = ipsec_prepend(*mp, sizeof(struct ip6_hdr), M_NOWAIT);
105961f37615SAndrey V. Elsukov 		if (*mp == NULL)
106061f37615SAndrey V. Elsukov 			return (ENOBUFS);
106161f37615SAndrey V. Elsukov 		ip6 = mtod(*mp, struct ip6_hdr *);
106261f37615SAndrey V. Elsukov 		ip6->ip6_flow = 0;
106361f37615SAndrey V. Elsukov 		ip6->ip6_vfc = IPV6_VERSION;
106461f37615SAndrey V. Elsukov 		ip6->ip6_hlim = V_ip6_defhlim;
106561f37615SAndrey V. Elsukov 		ip6->ip6_nxt = proto;
106661f37615SAndrey V. Elsukov 		ip6->ip6_dst = saidx->dst.sin6.sin6_addr;
10671ae800e7SAndrey V. Elsukov 		/* For link-local address embed scope zone id */
10681ae800e7SAndrey V. Elsukov 		if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
10691ae800e7SAndrey V. Elsukov 			ip6->ip6_dst.s6_addr16[1] =
10701ae800e7SAndrey V. Elsukov 			    htons(saidx->dst.sin6.sin6_scope_id & 0xffff);
107161f37615SAndrey V. Elsukov 		ip6->ip6_src = saidx->src.sin6.sin6_addr;
10721ae800e7SAndrey V. Elsukov 		if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
10731ae800e7SAndrey V. Elsukov 			ip6->ip6_src.s6_addr16[1] =
10741ae800e7SAndrey V. Elsukov 			    htons(saidx->src.sin6.sin6_scope_id & 0xffff);
107561f37615SAndrey V. Elsukov 		ip6->ip6_plen = htons((*mp)->m_pkthdr.len - sizeof(*ip6));
107661f37615SAndrey V. Elsukov 		ip_ecn_ingress(V_ip6_ipsec_ecn, &proto, &itos);
107761f37615SAndrey V. Elsukov 		ip6->ip6_flow |= htonl((uint32_t)proto << 20);
107861f37615SAndrey V. Elsukov 		break;
107961f37615SAndrey V. Elsukov #endif /* INET6 */
108061f37615SAndrey V. Elsukov 	default:
108161f37615SAndrey V. Elsukov 		return (EAFNOSUPPORT);
108261f37615SAndrey V. Elsukov 	}
1083fcf59617SAndrey V. Elsukov 	(*mp)->m_flags &= ~(M_BCAST | M_MCAST);
108461f37615SAndrey V. Elsukov 	return (0);
108561f37615SAndrey V. Elsukov }
1086