1 /* $OpenBSD: ip_ipcomp.c,v 1.74 2021/07/27 17:13:03 mvs 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 | CRYPTO_F_MPSAFE | CRYPTO_F_NOQUEUE; 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 error = crypto_dispatch(crp); 192 return error; 193 } 194 195 int 196 ipcomp_input_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, int clen) 197 { 198 int skip, protoff, roff, hlen = IPCOMP_HLENGTH; 199 u_int8_t nproto; 200 u_int64_t ibytes; 201 struct mbuf *m1, *mo; 202 struct ipcomp *ipcomp; 203 caddr_t addr; 204 #ifdef ENCDEBUG 205 char buf[INET6_ADDRSTRLEN]; 206 #endif 207 208 NET_ASSERT_LOCKED(); 209 210 skip = tc->tc_skip; 211 protoff = tc->tc_protoff; 212 213 /* update the counters */ 214 ibytes = m->m_pkthdr.len - (skip + hlen); 215 tdb->tdb_cur_bytes += ibytes; 216 tdb->tdb_ibytes += ibytes; 217 ipcompstat_add(ipcomps_ibytes, ibytes); 218 219 /* Hard expiration */ 220 if ((tdb->tdb_flags & TDBF_BYTES) && 221 (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { 222 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); 223 tdb_delete(tdb); 224 goto baddone; 225 } 226 /* Notify on soft expiration */ 227 if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && 228 (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { 229 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); 230 tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ 231 } 232 233 /* In case it's not done already, adjust the size of the mbuf chain */ 234 m->m_pkthdr.len = clen + hlen + skip; 235 236 if ((m->m_len < skip + hlen) && (m = m_pullup(m, skip + hlen)) == 0) { 237 ipcompstat_inc(ipcomps_hdrops); 238 goto baddone; 239 } 240 241 /* Find the beginning of the IPCOMP header */ 242 m1 = m_getptr(m, skip, &roff); 243 if (m1 == NULL) { 244 DPRINTF("bad mbuf chain, IPCA %s/%08x", 245 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 246 ntohl(tdb->tdb_spi)); 247 ipcompstat_inc(ipcomps_hdrops); 248 goto baddone; 249 } 250 /* Keep the next protocol field */ 251 addr = (caddr_t) mtod(m, struct ip *) + skip; 252 ipcomp = (struct ipcomp *) addr; 253 nproto = ipcomp->ipcomp_nh; 254 255 /* Remove the IPCOMP header from the mbuf */ 256 if (roff == 0) { 257 /* The IPCOMP header is at the beginning of m1 */ 258 m_adj(m1, hlen); 259 /* 260 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj() 261 * has already adjusted the packet header length for us. 262 */ 263 if (m1 != m) 264 m->m_pkthdr.len -= hlen; 265 } else if (roff + hlen >= m1->m_len) { 266 int adjlen; 267 268 if (roff + hlen > m1->m_len) { 269 adjlen = roff + hlen - m1->m_len; 270 271 /* Adjust the next mbuf by the remainder */ 272 m_adj(m1->m_next, adjlen); 273 274 /* 275 * The second mbuf is guaranteed not to have a 276 * pkthdr... 277 */ 278 m->m_pkthdr.len -= adjlen; 279 } 280 /* Now, let's unlink the mbuf chain for a second... */ 281 mo = m1->m_next; 282 m1->m_next = NULL; 283 284 /* ...and trim the end of the first part of the chain...sick */ 285 adjlen = m1->m_len - roff; 286 m_adj(m1, -adjlen); 287 /* 288 * If m1 is the first mbuf, it has set M_PKTHDR and m_adj() 289 * has already adjusted the packet header length for us. 290 */ 291 if (m1 != m) 292 m->m_pkthdr.len -= adjlen; 293 294 /* Finally, let's relink */ 295 m1->m_next = mo; 296 } else { 297 memmove(mtod(m1, u_char *) + roff, 298 mtod(m1, u_char *) + roff + hlen, 299 m1->m_len - (roff + hlen)); 300 m1->m_len -= hlen; 301 m->m_pkthdr.len -= hlen; 302 } 303 304 /* Release the crypto descriptors */ 305 free(tc, M_XDATA, 0); 306 307 /* Restore the Next Protocol field */ 308 m_copyback(m, protoff, sizeof(u_int8_t), &nproto, M_NOWAIT); 309 310 /* Back to generic IPsec input processing */ 311 return ipsec_common_input_cb(m, tdb, skip, protoff); 312 313 baddone: 314 m_freem(m); 315 free(tc, M_XDATA, 0); 316 return -1; 317 } 318 319 /* 320 * IPComp output routine, called by ipsp_process_packet() 321 */ 322 int 323 ipcomp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, 324 int protoff) 325 { 326 const struct comp_algo *ipcompx = tdb->tdb_compalgxform; 327 int error, hlen; 328 struct cryptodesc *crdc = NULL; 329 struct cryptop *crp = NULL; 330 struct tdb_crypto *tc; 331 struct mbuf *mi; 332 #ifdef ENCDEBUG 333 char buf[INET6_ADDRSTRLEN]; 334 #endif 335 #if NBPFILTER > 0 336 struct ifnet *encif; 337 338 if ((encif = enc_getif(0, tdb->tdb_tap)) != NULL) { 339 encif->if_opackets++; 340 encif->if_obytes += m->m_pkthdr.len; 341 342 if (encif->if_bpf) { 343 struct enchdr hdr; 344 345 memset(&hdr, 0, sizeof(hdr)); 346 347 hdr.af = tdb->tdb_dst.sa.sa_family; 348 hdr.spi = tdb->tdb_spi; 349 350 bpf_mtap_hdr(encif->if_bpf, (char *)&hdr, 351 ENC_HDRLEN, m, BPF_DIRECTION_OUT); 352 } 353 } 354 #endif 355 hlen = IPCOMP_HLENGTH; 356 357 ipcompstat_inc(ipcomps_output); 358 359 switch (tdb->tdb_dst.sa.sa_family) { 360 case AF_INET: 361 /* Check for IPv4 maximum packet size violations */ 362 /* 363 * Since compression is going to reduce the size, no need to 364 * worry 365 */ 366 if (m->m_pkthdr.len + hlen > IP_MAXPACKET) { 367 DPRINTF("packet in IPCA %s/%08x got too big", 368 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 369 ntohl(tdb->tdb_spi)); 370 ipcompstat_inc(ipcomps_toobig); 371 error = EMSGSIZE; 372 goto drop; 373 } 374 break; 375 376 #ifdef INET6 377 case AF_INET6: 378 /* Check for IPv6 maximum packet size violations */ 379 if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) { 380 DPRINTF("packet in IPCA %s/%08x got too big", 381 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 382 ntohl(tdb->tdb_spi)); 383 ipcompstat_inc(ipcomps_toobig); 384 error = EMSGSIZE; 385 goto drop; 386 } 387 break; 388 #endif /* INET6 */ 389 390 default: 391 DPRINTF("unknown/unsupported protocol family %d, IPCA %s/%08x", 392 tdb->tdb_dst.sa.sa_family, 393 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 394 ntohl(tdb->tdb_spi)); 395 ipcompstat_inc(ipcomps_nopf); 396 error = EPFNOSUPPORT; 397 goto drop; 398 } 399 400 /* Update the counters */ 401 402 tdb->tdb_cur_bytes += m->m_pkthdr.len - skip; 403 ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip); 404 405 /* Hard byte expiration */ 406 if ((tdb->tdb_flags & TDBF_BYTES) && 407 (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { 408 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); 409 tdb_delete(tdb); 410 error = EINVAL; 411 goto drop; 412 } 413 /* Soft byte expiration */ 414 if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && 415 (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { 416 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); 417 tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ 418 } 419 /* 420 * Loop through mbuf chain; if we find a readonly mbuf, 421 * copy the packet. 422 */ 423 mi = m; 424 while (mi != NULL && !M_READONLY(mi)) 425 mi = mi->m_next; 426 427 if (mi != NULL) { 428 struct mbuf *n = m_dup_pkt(m, 0, M_DONTWAIT); 429 430 if (n == NULL) { 431 DPRINTF("bad mbuf chain, IPCA %s/%08x", 432 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 433 ntohl(tdb->tdb_spi)); 434 ipcompstat_inc(ipcomps_hdrops); 435 error = ENOBUFS; 436 goto drop; 437 } 438 439 m_freem(m); 440 m = n; 441 } 442 /* Ok now, we can pass to the crypto processing */ 443 444 /* Get crypto descriptors */ 445 crp = crypto_getreq(1); 446 if (crp == NULL) { 447 DPRINTF("failed to acquire crypto descriptors"); 448 ipcompstat_inc(ipcomps_crypto); 449 error = ENOBUFS; 450 goto drop; 451 } 452 crdc = &crp->crp_desc[0]; 453 454 /* Compression descriptor */ 455 crdc->crd_skip = skip; 456 crdc->crd_len = m->m_pkthdr.len - skip; 457 crdc->crd_flags = CRD_F_COMP; 458 crdc->crd_inject = skip; 459 460 /* Compression operation */ 461 crdc->crd_alg = ipcompx->type; 462 463 /* IPsec-specific opaque crypto info */ 464 tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO); 465 if (tc == NULL) { 466 DPRINTF("failed to allocate tdb_crypto"); 467 ipcompstat_inc(ipcomps_crypto); 468 error = ENOBUFS; 469 goto drop; 470 } 471 472 tc->tc_spi = tdb->tdb_spi; 473 tc->tc_proto = tdb->tdb_sproto; 474 tc->tc_skip = skip; 475 tc->tc_rdomain = tdb->tdb_rdomain; 476 tc->tc_dst = tdb->tdb_dst; 477 478 /* Crypto operation descriptor */ 479 crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ 480 crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_MPSAFE | CRYPTO_F_NOQUEUE; 481 crp->crp_buf = (caddr_t)m; 482 crp->crp_callback = ipsec_output_cb; 483 crp->crp_opaque = (caddr_t)tc; 484 crp->crp_sid = tdb->tdb_cryptoid; 485 486 error = crypto_dispatch(crp); 487 return error; 488 489 drop: 490 m_freem(m); 491 crypto_freereq(crp); 492 return error; 493 } 494 495 /* 496 * IPComp output callback. 497 */ 498 int 499 ipcomp_output_cb(struct tdb *tdb, struct tdb_crypto *tc, struct mbuf *m, 500 int ilen, int olen) 501 { 502 struct mbuf *mo; 503 int skip, rlen, roff; 504 u_int16_t cpi; 505 struct ip *ip; 506 #ifdef INET6 507 struct ip6_hdr *ip6; 508 #endif 509 struct ipcomp *ipcomp; 510 #ifdef ENCDEBUG 511 char buf[INET6_ADDRSTRLEN]; 512 #endif 513 514 skip = tc->tc_skip; 515 rlen = ilen - skip; 516 517 /* Check sizes. */ 518 if (rlen <= olen + IPCOMP_HLENGTH) { 519 /* Compression was useless, we have lost time. */ 520 ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */ 521 goto skiphdr; 522 } 523 524 /* Inject IPCOMP header */ 525 mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff); 526 if (mo == NULL) { 527 DPRINTF("ailed to inject IPCOMP header for IPCA %s/%08x", 528 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 529 ntohl(tdb->tdb_spi)); 530 ipcompstat_inc(ipcomps_wrap); 531 goto baddone; 532 } 533 534 /* Initialize the IPCOMP header */ 535 ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff); 536 memset(ipcomp, 0, sizeof(struct ipcomp)); 537 cpi = (u_int16_t) ntohl(tdb->tdb_spi); 538 ipcomp->ipcomp_cpi = htons(cpi); 539 540 /* m_pullup before ? */ 541 switch (tdb->tdb_dst.sa.sa_family) { 542 case AF_INET: 543 ip = mtod(m, struct ip *); 544 ipcomp->ipcomp_nh = ip->ip_p; 545 ip->ip_p = IPPROTO_IPCOMP; 546 break; 547 #ifdef INET6 548 case AF_INET6: 549 ip6 = mtod(m, struct ip6_hdr *); 550 ipcomp->ipcomp_nh = ip6->ip6_nxt; 551 ip6->ip6_nxt = IPPROTO_IPCOMP; 552 break; 553 #endif 554 default: 555 DPRINTF("unsupported protocol family %d, IPCA %s/%08x", 556 tdb->tdb_dst.sa.sa_family, 557 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 558 ntohl(tdb->tdb_spi)); 559 ipcompstat_inc(ipcomps_nopf); 560 goto baddone; 561 } 562 563 skiphdr: 564 /* Release the crypto descriptor. */ 565 free(tc, M_XDATA, 0); 566 567 if (ipsp_process_done(m, tdb)) { 568 ipcompstat_inc(ipcomps_outfail); 569 return -1; 570 } 571 return 0; 572 573 baddone: 574 m_freem(m); 575 free(tc, M_XDATA, 0); 576 return -1; 577 } 578