1 /* $OpenBSD: ip_ipcomp.c,v 1.67 2019/09/30 01:53:05 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org) 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* IP payload compression protocol (IPComp), see RFC 2393 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/mbuf.h> 35 #include <sys/socket.h> 36 37 #include <net/if.h> 38 #include <net/if_var.h> 39 #include <net/bpf.h> 40 41 #include <netinet/in.h> 42 #include <netinet/ip.h> 43 #include <netinet/ip_var.h> 44 45 #ifdef INET6 46 #include <netinet/ip6.h> 47 #endif /* INET6 */ 48 49 #include <netinet/ip_ipsp.h> 50 #include <netinet/ip_ipcomp.h> 51 #include <net/pfkeyv2.h> 52 #include <net/if_enc.h> 53 54 #include <crypto/cryptodev.h> 55 #include <crypto/xform.h> 56 57 #include "bpfilter.h" 58 59 #ifdef ENCDEBUG 60 #define DPRINTF(x) if (encdebug) printf x 61 #else 62 #define DPRINTF(x) 63 #endif 64 65 /* 66 * ipcomp_attach() is called from the transformation code 67 */ 68 int 69 ipcomp_attach(void) 70 { 71 return 0; 72 } 73 74 /* 75 * ipcomp_init() is called when an CPI is being set up. 76 */ 77 int 78 ipcomp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii) 79 { 80 struct comp_algo *tcomp = NULL; 81 struct cryptoini cric; 82 83 switch (ii->ii_compalg) { 84 case SADB_X_CALG_DEFLATE: 85 tcomp = &comp_algo_deflate; 86 break; 87 case SADB_X_CALG_LZS: 88 tcomp = &comp_algo_lzs; 89 break; 90 91 default: 92 DPRINTF(("%s: unsupported compression algorithm %d specified\n", 93 __func__, ii->ii_compalg)); 94 return EINVAL; 95 } 96 97 tdbp->tdb_compalgxform = tcomp; 98 99 DPRINTF(("%s: initialized TDB with ipcomp algorithm %s\n", __func__, 100 tcomp->name)); 101 102 tdbp->tdb_xform = xsp; 103 104 /* Initialize crypto session */ 105 memset(&cric, 0, sizeof(cric)); 106 cric.cri_alg = tdbp->tdb_compalgxform->type; 107 108 return crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0); 109 } 110 111 /* 112 * ipcomp_zeroize() used when IPCA is deleted 113 */ 114 int 115 ipcomp_zeroize(struct tdb *tdbp) 116 { 117 int err; 118 119 err = crypto_freesession(tdbp->tdb_cryptoid); 120 tdbp->tdb_cryptoid = 0; 121 return err; 122 } 123 124 /* 125 * ipcomp_input() gets called to uncompress an input packet 126 */ 127 int 128 ipcomp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) 129 { 130 struct comp_algo *ipcompx = (struct comp_algo *) tdb->tdb_compalgxform; 131 struct tdb_crypto *tc; 132 int hlen; 133 134 struct cryptodesc *crdc = NULL; 135 struct cryptop *crp; 136 137 hlen = IPCOMP_HLENGTH; 138 139 /* Get crypto descriptors */ 140 crp = crypto_getreq(1); 141 if (crp == NULL) { 142 m_freem(m); 143 DPRINTF(("%s: failed to acquire crypto descriptors\n", __func__)); 144 ipcompstat_inc(ipcomps_crypto); 145 return ENOBUFS; 146 } 147 /* Get IPsec-specific opaque pointer */ 148 tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO); 149 if (tc == NULL) { 150 m_freem(m); 151 crypto_freereq(crp); 152 DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__)); 153 ipcompstat_inc(ipcomps_crypto); 154 return ENOBUFS; 155 } 156 crdc = &crp->crp_desc[0]; 157 158 crdc->crd_skip = skip + hlen; 159 crdc->crd_len = m->m_pkthdr.len - (skip + hlen); 160 crdc->crd_inject = skip; 161 162 /* Decompression operation */ 163 crdc->crd_alg = ipcompx->type; 164 165 /* Crypto operation descriptor */ 166 crp->crp_ilen = m->m_pkthdr.len - (skip + hlen); 167 crp->crp_flags = CRYPTO_F_IMBUF; 168 crp->crp_buf = (caddr_t)m; 169 crp->crp_callback = ipsec_input_cb; 170 crp->crp_sid = tdb->tdb_cryptoid; 171 crp->crp_opaque = (caddr_t)tc; 172 173 /* These are passed as-is to the callback */ 174 tc->tc_skip = skip; 175 tc->tc_protoff = protoff; 176 tc->tc_spi = tdb->tdb_spi; 177 tc->tc_proto = IPPROTO_IPCOMP; 178 tc->tc_rdomain = tdb->tdb_rdomain; 179 tc->tc_dst = tdb->tdb_dst; 180 181 return crypto_dispatch(crp); 182 } 183 184 int 185 ipcomp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen) 186 { 187 int skip, protoff, roff, hlen = IPCOMP_HLENGTH; 188 u_int8_t nproto; 189 u_int64_t ibytes; 190 struct mbuf *m1, *mo; 191 struct ipcomp *ipcomp; 192 caddr_t addr; 193 #ifdef ENCDEBUG 194 char buf[INET6_ADDRSTRLEN]; 195 #endif 196 197 NET_ASSERT_LOCKED(); 198 199 skip = tc->tc_skip; 200 protoff = tc->tc_protoff; 201 202 /* update the counters */ 203 ibytes = m->m_pkthdr.len - (skip + hlen); 204 tdb->tdb_cur_bytes += ibytes; 205 tdb->tdb_ibytes += ibytes; 206 ipcompstat_add(ipcomps_ibytes, ibytes); 207 208 /* Hard expiration */ 209 if ((tdb->tdb_flags & TDBF_BYTES) && 210 (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { 211 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); 212 tdb_delete(tdb); 213 goto baddone; 214 } 215 /* Notify on soft expiration */ 216 if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && 217 (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { 218 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); 219 tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ 220 } 221 222 /* In case it's not done already, adjust the size of the mbuf chain */ 223 m->m_pkthdr.len = clen + hlen + skip; 224 225 if ((m->m_len < skip + hlen) && (m = m_pullup(m, skip + hlen)) == 0) { 226 ipcompstat_inc(ipcomps_hdrops); 227 goto baddone; 228 } 229 230 /* Find the beginning of the IPCOMP header */ 231 m1 = m_getptr(m, skip, &roff); 232 if (m1 == NULL) { 233 DPRINTF(("%s: bad mbuf chain, IPCA %s/%08x\n", __func__, 234 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 235 ntohl(tdb->tdb_spi))); 236 ipcompstat_inc(ipcomps_hdrops); 237 goto baddone; 238 } 239 /* Keep the next protocol field */ 240 addr = (caddr_t) mtod(m, struct ip *) + skip; 241 ipcomp = (struct ipcomp *) addr; 242 nproto = ipcomp->ipcomp_nh; 243 244 /* Remove the IPCOMP header from the mbuf */ 245 if (roff == 0) { 246 /* The IPCOMP header is at the beginning of m1 */ 247 m_adj(m1, hlen); 248 /* 249 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj() 250 * has already adjusted the packet header length for us. 251 */ 252 if (m1 != m) 253 m->m_pkthdr.len -= hlen; 254 } else if (roff + hlen >= m1->m_len) { 255 int adjlen; 256 257 if (roff + hlen > m1->m_len) { 258 adjlen = roff + hlen - m1->m_len; 259 260 /* Adjust the next mbuf by the remainder */ 261 m_adj(m1->m_next, adjlen); 262 263 /* 264 * The second mbuf is guaranteed not to have a 265 * pkthdr... 266 */ 267 m->m_pkthdr.len -= adjlen; 268 } 269 /* Now, let's unlink the mbuf chain for a second... */ 270 mo = m1->m_next; 271 m1->m_next = NULL; 272 273 /* ...and trim the end of the first part of the chain...sick */ 274 adjlen = m1->m_len - roff; 275 m_adj(m1, -adjlen); 276 /* 277 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj() 278 * has already adjusted the packet header length for us. 279 */ 280 if (m1 != m) 281 m->m_pkthdr.len -= adjlen; 282 283 /* Finally, let's relink */ 284 m1->m_next = mo; 285 } else { 286 memmove(mtod(m1, u_char *) + roff, 287 mtod(m1, u_char *) + roff + hlen, 288 m1->m_len - (roff + hlen)); 289 m1->m_len -= hlen; 290 m->m_pkthdr.len -= hlen; 291 } 292 293 /* Release the crypto descriptors */ 294 free(tc, M_XDATA, 0); 295 296 /* Restore the Next Protocol field */ 297 m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT); 298 299 /* Back to generic IPsec input processing */ 300 return ipsec_common_input_cb(m, tdb, skip, protoff); 301 302 baddone: 303 m_freem(m); 304 free(tc, M_XDATA, 0); 305 return -1; 306 } 307 308 /* 309 * IPComp output routine, called by ipsp_process_packet() 310 */ 311 int 312 ipcomp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, 313 int protoff) 314 { 315 struct comp_algo *ipcompx = (struct comp_algo *) tdb->tdb_compalgxform; 316 int error, hlen; 317 struct cryptodesc *crdc = NULL; 318 struct cryptop *crp = NULL; 319 struct tdb_crypto *tc; 320 struct mbuf *mi; 321 #ifdef ENCDEBUG 322 char buf[INET6_ADDRSTRLEN]; 323 #endif 324 #if NBPFILTER > 0 325 struct ifnet *encif; 326 327 if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) { 328 encif->if_opackets++; 329 encif->if_obytes += m->m_pkthdr.len; 330 331 if (encif->if_bpf) { 332 struct enchdr hdr; 333 334 memset(&hdr, 0, sizeof(hdr)); 335 336 hdr.af = tdb->tdb_dst.sa.sa_family; 337 hdr.spi = tdb->tdb_spi; 338 339 bpf_mtap_hdr(encif->if_bpf, (char *)&hdr, 340 ENC_HDRLEN, m, BPF_DIRECTION_OUT); 341 } 342 } 343 #endif 344 hlen = IPCOMP_HLENGTH; 345 346 ipcompstat_inc(ipcomps_output); 347 348 switch (tdb->tdb_dst.sa.sa_family) { 349 case AF_INET: 350 /* Check for IPv4 maximum packet size violations */ 351 /* 352 * Since compression is going to reduce the size, no need to 353 * worry 354 */ 355 if (m->m_pkthdr.len + hlen > IP_MAXPACKET) { 356 DPRINTF(("%s: packet in IPCA %s/%08x got too big\n", 357 __func__, ipsp_address(&tdb->tdb_dst, buf, 358 sizeof(buf)), ntohl(tdb->tdb_spi))); 359 ipcompstat_inc(ipcomps_toobig); 360 error = EMSGSIZE; 361 goto drop; 362 } 363 break; 364 365 #ifdef INET6 366 case AF_INET6: 367 /* Check for IPv6 maximum packet size violations */ 368 if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) { 369 DPRINTF(("%s: packet in IPCA %s/%08x got too big\n", 370 __func__, ipsp_address(&tdb->tdb_dst, buf, 371 sizeof(buf)), ntohl(tdb->tdb_spi))); 372 ipcompstat_inc(ipcomps_toobig); 373 error = EMSGSIZE; 374 goto drop; 375 } 376 break; 377 #endif /* INET6 */ 378 379 default: 380 DPRINTF(("%s: unknown/unsupported protocol family %d, " 381 "IPCA %s/%08x\n", __func__, tdb->tdb_dst.sa.sa_family, 382 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 383 ntohl(tdb->tdb_spi))); 384 ipcompstat_inc(ipcomps_nopf); 385 error = EPFNOSUPPORT; 386 goto drop; 387 } 388 389 /* Update the counters */ 390 391 tdb->tdb_cur_bytes += m->m_pkthdr.len - skip; 392 ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip); 393 394 /* Hard byte expiration */ 395 if ((tdb->tdb_flags & TDBF_BYTES) && 396 (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { 397 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); 398 tdb_delete(tdb); 399 error = EINVAL; 400 goto drop; 401 } 402 /* Soft byte expiration */ 403 if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && 404 (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { 405 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); 406 tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ 407 } 408 /* 409 * Loop through mbuf chain; if we find a readonly mbuf, 410 * copy the packet. 411 */ 412 mi = m; 413 while (mi != NULL && !M_READONLY(mi)) 414 mi = mi->m_next; 415 416 if (mi != NULL) { 417 struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT); 418 419 if (n == NULL) { 420 DPRINTF(("%s: bad mbuf chain, IPCA %s/%08x\n", __func__, 421 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 422 ntohl(tdb->tdb_spi))); 423 ipcompstat_inc(ipcomps_hdrops); 424 error = ENOBUFS; 425 goto drop; 426 } 427 428 m_freem(m); 429 m = n; 430 } 431 /* Ok now, we can pass to the crypto processing */ 432 433 /* Get crypto descriptors */ 434 crp = crypto_getreq(1); 435 if (crp == NULL) { 436 DPRINTF(("%s: failed to acquire crypto descriptors\n", __func__)); 437 ipcompstat_inc(ipcomps_crypto); 438 error = ENOBUFS; 439 goto drop; 440 } 441 crdc = &crp->crp_desc[0]; 442 443 /* Compression descriptor */ 444 crdc->crd_skip = skip; 445 crdc->crd_len = m->m_pkthdr.len - skip; 446 crdc->crd_flags = CRD_F_COMP; 447 crdc->crd_inject = skip; 448 449 /* Compression operation */ 450 crdc->crd_alg = ipcompx->type; 451 452 /* IPsec-specific opaque crypto info */ 453 tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO); 454 if (tc == NULL) { 455 DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__)); 456 ipcompstat_inc(ipcomps_crypto); 457 error = ENOBUFS; 458 goto drop; 459 } 460 461 tc->tc_spi = tdb->tdb_spi; 462 tc->tc_proto = tdb->tdb_sproto; 463 tc->tc_skip = skip; 464 tc->tc_rdomain = tdb->tdb_rdomain; 465 tc->tc_dst = tdb->tdb_dst; 466 467 /* Crypto operation descriptor */ 468 crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ 469 crp->crp_flags = CRYPTO_F_IMBUF; 470 crp->crp_buf = (caddr_t)m; 471 crp->crp_callback = ipsec_output_cb; 472 crp->crp_opaque = (caddr_t)tc; 473 crp->crp_sid = tdb->tdb_cryptoid; 474 475 return crypto_dispatch(crp); 476 477 drop: 478 m_freem(m); 479 crypto_freereq(crp); 480 return error; 481 } 482 483 /* 484 * IPComp output callback. 485 */ 486 int 487 ipcomp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, 488 int ilen, int olen) 489 { 490 struct mbuf *mo; 491 int skip, rlen, roff; 492 u_int16_t cpi; 493 struct ip *ip; 494 #ifdef INET6 495 struct ip6_hdr *ip6; 496 #endif 497 struct ipcomp *ipcomp; 498 #ifdef ENCDEBUG 499 char buf[INET6_ADDRSTRLEN]; 500 #endif 501 502 skip = tc->tc_skip; 503 rlen = ilen - skip; 504 505 /* Check sizes. */ 506 if (rlen <= olen + IPCOMP_HLENGTH) { 507 /* Compression was useless, we have lost time. */ 508 ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */ 509 goto skiphdr; 510 } 511 512 /* Inject IPCOMP header */ 513 mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff); 514 if (mo == NULL) { 515 DPRINTF(("%s: failed to inject IPCOMP header for " 516 "IPCA %s/%08x\n", __func__, ipsp_address(&tdb->tdb_dst, buf, 517 sizeof(buf)), ntohl(tdb->tdb_spi))); 518 ipcompstat_inc(ipcomps_wrap); 519 goto baddone; 520 } 521 522 /* Initialize the IPCOMP header */ 523 ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff); 524 memset(ipcomp, 0, sizeof(struct ipcomp)); 525 cpi = (u_int16_t) ntohl(tdb->tdb_spi); 526 ipcomp->ipcomp_cpi = htons(cpi); 527 528 /* m_pullup before ? */ 529 switch (tdb->tdb_dst.sa.sa_family) { 530 case AF_INET: 531 ip = mtod(m, struct ip *); 532 ipcomp->ipcomp_nh = ip->ip_p; 533 ip->ip_p = IPPROTO_IPCOMP; 534 break; 535 #ifdef INET6 536 case AF_INET6: 537 ip6 = mtod(m, struct ip6_hdr *); 538 ipcomp->ipcomp_nh = ip6->ip6_nxt; 539 ip6->ip6_nxt = IPPROTO_IPCOMP; 540 break; 541 #endif 542 default: 543 DPRINTF(("%s: unsupported protocol family %d, IPCA %s/%08x\n", 544 __func__, tdb->tdb_dst.sa.sa_family, 545 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 546 ntohl(tdb->tdb_spi))); 547 ipcompstat_inc(ipcomps_nopf); 548 goto baddone; 549 } 550 551 skiphdr: 552 /* Release the crypto descriptor. */ 553 free(tc, M_XDATA, 0); 554 555 if (ipsp_process_done(m, tdb)) { 556 ipcompstat_inc(ipcomps_outfail); 557 return -1; 558 } 559 return 0; 560 561 baddone: 562 m_freem(m); 563 free(tc, M_XDATA, 0); 564 return -1; 565 } 566