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