1 /* $OpenBSD: ip_ipcomp.c,v 1.71 2021/07/08 21:07:19 bluhm 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(fmt, args...) \ 61 do { \ 62 if (encdebug) \ 63 printf("%s: " fmt "\n", __func__, ## args); \ 64 } while (0) 65 #else 66 #define DPRINTF(fmt, args...) \ 67 do { } while (0) 68 #endif 69 70 /* 71 * ipcomp_attach() is called from the transformation code 72 */ 73 int 74 ipcomp_attach(void) 75 { 76 return 0; 77 } 78 79 /* 80 * ipcomp_init() is called when an CPI is being set up. 81 */ 82 int 83 ipcomp_init(struct tdb *tdbp, const struct xformsw *xsp, struct ipsecinit *ii) 84 { 85 const struct comp_algo *tcomp = NULL; 86 struct cryptoini cric; 87 int error; 88 89 switch (ii->ii_compalg) { 90 case SADB_X_CALG_DEFLATE: 91 tcomp = &comp_algo_deflate; 92 break; 93 case SADB_X_CALG_LZS: 94 tcomp = &comp_algo_lzs; 95 break; 96 97 default: 98 DPRINTF("unsupported compression algorithm %d specified", 99 ii->ii_compalg); 100 return EINVAL; 101 } 102 103 tdbp->tdb_compalgxform = tcomp; 104 105 DPRINTF("initialized TDB with ipcomp algorithm %s", tcomp->name); 106 107 tdbp->tdb_xform = xsp; 108 109 /* Initialize crypto session */ 110 memset(&cric, 0, sizeof(cric)); 111 cric.cri_alg = tdbp->tdb_compalgxform->type; 112 113 KERNEL_LOCK(); 114 error = crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0); 115 KERNEL_UNLOCK(); 116 return error; 117 } 118 119 /* 120 * ipcomp_zeroize() used when IPCA is deleted 121 */ 122 int 123 ipcomp_zeroize(struct tdb *tdbp) 124 { 125 int error; 126 127 KERNEL_LOCK(); 128 error = crypto_freesession(tdbp->tdb_cryptoid); 129 KERNEL_UNLOCK(); 130 tdbp->tdb_cryptoid = 0; 131 return error; 132 } 133 134 /* 135 * ipcomp_input() gets called to uncompress an input packet 136 */ 137 int 138 ipcomp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) 139 { 140 const struct comp_algo *ipcompx = tdb->tdb_compalgxform; 141 struct tdb_crypto *tc; 142 int hlen, error; 143 144 struct cryptodesc *crdc = NULL; 145 struct cryptop *crp; 146 147 hlen = IPCOMP_HLENGTH; 148 149 /* Get crypto descriptors */ 150 crp = crypto_getreq(1); 151 if (crp == NULL) { 152 m_freem(m); 153 DPRINTF("failed to acquire crypto descriptors"); 154 ipcompstat_inc(ipcomps_crypto); 155 return ENOBUFS; 156 } 157 /* Get IPsec-specific opaque pointer */ 158 tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO); 159 if (tc == NULL) { 160 m_freem(m); 161 crypto_freereq(crp); 162 DPRINTF("failed to allocate tdb_crypto"); 163 ipcompstat_inc(ipcomps_crypto); 164 return ENOBUFS; 165 } 166 crdc = &crp->crp_desc[0]; 167 168 crdc->crd_skip = skip + hlen; 169 crdc->crd_len = m->m_pkthdr.len - (skip + hlen); 170 crdc->crd_inject = skip; 171 172 /* Decompression operation */ 173 crdc->crd_alg = ipcompx->type; 174 175 /* Crypto operation descriptor */ 176 crp->crp_ilen = m->m_pkthdr.len - (skip + hlen); 177 crp->crp_flags = CRYPTO_F_IMBUF; 178 crp->crp_buf = (caddr_t)m; 179 crp->crp_callback = ipsec_input_cb; 180 crp->crp_sid = tdb->tdb_cryptoid; 181 crp->crp_opaque = (caddr_t)tc; 182 183 /* These are passed as-is to the callback */ 184 tc->tc_skip = skip; 185 tc->tc_protoff = protoff; 186 tc->tc_spi = tdb->tdb_spi; 187 tc->tc_proto = IPPROTO_IPCOMP; 188 tc->tc_rdomain = tdb->tdb_rdomain; 189 tc->tc_dst = tdb->tdb_dst; 190 191 KERNEL_LOCK(); 192 error = crypto_dispatch(crp); 193 KERNEL_UNLOCK(); 194 return error; 195 } 196 197 int 198 ipcomp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen) 199 { 200 int skip, protoff, roff, hlen = IPCOMP_HLENGTH; 201 u_int8_t nproto; 202 u_int64_t ibytes; 203 struct mbuf *m1, *mo; 204 struct ipcomp *ipcomp; 205 caddr_t addr; 206 #ifdef ENCDEBUG 207 char buf[INET6_ADDRSTRLEN]; 208 #endif 209 210 NET_ASSERT_LOCKED(); 211 212 skip = tc->tc_skip; 213 protoff = tc->tc_protoff; 214 215 /* update the counters */ 216 ibytes = m->m_pkthdr.len - (skip + hlen); 217 tdb->tdb_cur_bytes += ibytes; 218 tdb->tdb_ibytes += ibytes; 219 ipcompstat_add(ipcomps_ibytes, ibytes); 220 221 /* Hard expiration */ 222 if ((tdb->tdb_flags & TDBF_BYTES) && 223 (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { 224 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); 225 tdb_delete(tdb); 226 goto baddone; 227 } 228 /* Notify on soft expiration */ 229 if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && 230 (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { 231 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); 232 tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ 233 } 234 235 /* In case it's not done already, adjust the size of the mbuf chain */ 236 m->m_pkthdr.len = clen + hlen + skip; 237 238 if ((m->m_len < skip + hlen) && (m = m_pullup(m, skip + hlen)) == 0) { 239 ipcompstat_inc(ipcomps_hdrops); 240 goto baddone; 241 } 242 243 /* Find the beginning of the IPCOMP header */ 244 m1 = m_getptr(m, skip, &roff); 245 if (m1 == NULL) { 246 DPRINTF("bad mbuf chain, IPCA %s/%08x", 247 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 248 ntohl(tdb->tdb_spi)); 249 ipcompstat_inc(ipcomps_hdrops); 250 goto baddone; 251 } 252 /* Keep the next protocol field */ 253 addr = (caddr_t) mtod(m, struct ip *) + skip; 254 ipcomp = (struct ipcomp *) addr; 255 nproto = ipcomp->ipcomp_nh; 256 257 /* Remove the IPCOMP header from the mbuf */ 258 if (roff == 0) { 259 /* The IPCOMP header is at the beginning of m1 */ 260 m_adj(m1, hlen); 261 /* 262 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj() 263 * has already adjusted the packet header length for us. 264 */ 265 if (m1 != m) 266 m->m_pkthdr.len -= hlen; 267 } else if (roff + hlen >= m1->m_len) { 268 int adjlen; 269 270 if (roff + hlen > m1->m_len) { 271 adjlen = roff + hlen - m1->m_len; 272 273 /* Adjust the next mbuf by the remainder */ 274 m_adj(m1->m_next, adjlen); 275 276 /* 277 * The second mbuf is guaranteed not to have a 278 * pkthdr... 279 */ 280 m->m_pkthdr.len -= adjlen; 281 } 282 /* Now, let's unlink the mbuf chain for a second... */ 283 mo = m1->m_next; 284 m1->m_next = NULL; 285 286 /* ...and trim the end of the first part of the chain...sick */ 287 adjlen = m1->m_len - roff; 288 m_adj(m1, -adjlen); 289 /* 290 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj() 291 * has already adjusted the packet header length for us. 292 */ 293 if (m1 != m) 294 m->m_pkthdr.len -= adjlen; 295 296 /* Finally, let's relink */ 297 m1->m_next = mo; 298 } else { 299 memmove(mtod(m1, u_char *) + roff, 300 mtod(m1, u_char *) + roff + hlen, 301 m1->m_len - (roff + hlen)); 302 m1->m_len -= hlen; 303 m->m_pkthdr.len -= hlen; 304 } 305 306 /* Release the crypto descriptors */ 307 free(tc, M_XDATA, 0); 308 309 /* Restore the Next Protocol field */ 310 m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT); 311 312 /* Back to generic IPsec input processing */ 313 return ipsec_common_input_cb(m, tdb, skip, protoff); 314 315 baddone: 316 m_freem(m); 317 free(tc, M_XDATA, 0); 318 return -1; 319 } 320 321 /* 322 * IPComp output routine, called by ipsp_process_packet() 323 */ 324 int 325 ipcomp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, 326 int protoff) 327 { 328 const struct comp_algo *ipcompx = tdb->tdb_compalgxform; 329 int error, hlen; 330 struct cryptodesc *crdc = NULL; 331 struct cryptop *crp = NULL; 332 struct tdb_crypto *tc; 333 struct mbuf *mi; 334 #ifdef ENCDEBUG 335 char buf[INET6_ADDRSTRLEN]; 336 #endif 337 #if NBPFILTER > 0 338 struct ifnet *encif; 339 340 if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) { 341 encif->if_opackets++; 342 encif->if_obytes += m->m_pkthdr.len; 343 344 if (encif->if_bpf) { 345 struct enchdr hdr; 346 347 memset(&hdr, 0, sizeof(hdr)); 348 349 hdr.af = tdb->tdb_dst.sa.sa_family; 350 hdr.spi = tdb->tdb_spi; 351 352 bpf_mtap_hdr(encif->if_bpf, (char *)&hdr, 353 ENC_HDRLEN, m, BPF_DIRECTION_OUT); 354 } 355 } 356 #endif 357 hlen = IPCOMP_HLENGTH; 358 359 ipcompstat_inc(ipcomps_output); 360 361 switch (tdb->tdb_dst.sa.sa_family) { 362 case AF_INET: 363 /* Check for IPv4 maximum packet size violations */ 364 /* 365 * Since compression is going to reduce the size, no need to 366 * worry 367 */ 368 if (m->m_pkthdr.len + hlen > IP_MAXPACKET) { 369 DPRINTF("packet in IPCA %s/%08x got too big", 370 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 371 ntohl(tdb->tdb_spi)); 372 ipcompstat_inc(ipcomps_toobig); 373 error = EMSGSIZE; 374 goto drop; 375 } 376 break; 377 378 #ifdef INET6 379 case AF_INET6: 380 /* Check for IPv6 maximum packet size violations */ 381 if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) { 382 DPRINTF("packet in IPCA %s/%08x got too big", 383 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 384 ntohl(tdb->tdb_spi)); 385 ipcompstat_inc(ipcomps_toobig); 386 error = EMSGSIZE; 387 goto drop; 388 } 389 break; 390 #endif /* INET6 */ 391 392 default: 393 DPRINTF("unknown/unsupported protocol family %d, IPCA %s/%08x", 394 tdb->tdb_dst.sa.sa_family, 395 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 396 ntohl(tdb->tdb_spi)); 397 ipcompstat_inc(ipcomps_nopf); 398 error = EPFNOSUPPORT; 399 goto drop; 400 } 401 402 /* Update the counters */ 403 404 tdb->tdb_cur_bytes += m->m_pkthdr.len - skip; 405 ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip); 406 407 /* Hard byte expiration */ 408 if ((tdb->tdb_flags & TDBF_BYTES) && 409 (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { 410 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); 411 tdb_delete(tdb); 412 error = EINVAL; 413 goto drop; 414 } 415 /* Soft byte expiration */ 416 if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && 417 (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { 418 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); 419 tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ 420 } 421 /* 422 * Loop through mbuf chain; if we find a readonly mbuf, 423 * copy the packet. 424 */ 425 mi = m; 426 while (mi != NULL && !M_READONLY(mi)) 427 mi = mi->m_next; 428 429 if (mi != NULL) { 430 struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT); 431 432 if (n == NULL) { 433 DPRINTF("bad mbuf chain, IPCA %s/%08x", 434 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 435 ntohl(tdb->tdb_spi)); 436 ipcompstat_inc(ipcomps_hdrops); 437 error = ENOBUFS; 438 goto drop; 439 } 440 441 m_freem(m); 442 m = n; 443 } 444 /* Ok now, we can pass to the crypto processing */ 445 446 /* Get crypto descriptors */ 447 crp = crypto_getreq(1); 448 if (crp == NULL) { 449 DPRINTF("failed to acquire crypto descriptors"); 450 ipcompstat_inc(ipcomps_crypto); 451 error = ENOBUFS; 452 goto drop; 453 } 454 crdc = &crp->crp_desc[0]; 455 456 /* Compression descriptor */ 457 crdc->crd_skip = skip; 458 crdc->crd_len = m->m_pkthdr.len - skip; 459 crdc->crd_flags = CRD_F_COMP; 460 crdc->crd_inject = skip; 461 462 /* Compression operation */ 463 crdc->crd_alg = ipcompx->type; 464 465 /* IPsec-specific opaque crypto info */ 466 tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO); 467 if (tc == NULL) { 468 DPRINTF("failed to allocate tdb_crypto"); 469 ipcompstat_inc(ipcomps_crypto); 470 error = ENOBUFS; 471 goto drop; 472 } 473 474 tc->tc_spi = tdb->tdb_spi; 475 tc->tc_proto = tdb->tdb_sproto; 476 tc->tc_skip = skip; 477 tc->tc_rdomain = tdb->tdb_rdomain; 478 tc->tc_dst = tdb->tdb_dst; 479 480 /* Crypto operation descriptor */ 481 crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ 482 crp->crp_flags = CRYPTO_F_IMBUF; 483 crp->crp_buf = (caddr_t)m; 484 crp->crp_callback = ipsec_output_cb; 485 crp->crp_opaque = (caddr_t)tc; 486 crp->crp_sid = tdb->tdb_cryptoid; 487 488 KERNEL_LOCK(); 489 error = crypto_dispatch(crp); 490 KERNEL_UNLOCK(); 491 return error; 492 493 drop: 494 m_freem(m); 495 crypto_freereq(crp); 496 return error; 497 } 498 499 /* 500 * IPComp output callback. 501 */ 502 int 503 ipcomp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, 504 int ilen, int olen) 505 { 506 struct mbuf *mo; 507 int skip, rlen, roff; 508 u_int16_t cpi; 509 struct ip *ip; 510 #ifdef INET6 511 struct ip6_hdr *ip6; 512 #endif 513 struct ipcomp *ipcomp; 514 #ifdef ENCDEBUG 515 char buf[INET6_ADDRSTRLEN]; 516 #endif 517 518 skip = tc->tc_skip; 519 rlen = ilen - skip; 520 521 /* Check sizes. */ 522 if (rlen <= olen + IPCOMP_HLENGTH) { 523 /* Compression was useless, we have lost time. */ 524 ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */ 525 goto skiphdr; 526 } 527 528 /* Inject IPCOMP header */ 529 mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff); 530 if (mo == NULL) { 531 DPRINTF("ailed to inject IPCOMP header for IPCA %s/%08x", 532 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 533 ntohl(tdb->tdb_spi)); 534 ipcompstat_inc(ipcomps_wrap); 535 goto baddone; 536 } 537 538 /* Initialize the IPCOMP header */ 539 ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff); 540 memset(ipcomp, 0, sizeof(struct ipcomp)); 541 cpi = (u_int16_t) ntohl(tdb->tdb_spi); 542 ipcomp->ipcomp_cpi = htons(cpi); 543 544 /* m_pullup before ? */ 545 switch (tdb->tdb_dst.sa.sa_family) { 546 case AF_INET: 547 ip = mtod(m, struct ip *); 548 ipcomp->ipcomp_nh = ip->ip_p; 549 ip->ip_p = IPPROTO_IPCOMP; 550 break; 551 #ifdef INET6 552 case AF_INET6: 553 ip6 = mtod(m, struct ip6_hdr *); 554 ipcomp->ipcomp_nh = ip6->ip6_nxt; 555 ip6->ip6_nxt = IPPROTO_IPCOMP; 556 break; 557 #endif 558 default: 559 DPRINTF("unsupported protocol family %d, IPCA %s/%08x", 560 tdb->tdb_dst.sa.sa_family, 561 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 562 ntohl(tdb->tdb_spi)); 563 ipcompstat_inc(ipcomps_nopf); 564 goto baddone; 565 } 566 567 skiphdr: 568 /* Release the crypto descriptor. */ 569 free(tc, M_XDATA, 0); 570 571 if (ipsp_process_done(m, tdb)) { 572 ipcompstat_inc(ipcomps_outfail); 573 return -1; 574 } 575 return 0; 576 577 baddone: 578 m_freem(m); 579 free(tc, M_XDATA, 0); 580 return -1; 581 } 582