xref: /openbsd-src/sys/netinet/ip_ipcomp.c (revision fb8de0f1e1a0e38878bacd50911133df5b0c83e9)
1*fb8de0f1Sclaudio /* $OpenBSD: ip_ipcomp.c,v 1.92 2022/05/03 09:18:11 claudio Exp $ */
21aaeb53aSjjbg 
31aaeb53aSjjbg /*
41aaeb53aSjjbg  * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
51aaeb53aSjjbg  *
61aaeb53aSjjbg  * Redistribution and use in source and binary forms, with or without
71aaeb53aSjjbg  * modification, are permitted provided that the following conditions
81aaeb53aSjjbg  * are met:
91aaeb53aSjjbg  *
101aaeb53aSjjbg  * 1. Redistributions of source code must retain the above copyright
111aaeb53aSjjbg  *   notice, this list of conditions and the following disclaimer.
121aaeb53aSjjbg  * 2. Redistributions in binary form must reproduce the above copyright
131aaeb53aSjjbg  *   notice, this list of conditions and the following disclaimer in the
141aaeb53aSjjbg  *   documentation and/or other materials provided with the distribution.
151aaeb53aSjjbg  * 3. The name of the author may not be used to endorse or promote products
161aaeb53aSjjbg  *   derived from this software without specific prior written permission.
171aaeb53aSjjbg  *
181aaeb53aSjjbg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
191aaeb53aSjjbg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
201aaeb53aSjjbg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
211aaeb53aSjjbg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
221aaeb53aSjjbg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
231aaeb53aSjjbg  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
241aaeb53aSjjbg  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
251aaeb53aSjjbg  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
261aaeb53aSjjbg  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
271aaeb53aSjjbg  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
281aaeb53aSjjbg  */
291aaeb53aSjjbg 
301aaeb53aSjjbg /* IP payload compression protocol (IPComp), see RFC 2393 */
311aaeb53aSjjbg 
321aaeb53aSjjbg #include <sys/param.h>
331aaeb53aSjjbg #include <sys/systm.h>
341aaeb53aSjjbg #include <sys/mbuf.h>
351aaeb53aSjjbg #include <sys/socket.h>
361aaeb53aSjjbg 
371aaeb53aSjjbg #include <net/if.h>
380deb6685Smpi #include <net/if_var.h>
391aaeb53aSjjbg #include <net/bpf.h>
401aaeb53aSjjbg 
411aaeb53aSjjbg #include <netinet/in.h>
421aaeb53aSjjbg #include <netinet/ip.h>
4344570b44Smpi #include <netinet/ip_var.h>
441aaeb53aSjjbg 
451aaeb53aSjjbg #ifdef INET6
461aaeb53aSjjbg #include <netinet/ip6.h>
471aaeb53aSjjbg #endif				/* INET6 */
481aaeb53aSjjbg 
491aaeb53aSjjbg #include <netinet/ip_ipsp.h>
501aaeb53aSjjbg #include <netinet/ip_ipcomp.h>
511aaeb53aSjjbg #include <net/pfkeyv2.h>
521aaeb53aSjjbg #include <net/if_enc.h>
531aaeb53aSjjbg 
541aaeb53aSjjbg #include <crypto/cryptodev.h>
551aaeb53aSjjbg #include <crypto/xform.h>
561aaeb53aSjjbg 
571aaeb53aSjjbg #include "bpfilter.h"
581aaeb53aSjjbg 
591aaeb53aSjjbg #ifdef ENCDEBUG
60698a75ddSbluhm #define DPRINTF(fmt, args...)						\
61698a75ddSbluhm 	do {								\
62698a75ddSbluhm 		if (encdebug)						\
63698a75ddSbluhm 			printf("%s: " fmt "\n", __func__, ## args);	\
64698a75ddSbluhm 	} while (0)
651aaeb53aSjjbg #else
66698a75ddSbluhm #define DPRINTF(fmt, args...)						\
67698a75ddSbluhm 	do { } while (0)
681aaeb53aSjjbg #endif
691aaeb53aSjjbg 
701aaeb53aSjjbg /*
711aaeb53aSjjbg  * ipcomp_attach() is called from the transformation code
721aaeb53aSjjbg  */
731aaeb53aSjjbg int
ipcomp_attach(void)741aaeb53aSjjbg ipcomp_attach(void)
751aaeb53aSjjbg {
761aaeb53aSjjbg 	return 0;
771aaeb53aSjjbg }
781aaeb53aSjjbg 
791aaeb53aSjjbg /*
801aaeb53aSjjbg  * ipcomp_init() is called when an CPI is being set up.
811aaeb53aSjjbg  */
821aaeb53aSjjbg int
ipcomp_init(struct tdb * tdbp,const struct xformsw * xsp,struct ipsecinit * ii)835e3836acSbluhm ipcomp_init(struct tdb *tdbp, const struct xformsw *xsp, struct ipsecinit *ii)
841aaeb53aSjjbg {
854d13edafSbluhm 	const struct comp_algo *tcomp = NULL;
861aaeb53aSjjbg 	struct cryptoini cric;
87b7be02dcSbluhm 	int error;
881aaeb53aSjjbg 
891aaeb53aSjjbg 	switch (ii->ii_compalg) {
901aaeb53aSjjbg 	case SADB_X_CALG_DEFLATE:
911aaeb53aSjjbg 		tcomp = &comp_algo_deflate;
921aaeb53aSjjbg 		break;
931aaeb53aSjjbg 	default:
94698a75ddSbluhm 		DPRINTF("unsupported compression algorithm %d specified",
95698a75ddSbluhm 		    ii->ii_compalg);
961aaeb53aSjjbg 		return EINVAL;
971aaeb53aSjjbg 	}
981aaeb53aSjjbg 
991aaeb53aSjjbg 	tdbp->tdb_compalgxform = tcomp;
1001aaeb53aSjjbg 
101698a75ddSbluhm 	DPRINTF("initialized TDB with ipcomp algorithm %s", tcomp->name);
1021aaeb53aSjjbg 
1031aaeb53aSjjbg 	tdbp->tdb_xform = xsp;
1041aaeb53aSjjbg 
1051aaeb53aSjjbg 	/* Initialize crypto session */
106f8575965Stedu 	memset(&cric, 0, sizeof(cric));
1071aaeb53aSjjbg 	cric.cri_alg = tdbp->tdb_compalgxform->type;
1081aaeb53aSjjbg 
109b7be02dcSbluhm 	KERNEL_LOCK();
110b7be02dcSbluhm 	error = crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0);
111b7be02dcSbluhm 	KERNEL_UNLOCK();
112b7be02dcSbluhm 	return error;
1131aaeb53aSjjbg }
1141aaeb53aSjjbg 
1151aaeb53aSjjbg /*
1161aaeb53aSjjbg  * ipcomp_zeroize() used when IPCA is deleted
1171aaeb53aSjjbg  */
1181aaeb53aSjjbg int
ipcomp_zeroize(struct tdb * tdbp)119288aa5efSnaddy ipcomp_zeroize(struct tdb *tdbp)
1201aaeb53aSjjbg {
121b7be02dcSbluhm 	int error;
1221aaeb53aSjjbg 
123b7be02dcSbluhm 	KERNEL_LOCK();
124b7be02dcSbluhm 	error = crypto_freesession(tdbp->tdb_cryptoid);
125b7be02dcSbluhm 	KERNEL_UNLOCK();
1261aaeb53aSjjbg 	tdbp->tdb_cryptoid = 0;
127b7be02dcSbluhm 	return error;
1281aaeb53aSjjbg }
1291aaeb53aSjjbg 
1301aaeb53aSjjbg /*
1311aaeb53aSjjbg  * ipcomp_input() gets called to uncompress an input packet
1321aaeb53aSjjbg  */
1331aaeb53aSjjbg int
ipcomp_input(struct mbuf ** mp,struct tdb * tdb,int skip,int protoff)134d5072c26Sbluhm ipcomp_input(struct mbuf **mp, struct tdb *tdb, int skip, int protoff)
1351aaeb53aSjjbg {
1364d13edafSbluhm 	const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
137d5072c26Sbluhm 	struct mbuf *m = *mp;
1381aaeb53aSjjbg 	struct cryptodesc *crdc = NULL;
1391aaeb53aSjjbg 	struct cryptop *crp;
1404f56c6f5Stobhe 	int hlen, error, clen, roff;
1414f56c6f5Stobhe 	u_int8_t nproto;
1424f56c6f5Stobhe 	u_int64_t ibytes;
1434f56c6f5Stobhe 	struct mbuf *m1, *mo;
1444f56c6f5Stobhe 	struct ipcomp  *ipcomp;
1454f56c6f5Stobhe 	caddr_t addr;
1464f56c6f5Stobhe #ifdef ENCDEBUG
1474f56c6f5Stobhe 	char buf[INET6_ADDRSTRLEN];
1484f56c6f5Stobhe #endif
1491aaeb53aSjjbg 
1501aaeb53aSjjbg 	hlen = IPCOMP_HLENGTH;
1511aaeb53aSjjbg 
1521aaeb53aSjjbg 	/* Get crypto descriptors */
1531aaeb53aSjjbg 	crp = crypto_getreq(1);
1541aaeb53aSjjbg 	if (crp == NULL) {
155698a75ddSbluhm 		DPRINTF("failed to acquire crypto descriptors");
1566460338bSvisa 		ipcompstat_inc(ipcomps_crypto);
157d5072c26Sbluhm 		goto drop;
1581aaeb53aSjjbg 	}
159e410e70dSpatrick 	crdc = &crp->crp_desc[0];
1601aaeb53aSjjbg 
1611aaeb53aSjjbg 	crdc->crd_skip = skip + hlen;
1621aaeb53aSjjbg 	crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
1631aaeb53aSjjbg 	crdc->crd_inject = skip;
1641aaeb53aSjjbg 
1651aaeb53aSjjbg 	/* Decompression operation */
1661aaeb53aSjjbg 	crdc->crd_alg = ipcompx->type;
1671aaeb53aSjjbg 
1681aaeb53aSjjbg 	/* Crypto operation descriptor */
1691aaeb53aSjjbg 	crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
170*fb8de0f1Sclaudio 	crp->crp_flags = CRYPTO_F_IMBUF;
1711aaeb53aSjjbg 	crp->crp_buf = (caddr_t)m;
1721aaeb53aSjjbg 	crp->crp_sid = tdb->tdb_cryptoid;
1731aaeb53aSjjbg 
17487edded1Stobhe 	while ((error = crypto_invoke(crp)) == EAGAIN) {
17512758001Stobhe 		/* Reset the session ID */
17612758001Stobhe 		if (tdb->tdb_cryptoid != 0)
17712758001Stobhe 			tdb->tdb_cryptoid = crp->crp_sid;
17812758001Stobhe 	}
17987edded1Stobhe 	if (error) {
18087edded1Stobhe 		DPRINTF("crypto error %d", error);
18112758001Stobhe 		ipsecstat_inc(ipsec_noxform);
182d5072c26Sbluhm 		goto drop;
18312758001Stobhe 	}
18412758001Stobhe 
18512758001Stobhe 	clen = crp->crp_olen;
18612758001Stobhe 
18712758001Stobhe 	/* Release the crypto descriptors */
18812758001Stobhe 	crypto_freereq(crp);
1894f56c6f5Stobhe 	crp = NULL;
1901e2c197eSmpi 
1911aaeb53aSjjbg 	/* update the counters */
1922edaa7baSmpi 	ibytes = m->m_pkthdr.len - (skip + hlen);
1932edaa7baSmpi 	tdb->tdb_cur_bytes += ibytes;
194d997d144Smvs 	tdbstat_add(tdb, tdb_ibytes, ibytes);
1952edaa7baSmpi 	ipcompstat_add(ipcomps_ibytes, ibytes);
1961aaeb53aSjjbg 
1971aaeb53aSjjbg 	/* Hard expiration */
1981aaeb53aSjjbg 	if ((tdb->tdb_flags & TDBF_BYTES) &&
1991aaeb53aSjjbg 	    (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
2006b86e016Smvs 		ipsecstat_inc(ipsec_exctdb);
2011aaeb53aSjjbg 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
2021aaeb53aSjjbg 		tdb_delete(tdb);
2034f56c6f5Stobhe 		goto drop;
2041aaeb53aSjjbg 	}
2051aaeb53aSjjbg 	/* Notify on soft expiration */
20659b9936bSbluhm 	mtx_enter(&tdb->tdb_mtx);
2071aaeb53aSjjbg 	if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
2081aaeb53aSjjbg 	    (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
2091aaeb53aSjjbg 		tdb->tdb_flags &= ~TDBF_SOFT_BYTES;  /* Turn off checking */
21059b9936bSbluhm 		mtx_leave(&tdb->tdb_mtx);
21159b9936bSbluhm 		/* may sleep in solock() for the pfkey socket */
21259b9936bSbluhm 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
21359b9936bSbluhm 	} else
21459b9936bSbluhm 		mtx_leave(&tdb->tdb_mtx);
2151aaeb53aSjjbg 
2161aaeb53aSjjbg 	/* In case it's not done already, adjust the size of the mbuf chain */
2171aaeb53aSjjbg 	m->m_pkthdr.len = clen + hlen + skip;
2181aaeb53aSjjbg 
219e63ce21bSbluhm 	if (m->m_len < skip + hlen &&
220e63ce21bSbluhm 	    (m = *mp = m_pullup(m, skip + hlen)) == NULL) {
221448c5f3fSbluhm 		ipcompstat_inc(ipcomps_hdrops);
2224f56c6f5Stobhe 		goto drop;
22390596d0eSmillert 	}
2241aaeb53aSjjbg 
2251aaeb53aSjjbg 	/* Find the beginning of the IPCOMP header */
2261aaeb53aSjjbg 	m1 = m_getptr(m, skip, &roff);
2271aaeb53aSjjbg 	if (m1 == NULL) {
228698a75ddSbluhm 		DPRINTF("bad mbuf chain, IPCA %s/%08x",
2293514aacbSmikeb 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
230698a75ddSbluhm 		    ntohl(tdb->tdb_spi));
231448c5f3fSbluhm 		ipcompstat_inc(ipcomps_hdrops);
2324f56c6f5Stobhe 		goto drop;
2331aaeb53aSjjbg 	}
2341aaeb53aSjjbg 	/* Keep the next protocol field */
2351aaeb53aSjjbg 	addr = (caddr_t) mtod(m, struct ip *) + skip;
2361aaeb53aSjjbg 	ipcomp = (struct ipcomp *) addr;
2371aaeb53aSjjbg 	nproto = ipcomp->ipcomp_nh;
2381aaeb53aSjjbg 
2391aaeb53aSjjbg 	/* Remove the IPCOMP header from the mbuf */
2401aaeb53aSjjbg 	if (roff == 0) {
2411aaeb53aSjjbg 		/* The IPCOMP header is at the beginning of m1 */
2421aaeb53aSjjbg 		m_adj(m1, hlen);
24323702ca1Sbluhm 		/*
24423702ca1Sbluhm 		 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
24523702ca1Sbluhm 		 * has already adjusted the packet header length for us.
24623702ca1Sbluhm 		 */
24723702ca1Sbluhm 		if (m1 != m)
2481aaeb53aSjjbg 			m->m_pkthdr.len -= hlen;
2491aaeb53aSjjbg 	} else if (roff + hlen >= m1->m_len) {
25023702ca1Sbluhm 		int adjlen;
25123702ca1Sbluhm 
2521aaeb53aSjjbg 		if (roff + hlen > m1->m_len) {
25323702ca1Sbluhm 			adjlen = roff + hlen - m1->m_len;
25423702ca1Sbluhm 
2551aaeb53aSjjbg 			/* Adjust the next mbuf by the remainder */
25623702ca1Sbluhm 			m_adj(m1->m_next, adjlen);
2571aaeb53aSjjbg 
2581aaeb53aSjjbg 			/*
2591aaeb53aSjjbg 			 * The second mbuf is guaranteed not to have a
2601aaeb53aSjjbg 			 * pkthdr...
2611aaeb53aSjjbg 			 */
26223702ca1Sbluhm 			m->m_pkthdr.len -= adjlen;
2631aaeb53aSjjbg 		}
2641aaeb53aSjjbg 		/* Now, let's unlink the mbuf chain for a second... */
2651aaeb53aSjjbg 		mo = m1->m_next;
2661aaeb53aSjjbg 		m1->m_next = NULL;
2671aaeb53aSjjbg 
2681aaeb53aSjjbg 		/* ...and trim the end of the first part of the chain...sick */
26923702ca1Sbluhm 		adjlen = m1->m_len - roff;
27023702ca1Sbluhm 		m_adj(m1, -adjlen);
27123702ca1Sbluhm 		/*
27223702ca1Sbluhm 		 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj()
27323702ca1Sbluhm 		 * has already adjusted the packet header length for us.
27423702ca1Sbluhm 		 */
27523702ca1Sbluhm 		if (m1 != m)
27623702ca1Sbluhm 			m->m_pkthdr.len -= adjlen;
2771aaeb53aSjjbg 
2781aaeb53aSjjbg 		/* Finally, let's relink */
2791aaeb53aSjjbg 		m1->m_next = mo;
2801aaeb53aSjjbg 	} else {
2812d0f50edSdhill 		memmove(mtod(m1, u_char *) + roff,
2822d0f50edSdhill 		    mtod(m1, u_char *) + roff + hlen,
2831aaeb53aSjjbg 		    m1->m_len - (roff + hlen));
2841aaeb53aSjjbg 		m1->m_len -= hlen;
2851aaeb53aSjjbg 		m->m_pkthdr.len -= hlen;
2861aaeb53aSjjbg 	}
2871aaeb53aSjjbg 
2881aaeb53aSjjbg 	/* Restore the Next Protocol field */
28941b18b7eSblambert 	m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT);
2901aaeb53aSjjbg 
2911aaeb53aSjjbg 	/* Back to generic IPsec input processing */
292e63ce21bSbluhm 	return ipsec_common_input_cb(mp, tdb, skip, protoff);
2931aaeb53aSjjbg 
2944f56c6f5Stobhe  drop:
295e63ce21bSbluhm 	m_freemp(mp);
2964f56c6f5Stobhe 	crypto_freereq(crp);
297bec0ed23Sbluhm 	return IPPROTO_DONE;
2981aaeb53aSjjbg }
2991aaeb53aSjjbg 
3001aaeb53aSjjbg /*
3011aaeb53aSjjbg  * IPComp output routine, called by ipsp_process_packet()
3021aaeb53aSjjbg  */
3031aaeb53aSjjbg int
ipcomp_output(struct mbuf * m,struct tdb * tdb,int skip,int protoff)304ead5a062Sbluhm ipcomp_output(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
3051aaeb53aSjjbg {
3064d13edafSbluhm 	const struct comp_algo *ipcompx = tdb->tdb_compalgxform;
3074f56c6f5Stobhe 	int error, hlen, ilen, olen, rlen, roff;
3081aaeb53aSjjbg 	struct cryptodesc *crdc = NULL;
309448c5f3fSbluhm 	struct cryptop *crp = NULL;
3104f56c6f5Stobhe 	struct mbuf *mi, *mo;
3114f56c6f5Stobhe 	struct ip *ip;
3124f56c6f5Stobhe 	u_int16_t cpi;
3134f56c6f5Stobhe #ifdef INET6
3144f56c6f5Stobhe 	struct ip6_hdr *ip6;
3154f56c6f5Stobhe #endif
3163514aacbSmikeb #ifdef ENCDEBUG
3173514aacbSmikeb 	char buf[INET6_ADDRSTRLEN];
3183514aacbSmikeb #endif
3191aaeb53aSjjbg #if NBPFILTER > 0
3208ddcae73Sreyk 	struct ifnet *encif;
3214f56c6f5Stobhe 	struct ipcomp  *ipcomp;
32263df607fSpascoe 
323a43d4d9bSreyk 	if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) {
3248ddcae73Sreyk 		encif->if_opackets++;
3258ddcae73Sreyk 		encif->if_obytes += m->m_pkthdr.len;
3268ddcae73Sreyk 
3278ddcae73Sreyk 		if (encif->if_bpf) {
3281aaeb53aSjjbg 			struct enchdr hdr;
3291aaeb53aSjjbg 
330f8575965Stedu 			memset(&hdr, 0, sizeof(hdr));
3311aaeb53aSjjbg 
3321aaeb53aSjjbg 			hdr.af = tdb->tdb_dst.sa.sa_family;
3331aaeb53aSjjbg 			hdr.spi = tdb->tdb_spi;
3341aaeb53aSjjbg 
3358ddcae73Sreyk 			bpf_mtap_hdr(encif->if_bpf, (char *)&hdr,
336f1d9eb97Sdlg 			    ENC_HDRLEN, m, BPF_DIRECTION_OUT);
3378ddcae73Sreyk 		}
3381aaeb53aSjjbg 	}
3391aaeb53aSjjbg #endif
3401aaeb53aSjjbg 	hlen = IPCOMP_HLENGTH;
3411aaeb53aSjjbg 
3426460338bSvisa 	ipcompstat_inc(ipcomps_output);
3431aaeb53aSjjbg 
3441aaeb53aSjjbg 	switch (tdb->tdb_dst.sa.sa_family) {
3451aaeb53aSjjbg 	case AF_INET:
3461aaeb53aSjjbg 		/* Check for IPv4 maximum packet size violations */
3471aaeb53aSjjbg 		/*
3481aaeb53aSjjbg 		 * Since compression is going to reduce the size, no need to
3491aaeb53aSjjbg 		 * worry
3501aaeb53aSjjbg 		 */
351ccfb3f57Sho 		if (m->m_pkthdr.len + hlen > IP_MAXPACKET) {
352698a75ddSbluhm 			DPRINTF("packet in IPCA %s/%08x got too big",
353698a75ddSbluhm 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
354698a75ddSbluhm 			    ntohl(tdb->tdb_spi));
3556460338bSvisa 			ipcompstat_inc(ipcomps_toobig);
356448c5f3fSbluhm 			error = EMSGSIZE;
357448c5f3fSbluhm 			goto drop;
3581aaeb53aSjjbg 		}
3591aaeb53aSjjbg 		break;
3601aaeb53aSjjbg 
3611aaeb53aSjjbg #ifdef INET6
3621aaeb53aSjjbg 	case AF_INET6:
3631aaeb53aSjjbg 		/* Check for IPv6 maximum packet size violations */
364ccfb3f57Sho 		if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) {
365698a75ddSbluhm 			DPRINTF("packet in IPCA %s/%08x got too big",
366698a75ddSbluhm 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
367698a75ddSbluhm 			    ntohl(tdb->tdb_spi));
3686460338bSvisa 			ipcompstat_inc(ipcomps_toobig);
369448c5f3fSbluhm 			error = EMSGSIZE;
370448c5f3fSbluhm 			goto drop;
3711aaeb53aSjjbg 		}
372a10a4f65Sbluhm 		break;
3731aaeb53aSjjbg #endif /* INET6 */
3741aaeb53aSjjbg 
3751aaeb53aSjjbg 	default:
376698a75ddSbluhm 		DPRINTF("unknown/unsupported protocol family %d, IPCA %s/%08x",
377698a75ddSbluhm 		    tdb->tdb_dst.sa.sa_family,
3783514aacbSmikeb 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
379698a75ddSbluhm 		    ntohl(tdb->tdb_spi));
3806460338bSvisa 		ipcompstat_inc(ipcomps_nopf);
381448c5f3fSbluhm 		error = EPFNOSUPPORT;
382448c5f3fSbluhm 		goto drop;
3831aaeb53aSjjbg 	}
3841aaeb53aSjjbg 
3851aaeb53aSjjbg 	/* Update the counters */
3861aaeb53aSjjbg 	tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
3876460338bSvisa 	ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip);
3881aaeb53aSjjbg 
3891aaeb53aSjjbg 	/* Hard byte expiration */
3901aaeb53aSjjbg 	if ((tdb->tdb_flags & TDBF_BYTES) &&
3911aaeb53aSjjbg 	    (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
3926b86e016Smvs 		ipsecstat_inc(ipsec_exctdb);
3931aaeb53aSjjbg 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
3941aaeb53aSjjbg 		tdb_delete(tdb);
395448c5f3fSbluhm 		error = EINVAL;
396448c5f3fSbluhm 		goto drop;
3971aaeb53aSjjbg 	}
39859b9936bSbluhm 
3991aaeb53aSjjbg 	/* Soft byte expiration */
40059b9936bSbluhm 	mtx_enter(&tdb->tdb_mtx);
4011aaeb53aSjjbg 	if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
4021aaeb53aSjjbg 	    (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
4031aaeb53aSjjbg 		tdb->tdb_flags &= ~TDBF_SOFT_BYTES;  /* Turn off checking */
40459b9936bSbluhm 		mtx_leave(&tdb->tdb_mtx);
40559b9936bSbluhm 		/* may sleep in solock() for the pfkey socket */
40659b9936bSbluhm 		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
40759b9936bSbluhm 	} else
40859b9936bSbluhm 		mtx_leave(&tdb->tdb_mtx);
40959b9936bSbluhm 
4101aaeb53aSjjbg 	/*
41138d7d608Smarkus 	 * Loop through mbuf chain; if we find a readonly mbuf,
4122be2e590Sdlg 	 * copy the packet.
4131aaeb53aSjjbg 	 */
4141aaeb53aSjjbg 	mi = m;
4152be2e590Sdlg 	while (mi != NULL && !M_READONLY(mi))
4161aaeb53aSjjbg 		mi = mi->m_next;
4171aaeb53aSjjbg 
4181aaeb53aSjjbg 	if (mi != NULL) {
4192be2e590Sdlg 		struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT);
4201aaeb53aSjjbg 
4211aaeb53aSjjbg 		if (n == NULL) {
422698a75ddSbluhm 			DPRINTF("bad mbuf chain, IPCA %s/%08x",
4233514aacbSmikeb 			    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
424698a75ddSbluhm 			    ntohl(tdb->tdb_spi));
4256460338bSvisa 			ipcompstat_inc(ipcomps_hdrops);
426448c5f3fSbluhm 			error = ENOBUFS;
427448c5f3fSbluhm 			goto drop;
4281aaeb53aSjjbg 		}
4291aaeb53aSjjbg 
4302be2e590Sdlg 		m_freem(m);
4312be2e590Sdlg 		m = n;
4321aaeb53aSjjbg 	}
4331aaeb53aSjjbg 	/* Ok now, we can pass to the crypto processing */
4341aaeb53aSjjbg 
4351aaeb53aSjjbg 	/* Get crypto descriptors */
4361aaeb53aSjjbg 	crp = crypto_getreq(1);
4371aaeb53aSjjbg 	if (crp == NULL) {
438698a75ddSbluhm 		DPRINTF("failed to acquire crypto descriptors");
4396460338bSvisa 		ipcompstat_inc(ipcomps_crypto);
440448c5f3fSbluhm 		error = ENOBUFS;
441448c5f3fSbluhm 		goto drop;
4421aaeb53aSjjbg 	}
443e410e70dSpatrick 	crdc = &crp->crp_desc[0];
4441aaeb53aSjjbg 
4451aaeb53aSjjbg 	/* Compression descriptor */
4468ba4ecc8Smarkus 	crdc->crd_skip = skip;
4478ba4ecc8Smarkus 	crdc->crd_len = m->m_pkthdr.len - skip;
4481aaeb53aSjjbg 	crdc->crd_flags = CRD_F_COMP;
4498ba4ecc8Smarkus 	crdc->crd_inject = skip;
4501aaeb53aSjjbg 
4511aaeb53aSjjbg 	/* Compression operation */
4521aaeb53aSjjbg 	crdc->crd_alg = ipcompx->type;
4531aaeb53aSjjbg 
4541aaeb53aSjjbg 	/* Crypto operation descriptor */
4551aaeb53aSjjbg 	crp->crp_ilen = m->m_pkthdr.len;	/* Total input length */
456*fb8de0f1Sclaudio 	crp->crp_flags = CRYPTO_F_IMBUF;
4571aaeb53aSjjbg 	crp->crp_buf = (caddr_t)m;
4581aaeb53aSjjbg 	crp->crp_sid = tdb->tdb_cryptoid;
4591aaeb53aSjjbg 
46087edded1Stobhe 	while ((error = crypto_invoke(crp)) == EAGAIN) {
46112758001Stobhe 		/* Reset the session ID */
46212758001Stobhe 		if (tdb->tdb_cryptoid != 0)
46312758001Stobhe 			tdb->tdb_cryptoid = crp->crp_sid;
46412758001Stobhe 	}
46587edded1Stobhe 	if (error) {
46687edded1Stobhe 		DPRINTF("crypto error %d", error);
46712758001Stobhe 		ipsecstat_inc(ipsec_noxform);
4684f56c6f5Stobhe 		goto drop;
46912758001Stobhe 	}
47012758001Stobhe 
47112758001Stobhe 	ilen = crp->crp_ilen;
47212758001Stobhe 	olen = crp->crp_olen;
47312758001Stobhe 
47412758001Stobhe 	/* Release the crypto descriptors */
47512758001Stobhe 	crypto_freereq(crp);
4764f56c6f5Stobhe 	crp = NULL;
47712758001Stobhe 
4787ced204eSmpi 	rlen = ilen - skip;
479e53dc863Sangelos 
480a4f90811Sangelos 	/* Check sizes. */
4814b5fa55eSmpi 	if (rlen <= olen + IPCOMP_HLENGTH) {
482a4f90811Sangelos 		/* Compression was useless, we have lost time. */
483448c5f3fSbluhm 		ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */
4847ced204eSmpi 		goto skiphdr;
4851aaeb53aSjjbg 	}
4861aaeb53aSjjbg 
4878ba4ecc8Smarkus 	/* Inject IPCOMP header */
4885db30710Smarkus 	mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff);
4898ba4ecc8Smarkus 	if (mo == NULL) {
490698a75ddSbluhm 		DPRINTF("ailed to inject IPCOMP header for IPCA %s/%08x",
491698a75ddSbluhm 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
492698a75ddSbluhm 		    ntohl(tdb->tdb_spi));
4936460338bSvisa 		ipcompstat_inc(ipcomps_wrap);
494bc489a1cSbluhm 		error = ENOBUFS;
495bc489a1cSbluhm 		goto drop;
4968ba4ecc8Smarkus 	}
4978ba4ecc8Smarkus 
4988ba4ecc8Smarkus 	/* Initialize the IPCOMP header */
4995db30710Smarkus 	ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff);
500f8575965Stedu 	memset(ipcomp, 0, sizeof(struct ipcomp));
5018ba4ecc8Smarkus 	cpi = (u_int16_t) ntohl(tdb->tdb_spi);
5028ba4ecc8Smarkus 	ipcomp->ipcomp_cpi = htons(cpi);
5038ba4ecc8Smarkus 
5048ba4ecc8Smarkus 	/* m_pullup before ? */
5051aaeb53aSjjbg 	switch (tdb->tdb_dst.sa.sa_family) {
5061aaeb53aSjjbg 	case AF_INET:
5071aaeb53aSjjbg 		ip = mtod(m, struct ip *);
5088ba4ecc8Smarkus 		ipcomp->ipcomp_nh = ip->ip_p;
5098ba4ecc8Smarkus 		ip->ip_p = IPPROTO_IPCOMP;
5101aaeb53aSjjbg 		break;
5111aaeb53aSjjbg #ifdef INET6
5121aaeb53aSjjbg 	case AF_INET6:
5131aaeb53aSjjbg 		ip6 = mtod(m, struct ip6_hdr *);
5148ba4ecc8Smarkus 		ipcomp->ipcomp_nh = ip6->ip6_nxt;
5158ba4ecc8Smarkus 		ip6->ip6_nxt = IPPROTO_IPCOMP;
5161aaeb53aSjjbg 		break;
5178ba4ecc8Smarkus #endif
5181aaeb53aSjjbg 	default:
519698a75ddSbluhm 		DPRINTF("unsupported protocol family %d, IPCA %s/%08x",
520698a75ddSbluhm 		    tdb->tdb_dst.sa.sa_family,
5213514aacbSmikeb 		    ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)),
522698a75ddSbluhm 		    ntohl(tdb->tdb_spi));
5236460338bSvisa 		ipcompstat_inc(ipcomps_nopf);
524bc489a1cSbluhm 		error = EPFNOSUPPORT;
525bc489a1cSbluhm 		goto drop;
5261aaeb53aSjjbg 	}
5271aaeb53aSjjbg 
5287ced204eSmpi  skiphdr:
529bc489a1cSbluhm 	error = ipsp_process_done(m, tdb);
530bc489a1cSbluhm 	if (error)
5316460338bSvisa 		ipcompstat_inc(ipcomps_outfail);
532bc489a1cSbluhm 	return error;
5331aaeb53aSjjbg 
534bc489a1cSbluhm  drop:
5351aaeb53aSjjbg 	m_freem(m);
5364f56c6f5Stobhe 	crypto_freereq(crp);
537bc489a1cSbluhm 	return error;
5381aaeb53aSjjbg }
539