jcs's openbsd hax
openbsd
at jcs 536 lines 14 kB view raw
1/* $OpenBSD: ip_ipcomp.c,v 1.96 2025/12/11 05:06:02 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 44#ifdef INET6 45#include <netinet/ip6.h> 46#endif /* INET6 */ 47 48#include <netinet/ip_ipsp.h> 49#include <netinet/ip_ipcomp.h> 50#include <net/pfkeyv2.h> 51#include <net/if_enc.h> 52 53#include <crypto/cryptodev.h> 54#include <crypto/xform.h> 55 56#include "bpfilter.h" 57 58#ifdef ENCDEBUG 59#define DPRINTF(fmt, args...) \ 60 do { \ 61 if (atomic_load_int(&encdebug)) \ 62 printf("%s: " fmt "\n", __func__, ## args); \ 63 } while (0) 64#else 65#define DPRINTF(fmt, args...) \ 66 do { } while (0) 67#endif 68 69/* 70 * ipcomp_attach() is called from the transformation code 71 */ 72int 73ipcomp_attach(void) 74{ 75 return 0; 76} 77 78/* 79 * ipcomp_init() is called when an CPI is being set up. 80 */ 81int 82ipcomp_init(struct tdb *tdbp, const struct xformsw *xsp, struct ipsecinit *ii) 83{ 84 const struct comp_algo *tcomp = NULL; 85 struct cryptoini cric; 86 int error; 87 88 switch (ii->ii_compalg) { 89 case SADB_X_CALG_DEFLATE: 90 tcomp = &comp_algo_deflate; 91 break; 92 default: 93 DPRINTF("unsupported compression algorithm %d specified", 94 ii->ii_compalg); 95 return EINVAL; 96 } 97 98 tdbp->tdb_compalgxform = tcomp; 99 100 DPRINTF("initialized TDB with ipcomp algorithm %s", 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 KERNEL_LOCK(); 109 error = crypto_newsession(&tdbp->tdb_cryptoid, &cric, 0); 110 KERNEL_UNLOCK(); 111 return error; 112} 113 114/* 115 * ipcomp_zeroize() used when IPCA is deleted 116 */ 117int 118ipcomp_zeroize(struct tdb *tdbp) 119{ 120 int error; 121 122 KERNEL_LOCK(); 123 error = crypto_freesession(tdbp->tdb_cryptoid); 124 KERNEL_UNLOCK(); 125 tdbp->tdb_cryptoid = 0; 126 return error; 127} 128 129/* 130 * ipcomp_input() gets called to uncompress an input packet 131 */ 132int 133ipcomp_input(struct mbuf **mp, struct tdb *tdb, int skip, int protoff, 134 struct netstack *ns) 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, ns); 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 */ 303int 304ipcomp_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 .af = htonl(tdb->tdb_dst.sa.sa_family), 330 .spi = tdb->tdb_spi, 331 }; 332 333 bpf_mtap_hdr(encif->if_bpf, (char *)&hdr, 334 ENC_HDRLEN, m, BPF_DIRECTION_OUT); 335 } 336 } 337#endif 338 hlen = IPCOMP_HLENGTH; 339 340 ipcompstat_inc(ipcomps_output); 341 342 switch (tdb->tdb_dst.sa.sa_family) { 343 case AF_INET: 344 /* Check for IPv4 maximum packet size violations */ 345 /* 346 * Since compression is going to reduce the size, no need to 347 * worry 348 */ 349 if (m->m_pkthdr.len + hlen > IP_MAXPACKET) { 350 DPRINTF("packet in IPCA %s/%08x got too big", 351 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 352 ntohl(tdb->tdb_spi)); 353 ipcompstat_inc(ipcomps_toobig); 354 error = EMSGSIZE; 355 goto drop; 356 } 357 break; 358 359#ifdef INET6 360 case AF_INET6: 361 /* Check for IPv6 maximum packet size violations */ 362 if (m->m_pkthdr.len + hlen > IPV6_MAXPACKET) { 363 DPRINTF("packet in IPCA %s/%08x got too big", 364 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 365 ntohl(tdb->tdb_spi)); 366 ipcompstat_inc(ipcomps_toobig); 367 error = EMSGSIZE; 368 goto drop; 369 } 370 break; 371#endif /* INET6 */ 372 373 default: 374 DPRINTF("unknown/unsupported protocol family %d, IPCA %s/%08x", 375 tdb->tdb_dst.sa.sa_family, 376 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 377 ntohl(tdb->tdb_spi)); 378 ipcompstat_inc(ipcomps_nopf); 379 error = EPFNOSUPPORT; 380 goto drop; 381 } 382 383 /* Update the counters */ 384 tdb->tdb_cur_bytes += m->m_pkthdr.len - skip; 385 ipcompstat_add(ipcomps_obytes, m->m_pkthdr.len - skip); 386 387 /* Hard byte expiration */ 388 if ((tdb->tdb_flags & TDBF_BYTES) && 389 (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { 390 ipsecstat_inc(ipsec_exctdb); 391 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); 392 tdb_delete(tdb); 393 error = EINVAL; 394 goto drop; 395 } 396 397 /* Soft byte expiration */ 398 mtx_enter(&tdb->tdb_mtx); 399 if ((tdb->tdb_flags & TDBF_SOFT_BYTES) && 400 (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) { 401 tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */ 402 mtx_leave(&tdb->tdb_mtx); 403 /* may sleep in solock() for the pfkey socket */ 404 pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); 405 } else 406 mtx_leave(&tdb->tdb_mtx); 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("bad mbuf chain, IPCA %s/%08x", 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("failed to acquire crypto descriptors"); 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 /* Crypto operation descriptor */ 453 crp->crp_ilen = m->m_pkthdr.len; /* Total input length */ 454 crp->crp_flags = CRYPTO_F_IMBUF; 455 crp->crp_buf = (caddr_t)m; 456 crp->crp_sid = tdb->tdb_cryptoid; 457 458 while ((error = crypto_invoke(crp)) == EAGAIN) { 459 /* Reset the session ID */ 460 if (tdb->tdb_cryptoid != 0) 461 tdb->tdb_cryptoid = crp->crp_sid; 462 } 463 if (error) { 464 DPRINTF("crypto error %d", error); 465 ipsecstat_inc(ipsec_noxform); 466 goto drop; 467 } 468 469 ilen = crp->crp_ilen; 470 olen = crp->crp_olen; 471 472 /* Release the crypto descriptors */ 473 crypto_freereq(crp); 474 crp = NULL; 475 476 rlen = ilen - skip; 477 478 /* Check sizes. */ 479 if (rlen <= olen + IPCOMP_HLENGTH) { 480 /* Compression was useless, we have lost time. */ 481 ipcompstat_inc(ipcomps_minlen); /* misnomer, but like to count */ 482 goto skiphdr; 483 } 484 485 /* Inject IPCOMP header */ 486 mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff); 487 if (mo == NULL) { 488 DPRINTF("ailed to inject IPCOMP header for IPCA %s/%08x", 489 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 490 ntohl(tdb->tdb_spi)); 491 ipcompstat_inc(ipcomps_wrap); 492 error = ENOBUFS; 493 goto drop; 494 } 495 496 /* Initialize the IPCOMP header */ 497 ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff); 498 memset(ipcomp, 0, sizeof(struct ipcomp)); 499 cpi = (u_int16_t) ntohl(tdb->tdb_spi); 500 ipcomp->ipcomp_cpi = htons(cpi); 501 502 /* m_pullup before ? */ 503 switch (tdb->tdb_dst.sa.sa_family) { 504 case AF_INET: 505 ip = mtod(m, struct ip *); 506 ipcomp->ipcomp_nh = ip->ip_p; 507 ip->ip_p = IPPROTO_IPCOMP; 508 break; 509#ifdef INET6 510 case AF_INET6: 511 ip6 = mtod(m, struct ip6_hdr *); 512 ipcomp->ipcomp_nh = ip6->ip6_nxt; 513 ip6->ip6_nxt = IPPROTO_IPCOMP; 514 break; 515#endif 516 default: 517 DPRINTF("unsupported protocol family %d, IPCA %s/%08x", 518 tdb->tdb_dst.sa.sa_family, 519 ipsp_address(&tdb->tdb_dst, buf, sizeof(buf)), 520 ntohl(tdb->tdb_spi)); 521 ipcompstat_inc(ipcomps_nopf); 522 error = EPFNOSUPPORT; 523 goto drop; 524 } 525 526 skiphdr: 527 error = ipsp_process_done(m, tdb); 528 if (error) 529 ipcompstat_inc(ipcomps_outfail); 530 return error; 531 532 drop: 533 m_freem(m); 534 crypto_freereq(crp); 535 return error; 536}