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