xform_ipcomp.c revision 117058
1105197Ssam/* $FreeBSD: head/sys/netipsec/xform_ipcomp.c 117058 2003-06-30 05:09:32Z sam $ */ 2105197Ssam/* $OpenBSD: ip_ipcomp.c,v 1.1 2001/07/05 12:08:52 jjbg Exp $ */ 3105197Ssam 4105197Ssam/* 5105197Ssam * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org) 6105197Ssam * 7105197Ssam * Redistribution and use in source and binary forms, with or without 8105197Ssam * modification, are permitted provided that the following conditions 9105197Ssam * are met: 10105197Ssam * 11105197Ssam * 1. Redistributions of source code must retain the above copyright 12105197Ssam * notice, this list of conditions and the following disclaimer. 13105197Ssam * 2. Redistributions in binary form must reproduce the above copyright 14105197Ssam * notice, this list of conditions and the following disclaimer in the 15105197Ssam * documentation and/or other materials provided with the distribution. 16105197Ssam * 3. The name of the author may not be used to endorse or promote products 17105197Ssam * derived from this software without specific prior written permission. 18105197Ssam * 19105197Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20105197Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21105197Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22105197Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23105197Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24105197Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25105197Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26105197Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27105197Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28105197Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29105197Ssam */ 30105197Ssam 31105197Ssam/* IP payload compression protocol (IPComp), see RFC 2393 */ 32105197Ssam#include "opt_inet.h" 33105197Ssam#include "opt_inet6.h" 34105197Ssam 35105197Ssam#include <sys/param.h> 36105197Ssam#include <sys/systm.h> 37105197Ssam#include <sys/mbuf.h> 38105197Ssam#include <sys/socket.h> 39105197Ssam#include <sys/kernel.h> 40105197Ssam#include <sys/protosw.h> 41105197Ssam#include <sys/sysctl.h> 42105197Ssam 43105197Ssam#include <netinet/in.h> 44105197Ssam#include <netinet/in_systm.h> 45105197Ssam#include <netinet/ip.h> 46105197Ssam#include <netinet/ip_var.h> 47105197Ssam 48105197Ssam#include <net/route.h> 49105197Ssam#include <netipsec/ipsec.h> 50105197Ssam#include <netipsec/xform.h> 51105197Ssam 52105197Ssam#ifdef INET6 53105197Ssam#include <netinet/ip6.h> 54105197Ssam#include <netipsec/ipsec6.h> 55105197Ssam#endif 56105197Ssam 57105197Ssam#include <netipsec/ipcomp.h> 58105197Ssam#include <netipsec/ipcomp_var.h> 59105197Ssam 60105197Ssam#include <netipsec/key.h> 61105197Ssam#include <netipsec/key_debug.h> 62105197Ssam 63105197Ssam#include <opencrypto/cryptodev.h> 64105197Ssam#include <opencrypto/deflate.h> 65105197Ssam#include <opencrypto/xform.h> 66105197Ssam 67105197Ssamint ipcomp_enable = 0; 68105197Ssamstruct ipcompstat ipcompstat; 69105197Ssam 70105197SsamSYSCTL_DECL(_net_inet_ipcomp); 71105197SsamSYSCTL_INT(_net_inet_ipcomp, OID_AUTO, 72105197Ssam ipcomp_enable, CTLFLAG_RW, &ipcomp_enable, 0, ""); 73105197SsamSYSCTL_STRUCT(_net_inet_ipcomp, IPSECCTL_STATS, 74105197Ssam stats, CTLFLAG_RD, &ipcompstat, ipcompstat, ""); 75105197Ssam 76105197Ssamstatic int ipcomp_input_cb(struct cryptop *crp); 77105197Ssamstatic int ipcomp_output_cb(struct cryptop *crp); 78105197Ssam 79105197Ssamstruct comp_algo * 80105197Ssamipcomp_algorithm_lookup(int alg) 81105197Ssam{ 82105197Ssam if (alg >= IPCOMP_ALG_MAX) 83105197Ssam return NULL; 84105197Ssam switch (alg) { 85105197Ssam case SADB_X_CALG_DEFLATE: 86105197Ssam return &comp_algo_deflate; 87105197Ssam } 88105197Ssam return NULL; 89105197Ssam} 90105197Ssam 91105197Ssam/* 92105197Ssam * ipcomp_init() is called when an CPI is being set up. 93105197Ssam */ 94105197Ssamstatic int 95105197Ssamipcomp_init(struct secasvar *sav, struct xformsw *xsp) 96105197Ssam{ 97105197Ssam struct comp_algo *tcomp; 98105197Ssam struct cryptoini cric; 99105197Ssam 100105197Ssam /* NB: algorithm really comes in alg_enc and not alg_comp! */ 101105197Ssam tcomp = ipcomp_algorithm_lookup(sav->alg_enc); 102105197Ssam if (tcomp == NULL) { 103105197Ssam DPRINTF(("ipcomp_init: unsupported compression algorithm %d\n", 104105197Ssam sav->alg_comp)); 105105197Ssam return EINVAL; 106105197Ssam } 107105197Ssam sav->alg_comp = sav->alg_enc; /* set for doing histogram */ 108105197Ssam sav->tdb_xform = xsp; 109105197Ssam sav->tdb_compalgxform = tcomp; 110105197Ssam 111105197Ssam /* Initialize crypto session */ 112105197Ssam bzero(&cric, sizeof (cric)); 113105197Ssam cric.cri_alg = sav->tdb_compalgxform->type; 114105197Ssam 115105197Ssam return crypto_newsession(&sav->tdb_cryptoid, &cric, crypto_support); 116105197Ssam} 117105197Ssam 118105197Ssam/* 119105197Ssam * ipcomp_zeroize() used when IPCA is deleted 120105197Ssam */ 121105197Ssamstatic int 122105197Ssamipcomp_zeroize(struct secasvar *sav) 123105197Ssam{ 124105197Ssam int err; 125105197Ssam 126105197Ssam err = crypto_freesession(sav->tdb_cryptoid); 127105197Ssam sav->tdb_cryptoid = 0; 128105197Ssam return err; 129105197Ssam} 130105197Ssam 131105197Ssam/* 132105197Ssam * ipcomp_input() gets called to uncompress an input packet 133105197Ssam */ 134105197Ssamstatic int 135105197Ssamipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) 136105197Ssam{ 137105197Ssam struct tdb_crypto *tc; 138105197Ssam struct cryptodesc *crdc; 139105197Ssam struct cryptop *crp; 140105197Ssam int hlen = IPCOMP_HLENGTH; 141105197Ssam 142105197Ssam#if 0 143105197Ssam SPLASSERT(net, "ipcomp_input"); 144105197Ssam#endif 145105197Ssam 146105197Ssam /* Get crypto descriptors */ 147105197Ssam crp = crypto_getreq(1); 148105197Ssam if (crp == NULL) { 149105197Ssam m_freem(m); 150105197Ssam DPRINTF(("ipcomp_input: no crypto descriptors\n")); 151105197Ssam ipcompstat.ipcomps_crypto++; 152105197Ssam return ENOBUFS; 153105197Ssam } 154105197Ssam /* Get IPsec-specific opaque pointer */ 155105197Ssam tc = (struct tdb_crypto *) malloc(sizeof (*tc), M_XDATA, M_NOWAIT|M_ZERO); 156105197Ssam if (tc == NULL) { 157105197Ssam m_freem(m); 158105197Ssam crypto_freereq(crp); 159105197Ssam DPRINTF(("ipcomp_input: cannot allocate tdb_crypto\n")); 160105197Ssam ipcompstat.ipcomps_crypto++; 161105197Ssam return ENOBUFS; 162105197Ssam } 163105197Ssam crdc = crp->crp_desc; 164105197Ssam 165105197Ssam crdc->crd_skip = skip + hlen; 166105197Ssam crdc->crd_len = m->m_pkthdr.len - (skip + hlen); 167105197Ssam crdc->crd_inject = skip; 168105197Ssam 169105197Ssam tc->tc_ptr = 0; 170105197Ssam 171105197Ssam /* Decompression operation */ 172105197Ssam crdc->crd_alg = sav->tdb_compalgxform->type; 173105197Ssam 174105197Ssam /* Crypto operation descriptor */ 175105197Ssam crp->crp_ilen = m->m_pkthdr.len - (skip + hlen); 176117058Ssam crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC; 177105197Ssam crp->crp_buf = (caddr_t) m; 178105197Ssam crp->crp_callback = ipcomp_input_cb; 179105197Ssam crp->crp_sid = sav->tdb_cryptoid; 180105197Ssam crp->crp_opaque = (caddr_t) tc; 181105197Ssam 182105197Ssam /* These are passed as-is to the callback */ 183105197Ssam tc->tc_spi = sav->spi; 184105197Ssam tc->tc_dst = sav->sah->saidx.dst; 185105197Ssam tc->tc_proto = sav->sah->saidx.proto; 186105197Ssam tc->tc_protoff = protoff; 187105197Ssam tc->tc_skip = skip; 188105197Ssam 189105197Ssam return crypto_dispatch(crp); 190105197Ssam} 191105197Ssam 192105197Ssam#ifdef INET6 193105197Ssam#define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) do { \ 194105197Ssam if (saidx->dst.sa.sa_family == AF_INET6) { \ 195105197Ssam error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag); \ 196105197Ssam } else { \ 197105197Ssam error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag); \ 198105197Ssam } \ 199105197Ssam} while (0) 200105197Ssam#else 201105197Ssam#define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) \ 202105197Ssam (error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag)) 203105197Ssam#endif 204105197Ssam 205105197Ssam/* 206105197Ssam * IPComp input callback from the crypto driver. 207105197Ssam */ 208105197Ssamstatic int 209105197Ssamipcomp_input_cb(struct cryptop *crp) 210105197Ssam{ 211105197Ssam struct cryptodesc *crd; 212105197Ssam struct tdb_crypto *tc; 213105197Ssam int skip, protoff; 214105197Ssam struct mtag *mtag; 215105197Ssam struct mbuf *m; 216105197Ssam struct secasvar *sav; 217105197Ssam struct secasindex *saidx; 218105197Ssam int s, hlen = IPCOMP_HLENGTH, error, clen; 219105197Ssam u_int8_t nproto; 220105197Ssam caddr_t addr; 221105197Ssam 222105197Ssam crd = crp->crp_desc; 223105197Ssam 224105197Ssam tc = (struct tdb_crypto *) crp->crp_opaque; 225105197Ssam KASSERT(tc != NULL, ("ipcomp_input_cb: null opaque crypto data area!")); 226105197Ssam skip = tc->tc_skip; 227105197Ssam protoff = tc->tc_protoff; 228105197Ssam mtag = (struct mtag *) tc->tc_ptr; 229105197Ssam m = (struct mbuf *) crp->crp_buf; 230105197Ssam 231105197Ssam s = splnet(); 232105197Ssam 233105197Ssam sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi); 234105197Ssam if (sav == NULL) { 235105197Ssam ipcompstat.ipcomps_notdb++; 236105197Ssam DPRINTF(("ipcomp_input_cb: SA expired while in crypto\n")); 237105197Ssam error = ENOBUFS; /*XXX*/ 238105197Ssam goto bad; 239105197Ssam } 240105197Ssam 241105197Ssam saidx = &sav->sah->saidx; 242105197Ssam KASSERT(saidx->dst.sa.sa_family == AF_INET || 243105197Ssam saidx->dst.sa.sa_family == AF_INET6, 244105197Ssam ("ah_input_cb: unexpected protocol family %u", 245105197Ssam saidx->dst.sa.sa_family)); 246105197Ssam 247105197Ssam /* Check for crypto errors */ 248105197Ssam if (crp->crp_etype) { 249105197Ssam /* Reset the session ID */ 250105197Ssam if (sav->tdb_cryptoid != 0) 251105197Ssam sav->tdb_cryptoid = crp->crp_sid; 252105197Ssam 253105197Ssam if (crp->crp_etype == EAGAIN) { 254105197Ssam KEY_FREESAV(&sav); 255105197Ssam splx(s); 256105197Ssam return crypto_dispatch(crp); 257105197Ssam } 258105197Ssam 259105197Ssam ipcompstat.ipcomps_noxform++; 260105197Ssam DPRINTF(("ipcomp_input_cb: crypto error %d\n", crp->crp_etype)); 261105197Ssam error = crp->crp_etype; 262105197Ssam goto bad; 263105197Ssam } 264105197Ssam /* Shouldn't happen... */ 265105197Ssam if (m == NULL) { 266105197Ssam ipcompstat.ipcomps_crypto++; 267105197Ssam DPRINTF(("ipcomp_input_cb: null mbuf returned from crypto\n")); 268105197Ssam error = EINVAL; 269105197Ssam goto bad; 270105197Ssam } 271105197Ssam ipcompstat.ipcomps_hist[sav->alg_comp]++; 272105197Ssam 273105197Ssam clen = crp->crp_olen; /* Length of data after processing */ 274105197Ssam 275105197Ssam /* Release the crypto descriptors */ 276105197Ssam free(tc, M_XDATA), tc = NULL; 277105197Ssam crypto_freereq(crp), crp = NULL; 278105197Ssam 279105197Ssam /* In case it's not done already, adjust the size of the mbuf chain */ 280105197Ssam m->m_pkthdr.len = clen + hlen + skip; 281105197Ssam 282105197Ssam if (m->m_len < skip + hlen && (m = m_pullup(m, skip + hlen)) == 0) { 283105197Ssam ipcompstat.ipcomps_hdrops++; /*XXX*/ 284105197Ssam DPRINTF(("ipcomp_input_cb: m_pullup failed\n")); 285105197Ssam error = EINVAL; /*XXX*/ 286105197Ssam goto bad; 287105197Ssam } 288105197Ssam 289105197Ssam /* Keep the next protocol field */ 290105197Ssam addr = (caddr_t) mtod(m, struct ip *) + skip; 291105197Ssam nproto = ((struct ipcomp *) addr)->comp_nxt; 292105197Ssam 293105197Ssam /* Remove the IPCOMP header */ 294105197Ssam error = m_striphdr(m, skip, hlen); 295105197Ssam if (error) { 296105197Ssam ipcompstat.ipcomps_hdrops++; 297105197Ssam DPRINTF(("ipcomp_input_cb: bad mbuf chain, IPCA %s/%08lx\n", 298105197Ssam ipsec_address(&sav->sah->saidx.dst), 299105197Ssam (u_long) ntohl(sav->spi))); 300105197Ssam goto bad; 301105197Ssam } 302105197Ssam 303105197Ssam /* Restore the Next Protocol field */ 304105197Ssam m_copyback(m, protoff, sizeof (u_int8_t), (u_int8_t *) &nproto); 305105197Ssam 306105197Ssam IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, NULL); 307105197Ssam 308105197Ssam KEY_FREESAV(&sav); 309105197Ssam splx(s); 310105197Ssam return error; 311105197Ssambad: 312105197Ssam if (sav) 313105197Ssam KEY_FREESAV(&sav); 314105197Ssam splx(s); 315105197Ssam if (m) 316105197Ssam m_freem(m); 317105197Ssam if (tc != NULL) 318105197Ssam free(tc, M_XDATA); 319105197Ssam if (crp) 320105197Ssam crypto_freereq(crp); 321105197Ssam return error; 322105197Ssam} 323105197Ssam 324105197Ssam/* 325105197Ssam * IPComp output routine, called by ipsec[46]_process_packet() 326105197Ssam */ 327105197Ssamstatic int 328105197Ssamipcomp_output( 329105197Ssam struct mbuf *m, 330105197Ssam struct ipsecrequest *isr, 331105197Ssam struct mbuf **mp, 332105197Ssam int skip, 333105197Ssam int protoff 334105197Ssam) 335105197Ssam{ 336105197Ssam struct secasvar *sav; 337105197Ssam struct comp_algo *ipcompx; 338105197Ssam int error, ralen, hlen, maxpacketsize, roff; 339105197Ssam u_int8_t prot; 340105197Ssam struct cryptodesc *crdc; 341105197Ssam struct cryptop *crp; 342105197Ssam struct tdb_crypto *tc; 343105197Ssam struct mbuf *mo; 344105197Ssam struct ipcomp *ipcomp; 345105197Ssam 346105197Ssam#if 0 347105197Ssam SPLASSERT(net, "ipcomp_output"); 348105197Ssam#endif 349105197Ssam 350105197Ssam sav = isr->sav; 351105197Ssam KASSERT(sav != NULL, ("ipcomp_output: null SA")); 352105197Ssam ipcompx = sav->tdb_compalgxform; 353105197Ssam KASSERT(ipcompx != NULL, ("ipcomp_output: null compression xform")); 354105197Ssam 355105197Ssam ralen = m->m_pkthdr.len - skip; /* Raw payload length before comp. */ 356105197Ssam hlen = IPCOMP_HLENGTH; 357105197Ssam 358105197Ssam ipcompstat.ipcomps_output++; 359105197Ssam 360105197Ssam /* Check for maximum packet size violations. */ 361105197Ssam switch (sav->sah->saidx.dst.sa.sa_family) { 362105197Ssam#ifdef INET 363105197Ssam case AF_INET: 364105197Ssam maxpacketsize = IP_MAXPACKET; 365105197Ssam break; 366105197Ssam#endif /* INET */ 367105197Ssam#ifdef INET6 368105197Ssam case AF_INET6: 369105197Ssam maxpacketsize = IPV6_MAXPACKET; 370105197Ssam break; 371105197Ssam#endif /* INET6 */ 372105197Ssam default: 373105197Ssam ipcompstat.ipcomps_nopf++; 374105197Ssam DPRINTF(("ipcomp_output: unknown/unsupported protocol family %d" 375105197Ssam ", IPCA %s/%08lx\n", 376105197Ssam sav->sah->saidx.dst.sa.sa_family, 377105197Ssam ipsec_address(&sav->sah->saidx.dst), 378105197Ssam (u_long) ntohl(sav->spi))); 379105197Ssam error = EPFNOSUPPORT; 380105197Ssam goto bad; 381105197Ssam } 382105197Ssam if (skip + hlen + ralen > maxpacketsize) { 383105197Ssam ipcompstat.ipcomps_toobig++; 384105197Ssam DPRINTF(("ipcomp_output: packet in IPCA %s/%08lx got too big " 385105197Ssam "(len %u, max len %u)\n", 386105197Ssam ipsec_address(&sav->sah->saidx.dst), 387105197Ssam (u_long) ntohl(sav->spi), 388105197Ssam skip + hlen + ralen, maxpacketsize)); 389105197Ssam error = EMSGSIZE; 390105197Ssam goto bad; 391105197Ssam } 392105197Ssam 393105197Ssam /* Update the counters */ 394105197Ssam ipcompstat.ipcomps_obytes += m->m_pkthdr.len - skip; 395105197Ssam 396105197Ssam m = m_clone(m); 397105197Ssam if (m == NULL) { 398105197Ssam ipcompstat.ipcomps_hdrops++; 399105197Ssam DPRINTF(("ipcomp_output: cannot clone mbuf chain, IPCA %s/%08lx\n", 400105197Ssam ipsec_address(&sav->sah->saidx.dst), 401105197Ssam (u_long) ntohl(sav->spi))); 402105197Ssam error = ENOBUFS; 403105197Ssam goto bad; 404105197Ssam } 405105197Ssam 406105197Ssam /* Inject IPCOMP header */ 407105197Ssam mo = m_makespace(m, skip, hlen, &roff); 408105197Ssam if (mo == NULL) { 409105197Ssam ipcompstat.ipcomps_wrap++; 410105197Ssam DPRINTF(("ipcomp_output: failed to inject IPCOMP header for " 411105197Ssam "IPCA %s/%08lx\n", 412105197Ssam ipsec_address(&sav->sah->saidx.dst), 413105197Ssam (u_long) ntohl(sav->spi))); 414105197Ssam error = ENOBUFS; 415105197Ssam goto bad; 416105197Ssam } 417105197Ssam ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff); 418105197Ssam 419105197Ssam /* Initialize the IPCOMP header */ 420105197Ssam /* XXX alignment always correct? */ 421105197Ssam switch (sav->sah->saidx.dst.sa.sa_family) { 422105197Ssam#ifdef INET 423105197Ssam case AF_INET: 424105197Ssam ipcomp->comp_nxt = mtod(m, struct ip *)->ip_p; 425105197Ssam break; 426105197Ssam#endif /* INET */ 427105197Ssam#ifdef INET6 428105197Ssam case AF_INET6: 429105197Ssam ipcomp->comp_nxt = mtod(m, struct ip6_hdr *)->ip6_nxt; 430105197Ssam break; 431105197Ssam#endif 432105197Ssam } 433105197Ssam ipcomp->comp_flags = 0; 434105197Ssam ipcomp->comp_cpi = htons((u_int16_t) ntohl(sav->spi)); 435105197Ssam 436105197Ssam /* Fix Next Protocol in IPv4/IPv6 header */ 437105197Ssam prot = IPPROTO_IPCOMP; 438105197Ssam m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &prot); 439105197Ssam 440105197Ssam /* Ok now, we can pass to the crypto processing */ 441105197Ssam 442105197Ssam /* Get crypto descriptors */ 443105197Ssam crp = crypto_getreq(1); 444105197Ssam if (crp == NULL) { 445105197Ssam ipcompstat.ipcomps_crypto++; 446105197Ssam DPRINTF(("ipcomp_output: failed to acquire crypto descriptor\n")); 447105197Ssam error = ENOBUFS; 448105197Ssam goto bad; 449105197Ssam } 450105197Ssam crdc = crp->crp_desc; 451105197Ssam 452105197Ssam /* Compression descriptor */ 453105197Ssam crdc->crd_skip = skip + hlen; 454105197Ssam crdc->crd_len = m->m_pkthdr.len - (skip + hlen); 455105197Ssam crdc->crd_flags = CRD_F_COMP; 456105197Ssam crdc->crd_inject = skip + hlen; 457105197Ssam 458105197Ssam /* Compression operation */ 459105197Ssam crdc->crd_alg = ipcompx->type; 460105197Ssam 461105197Ssam /* IPsec-specific opaque crypto info */ 462105197Ssam tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto), 463105197Ssam M_XDATA, M_NOWAIT|M_ZERO); 464105197Ssam if (tc == NULL) { 465105197Ssam ipcompstat.ipcomps_crypto++; 466105197Ssam DPRINTF(("ipcomp_output: failed to allocate tdb_crypto\n")); 467105197Ssam crypto_freereq(crp); 468105197Ssam error = ENOBUFS; 469105197Ssam goto bad; 470105197Ssam } 471105197Ssam 472105197Ssam tc->tc_isr = isr; 473105197Ssam tc->tc_spi = sav->spi; 474105197Ssam tc->tc_dst = sav->sah->saidx.dst; 475105197Ssam tc->tc_proto = sav->sah->saidx.proto; 476105197Ssam tc->tc_skip = skip + hlen; 477105197Ssam 478105197Ssam /* Crypto operation descriptor */ 479105197Ssam crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ 480117058Ssam crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC; 481105197Ssam crp->crp_buf = (caddr_t) m; 482105197Ssam crp->crp_callback = ipcomp_output_cb; 483105197Ssam crp->crp_opaque = (caddr_t) tc; 484105197Ssam crp->crp_sid = sav->tdb_cryptoid; 485105197Ssam 486105197Ssam return crypto_dispatch(crp); 487105197Ssambad: 488105197Ssam if (m) 489105197Ssam m_freem(m); 490105197Ssam return (error); 491105197Ssam} 492105197Ssam 493105197Ssam/* 494105197Ssam * IPComp output callback from the crypto driver. 495105197Ssam */ 496105197Ssamstatic int 497105197Ssamipcomp_output_cb(struct cryptop *crp) 498105197Ssam{ 499105197Ssam struct tdb_crypto *tc; 500105197Ssam struct ipsecrequest *isr; 501105197Ssam struct secasvar *sav; 502105197Ssam struct mbuf *m; 503105197Ssam int s, error, skip, rlen; 504105197Ssam 505105197Ssam tc = (struct tdb_crypto *) crp->crp_opaque; 506105197Ssam KASSERT(tc != NULL, ("ipcomp_output_cb: null opaque data area!")); 507105197Ssam m = (struct mbuf *) crp->crp_buf; 508105197Ssam skip = tc->tc_skip; 509105197Ssam rlen = crp->crp_ilen - skip; 510105197Ssam 511105197Ssam s = splnet(); 512105197Ssam 513105197Ssam isr = tc->tc_isr; 514105197Ssam sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi); 515105197Ssam if (sav == NULL) { 516105197Ssam ipcompstat.ipcomps_notdb++; 517105197Ssam DPRINTF(("ipcomp_output_cb: SA expired while in crypto\n")); 518105197Ssam error = ENOBUFS; /*XXX*/ 519105197Ssam goto bad; 520105197Ssam } 521105197Ssam KASSERT(isr->sav == sav, ("ipcomp_output_cb: SA changed\n")); 522105197Ssam 523105197Ssam /* Check for crypto errors */ 524105197Ssam if (crp->crp_etype) { 525105197Ssam /* Reset session ID */ 526105197Ssam if (sav->tdb_cryptoid != 0) 527105197Ssam sav->tdb_cryptoid = crp->crp_sid; 528105197Ssam 529105197Ssam if (crp->crp_etype == EAGAIN) { 530105197Ssam KEY_FREESAV(&sav); 531105197Ssam splx(s); 532105197Ssam return crypto_dispatch(crp); 533105197Ssam } 534105197Ssam ipcompstat.ipcomps_noxform++; 535105197Ssam DPRINTF(("ipcomp_output_cb: crypto error %d\n", crp->crp_etype)); 536105197Ssam error = crp->crp_etype; 537105197Ssam goto bad; 538105197Ssam } 539105197Ssam /* Shouldn't happen... */ 540105197Ssam if (m == NULL) { 541105197Ssam ipcompstat.ipcomps_crypto++; 542105197Ssam DPRINTF(("ipcomp_output_cb: bogus return buffer from crypto\n")); 543105197Ssam error = EINVAL; 544105197Ssam goto bad; 545105197Ssam } 546105197Ssam ipcompstat.ipcomps_hist[sav->alg_comp]++; 547105197Ssam 548105197Ssam if (rlen > crp->crp_olen) { 549105197Ssam /* Adjust the length in the IP header */ 550105197Ssam switch (sav->sah->saidx.dst.sa.sa_family) { 551105197Ssam#ifdef INET 552105197Ssam case AF_INET: 553105197Ssam mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len); 554105197Ssam break; 555105197Ssam#endif /* INET */ 556105197Ssam#ifdef INET6 557105197Ssam case AF_INET6: 558105197Ssam mtod(m, struct ip6_hdr *)->ip6_plen = 559105197Ssam htons(m->m_pkthdr.len) - sizeof(struct ip6_hdr); 560105197Ssam break; 561105197Ssam#endif /* INET6 */ 562105197Ssam default: 563105197Ssam ipcompstat.ipcomps_nopf++; 564105197Ssam DPRINTF(("ipcomp_output: unknown/unsupported protocol " 565105197Ssam "family %d, IPCA %s/%08lx\n", 566105197Ssam sav->sah->saidx.dst.sa.sa_family, 567105197Ssam ipsec_address(&sav->sah->saidx.dst), 568105197Ssam (u_long) ntohl(sav->spi))); 569105197Ssam error = EPFNOSUPPORT; 570105197Ssam goto bad; 571105197Ssam } 572105197Ssam } else { 573105197Ssam /* compression was useless, we have lost time */ 574105197Ssam /* XXX add statistic */ 575105197Ssam } 576105197Ssam 577105197Ssam /* Release the crypto descriptor */ 578105197Ssam free(tc, M_XDATA); 579105197Ssam crypto_freereq(crp); 580105197Ssam 581105197Ssam /* NB: m is reclaimed by ipsec_process_done. */ 582105197Ssam error = ipsec_process_done(m, isr); 583105197Ssam KEY_FREESAV(&sav); 584105197Ssam splx(s); 585105197Ssam return error; 586105197Ssambad: 587105197Ssam if (sav) 588105197Ssam KEY_FREESAV(&sav); 589105197Ssam splx(s); 590105197Ssam if (m) 591105197Ssam m_freem(m); 592105197Ssam free(tc, M_XDATA); 593105197Ssam crypto_freereq(crp); 594105197Ssam return error; 595105197Ssam} 596105197Ssam 597105197Ssamstatic struct xformsw ipcomp_xformsw = { 598105197Ssam XF_IPCOMP, XFT_COMP, "IPcomp", 599105197Ssam ipcomp_init, ipcomp_zeroize, ipcomp_input, 600105197Ssam ipcomp_output 601105197Ssam}; 602105197Ssam 603105197Ssamstatic void 604105197Ssamipcomp_attach(void) 605105197Ssam{ 606105197Ssam xform_register(&ipcomp_xformsw); 607105197Ssam} 608105197SsamSYSINIT(ipcomp_xform_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipcomp_attach, NULL) 609