summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/ip_ipcomp.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/sys/netinet/ip_ipcomp.c b/sys/netinet/ip_ipcomp.c
new file mode 100644
index 00000000000..684a865f4de
--- /dev/null
+++ b/sys/netinet/ip_ipcomp.c
@@ -0,0 +1,725 @@
+/* $OpenBSD: ip_ipcomp.c,v 1.1 2001/07/05 12:08:52 jjbg Exp $ */
+
+/*
+ * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* IP payload compression protocol (IPComp), see RFC 2393 */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/bpf.h>
+
+#include <dev/rndvar.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif /* INET */
+
+#ifdef INET6
+#ifndef INET
+#include <netinet/in.h>
+#endif
+#include <netinet/ip6.h>
+#endif /* INET6 */
+
+#include <netinet/ip_ipsp.h>
+#include <netinet/ip_ipcomp.h>
+#include <net/pfkeyv2.h>
+#include <net/if_enc.h>
+
+#include <crypto/cryptodev.h>
+#include <crypto/deflate.h>
+#include <crypto/xform.h>
+
+#include "bpfilter.h"
+
+#ifdef ENCDEBUG
+#define DPRINTF(x) if (encdebug) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+/*
+ * ipcomp_attach() is called from the transformation code
+ */
+int
+ipcomp_attach(void)
+{
+ return 0;
+}
+
+/*
+ * ipcomp_init() is called when an CPI is being set up.
+ */
+int
+ipcomp_init(tdbp, xsp, ii)
+ struct tdb *tdbp;
+ struct xformsw *xsp;
+ struct ipsecinit *ii;
+{
+ struct comp_algo *tcomp = NULL;
+ struct cryptoini cric;
+
+ switch (ii->ii_compalg) {
+ case SADB_X_CALG_DEFLATE:
+ tcomp = &comp_algo_deflate;
+ break;
+
+ /* Only deflate is implemented */
+
+ default:
+ DPRINTF(
+ ("ipcomp_init(): unsupported compression algorithm %d specified\n",
+ ii->ii_compalg));
+ return EINVAL;
+ }
+
+ tdbp->tdb_compalgxform = tcomp;
+
+ DPRINTF(("ipcomp_init(): initialized TDB with ipcomp algorithm %s\n",
+ tcomp->name));
+
+ tdbp->tdb_xform = xsp;
+ tdbp->tdb_bitmap = 0;
+
+ /* Initialize crypto session */
+ bzero(&cric, sizeof(cric));
+ cric.cri_alg = tdbp->tdb_compalgxform->type;
+
+ return crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0);
+}
+
+/*
+ * ipcomp_zeroize() used when IPCA is deleted
+ */
+int
+ipcomp_zeroize(tdbp)
+ struct tdb *tdbp;
+{
+ int err;
+
+ err = crypto_freesession(tdbp->tdb_cryptoid);
+ tdbp->tdb_cryptoid = 0;
+ return err;
+}
+
+/*
+ * ipcomp_input() gets called to uncompress an input packet
+ */
+int
+ipcomp_input(m, tdb, skip, protoff)
+ struct mbuf *m;
+ struct tdb *tdb;
+ int skip;
+ int protoff;
+{
+ struct comp_algo *ipcompx = (struct comp_algo *) tdb->tdb_compalgxform;
+ struct tdb_crypto *tc;
+ int hlen;
+
+ struct cryptodesc *crdc = NULL;
+ struct cryptop *crp;
+
+ hlen = IPCOMP_HLENGTH;
+
+ /* Get crypto descriptors */
+ crp = crypto_getreq(1);
+ if (crp == NULL) {
+ m_freem(m);
+ DPRINTF(
+ ("ipcomp_input(): failed to acquire crypto descriptors\n"));
+ ipcompstat.ipcomps_crypto++;
+ return ENOBUFS;
+ }
+ /* Get IPsec-specific opaque pointer */
+ MALLOC(tc, struct tdb_crypto *, sizeof(struct tdb_crypto),
+ M_XDATA, M_NOWAIT);
+ if (tc == NULL) {
+ m_freem(m);
+ crypto_freereq(crp);
+ DPRINTF(("ipcomp_input(): failed to allocate tdb_crypto\n"));
+ ipcompstat.ipcomps_crypto++;
+ return ENOBUFS;
+ }
+ bzero(tc, sizeof(struct tdb_crypto));
+ crdc = crp->crp_desc;
+
+ crdc->crd_skip = skip + hlen;
+ crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
+ crdc->crd_inject = skip;
+
+ tc->tc_ptr = 0;
+
+ /* Decompression operation */
+ crdc->crd_alg = ipcompx->type;
+
+ /* Crypto operation descriptor */
+ crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
+ crp->crp_flags = CRYPTO_F_IMBUF;
+ crp->crp_buf = (caddr_t) m;
+ crp->crp_callback = (int (*) (struct cryptop *)) ipcomp_input_cb;
+ crp->crp_sid = tdb->tdb_cryptoid;
+ crp->crp_opaque = (caddr_t) tc;
+
+ /* These are passed as-is to the callback */
+ tc->tc_skip = skip;
+ tc->tc_protoff = protoff;
+ tc->tc_spi = tdb->tdb_spi;
+ tc->tc_proto = IPPROTO_IPCOMP;
+ bcopy(&tdb->tdb_dst, &tc->tc_dst, sizeof(union sockaddr_union));
+
+ return crypto_dispatch(crp);
+}
+
+/*
+ * IPComp input callback, called directly by the crypto driver
+ */
+int
+ipcomp_input_cb(op)
+ void *op;
+{
+ int error, s, skip, protoff, roff, hlen = IPCOMP_HLENGTH, clen;
+ u_int8_t nproto;
+ struct mbuf *m, *m1, *mo;
+ struct cryptodesc *crd;
+ struct comp_algo *ipcompx;
+ struct tdb_crypto *tc;
+ struct cryptop *crp;
+ struct tdb *tdb;
+ struct ipcomp *ipcomp;
+ caddr_t addr;
+
+ crp = (struct cryptop *) op;
+ crd = crp->crp_desc;
+
+ tc = (struct tdb_crypto *) crp->crp_opaque;
+ skip = tc->tc_skip;
+ protoff = tc->tc_protoff;
+ m = (struct mbuf *) crp->crp_buf;
+
+ s = spltdb();
+
+ tdb = gettdb(tc->tc_spi, &tc->tc_dst, tc->tc_proto);
+ FREE(tc, M_XDATA);
+ if (tdb == NULL) {
+ ipcompstat.ipcomps_notdb++;
+ DPRINTF(("ipcomp_input_cb(): TDB expired while in crypto"));
+ goto baddone;
+ }
+ ipcompx = (struct comp_algo *) tdb->tdb_compalgxform;
+
+ /* update the counters */
+ tdb->tdb_cur_bytes += m->m_pkthdr.len - (skip + hlen);
+ ipcompstat.ipcomps_ibytes += m->m_pkthdr.len - (skip + hlen);
+
+ /* Hard expiration */
+ if ((tdb->tdb_flags & TDBF_BYTES) &&
+ (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
+ pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
+ tdb_delete(tdb);
+ m_freem(m);
+ return ENXIO;
+ }
+ /* Notify on soft expiration */
+ if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
+ (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
+ pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
+ tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */
+ }
+
+ /* Check for crypto errors */
+ if (crp->crp_etype) {
+ /* Reset the session ID */
+ if (tdb->tdb_cryptoid != 0)
+ tdb->tdb_cryptoid = crp->crp_sid;
+ if (crp->crp_etype == EAGAIN) {
+ splx(s);
+ return crypto_dispatch(crp);
+ }
+ ipcompstat.ipcomps_noxform++;
+ DPRINTF(
+
+ ("ipcomp_input_cb(): crypto error %d\n", crp->crp_etype));
+ error = crp->crp_etype;
+ goto baddone;
+ }
+ /* Shouldn't happen... */
+ if (m == NULL) {
+ ipcompstat.ipcomps_crypto++;
+ DPRINTF(
+ ("ipcomp_input_cb(): bogus returned buffer from crypto\n")
+ );
+ error = EINVAL;
+ goto baddone;
+ }
+ /* Release the crypto descriptors */
+ crypto_freereq(crp);
+
+ /* Length of data after processing */
+ clen = crp->crp_olen;
+
+ /* In case it's not done already, adjust the size of the mbuf chain */
+ m->m_pkthdr.len = clen + hlen + skip;
+
+ if ((m->m_len < skip + hlen) && (m = m_pullup(m, skip + hlen)) == 0)
+ goto baddone;
+
+ /* Find the beginning of the IPCOMP header */
+ m1 = m_getptr(m, skip, &roff);
+ if (m1 == NULL) {
+ ipcompstat.ipcomps_hdrops++;
+ splx(s);
+ DPRINTF(("ipcomp_input_cb(): bad mbuf chain, IPCA %s/%08x\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ return EINVAL;
+ }
+ /* Keep the next protocol field */
+ addr = (caddr_t) mtod(m, struct ip *) + skip;
+ ipcomp = (struct ipcomp *) addr;
+ nproto = ipcomp->ipcomp_nh;
+
+ /* Remove the IPCOMP header from the mbuf */
+ if (roff == 0) {
+ /* The IPCOMP header is at the beginning of m1 */
+ m_adj(m1, hlen);
+ if (!(m1->m_flags & M_PKTHDR))
+ m->m_pkthdr.len -= hlen;
+ } else if (roff + hlen >= m1->m_len) {
+ if (roff + hlen > m1->m_len) {
+ /* Adjust the next mbuf by the remainder */
+ m_adj(m1->m_next, roff + hlen - m1->m_len);
+
+ /*
+ * The second mbuf is guaranteed not to have a
+ * pkthdr...
+ */
+ m->m_pkthdr.len -= (roff + hlen - m1->m_len);
+ }
+ /* Now, let's unlink the mbuf chain for a second... */
+ mo = m1->m_next;
+ m1->m_next = NULL;
+
+ /* ...and trim the end of the first part of the chain...sick */
+ m_adj(m1, -(m1->m_len - roff));
+ if (!(m1->m_flags & M_PKTHDR))
+ m->m_pkthdr.len -= (m1->m_len - roff);
+
+ /* Finally, let's relink */
+ m1->m_next = mo;
+ } else {
+ bcopy(mtod(m1, u_char *) + roff + hlen,
+ mtod(m1, u_char *) + roff,
+ m1->m_len - (roff + hlen));
+ m1->m_len -= hlen;
+ m->m_pkthdr.len -= hlen;
+ }
+
+ /* Restore the Next Protocol field */
+ m_copyback(m, protoff, sizeof(u_int8_t), (u_int8_t *) & nproto);
+
+ /* Back to generic IPsec input processing */
+ error = ipsec_common_input_cb(m, tdb, skip, protoff, NULL);
+ splx(s);
+ return error;
+
+baddone:
+ splx(s);
+
+ if (m)
+ m_freem(m);
+ crypto_freereq(crp);
+
+ return error;
+}
+
+/*
+ * IPComp output routine, called by ipsp_process_packet()
+ */
+int
+ipcomp_output(m, tdb, mp, skip, protoff)
+ struct mbuf *m;
+ struct tdb *tdb;
+ struct mbuf **mp;
+ int skip;
+ int protoff;
+{
+ struct comp_algo *ipcompx = (struct comp_algo *) tdb->tdb_compalgxform;
+ int rblen, ralen, hlen;
+ u_int8_t prot;
+ u_int16_t cpi;
+ struct cryptodesc *crdc = NULL;
+ struct cryptop *crp;
+ struct tdb_crypto *tc;
+ struct mbuf *mi, *mo;
+ struct ipcomp *ipcomp;
+#ifdef INET
+ struct ip *ip;
+#endif
+#ifdef INET6
+ struct ip6_hdr *ip6;
+#endif
+
+#if NBPFILTER > 0
+ {
+ struct ifnet *ifn;
+ struct enchdr hdr;
+ struct mbuf m1;
+
+ bzero(&hdr, sizeof(hdr));
+
+ hdr.af = tdb->tdb_dst.sa.sa_family;
+ hdr.spi = tdb->tdb_spi;
+ hdr.flags |= M_COMP;
+
+ m1.m_next = m;
+ m1.m_len = ENC_HDRLEN;
+ m1.m_data = (char *) &hdr;
+
+ ifn = &(encif[0].sc_if);
+
+ if (ifn->if_bpf)
+ bpf_mtap(ifn->if_bpf, &m1);
+ }
+#endif
+
+ rblen = m->m_pkthdr.len - skip; /* Raw payload length before comp. */
+ hlen = IPCOMP_HLENGTH;
+
+ ipcompstat.ipcomps_output++;
+
+ switch (tdb->tdb_dst.sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ /* Check for IPv4 maximum packet size violations */
+ /*
+ * Since compression is going to reduce the size, no need to
+ * worry
+ */
+ if (skip + hlen + ralen > IP_MAXPACKET) {
+ DPRINTF(
+ ("ipcomp_output(): packet in IPCA %s/%08x got too big\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ipcompstat.ipcomps_toobig++;
+ return EMSGSIZE;
+ }
+ break;
+#endif /* INET */
+
+#ifdef INET6
+ case AF_INET6:
+ /* Check for IPv6 maximum packet size violations */
+ if (skip + hlen + ralen > IPV6_MAXPACKET) {
+ DPRINTF(
+ ("ipcomp_output(): packet in IPCA %s/%08x got too big\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ipcompstat.ipcomps_toobig++;
+ return EMSGSIZE;
+ }
+#endif /* INET6 */
+
+ default:
+ DPRINTF(
+ ("ipcomp_output(): unknown/unsupported protocol family %d, IPCA %s/%08x\n",
+ tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst),
+ ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ipcompstat.ipcomps_nopf++;
+ return EPFNOSUPPORT;
+ }
+
+ /* Update the counters */
+
+ tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
+ ipcompstat.ipcomps_obytes += m->m_pkthdr.len - skip;
+
+ /* Hard byte expiration */
+ if ((tdb->tdb_flags & TDBF_BYTES) &&
+ (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) {
+ pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
+ tdb_delete(tdb);
+ m_freem(m);
+ return EINVAL;
+ }
+ /* Soft byte expiration */
+ if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
+ (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
+ pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
+ tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */
+ }
+ /*
+ * Loop through mbuf chain; if we find an M_EXT mbuf with
+ * more than one reference, replace the rest of the chain.
+ */
+ mi = m;
+ while (mi != NULL &&
+ (!(mi->m_flags & M_EXT) || !MCLISREFERENCED(mi))) {
+ mo = mi;
+ mi = mi->m_next;
+ }
+
+ if (mi != NULL) {
+ /* Replace the rest of the mbuf chain. */
+ struct mbuf *n = m_copym2(mi, 0, M_COPYALL, M_DONTWAIT);
+
+ if (n == NULL) {
+ DPRINTF(
+ ("ipcomp_output(): bad mbuf chain, IPCA %s/%08x\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ipcompstat.ipcomps_hdrops++;
+ m_freem(m);
+ return ENOBUFS;
+ }
+ if (mo != NULL)
+ mo->m_next = n;
+ else
+ m = n;
+
+ m_freem(mi);
+ }
+ /* Inject IPCOMP header */
+ mo = m_inject(m, skip, hlen, M_DONTWAIT);
+ if (mo == NULL) {
+ DPRINTF(
+ ("ipcomp_output(): failed to inject IPCOMP header for IPCA %s/%08x\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ipcompstat.ipcomps_wrap++;
+ return ENOBUFS;
+ }
+ ipcomp = mtod(mo, struct ipcomp *);
+
+ /* Initialize the IPCOMP header */
+
+ bzero(ipcomp, sizeof(struct ipcomp));
+
+ cpi = (u_int16_t) ntohl(tdb->tdb_spi);
+ ipcomp->ipcomp_cpi = htons(cpi);
+
+ /* m_pullup before ? */
+
+ switch (tdb->tdb_dst.sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ ip = mtod(m, struct ip *);
+ ipcomp->ipcomp_nh = ip->ip_p;
+ break;
+#endif /* INET */
+
+#ifdef INET6
+ case AF_INET6:
+ ip6 = mtod(m, struct ip6_hdr *);
+ ipcomp->ipcomp_nh = ip6->ip6_nxt;
+ break;
+#endif
+
+ default:
+ DPRINTF(
+ ("ipcomp_output(): unknown/unsupported protocol family %d, IPCA %s/%08x\n",
+ tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst),
+ ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ipcompstat.ipcomps_nopf++;
+ return EPFNOSUPPORT;
+ break;
+ }
+
+ /* Fix Next Protocol in IPv4/IPv6 header */
+ prot = IPPROTO_IPCOMP;
+ m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) & prot);
+
+ /* Ok now, we can pass to the crypto processing */
+
+ /* Get crypto descriptors */
+ crp = crypto_getreq(1);
+ if (crp == NULL) {
+ m_freem(m);
+ DPRINTF(
+ ("ipcomp_output(): failed to acquire crypto descriptors\n"
+ ));
+ ipcompstat.ipcomps_crypto++;
+ return ENOBUFS;
+ }
+ crdc = crp->crp_desc;
+
+ /* Compression descriptor */
+ crdc->crd_skip = skip + hlen;
+ crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
+ crdc->crd_flags = CRD_F_COMP;
+ crdc->crd_inject = skip + hlen;
+
+ /* Compression operation */
+ crdc->crd_alg = ipcompx->type;
+
+ /* IPsec-specific opaque crypto info */
+ MALLOC(tc, struct tdb_crypto *, sizeof(struct tdb_crypto),
+ M_XDATA, M_NOWAIT);
+ if (tc == NULL) {
+ m_freem(m);
+ crypto_freereq(crp);
+ DPRINTF(("ipcomp_output(): failed to allocate tdb_crypto\n"));
+ ipcompstat.ipcomps_crypto++;
+ return ENOBUFS;
+ }
+ bzero(tc, sizeof(struct tdb_crypto));
+
+ tc->tc_spi = tdb->tdb_spi;
+ tc->tc_proto = tdb->tdb_sproto;
+ tc->tc_skip = skip + hlen;
+ bcopy(&tdb->tdb_dst, &tc->tc_dst, sizeof(union sockaddr_union));
+
+ /* Crypto operation descriptor */
+ crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
+ crp->crp_flags = CRYPTO_F_IMBUF;
+ crp->crp_buf = (caddr_t) m;
+ crp->crp_callback = (int (*) (struct cryptop *)) ipcomp_output_cb;
+ crp->crp_opaque = (caddr_t) tc;
+ crp->crp_sid = tdb->tdb_cryptoid;
+
+ return crypto_dispatch(crp);
+}
+
+/*
+ * IPComp output callback, called directly from the crypto driver
+ */
+int
+ipcomp_output_cb(cp)
+ void *cp;
+{
+ struct cryptop *crp = (struct cryptop *) cp;
+ struct tdb_crypto *tc;
+ struct tdb *tdb;
+ struct mbuf *m;
+ int error, s, skip, rlen;
+#ifdef INET
+ struct ip *ip;
+#endif
+#ifdef INET6
+ struct ip6_hdr *ip6;
+#endif
+
+ tc = (struct tdb_crypto *) crp->crp_opaque;
+ m = (struct mbuf *) crp->crp_buf;
+ skip = tc->tc_skip;
+ rlen = crp->crp_ilen - skip;
+
+ s = spltdb();
+
+ tdb = gettdb(tc->tc_spi, &tc->tc_dst, tc->tc_proto);
+ FREE(tc, M_XDATA);
+ if (tdb == NULL) {
+ ipcompstat.ipcomps_notdb++;
+ DPRINTF(("ipcomp_output_cb(): TDB expired while in crypto\n"));
+ goto baddone;
+ }
+ /* Check for crypto errors */
+ if (crp->crp_etype) {
+ /* Reset session ID */
+ if (tdb->tdb_cryptoid != 0)
+ tdb->tdb_cryptoid = crp->crp_sid;
+
+ if (crp->crp_etype == EAGAIN) {
+ splx(s);
+ return crypto_dispatch(crp);
+ }
+ ipcompstat.ipcomps_noxform++;
+ DPRINTF(("ipcomp_output_cb(): crypto error %d\n",
+ crp->crp_etype));
+
+ goto baddone;
+ }
+ /* Shouldn't happen... */
+ if (m == NULL) {
+ ipcompstat.ipcomps_crypto++;
+ DPRINTF(
+ ("ipcomp_output_cb(): bogus returned buffer from crypto\n"
+ ));
+ error = EINVAL;
+ goto baddone;
+ }
+ /* Check sizes */
+ if (rlen < crp->crp_olen) {
+ /* compression was useless, we have lost time */
+ FREE(tc, M_XDATA);
+ crypto_freereq(crp);
+ error = ipsp_process_done(m, tdb);
+ splx(s);
+ return error;
+ }
+
+ /* Adjust the length in the IP header */
+
+ switch (tdb->tdb_dst.sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ ip = mtod(m, struct ip *);
+ ip->ip_len = htons(m->m_pkthdr.len);
+ break;
+#endif /* INET */
+
+#ifdef INET6
+ case AF_INET6:
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(m->m_pkthdr.len) - sizeof(struct ip6_hdr);
+ break;
+#endif /* INET6 */
+
+ default:
+ DPRINTF(
+ ("ipcomp_output(): unknown/unsupported protocol family %d, IPCA %s/%08x\n",
+ tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst),
+ ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ipcompstat.ipcomps_nopf++;
+ return EPFNOSUPPORT;
+ break;
+ }
+
+ /* Release the crypto descriptor */
+ crypto_freereq(crp);
+
+ error = ipsp_process_done(m, tdb);
+ splx(s);
+ return error;
+
+baddone:
+ splx(s);
+ if (m)
+ m_freem(m);
+
+ crypto_freereq(crp);
+
+ return error;
+}