xref: /freebsd-src/sys/netipsec/xform_ipcomp.c (revision b1c3a4d75f4ff74218434a11cdd4e56632e13711)
188768458SSam Leffler /* $OpenBSD: ip_ipcomp.c,v 1.1 2001/07/05 12:08:52 jjbg Exp $ */
288768458SSam Leffler 
3c398230bSWarner Losh /*-
4fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
5fe267a55SPedro F. Giffuni  *
688768458SSam Leffler  * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
788768458SSam Leffler  *
888768458SSam Leffler  * Redistribution and use in source and binary forms, with or without
988768458SSam Leffler  * modification, are permitted provided that the following conditions
1088768458SSam Leffler  * are met:
1188768458SSam Leffler  *
1288768458SSam Leffler  * 1. Redistributions of source code must retain the above copyright
1388768458SSam Leffler  *   notice, this list of conditions and the following disclaimer.
1488768458SSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
1588768458SSam Leffler  *   notice, this list of conditions and the following disclaimer in the
1688768458SSam Leffler  *   documentation and/or other materials provided with the distribution.
1788768458SSam Leffler  * 3. The name of the author may not be used to endorse or promote products
1888768458SSam Leffler  *   derived from this software without specific prior written permission.
1988768458SSam Leffler  *
2088768458SSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2188768458SSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2288768458SSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2388768458SSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2488768458SSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2588768458SSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2688768458SSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2788768458SSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2888768458SSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2988768458SSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3088768458SSam Leffler  */
3188768458SSam Leffler 
3288768458SSam Leffler /* IP payload compression protocol (IPComp), see RFC 2393 */
3388768458SSam Leffler #include "opt_inet.h"
3488768458SSam Leffler #include "opt_inet6.h"
3528d2a72bSJohn Baldwin #include "opt_ipsec.h"
3688768458SSam Leffler 
3788768458SSam Leffler #include <sys/param.h>
3888768458SSam Leffler #include <sys/systm.h>
3935d9e00dSJohn Baldwin #include <sys/malloc.h>
4088768458SSam Leffler #include <sys/mbuf.h>
416464079fSSam Leffler #include <sys/lock.h>
426464079fSSam Leffler #include <sys/mutex.h>
4388768458SSam Leffler #include <sys/socket.h>
4488768458SSam Leffler #include <sys/kernel.h>
4588768458SSam Leffler #include <sys/protosw.h>
4688768458SSam Leffler #include <sys/sysctl.h>
4788768458SSam Leffler 
4888768458SSam Leffler #include <netinet/in.h>
4988768458SSam Leffler #include <netinet/in_systm.h>
5088768458SSam Leffler #include <netinet/ip.h>
5188768458SSam Leffler #include <netinet/ip_var.h>
523cbd4ec3SAndrey V. Elsukov #include <netinet/ip_encap.h>
5388768458SSam Leffler 
543cbd4ec3SAndrey V. Elsukov #include <net/netisr.h>
55eddfbb76SRobert Watson #include <net/vnet.h>
56b8a6e03fSGleb Smirnoff #include <net/if.h>	/* XXXGL: net_epoch should move out there */
57b8a6e03fSGleb Smirnoff #include <net/if_var.h>	/* XXXGL: net_epoch should move out there */
58eddfbb76SRobert Watson 
5988768458SSam Leffler #include <netipsec/ipsec.h>
6088768458SSam Leffler #include <netipsec/xform.h>
6188768458SSam Leffler 
6288768458SSam Leffler #ifdef INET6
6388768458SSam Leffler #include <netinet/ip6.h>
640d056664SAndrey V. Elsukov #include <netinet6/ip6_var.h>
6588768458SSam Leffler #include <netipsec/ipsec6.h>
6688768458SSam Leffler #endif
6788768458SSam Leffler 
6888768458SSam Leffler #include <netipsec/ipcomp.h>
6988768458SSam Leffler #include <netipsec/ipcomp_var.h>
7088768458SSam Leffler 
7188768458SSam Leffler #include <netipsec/key.h>
72fcf59617SAndrey V. Elsukov #include <netipsec/key_debug.h>
7388768458SSam Leffler 
7488768458SSam Leffler #include <opencrypto/cryptodev.h>
7588768458SSam Leffler #include <opencrypto/deflate.h>
7688768458SSam Leffler #include <opencrypto/xform.h>
7788768458SSam Leffler 
78a77cb332SBjoern A. Zeeb VNET_DEFINE(int, ipcomp_enable) = 1;
79db8c0879SAndrey V. Elsukov VNET_PCPUSTAT_DEFINE(struct ipcompstat, ipcompstat);
80db8c0879SAndrey V. Elsukov VNET_PCPUSTAT_SYSINIT(ipcompstat);
81db8c0879SAndrey V. Elsukov 
82db8c0879SAndrey V. Elsukov #ifdef VIMAGE
83db8c0879SAndrey V. Elsukov VNET_PCPUSTAT_SYSUNINIT(ipcompstat);
84db8c0879SAndrey V. Elsukov #endif /* VIMAGE */
8588768458SSam Leffler 
8688768458SSam Leffler SYSCTL_DECL(_net_inet_ipcomp);
876df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_ipcomp, OID_AUTO, ipcomp_enable,
886df8a710SGleb Smirnoff 	CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ipcomp_enable), 0, "");
89db8c0879SAndrey V. Elsukov SYSCTL_VNET_PCPUSTAT(_net_inet_ipcomp, IPSECCTL_STATS, stats,
90db8c0879SAndrey V. Elsukov     struct ipcompstat, ipcompstat,
91db8c0879SAndrey V. Elsukov     "IPCOMP statistics (struct ipcompstat, netipsec/ipcomp_var.h");
9288768458SSam Leffler 
9335d9e00dSJohn Baldwin static MALLOC_DEFINE(M_IPCOMP, "ipcomp", "IPCOMP");
9435d9e00dSJohn Baldwin 
9588768458SSam Leffler static int ipcomp_input_cb(struct cryptop *crp);
9688768458SSam Leffler static int ipcomp_output_cb(struct cryptop *crp);
9788768458SSam Leffler 
983cbd4ec3SAndrey V. Elsukov /*
993cbd4ec3SAndrey V. Elsukov  * RFC 3173 p 2.2. Non-Expansion Policy:
1003cbd4ec3SAndrey V. Elsukov  * If the total size of a compressed payload and the IPComp header, as
1013cbd4ec3SAndrey V. Elsukov  * defined in section 3, is not smaller than the size of the original
1023cbd4ec3SAndrey V. Elsukov  * payload, the IP datagram MUST be sent in the original non-compressed
1033cbd4ec3SAndrey V. Elsukov  * form.
1043cbd4ec3SAndrey V. Elsukov  *
1053cbd4ec3SAndrey V. Elsukov  * When we use IPComp in tunnel mode, for small packets we will receive
1063cbd4ec3SAndrey V. Elsukov  * encapsulated IP-IP datagrams without any compression and without IPComp
1073cbd4ec3SAndrey V. Elsukov  * header.
1083cbd4ec3SAndrey V. Elsukov  */
1093cbd4ec3SAndrey V. Elsukov static int
1103cbd4ec3SAndrey V. Elsukov ipcomp_encapcheck(union sockaddr_union *src, union sockaddr_union *dst)
1113cbd4ec3SAndrey V. Elsukov {
1123cbd4ec3SAndrey V. Elsukov 	struct secasvar *sav;
1133cbd4ec3SAndrey V. Elsukov 
114fcf59617SAndrey V. Elsukov 	sav = key_allocsa_tunnel(src, dst, IPPROTO_IPCOMP);
1153cbd4ec3SAndrey V. Elsukov 	if (sav == NULL)
1163cbd4ec3SAndrey V. Elsukov 		return (0);
117fcf59617SAndrey V. Elsukov 	key_freesav(&sav);
1183cbd4ec3SAndrey V. Elsukov 
1193cbd4ec3SAndrey V. Elsukov 	if (src->sa.sa_family == AF_INET)
1203cbd4ec3SAndrey V. Elsukov 		return (sizeof(struct in_addr) << 4);
1213cbd4ec3SAndrey V. Elsukov 	else
1223cbd4ec3SAndrey V. Elsukov 		return (sizeof(struct in6_addr) << 4);
1233cbd4ec3SAndrey V. Elsukov }
1243cbd4ec3SAndrey V. Elsukov 
1253cbd4ec3SAndrey V. Elsukov static int
1266d8fdfa9SAndrey V. Elsukov ipcomp_nonexp_input(struct mbuf *m, int off, int proto, void *arg __unused)
1273cbd4ec3SAndrey V. Elsukov {
1283cbd4ec3SAndrey V. Elsukov 	int isr;
1293cbd4ec3SAndrey V. Elsukov 
130b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
131b8a6e03fSGleb Smirnoff 
1323cbd4ec3SAndrey V. Elsukov 	switch (proto) {
1333cbd4ec3SAndrey V. Elsukov #ifdef INET
1343cbd4ec3SAndrey V. Elsukov 	case IPPROTO_IPV4:
1353cbd4ec3SAndrey V. Elsukov 		isr = NETISR_IP;
1363cbd4ec3SAndrey V. Elsukov 		break;
1373cbd4ec3SAndrey V. Elsukov #endif
1383cbd4ec3SAndrey V. Elsukov #ifdef INET6
1393cbd4ec3SAndrey V. Elsukov 	case IPPROTO_IPV6:
1403cbd4ec3SAndrey V. Elsukov 		isr = NETISR_IPV6;
1413cbd4ec3SAndrey V. Elsukov 		break;
1423cbd4ec3SAndrey V. Elsukov #endif
1433cbd4ec3SAndrey V. Elsukov 	default:
1443cbd4ec3SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_nopf);
1456d8fdfa9SAndrey V. Elsukov 		m_freem(m);
1463cbd4ec3SAndrey V. Elsukov 		return (IPPROTO_DONE);
1473cbd4ec3SAndrey V. Elsukov 	}
1486d8fdfa9SAndrey V. Elsukov 	m_adj(m, off);
1496d8fdfa9SAndrey V. Elsukov 	IPCOMPSTAT_ADD(ipcomps_ibytes, m->m_pkthdr.len);
1503cbd4ec3SAndrey V. Elsukov 	IPCOMPSTAT_INC(ipcomps_input);
1516d8fdfa9SAndrey V. Elsukov 	netisr_dispatch(isr, m);
1523cbd4ec3SAndrey V. Elsukov 	return (IPPROTO_DONE);
1533cbd4ec3SAndrey V. Elsukov }
1543cbd4ec3SAndrey V. Elsukov 
15588768458SSam Leffler /*
15688768458SSam Leffler  * ipcomp_init() is called when an CPI is being set up.
15788768458SSam Leffler  */
15888768458SSam Leffler static int
15988768458SSam Leffler ipcomp_init(struct secasvar *sav, struct xformsw *xsp)
16088768458SSam Leffler {
161fcf59617SAndrey V. Elsukov 	const struct comp_algo *tcomp;
162c0341432SJohn Baldwin 	struct crypto_session_params csp;
16388768458SSam Leffler 
16488768458SSam Leffler 	/* NB: algorithm really comes in alg_enc and not alg_comp! */
165fcf59617SAndrey V. Elsukov 	tcomp = comp_algorithm_lookup(sav->alg_enc);
16688768458SSam Leffler 	if (tcomp == NULL) {
1679ffa9677SSam Leffler 		DPRINTF(("%s: unsupported compression algorithm %d\n", __func__,
16888768458SSam Leffler 			 sav->alg_comp));
16988768458SSam Leffler 		return EINVAL;
17088768458SSam Leffler 	}
17188768458SSam Leffler 	sav->alg_comp = sav->alg_enc;		/* set for doing histogram */
17288768458SSam Leffler 	sav->tdb_xform = xsp;
17388768458SSam Leffler 	sav->tdb_compalgxform = tcomp;
17488768458SSam Leffler 
17588768458SSam Leffler 	/* Initialize crypto session */
176c0341432SJohn Baldwin 	memset(&csp, 0, sizeof(csp));
177c0341432SJohn Baldwin 	csp.csp_mode = CSP_MODE_COMPRESS;
178c0341432SJohn Baldwin 	csp.csp_cipher_alg = sav->tdb_compalgxform->type;
17988768458SSam Leffler 
180c0341432SJohn Baldwin 	return crypto_newsession(&sav->tdb_cryptoid, &csp, V_crypto_support);
18188768458SSam Leffler }
18288768458SSam Leffler 
18388768458SSam Leffler /*
184dae61c9dSJohn Baldwin  * ipcomp_cleanup() used when IPCA is deleted
18588768458SSam Leffler  */
186dae61c9dSJohn Baldwin static void
187dae61c9dSJohn Baldwin ipcomp_cleanup(struct secasvar *sav)
18888768458SSam Leffler {
18988768458SSam Leffler 
1901b0909d5SConrad Meyer 	crypto_freesession(sav->tdb_cryptoid);
1911b0909d5SConrad Meyer 	sav->tdb_cryptoid = NULL;
19288768458SSam Leffler }
19388768458SSam Leffler 
19488768458SSam Leffler /*
19588768458SSam Leffler  * ipcomp_input() gets called to uncompress an input packet
19688768458SSam Leffler  */
19788768458SSam Leffler static int
19888768458SSam Leffler ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
19988768458SSam Leffler {
200fcf59617SAndrey V. Elsukov 	struct xform_data *xd;
20188768458SSam Leffler 	struct cryptop *crp;
202dc49da97SBjoern A. Zeeb 	struct ipcomp *ipcomp;
203c0341432SJohn Baldwin 	crypto_session_t cryptoid;
204dc49da97SBjoern A. Zeeb 	caddr_t addr;
2055f7c516fSAndrey V. Elsukov 	int error, hlen = IPCOMP_HLENGTH;
20688768458SSam Leffler 
2070361f165SKristof Provost 	SECASVAR_RLOCK_TRACKER;
2080361f165SKristof Provost 
209dc49da97SBjoern A. Zeeb 	/*
210dc49da97SBjoern A. Zeeb 	 * Check that the next header of the IPComp is not IPComp again, before
211dc49da97SBjoern A. Zeeb 	 * doing any real work.  Given it is not possible to do double
212dc49da97SBjoern A. Zeeb 	 * compression it means someone is playing tricks on us.
213dc49da97SBjoern A. Zeeb 	 */
2145f7c516fSAndrey V. Elsukov 	error = ENOBUFS;
215dc49da97SBjoern A. Zeeb 	if (m->m_len < skip + hlen && (m = m_pullup(m, skip + hlen)) == NULL) {
216a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_hdrops);		/*XXX*/
217dc49da97SBjoern A. Zeeb 		DPRINTF(("%s: m_pullup failed\n", __func__));
2185f7c516fSAndrey V. Elsukov 		key_freesav(&sav);
2195f7c516fSAndrey V. Elsukov 		return (error);
220dc49da97SBjoern A. Zeeb 	}
221dc49da97SBjoern A. Zeeb 	addr = (caddr_t) mtod(m, struct ip *) + skip;
222dc49da97SBjoern A. Zeeb 	ipcomp = (struct ipcomp *)addr;
223dc49da97SBjoern A. Zeeb 	if (ipcomp->comp_nxt == IPPROTO_IPCOMP) {
224a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_pdrops);	/* XXX have our own stats? */
225dc49da97SBjoern A. Zeeb 		DPRINTF(("%s: recursive compression detected\n", __func__));
2265f7c516fSAndrey V. Elsukov 		error = EINVAL;
2275f7c516fSAndrey V. Elsukov 		goto bad;
228dc49da97SBjoern A. Zeeb 	}
229dc49da97SBjoern A. Zeeb 
2300361f165SKristof Provost 	SECASVAR_RLOCK(sav);
231c0341432SJohn Baldwin 	cryptoid = sav->tdb_cryptoid;
2320361f165SKristof Provost 	SECASVAR_RUNLOCK(sav);
233c0341432SJohn Baldwin 
23488768458SSam Leffler 	/* Get crypto descriptors */
235c0341432SJohn Baldwin 	crp = crypto_getreq(cryptoid, M_NOWAIT);
23688768458SSam Leffler 	if (crp == NULL) {
2379ffa9677SSam Leffler 		DPRINTF(("%s: no crypto descriptors\n", __func__));
238a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_crypto);
2395f7c516fSAndrey V. Elsukov 		goto bad;
24088768458SSam Leffler 	}
24188768458SSam Leffler 	/* Get IPsec-specific opaque pointer */
24235d9e00dSJohn Baldwin 	xd = malloc(sizeof(*xd), M_IPCOMP, M_NOWAIT | M_ZERO);
243fcf59617SAndrey V. Elsukov 	if (xd == NULL) {
244fcf59617SAndrey V. Elsukov 		DPRINTF(("%s: cannot allocate xform_data\n", __func__));
245a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_crypto);
246fcf59617SAndrey V. Elsukov 		crypto_freereq(crp);
2475f7c516fSAndrey V. Elsukov 		goto bad;
24888768458SSam Leffler 	}
24988768458SSam Leffler 
25088768458SSam Leffler 	/* Decompression operation */
251c0341432SJohn Baldwin 	crp->crp_op = CRYPTO_OP_DECOMPRESS;
252c0341432SJohn Baldwin 	crp->crp_payload_start = skip + hlen;
253c0341432SJohn Baldwin 	crp->crp_payload_length = m->m_pkthdr.len - (skip + hlen);
254fcf59617SAndrey V. Elsukov 
25588768458SSam Leffler 	/* Crypto operation descriptor */
256c0341432SJohn Baldwin 	crp->crp_flags = CRYPTO_F_CBIFSYNC;
2579c0e3d3aSJohn Baldwin 	crypto_use_mbuf(crp, m);
25888768458SSam Leffler 	crp->crp_callback = ipcomp_input_cb;
259c0341432SJohn Baldwin 	crp->crp_opaque = xd;
26088768458SSam Leffler 
26188768458SSam Leffler 	/* These are passed as-is to the callback */
262fcf59617SAndrey V. Elsukov 	xd->sav = sav;
263fcf59617SAndrey V. Elsukov 	xd->protoff = protoff;
264fcf59617SAndrey V. Elsukov 	xd->skip = skip;
265fd40ecf3SJohn Baldwin 	xd->vnet = curvnet;
266c0341432SJohn Baldwin 	xd->cryptoid = cryptoid;
267fcf59617SAndrey V. Elsukov 
2680361f165SKristof Provost 	SECASVAR_RLOCK(sav);
2691b0909d5SConrad Meyer 	crp->crp_session = xd->cryptoid = sav->tdb_cryptoid;
2700361f165SKristof Provost 	SECASVAR_RUNLOCK(sav);
27188768458SSam Leffler 
27288768458SSam Leffler 	return crypto_dispatch(crp);
2735f7c516fSAndrey V. Elsukov bad:
2745f7c516fSAndrey V. Elsukov 	m_freem(m);
2755f7c516fSAndrey V. Elsukov 	key_freesav(&sav);
2765f7c516fSAndrey V. Elsukov 	return (error);
27788768458SSam Leffler }
27888768458SSam Leffler 
27988768458SSam Leffler /*
28088768458SSam Leffler  * IPComp input callback from the crypto driver.
28188768458SSam Leffler  */
28288768458SSam Leffler static int
28388768458SSam Leffler ipcomp_input_cb(struct cryptop *crp)
28488768458SSam Leffler {
2857f1f6591SAndrey V. Elsukov 	IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
286fcf59617SAndrey V. Elsukov 	struct xform_data *xd;
28788768458SSam Leffler 	struct mbuf *m;
28888768458SSam Leffler 	struct secasvar *sav;
28988768458SSam Leffler 	struct secasindex *saidx;
29088768458SSam Leffler 	caddr_t addr;
2912e08e39fSConrad Meyer 	crypto_session_t cryptoid;
292fcf59617SAndrey V. Elsukov 	int hlen = IPCOMP_HLENGTH, error, clen;
293fcf59617SAndrey V. Elsukov 	int skip, protoff;
294fcf59617SAndrey V. Elsukov 	uint8_t nproto;
29588768458SSam Leffler 
2969c0e3d3aSJohn Baldwin 	m = crp->crp_buf.cb_mbuf;
297c0341432SJohn Baldwin 	xd = crp->crp_opaque;
298fd40ecf3SJohn Baldwin 	CURVNET_SET(xd->vnet);
299fcf59617SAndrey V. Elsukov 	sav = xd->sav;
300fcf59617SAndrey V. Elsukov 	skip = xd->skip;
301fcf59617SAndrey V. Elsukov 	protoff = xd->protoff;
302fcf59617SAndrey V. Elsukov 	cryptoid = xd->cryptoid;
30388768458SSam Leffler 	saidx = &sav->sah->saidx;
3049ffa9677SSam Leffler 	IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
30588768458SSam Leffler 		saidx->dst.sa.sa_family == AF_INET6,
3069ffa9677SSam Leffler 		("unexpected protocol family %u", saidx->dst.sa.sa_family));
30788768458SSam Leffler 
30888768458SSam Leffler 	/* Check for crypto errors */
30988768458SSam Leffler 	if (crp->crp_etype) {
31088768458SSam Leffler 		if (crp->crp_etype == EAGAIN) {
311fcf59617SAndrey V. Elsukov 			/* Reset the session ID */
3121b0909d5SConrad Meyer 			if (ipsec_updateid(sav, &crp->crp_session, &cryptoid) != 0)
313fcf59617SAndrey V. Elsukov 				crypto_freesession(cryptoid);
3141b0909d5SConrad Meyer 			xd->cryptoid = crp->crp_session;
315fd40ecf3SJohn Baldwin 			CURVNET_RESTORE();
316fcf59617SAndrey V. Elsukov 			return (crypto_dispatch(crp));
31788768458SSam Leffler 		}
318a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_noxform);
3199ffa9677SSam Leffler 		DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
32088768458SSam Leffler 		error = crp->crp_etype;
32188768458SSam Leffler 		goto bad;
32288768458SSam Leffler 	}
32388768458SSam Leffler 	/* Shouldn't happen... */
32488768458SSam Leffler 	if (m == NULL) {
325a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_crypto);
3269ffa9677SSam Leffler 		DPRINTF(("%s: null mbuf returned from crypto\n", __func__));
32788768458SSam Leffler 		error = EINVAL;
32888768458SSam Leffler 		goto bad;
32988768458SSam Leffler 	}
330*b1c3a4d7SKristof Provost 	IPCOMPSTAT_INC2(ipcomps_hist, sav->alg_comp);
33188768458SSam Leffler 
33288768458SSam Leffler 	clen = crp->crp_olen;		/* Length of data after processing */
33388768458SSam Leffler 
33488768458SSam Leffler 	/* Release the crypto descriptors */
33535d9e00dSJohn Baldwin 	free(xd, M_IPCOMP), xd = NULL;
33688768458SSam Leffler 	crypto_freereq(crp), crp = NULL;
33788768458SSam Leffler 
33888768458SSam Leffler 	/* In case it's not done already, adjust the size of the mbuf chain */
33988768458SSam Leffler 	m->m_pkthdr.len = clen + hlen + skip;
34088768458SSam Leffler 
341155d72c4SPedro F. Giffuni 	if (m->m_len < skip + hlen && (m = m_pullup(m, skip + hlen)) == NULL) {
342a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_hdrops);		/*XXX*/
3439ffa9677SSam Leffler 		DPRINTF(("%s: m_pullup failed\n", __func__));
34488768458SSam Leffler 		error = EINVAL;				/*XXX*/
34588768458SSam Leffler 		goto bad;
34688768458SSam Leffler 	}
34788768458SSam Leffler 
34888768458SSam Leffler 	/* Keep the next protocol field */
34988768458SSam Leffler 	addr = (caddr_t) mtod(m, struct ip *) + skip;
35088768458SSam Leffler 	nproto = ((struct ipcomp *) addr)->comp_nxt;
35188768458SSam Leffler 
35288768458SSam Leffler 	/* Remove the IPCOMP header */
35388768458SSam Leffler 	error = m_striphdr(m, skip, hlen);
35488768458SSam Leffler 	if (error) {
355a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_hdrops);
3569ffa9677SSam Leffler 		DPRINTF(("%s: bad mbuf chain, IPCA %s/%08lx\n", __func__,
357962ac6c7SAndrey V. Elsukov 		    ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
35888768458SSam Leffler 		    (u_long) ntohl(sav->spi)));
35988768458SSam Leffler 		goto bad;
36088768458SSam Leffler 	}
36188768458SSam Leffler 
36288768458SSam Leffler 	/* Restore the Next Protocol field */
36388768458SSam Leffler 	m_copyback(m, protoff, sizeof (u_int8_t), (u_int8_t *) &nproto);
36488768458SSam Leffler 
365db178eb8SBjoern A. Zeeb 	switch (saidx->dst.sa.sa_family) {
366db178eb8SBjoern A. Zeeb #ifdef INET6
367db178eb8SBjoern A. Zeeb 	case AF_INET6:
368f0514a8bSAndrey V. Elsukov 		error = ipsec6_common_input_cb(m, sav, skip, protoff);
369db178eb8SBjoern A. Zeeb 		break;
370db178eb8SBjoern A. Zeeb #endif
371db178eb8SBjoern A. Zeeb #ifdef INET
372db178eb8SBjoern A. Zeeb 	case AF_INET:
373f0514a8bSAndrey V. Elsukov 		error = ipsec4_common_input_cb(m, sav, skip, protoff);
374db178eb8SBjoern A. Zeeb 		break;
375db178eb8SBjoern A. Zeeb #endif
376db178eb8SBjoern A. Zeeb 	default:
377db178eb8SBjoern A. Zeeb 		panic("%s: Unexpected address family: %d saidx=%p", __func__,
378db178eb8SBjoern A. Zeeb 		    saidx->dst.sa.sa_family, saidx);
379db178eb8SBjoern A. Zeeb 	}
380fd40ecf3SJohn Baldwin 	CURVNET_RESTORE();
38188768458SSam Leffler 	return error;
38288768458SSam Leffler bad:
383fd40ecf3SJohn Baldwin 	CURVNET_RESTORE();
384fcf59617SAndrey V. Elsukov 	if (sav != NULL)
385fcf59617SAndrey V. Elsukov 		key_freesav(&sav);
386fcf59617SAndrey V. Elsukov 	if (m != NULL)
38788768458SSam Leffler 		m_freem(m);
388fcf59617SAndrey V. Elsukov 	if (xd != NULL)
38935d9e00dSJohn Baldwin 		free(xd, M_IPCOMP);
390fcf59617SAndrey V. Elsukov 	if (crp != NULL)
39188768458SSam Leffler 		crypto_freereq(crp);
39288768458SSam Leffler 	return error;
39388768458SSam Leffler }
39488768458SSam Leffler 
39588768458SSam Leffler /*
396fcf59617SAndrey V. Elsukov  * IPComp output routine, called by ipsec[46]_perform_request()
39788768458SSam Leffler  */
39888768458SSam Leffler static int
399fcf59617SAndrey V. Elsukov ipcomp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
400fcf59617SAndrey V. Elsukov     u_int idx, int skip, int protoff)
40188768458SSam Leffler {
4027f1f6591SAndrey V. Elsukov 	IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
403fcf59617SAndrey V. Elsukov 	const struct comp_algo *ipcompx;
40488768458SSam Leffler 	struct cryptop *crp;
405fcf59617SAndrey V. Elsukov 	struct xform_data *xd;
406c0341432SJohn Baldwin 	crypto_session_t cryptoid;
407fcf59617SAndrey V. Elsukov 	int error, ralen, maxpacketsize;
40888768458SSam Leffler 
4090361f165SKristof Provost 	SECASVAR_RLOCK_TRACKER;
4100361f165SKristof Provost 
4119ffa9677SSam Leffler 	IPSEC_ASSERT(sav != NULL, ("null SA"));
41288768458SSam Leffler 	ipcompx = sav->tdb_compalgxform;
4139ffa9677SSam Leffler 	IPSEC_ASSERT(ipcompx != NULL, ("null compression xform"));
41488768458SSam Leffler 
4154ff98521SBjoern A. Zeeb 	/*
4164ff98521SBjoern A. Zeeb 	 * Do not touch the packet in case our payload to compress
4174ff98521SBjoern A. Zeeb 	 * is lower than the minimal threshold of the compression
4184ff98521SBjoern A. Zeeb 	 * alogrithm.  We will just send out the data uncompressed.
4194ff98521SBjoern A. Zeeb 	 * See RFC 3173, 2.2. Non-Expansion Policy.
4204ff98521SBjoern A. Zeeb 	 */
4214ff98521SBjoern A. Zeeb 	if (m->m_pkthdr.len <= ipcompx->minlen) {
422a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_threshold);
423fcf59617SAndrey V. Elsukov 		return ipsec_process_done(m, sp, sav, idx);
4244ff98521SBjoern A. Zeeb 	}
4254ff98521SBjoern A. Zeeb 
42688768458SSam Leffler 	ralen = m->m_pkthdr.len - skip;	/* Raw payload length before comp. */
427a04d64d8SAndrey V. Elsukov 	IPCOMPSTAT_INC(ipcomps_output);
42888768458SSam Leffler 
42988768458SSam Leffler 	/* Check for maximum packet size violations. */
43088768458SSam Leffler 	switch (sav->sah->saidx.dst.sa.sa_family) {
43188768458SSam Leffler #ifdef INET
43288768458SSam Leffler 	case AF_INET:
43388768458SSam Leffler 		maxpacketsize = IP_MAXPACKET;
43488768458SSam Leffler 		break;
43588768458SSam Leffler #endif /* INET */
43688768458SSam Leffler #ifdef INET6
43788768458SSam Leffler 	case AF_INET6:
43888768458SSam Leffler 		maxpacketsize = IPV6_MAXPACKET;
43988768458SSam Leffler 		break;
44088768458SSam Leffler #endif /* INET6 */
44188768458SSam Leffler 	default:
442a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_nopf);
4439ffa9677SSam Leffler 		DPRINTF(("%s: unknown/unsupported protocol family %d, "
4449ffa9677SSam Leffler 		    "IPCA %s/%08lx\n", __func__,
44588768458SSam Leffler 		    sav->sah->saidx.dst.sa.sa_family,
446962ac6c7SAndrey V. Elsukov 		    ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
44788768458SSam Leffler 		    (u_long) ntohl(sav->spi)));
44888768458SSam Leffler 		error = EPFNOSUPPORT;
44988768458SSam Leffler 		goto bad;
45088768458SSam Leffler 	}
451afa47e51SBjoern A. Zeeb 	if (ralen + skip + IPCOMP_HLENGTH > maxpacketsize) {
452a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_toobig);
4539ffa9677SSam Leffler 		DPRINTF(("%s: packet in IPCA %s/%08lx got too big "
4549ffa9677SSam Leffler 		    "(len %u, max len %u)\n", __func__,
455962ac6c7SAndrey V. Elsukov 		    ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
45688768458SSam Leffler 		    (u_long) ntohl(sav->spi),
457afa47e51SBjoern A. Zeeb 		    ralen + skip + IPCOMP_HLENGTH, maxpacketsize));
45888768458SSam Leffler 		error = EMSGSIZE;
45988768458SSam Leffler 		goto bad;
46088768458SSam Leffler 	}
46188768458SSam Leffler 
46288768458SSam Leffler 	/* Update the counters */
463a04d64d8SAndrey V. Elsukov 	IPCOMPSTAT_ADD(ipcomps_obytes, m->m_pkthdr.len - skip);
46488768458SSam Leffler 
46547e2996eSSam Leffler 	m = m_unshare(m, M_NOWAIT);
46688768458SSam Leffler 	if (m == NULL) {
467a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_hdrops);
4689ffa9677SSam Leffler 		DPRINTF(("%s: cannot clone mbuf chain, IPCA %s/%08lx\n",
469962ac6c7SAndrey V. Elsukov 		    __func__, ipsec_address(&sav->sah->saidx.dst, buf,
470962ac6c7SAndrey V. Elsukov 		    sizeof(buf)), (u_long) ntohl(sav->spi)));
47188768458SSam Leffler 		error = ENOBUFS;
47288768458SSam Leffler 		goto bad;
47388768458SSam Leffler 	}
47488768458SSam Leffler 
475afa47e51SBjoern A. Zeeb 	/* Ok now, we can pass to the crypto processing. */
4760361f165SKristof Provost 	SECASVAR_RLOCK(sav);
477c0341432SJohn Baldwin 	cryptoid = sav->tdb_cryptoid;
4780361f165SKristof Provost 	SECASVAR_RUNLOCK(sav);
479afa47e51SBjoern A. Zeeb 
480afa47e51SBjoern A. Zeeb 	/* Get crypto descriptors */
481c0341432SJohn Baldwin 	crp = crypto_getreq(cryptoid, M_NOWAIT);
482afa47e51SBjoern A. Zeeb 	if (crp == NULL) {
483a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_crypto);
484afa47e51SBjoern A. Zeeb 		DPRINTF(("%s: failed to acquire crypto descriptor\n",__func__));
485afa47e51SBjoern A. Zeeb 		error = ENOBUFS;
486afa47e51SBjoern A. Zeeb 		goto bad;
487afa47e51SBjoern A. Zeeb 	}
488afa47e51SBjoern A. Zeeb 
489afa47e51SBjoern A. Zeeb 	/* Compression descriptor */
490c0341432SJohn Baldwin 	crp->crp_op = CRYPTO_OP_COMPRESS;
491c0341432SJohn Baldwin 	crp->crp_payload_start = skip;
492c0341432SJohn Baldwin 	crp->crp_payload_length = ralen;
493afa47e51SBjoern A. Zeeb 
494afa47e51SBjoern A. Zeeb 	/* IPsec-specific opaque crypto info */
49535d9e00dSJohn Baldwin 	xd =  malloc(sizeof(struct xform_data), M_IPCOMP, M_NOWAIT | M_ZERO);
496fcf59617SAndrey V. Elsukov 	if (xd == NULL) {
497a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_crypto);
498fcf59617SAndrey V. Elsukov 		DPRINTF(("%s: failed to allocate xform_data\n", __func__));
499afa47e51SBjoern A. Zeeb 		crypto_freereq(crp);
500afa47e51SBjoern A. Zeeb 		error = ENOBUFS;
501afa47e51SBjoern A. Zeeb 		goto bad;
502afa47e51SBjoern A. Zeeb 	}
503afa47e51SBjoern A. Zeeb 
504fcf59617SAndrey V. Elsukov 	xd->sp = sp;
505fcf59617SAndrey V. Elsukov 	xd->sav = sav;
506fcf59617SAndrey V. Elsukov 	xd->idx = idx;
507fcf59617SAndrey V. Elsukov 	xd->skip = skip;
508fcf59617SAndrey V. Elsukov 	xd->protoff = protoff;
509fd40ecf3SJohn Baldwin 	xd->vnet = curvnet;
510c0341432SJohn Baldwin 	xd->cryptoid = cryptoid;
511afa47e51SBjoern A. Zeeb 
512afa47e51SBjoern A. Zeeb 	/* Crypto operation descriptor */
513c0341432SJohn Baldwin 	crp->crp_flags = CRYPTO_F_CBIFSYNC;
5149c0e3d3aSJohn Baldwin 	crypto_use_mbuf(crp, m);
515afa47e51SBjoern A. Zeeb 	crp->crp_callback = ipcomp_output_cb;
516c0341432SJohn Baldwin 	crp->crp_opaque = xd;
517afa47e51SBjoern A. Zeeb 
518afa47e51SBjoern A. Zeeb 	return crypto_dispatch(crp);
519afa47e51SBjoern A. Zeeb bad:
520afa47e51SBjoern A. Zeeb 	if (m)
521afa47e51SBjoern A. Zeeb 		m_freem(m);
5223aee7099SAndrey V. Elsukov 	key_freesav(&sav);
5233aee7099SAndrey V. Elsukov 	key_freesp(&sp);
524afa47e51SBjoern A. Zeeb 	return (error);
525afa47e51SBjoern A. Zeeb }
526afa47e51SBjoern A. Zeeb 
527afa47e51SBjoern A. Zeeb /*
528afa47e51SBjoern A. Zeeb  * IPComp output callback from the crypto driver.
529afa47e51SBjoern A. Zeeb  */
530afa47e51SBjoern A. Zeeb static int
531afa47e51SBjoern A. Zeeb ipcomp_output_cb(struct cryptop *crp)
532afa47e51SBjoern A. Zeeb {
5337f1f6591SAndrey V. Elsukov 	IPSEC_DEBUG_DECLARE(char buf[IPSEC_ADDRSTRLEN]);
534fcf59617SAndrey V. Elsukov 	struct xform_data *xd;
535fcf59617SAndrey V. Elsukov 	struct secpolicy *sp;
536afa47e51SBjoern A. Zeeb 	struct secasvar *sav;
537afa47e51SBjoern A. Zeeb 	struct mbuf *m;
5382e08e39fSConrad Meyer 	crypto_session_t cryptoid;
539fcf59617SAndrey V. Elsukov 	u_int idx;
540fcf59617SAndrey V. Elsukov 	int error, skip, protoff;
541afa47e51SBjoern A. Zeeb 
5429c0e3d3aSJohn Baldwin 	m = crp->crp_buf.cb_mbuf;
543c0341432SJohn Baldwin 	xd = crp->crp_opaque;
544fd40ecf3SJohn Baldwin 	CURVNET_SET(xd->vnet);
545fcf59617SAndrey V. Elsukov 	idx = xd->idx;
546fcf59617SAndrey V. Elsukov 	sp = xd->sp;
547fcf59617SAndrey V. Elsukov 	sav = xd->sav;
548fcf59617SAndrey V. Elsukov 	skip = xd->skip;
549fcf59617SAndrey V. Elsukov 	protoff = xd->protoff;
550fcf59617SAndrey V. Elsukov 	cryptoid = xd->cryptoid;
551afa47e51SBjoern A. Zeeb 
552afa47e51SBjoern A. Zeeb 	/* Check for crypto errors */
553afa47e51SBjoern A. Zeeb 	if (crp->crp_etype) {
554afa47e51SBjoern A. Zeeb 		if (crp->crp_etype == EAGAIN) {
555fcf59617SAndrey V. Elsukov 			/* Reset the session ID */
5561b0909d5SConrad Meyer 			if (ipsec_updateid(sav, &crp->crp_session, &cryptoid) != 0)
557fcf59617SAndrey V. Elsukov 				crypto_freesession(cryptoid);
5581b0909d5SConrad Meyer 			xd->cryptoid = crp->crp_session;
559fd40ecf3SJohn Baldwin 			CURVNET_RESTORE();
560fcf59617SAndrey V. Elsukov 			return (crypto_dispatch(crp));
561afa47e51SBjoern A. Zeeb 		}
562a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_noxform);
563afa47e51SBjoern A. Zeeb 		DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
564afa47e51SBjoern A. Zeeb 		error = crp->crp_etype;
565afa47e51SBjoern A. Zeeb 		goto bad;
566afa47e51SBjoern A. Zeeb 	}
567afa47e51SBjoern A. Zeeb 	/* Shouldn't happen... */
568afa47e51SBjoern A. Zeeb 	if (m == NULL) {
569a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_crypto);
570afa47e51SBjoern A. Zeeb 		DPRINTF(("%s: bogus return buffer from crypto\n", __func__));
571afa47e51SBjoern A. Zeeb 		error = EINVAL;
572afa47e51SBjoern A. Zeeb 		goto bad;
573afa47e51SBjoern A. Zeeb 	}
574*b1c3a4d7SKristof Provost 	IPCOMPSTAT_INC2(ipcomps_hist, sav->alg_comp);
575afa47e51SBjoern A. Zeeb 
576c0341432SJohn Baldwin 	if (crp->crp_payload_length > crp->crp_olen) {
577afa47e51SBjoern A. Zeeb 		struct mbuf *mo;
578afa47e51SBjoern A. Zeeb 		struct ipcomp *ipcomp;
579afa47e51SBjoern A. Zeeb 		int roff;
580afa47e51SBjoern A. Zeeb 		uint8_t prot;
581afa47e51SBjoern A. Zeeb 
582afa47e51SBjoern A. Zeeb 		/* Compression helped, inject IPCOMP header. */
583afa47e51SBjoern A. Zeeb 		mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff);
58488768458SSam Leffler 		if (mo == NULL) {
585a04d64d8SAndrey V. Elsukov 			IPCOMPSTAT_INC(ipcomps_wrap);
586fcf59617SAndrey V. Elsukov 			DPRINTF(("%s: IPCOMP header inject failed "
587fcf59617SAndrey V. Elsukov 			    "for IPCA %s/%08lx\n",
588962ac6c7SAndrey V. Elsukov 			    __func__, ipsec_address(&sav->sah->saidx.dst, buf,
589962ac6c7SAndrey V. Elsukov 			    sizeof(buf)), (u_long) ntohl(sav->spi)));
59088768458SSam Leffler 			error = ENOBUFS;
59188768458SSam Leffler 			goto bad;
59288768458SSam Leffler 		}
59388768458SSam Leffler 		ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff);
59488768458SSam Leffler 
59588768458SSam Leffler 		/* Initialize the IPCOMP header */
59688768458SSam Leffler 		/* XXX alignment always correct? */
59788768458SSam Leffler 		switch (sav->sah->saidx.dst.sa.sa_family) {
59888768458SSam Leffler #ifdef INET
59988768458SSam Leffler 		case AF_INET:
60088768458SSam Leffler 			ipcomp->comp_nxt = mtod(m, struct ip *)->ip_p;
60188768458SSam Leffler 			break;
60288768458SSam Leffler #endif /* INET */
60388768458SSam Leffler #ifdef INET6
60488768458SSam Leffler 		case AF_INET6:
60588768458SSam Leffler 			ipcomp->comp_nxt = mtod(m, struct ip6_hdr *)->ip6_nxt;
60688768458SSam Leffler 			break;
60788768458SSam Leffler #endif
60888768458SSam Leffler 		}
60988768458SSam Leffler 		ipcomp->comp_flags = 0;
61088768458SSam Leffler 		ipcomp->comp_cpi = htons((u_int16_t) ntohl(sav->spi));
61188768458SSam Leffler 
61288768458SSam Leffler 		/* Fix Next Protocol in IPv4/IPv6 header */
61388768458SSam Leffler 		prot = IPPROTO_IPCOMP;
614fcf59617SAndrey V. Elsukov 		m_copyback(m, protoff, sizeof(u_int8_t),
615afa47e51SBjoern A. Zeeb 		    (u_char *)&prot);
61688768458SSam Leffler 
61788768458SSam Leffler 		/* Adjust the length in the IP header */
61888768458SSam Leffler 		switch (sav->sah->saidx.dst.sa.sa_family) {
61988768458SSam Leffler #ifdef INET
62088768458SSam Leffler 		case AF_INET:
62188768458SSam Leffler 			mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
62288768458SSam Leffler 			break;
62388768458SSam Leffler #endif /* INET */
62488768458SSam Leffler #ifdef INET6
62588768458SSam Leffler 		case AF_INET6:
62688768458SSam Leffler 			mtod(m, struct ip6_hdr *)->ip6_plen =
62788768458SSam Leffler 				htons(m->m_pkthdr.len) - sizeof(struct ip6_hdr);
62888768458SSam Leffler 			break;
62988768458SSam Leffler #endif /* INET6 */
63088768458SSam Leffler 		default:
631a04d64d8SAndrey V. Elsukov 			IPCOMPSTAT_INC(ipcomps_nopf);
6329ffa9677SSam Leffler 			DPRINTF(("%s: unknown/unsupported protocol "
6339ffa9677SSam Leffler 			    "family %d, IPCA %s/%08lx\n", __func__,
63488768458SSam Leffler 			    sav->sah->saidx.dst.sa.sa_family,
635962ac6c7SAndrey V. Elsukov 			    ipsec_address(&sav->sah->saidx.dst, buf,
636962ac6c7SAndrey V. Elsukov 				sizeof(buf)), (u_long) ntohl(sav->spi)));
63788768458SSam Leffler 			error = EPFNOSUPPORT;
63888768458SSam Leffler 			goto bad;
63988768458SSam Leffler 		}
64088768458SSam Leffler 	} else {
64190b4c081SBjoern A. Zeeb 		/* Compression was useless, we have lost time. */
642a04d64d8SAndrey V. Elsukov 		IPCOMPSTAT_INC(ipcomps_uncompr);
643c0341432SJohn Baldwin 		DPRINTF(("%s: compressions was useless %d <= %d\n",
644c0341432SJohn Baldwin 		    __func__, crp->crp_payload_length, crp->crp_olen));
645afa47e51SBjoern A. Zeeb 		/* XXX remember state to not compress the next couple
646afa47e51SBjoern A. Zeeb 		 *     of packets, RFC 3173, 2.2. Non-Expansion Policy */
64788768458SSam Leffler 	}
64888768458SSam Leffler 
64988768458SSam Leffler 	/* Release the crypto descriptor */
65035d9e00dSJohn Baldwin 	free(xd, M_IPCOMP);
65188768458SSam Leffler 	crypto_freereq(crp);
65288768458SSam Leffler 
65388768458SSam Leffler 	/* NB: m is reclaimed by ipsec_process_done. */
654fcf59617SAndrey V. Elsukov 	error = ipsec_process_done(m, sp, sav, idx);
655fd40ecf3SJohn Baldwin 	CURVNET_RESTORE();
6563d80e82dSAndrey V. Elsukov 	return (error);
65788768458SSam Leffler bad:
65888768458SSam Leffler 	if (m)
65988768458SSam Leffler 		m_freem(m);
660fd40ecf3SJohn Baldwin 	CURVNET_RESTORE();
66135d9e00dSJohn Baldwin 	free(xd, M_IPCOMP);
66288768458SSam Leffler 	crypto_freereq(crp);
663fcf59617SAndrey V. Elsukov 	key_freesav(&sav);
664fcf59617SAndrey V. Elsukov 	key_freesp(&sp);
6653d80e82dSAndrey V. Elsukov 	return (error);
66688768458SSam Leffler }
66788768458SSam Leffler 
6683cbd4ec3SAndrey V. Elsukov #ifdef INET
6693cbd4ec3SAndrey V. Elsukov static int
6703cbd4ec3SAndrey V. Elsukov ipcomp4_nonexp_encapcheck(const struct mbuf *m, int off, int proto,
6713cbd4ec3SAndrey V. Elsukov     void *arg __unused)
6723cbd4ec3SAndrey V. Elsukov {
6733cbd4ec3SAndrey V. Elsukov 	union sockaddr_union src, dst;
6743cbd4ec3SAndrey V. Elsukov 	const struct ip *ip;
6753cbd4ec3SAndrey V. Elsukov 
6763cbd4ec3SAndrey V. Elsukov 	if (V_ipcomp_enable == 0)
6773cbd4ec3SAndrey V. Elsukov 		return (0);
6780d056664SAndrey V. Elsukov 	if (proto != IPPROTO_IPV4 && proto != IPPROTO_IPV6)
6790d056664SAndrey V. Elsukov 		return (0);
6803cbd4ec3SAndrey V. Elsukov 	bzero(&src, sizeof(src));
6813cbd4ec3SAndrey V. Elsukov 	bzero(&dst, sizeof(dst));
6823cbd4ec3SAndrey V. Elsukov 	src.sa.sa_family = dst.sa.sa_family = AF_INET;
6833cbd4ec3SAndrey V. Elsukov 	src.sin.sin_len = dst.sin.sin_len = sizeof(struct sockaddr_in);
6843cbd4ec3SAndrey V. Elsukov 	ip = mtod(m, const struct ip *);
6853cbd4ec3SAndrey V. Elsukov 	src.sin.sin_addr = ip->ip_src;
6863cbd4ec3SAndrey V. Elsukov 	dst.sin.sin_addr = ip->ip_dst;
6873cbd4ec3SAndrey V. Elsukov 	return (ipcomp_encapcheck(&src, &dst));
6883cbd4ec3SAndrey V. Elsukov }
6896d8fdfa9SAndrey V. Elsukov 
6906d8fdfa9SAndrey V. Elsukov static const struct encaptab *ipe4_cookie = NULL;
6916d8fdfa9SAndrey V. Elsukov static const struct encap_config ipv4_encap_cfg = {
6926d8fdfa9SAndrey V. Elsukov 	.proto = -1,
6936d8fdfa9SAndrey V. Elsukov 	.min_length = sizeof(struct ip),
6946d8fdfa9SAndrey V. Elsukov 	.exact_match = sizeof(in_addr_t) << 4,
6956d8fdfa9SAndrey V. Elsukov 	.check = ipcomp4_nonexp_encapcheck,
6966d8fdfa9SAndrey V. Elsukov 	.input = ipcomp_nonexp_input
6976d8fdfa9SAndrey V. Elsukov };
6983cbd4ec3SAndrey V. Elsukov #endif
6993cbd4ec3SAndrey V. Elsukov #ifdef INET6
7003cbd4ec3SAndrey V. Elsukov static int
7013cbd4ec3SAndrey V. Elsukov ipcomp6_nonexp_encapcheck(const struct mbuf *m, int off, int proto,
7023cbd4ec3SAndrey V. Elsukov     void *arg __unused)
7033cbd4ec3SAndrey V. Elsukov {
7043cbd4ec3SAndrey V. Elsukov 	union sockaddr_union src, dst;
7053cbd4ec3SAndrey V. Elsukov 	const struct ip6_hdr *ip6;
7063cbd4ec3SAndrey V. Elsukov 
7073cbd4ec3SAndrey V. Elsukov 	if (V_ipcomp_enable == 0)
7083cbd4ec3SAndrey V. Elsukov 		return (0);
7090d056664SAndrey V. Elsukov 	if (proto != IPPROTO_IPV4 && proto != IPPROTO_IPV6)
7100d056664SAndrey V. Elsukov 		return (0);
7113cbd4ec3SAndrey V. Elsukov 	bzero(&src, sizeof(src));
7123cbd4ec3SAndrey V. Elsukov 	bzero(&dst, sizeof(dst));
7133cbd4ec3SAndrey V. Elsukov 	src.sa.sa_family = dst.sa.sa_family = AF_INET;
7143cbd4ec3SAndrey V. Elsukov 	src.sin6.sin6_len = dst.sin6.sin6_len = sizeof(struct sockaddr_in6);
7153cbd4ec3SAndrey V. Elsukov 	ip6 = mtod(m, const struct ip6_hdr *);
7163cbd4ec3SAndrey V. Elsukov 	src.sin6.sin6_addr = ip6->ip6_src;
7173cbd4ec3SAndrey V. Elsukov 	dst.sin6.sin6_addr = ip6->ip6_dst;
7183cbd4ec3SAndrey V. Elsukov 	if (IN6_IS_SCOPE_LINKLOCAL(&src.sin6.sin6_addr)) {
7193cbd4ec3SAndrey V. Elsukov 		/* XXX: sa6_recoverscope() */
7203cbd4ec3SAndrey V. Elsukov 		src.sin6.sin6_scope_id =
7213cbd4ec3SAndrey V. Elsukov 		    ntohs(src.sin6.sin6_addr.s6_addr16[1]);
7223cbd4ec3SAndrey V. Elsukov 		src.sin6.sin6_addr.s6_addr16[1] = 0;
7233cbd4ec3SAndrey V. Elsukov 	}
7243cbd4ec3SAndrey V. Elsukov 	if (IN6_IS_SCOPE_LINKLOCAL(&dst.sin6.sin6_addr)) {
7253cbd4ec3SAndrey V. Elsukov 		/* XXX: sa6_recoverscope() */
7263cbd4ec3SAndrey V. Elsukov 		dst.sin6.sin6_scope_id =
7273cbd4ec3SAndrey V. Elsukov 		    ntohs(dst.sin6.sin6_addr.s6_addr16[1]);
7283cbd4ec3SAndrey V. Elsukov 		dst.sin6.sin6_addr.s6_addr16[1] = 0;
7293cbd4ec3SAndrey V. Elsukov 	}
7303cbd4ec3SAndrey V. Elsukov 	return (ipcomp_encapcheck(&src, &dst));
7313cbd4ec3SAndrey V. Elsukov }
7326d8fdfa9SAndrey V. Elsukov 
7336d8fdfa9SAndrey V. Elsukov static const struct encaptab *ipe6_cookie = NULL;
7346d8fdfa9SAndrey V. Elsukov static const struct encap_config ipv6_encap_cfg = {
7356d8fdfa9SAndrey V. Elsukov 	.proto = -1,
7366d8fdfa9SAndrey V. Elsukov 	.min_length = sizeof(struct ip6_hdr),
7376d8fdfa9SAndrey V. Elsukov 	.exact_match = sizeof(struct in6_addr) << 4,
7386d8fdfa9SAndrey V. Elsukov 	.check = ipcomp6_nonexp_encapcheck,
7396d8fdfa9SAndrey V. Elsukov 	.input = ipcomp_nonexp_input
7406d8fdfa9SAndrey V. Elsukov };
7413cbd4ec3SAndrey V. Elsukov #endif
7423cbd4ec3SAndrey V. Elsukov 
743fcf59617SAndrey V. Elsukov static struct xformsw ipcomp_xformsw = {
744fcf59617SAndrey V. Elsukov 	.xf_type =	XF_IPCOMP,
745fcf59617SAndrey V. Elsukov 	.xf_name =	"IPcomp",
746fcf59617SAndrey V. Elsukov 	.xf_init =	ipcomp_init,
747dae61c9dSJohn Baldwin 	.xf_cleanup =	ipcomp_cleanup,
748fcf59617SAndrey V. Elsukov 	.xf_input =	ipcomp_input,
749fcf59617SAndrey V. Elsukov 	.xf_output =	ipcomp_output,
750fcf59617SAndrey V. Elsukov };
751fcf59617SAndrey V. Elsukov 
75288768458SSam Leffler static void
75388768458SSam Leffler ipcomp_attach(void)
75488768458SSam Leffler {
75544e33a07SMarko Zec 
7563cbd4ec3SAndrey V. Elsukov #ifdef INET
7576d8fdfa9SAndrey V. Elsukov 	ipe4_cookie = ip_encap_attach(&ipv4_encap_cfg, NULL, M_WAITOK);
7583cbd4ec3SAndrey V. Elsukov #endif
7593cbd4ec3SAndrey V. Elsukov #ifdef INET6
7606d8fdfa9SAndrey V. Elsukov 	ipe6_cookie = ip6_encap_attach(&ipv6_encap_cfg, NULL, M_WAITOK);
7613cbd4ec3SAndrey V. Elsukov #endif
762fcf59617SAndrey V. Elsukov 	xform_attach(&ipcomp_xformsw);
763fcf59617SAndrey V. Elsukov }
764fcf59617SAndrey V. Elsukov 
765fcf59617SAndrey V. Elsukov static void
766fcf59617SAndrey V. Elsukov ipcomp_detach(void)
767fcf59617SAndrey V. Elsukov {
768fcf59617SAndrey V. Elsukov 
769fcf59617SAndrey V. Elsukov #ifdef INET
7706d8fdfa9SAndrey V. Elsukov 	ip_encap_detach(ipe4_cookie);
771fcf59617SAndrey V. Elsukov #endif
772fcf59617SAndrey V. Elsukov #ifdef INET6
7736d8fdfa9SAndrey V. Elsukov 	ip6_encap_detach(ipe6_cookie);
774fcf59617SAndrey V. Elsukov #endif
775fcf59617SAndrey V. Elsukov 	xform_detach(&ipcomp_xformsw);
7761ed81b73SMarko Zec }
7771ed81b73SMarko Zec 
7783cbd4ec3SAndrey V. Elsukov SYSINIT(ipcomp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
7793cbd4ec3SAndrey V. Elsukov     ipcomp_attach, NULL);
780fcf59617SAndrey V. Elsukov SYSUNINIT(ipcomp_xform_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
781fcf59617SAndrey V. Elsukov     ipcomp_detach, NULL);
782