1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2006 Alexander Motin <mav@alkar.net> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 /* 33 * Deflate PPP compression netgraph node type. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/mbuf.h> 40 #include <sys/malloc.h> 41 #include <sys/endian.h> 42 #include <sys/errno.h> 43 #include <sys/syslog.h> 44 #include <sys/zlib.h> 45 46 #include <netgraph/ng_message.h> 47 #include <netgraph/netgraph.h> 48 #include <netgraph/ng_parse.h> 49 #include <netgraph/ng_deflate.h> 50 51 #include "opt_netgraph.h" 52 53 static MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate", 54 "netgraph deflate node"); 55 56 /* DEFLATE header length */ 57 #define DEFLATE_HDRLEN 2 58 59 #define PROT_COMPD 0x00fd 60 61 #define DEFLATE_BUF_SIZE 4096 62 63 /* Node private data */ 64 struct ng_deflate_private { 65 struct ng_deflate_config cfg; /* configuration */ 66 u_char inbuf[DEFLATE_BUF_SIZE]; /* input buffer */ 67 u_char outbuf[DEFLATE_BUF_SIZE]; /* output buffer */ 68 z_stream cx; /* compression context */ 69 struct ng_deflate_stats stats; /* statistics */ 70 ng_ID_t ctrlnode; /* path to controlling node */ 71 uint16_t seqnum; /* sequence number */ 72 u_char compress; /* compress/decompress flag */ 73 }; 74 typedef struct ng_deflate_private *priv_p; 75 76 /* Netgraph node methods */ 77 static ng_constructor_t ng_deflate_constructor; 78 static ng_rcvmsg_t ng_deflate_rcvmsg; 79 static ng_shutdown_t ng_deflate_shutdown; 80 static ng_newhook_t ng_deflate_newhook; 81 static ng_rcvdata_t ng_deflate_rcvdata; 82 static ng_disconnect_t ng_deflate_disconnect; 83 84 /* Helper functions */ 85 static void *z_alloc(void *, u_int items, u_int size); 86 static void z_free(void *, void *ptr); 87 static int ng_deflate_compress(node_p node, 88 struct mbuf *m, struct mbuf **resultp); 89 static int ng_deflate_decompress(node_p node, 90 struct mbuf *m, struct mbuf **resultp); 91 static void ng_deflate_reset_req(node_p node); 92 93 /* Parse type for struct ng_deflate_config. */ 94 static const struct ng_parse_struct_field ng_deflate_config_type_fields[] 95 = NG_DEFLATE_CONFIG_INFO; 96 static const struct ng_parse_type ng_deflate_config_type = { 97 &ng_parse_struct_type, 98 ng_deflate_config_type_fields 99 }; 100 101 /* Parse type for struct ng_deflate_stat. */ 102 static const struct ng_parse_struct_field ng_deflate_stats_type_fields[] 103 = NG_DEFLATE_STATS_INFO; 104 static const struct ng_parse_type ng_deflate_stat_type = { 105 &ng_parse_struct_type, 106 ng_deflate_stats_type_fields 107 }; 108 109 /* List of commands and how to convert arguments to/from ASCII. */ 110 static const struct ng_cmdlist ng_deflate_cmds[] = { 111 { 112 NGM_DEFLATE_COOKIE, 113 NGM_DEFLATE_CONFIG, 114 "config", 115 &ng_deflate_config_type, 116 NULL 117 }, 118 { 119 NGM_DEFLATE_COOKIE, 120 NGM_DEFLATE_RESETREQ, 121 "resetreq", 122 NULL, 123 NULL 124 }, 125 { 126 NGM_DEFLATE_COOKIE, 127 NGM_DEFLATE_GET_STATS, 128 "getstats", 129 NULL, 130 &ng_deflate_stat_type 131 }, 132 { 133 NGM_DEFLATE_COOKIE, 134 NGM_DEFLATE_CLR_STATS, 135 "clrstats", 136 NULL, 137 NULL 138 }, 139 { 140 NGM_DEFLATE_COOKIE, 141 NGM_DEFLATE_GETCLR_STATS, 142 "getclrstats", 143 NULL, 144 &ng_deflate_stat_type 145 }, 146 { 0 } 147 }; 148 149 /* Node type descriptor */ 150 static struct ng_type ng_deflate_typestruct = { 151 .version = NG_ABI_VERSION, 152 .name = NG_DEFLATE_NODE_TYPE, 153 .constructor = ng_deflate_constructor, 154 .rcvmsg = ng_deflate_rcvmsg, 155 .shutdown = ng_deflate_shutdown, 156 .newhook = ng_deflate_newhook, 157 .rcvdata = ng_deflate_rcvdata, 158 .disconnect = ng_deflate_disconnect, 159 .cmdlist = ng_deflate_cmds, 160 }; 161 NETGRAPH_INIT(deflate, &ng_deflate_typestruct); 162 163 /* Depend on separate zlib module. */ 164 MODULE_DEPEND(ng_deflate, zlib, 1, 1, 1); 165 166 #define ERROUT(x) do { error = (x); goto done; } while (0) 167 168 /************************************************************************ 169 NETGRAPH NODE STUFF 170 ************************************************************************/ 171 172 /* 173 * Node type constructor 174 */ 175 static int 176 ng_deflate_constructor(node_p node) 177 { 178 priv_p priv; 179 180 /* Allocate private structure. */ 181 priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO); 182 183 NG_NODE_SET_PRIVATE(node, priv); 184 185 /* This node is not thread safe. */ 186 NG_NODE_FORCE_WRITER(node); 187 188 /* Done */ 189 return (0); 190 } 191 192 /* 193 * Give our OK for a hook to be added. 194 */ 195 static int 196 ng_deflate_newhook(node_p node, hook_p hook, const char *name) 197 { 198 const priv_p priv = NG_NODE_PRIVATE(node); 199 200 if (NG_NODE_NUMHOOKS(node) > 0) 201 return (EINVAL); 202 203 if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0) 204 priv->compress = 1; 205 else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0) 206 priv->compress = 0; 207 else 208 return (EINVAL); 209 210 return (0); 211 } 212 213 /* 214 * Receive a control message 215 */ 216 static int 217 ng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook) 218 { 219 const priv_p priv = NG_NODE_PRIVATE(node); 220 struct ng_mesg *resp = NULL; 221 int error = 0; 222 struct ng_mesg *msg; 223 224 NGI_GET_MSG(item, msg); 225 226 if (msg->header.typecookie != NGM_DEFLATE_COOKIE) 227 ERROUT(EINVAL); 228 229 switch (msg->header.cmd) { 230 case NGM_DEFLATE_CONFIG: 231 { 232 struct ng_deflate_config *const cfg 233 = (struct ng_deflate_config *)msg->data; 234 235 /* Check configuration. */ 236 if (msg->header.arglen != sizeof(*cfg)) 237 ERROUT(EINVAL); 238 if (cfg->enable) { 239 if (cfg->windowBits < 8 || cfg->windowBits > 15) 240 ERROUT(EINVAL); 241 } else 242 cfg->windowBits = 0; 243 244 /* Clear previous state. */ 245 if (priv->cfg.enable) { 246 if (priv->compress) 247 deflateEnd(&priv->cx); 248 else 249 inflateEnd(&priv->cx); 250 priv->cfg.enable = 0; 251 } 252 253 /* Configuration is OK, reset to it. */ 254 priv->cfg = *cfg; 255 256 if (priv->cfg.enable) { 257 priv->cx.next_in = NULL; 258 priv->cx.zalloc = z_alloc; 259 priv->cx.zfree = z_free; 260 int res; 261 if (priv->compress) { 262 if ((res = deflateInit2(&priv->cx, 263 Z_DEFAULT_COMPRESSION, Z_DEFLATED, 264 -cfg->windowBits, 8, 265 Z_DEFAULT_STRATEGY)) != Z_OK) { 266 log(LOG_NOTICE, 267 "deflateInit2: error %d, %s\n", 268 res, priv->cx.msg); 269 priv->cfg.enable = 0; 270 ERROUT(ENOMEM); 271 } 272 } else { 273 if ((res = inflateInit2(&priv->cx, 274 -cfg->windowBits)) != Z_OK) { 275 log(LOG_NOTICE, 276 "inflateInit2: error %d, %s\n", 277 res, priv->cx.msg); 278 priv->cfg.enable = 0; 279 ERROUT(ENOMEM); 280 } 281 } 282 } 283 284 /* Initialize other state. */ 285 priv->seqnum = 0; 286 287 /* Save return address so we can send reset-req's */ 288 priv->ctrlnode = NGI_RETADDR(item); 289 break; 290 } 291 292 case NGM_DEFLATE_RESETREQ: 293 ng_deflate_reset_req(node); 294 break; 295 296 case NGM_DEFLATE_GET_STATS: 297 case NGM_DEFLATE_CLR_STATS: 298 case NGM_DEFLATE_GETCLR_STATS: 299 /* Create response if requested. */ 300 if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) { 301 NG_MKRESPONSE(resp, msg, 302 sizeof(struct ng_deflate_stats), M_NOWAIT); 303 if (resp == NULL) 304 ERROUT(ENOMEM); 305 bcopy(&priv->stats, resp->data, 306 sizeof(struct ng_deflate_stats)); 307 } 308 309 /* Clear stats if requested. */ 310 if (msg->header.cmd != NGM_DEFLATE_GET_STATS) 311 bzero(&priv->stats, 312 sizeof(struct ng_deflate_stats)); 313 break; 314 315 default: 316 error = EINVAL; 317 break; 318 } 319 done: 320 NG_RESPOND_MSG(error, node, item, resp); 321 NG_FREE_MSG(msg); 322 return (error); 323 } 324 325 /* 326 * Receive incoming data on our hook. 327 */ 328 static int 329 ng_deflate_rcvdata(hook_p hook, item_p item) 330 { 331 const node_p node = NG_HOOK_NODE(hook); 332 const priv_p priv = NG_NODE_PRIVATE(node); 333 struct mbuf *m, *out; 334 int error; 335 336 if (!priv->cfg.enable) { 337 NG_FREE_ITEM(item); 338 return (ENXIO); 339 } 340 341 NGI_GET_M(item, m); 342 /* Compress */ 343 if (priv->compress) { 344 if ((error = ng_deflate_compress(node, m, &out)) != 0) { 345 NG_FREE_ITEM(item); 346 log(LOG_NOTICE, "%s: error: %d\n", __func__, error); 347 return (error); 348 } 349 350 } else { /* Decompress */ 351 if ((error = ng_deflate_decompress(node, m, &out)) != 0) { 352 NG_FREE_ITEM(item); 353 log(LOG_NOTICE, "%s: error: %d\n", __func__, error); 354 if (priv->ctrlnode != 0) { 355 struct ng_mesg *msg; 356 357 /* Need to send a reset-request. */ 358 NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE, 359 NGM_DEFLATE_RESETREQ, 0, M_NOWAIT); 360 if (msg == NULL) 361 return (error); 362 NG_SEND_MSG_ID(error, node, msg, 363 priv->ctrlnode, 0); 364 } 365 return (error); 366 } 367 } 368 369 NG_FWD_NEW_DATA(error, item, hook, out); 370 return (error); 371 } 372 373 /* 374 * Destroy node. 375 */ 376 static int 377 ng_deflate_shutdown(node_p node) 378 { 379 const priv_p priv = NG_NODE_PRIVATE(node); 380 381 /* Take down netgraph node. */ 382 if (priv->cfg.enable) { 383 if (priv->compress) 384 deflateEnd(&priv->cx); 385 else 386 inflateEnd(&priv->cx); 387 } 388 389 free(priv, M_NETGRAPH_DEFLATE); 390 NG_NODE_SET_PRIVATE(node, NULL); 391 NG_NODE_UNREF(node); /* let the node escape */ 392 return (0); 393 } 394 395 /* 396 * Hook disconnection 397 */ 398 static int 399 ng_deflate_disconnect(hook_p hook) 400 { 401 const node_p node = NG_HOOK_NODE(hook); 402 const priv_p priv = NG_NODE_PRIVATE(node); 403 404 if (priv->cfg.enable) { 405 if (priv->compress) 406 deflateEnd(&priv->cx); 407 else 408 inflateEnd(&priv->cx); 409 priv->cfg.enable = 0; 410 } 411 412 /* Go away if no longer connected. */ 413 if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node)) 414 ng_rmnode_self(node); 415 return (0); 416 } 417 418 /************************************************************************ 419 HELPER STUFF 420 ************************************************************************/ 421 422 /* 423 * Space allocation and freeing routines for use by zlib routines. 424 */ 425 426 static void * 427 z_alloc(void *notused, u_int items, u_int size) 428 { 429 430 return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT)); 431 } 432 433 static void 434 z_free(void *notused, void *ptr) 435 { 436 437 free(ptr, M_NETGRAPH_DEFLATE); 438 } 439 440 /* 441 * Compress/encrypt a packet and put the result in a new mbuf at *resultp. 442 * The original mbuf is not free'd. 443 */ 444 static int 445 ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp) 446 { 447 const priv_p priv = NG_NODE_PRIVATE(node); 448 int outlen, inlen; 449 int rtn; 450 451 /* Initialize. */ 452 *resultp = NULL; 453 454 inlen = m->m_pkthdr.len; 455 456 priv->stats.FramesPlain++; 457 priv->stats.InOctets+=inlen; 458 459 if (inlen > DEFLATE_BUF_SIZE) { 460 priv->stats.Errors++; 461 NG_FREE_M(m); 462 return (ENOMEM); 463 } 464 465 /* We must own the mbuf chain exclusively to modify it. */ 466 m = m_unshare(m, M_NOWAIT); 467 if (m == NULL) { 468 priv->stats.Errors++; 469 return (ENOMEM); 470 } 471 472 /* Work with contiguous regions of memory. */ 473 m_copydata(m, 0, inlen, (caddr_t)priv->inbuf); 474 outlen = DEFLATE_BUF_SIZE; 475 476 /* Compress "inbuf" into "outbuf". */ 477 /* Prepare to compress. */ 478 if (priv->inbuf[0] != 0) { 479 priv->cx.next_in = priv->inbuf; 480 priv->cx.avail_in = inlen; 481 } else { 482 priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ 483 priv->cx.avail_in = inlen - 1; 484 } 485 priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN; 486 priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN; 487 488 /* Compress. */ 489 rtn = deflate(&priv->cx, Z_PACKET_FLUSH); 490 491 /* Check return value. */ 492 if (rtn != Z_OK) { 493 priv->stats.Errors++; 494 log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n", 495 rtn, priv->cx.msg); 496 NG_FREE_M(m); 497 return (EINVAL); 498 } 499 500 /* Calculate resulting size. */ 501 outlen -= priv->cx.avail_out; 502 503 /* If we can't compress this packet, send it as-is. */ 504 if (outlen > inlen) { 505 /* Return original packet uncompressed. */ 506 *resultp = m; 507 priv->stats.FramesUncomp++; 508 priv->stats.OutOctets+=inlen; 509 } else { 510 /* Install header. */ 511 be16enc(priv->outbuf, PROT_COMPD); 512 be16enc(priv->outbuf + 2, priv->seqnum); 513 514 /* Return packet in an mbuf. */ 515 m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); 516 if (m->m_pkthdr.len < outlen) { 517 m_freem(m); 518 priv->stats.Errors++; 519 return (ENOMEM); 520 } else if (outlen < m->m_pkthdr.len) 521 m_adj(m, outlen - m->m_pkthdr.len); 522 *resultp = m; 523 priv->stats.FramesComp++; 524 priv->stats.OutOctets+=outlen; 525 } 526 527 /* Update sequence number. */ 528 priv->seqnum++; 529 530 return (0); 531 } 532 533 /* 534 * Decompress/decrypt packet and put the result in a new mbuf at *resultp. 535 * The original mbuf is not free'd. 536 */ 537 static int 538 ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp) 539 { 540 const priv_p priv = NG_NODE_PRIVATE(node); 541 int outlen, inlen; 542 int rtn; 543 uint16_t proto; 544 int offset; 545 uint16_t rseqnum; 546 547 /* Initialize. */ 548 *resultp = NULL; 549 550 inlen = m->m_pkthdr.len; 551 552 if (inlen > DEFLATE_BUF_SIZE) { 553 priv->stats.Errors++; 554 NG_FREE_M(m); 555 priv->seqnum = 0; 556 return (ENOMEM); 557 } 558 559 /* We must own the mbuf chain exclusively to modify it. */ 560 m = m_unshare(m, M_NOWAIT); 561 if (m == NULL) { 562 priv->stats.Errors++; 563 return (ENOMEM); 564 } 565 566 /* Work with contiguous regions of memory. */ 567 m_copydata(m, 0, inlen, (caddr_t)priv->inbuf); 568 569 /* Separate proto. */ 570 if ((priv->inbuf[0] & 0x01) != 0) { 571 proto = priv->inbuf[0]; 572 offset = 1; 573 } else { 574 proto = be16dec(priv->inbuf); 575 offset = 2; 576 } 577 578 priv->stats.InOctets += inlen; 579 580 /* Packet is compressed, so decompress. */ 581 if (proto == PROT_COMPD) { 582 priv->stats.FramesComp++; 583 584 /* Check sequence number. */ 585 rseqnum = be16dec(priv->inbuf + offset); 586 offset += 2; 587 if (rseqnum != priv->seqnum) { 588 priv->stats.Errors++; 589 log(LOG_NOTICE, "ng_deflate: wrong sequence: %u " 590 "instead of %u\n", rseqnum, priv->seqnum); 591 NG_FREE_M(m); 592 priv->seqnum = 0; 593 return (EPIPE); 594 } 595 596 outlen = DEFLATE_BUF_SIZE; 597 598 /* Decompress "inbuf" into "outbuf". */ 599 /* Prepare to decompress. */ 600 priv->cx.next_in = priv->inbuf + offset; 601 priv->cx.avail_in = inlen - offset; 602 /* Reserve space for protocol decompression. */ 603 priv->cx.next_out = priv->outbuf + 1; 604 priv->cx.avail_out = outlen - 1; 605 606 /* Decompress. */ 607 rtn = inflate(&priv->cx, Z_PACKET_FLUSH); 608 609 /* Check return value. */ 610 if (rtn != Z_OK && rtn != Z_STREAM_END) { 611 priv->stats.Errors++; 612 NG_FREE_M(m); 613 priv->seqnum = 0; 614 log(LOG_NOTICE, "%s: decompression error: %d (%s)\n", 615 __func__, rtn, priv->cx.msg); 616 617 switch (rtn) { 618 case Z_MEM_ERROR: 619 return (ENOMEM); 620 case Z_DATA_ERROR: 621 return (EIO); 622 default: 623 return (EINVAL); 624 } 625 } 626 627 /* Calculate resulting size. */ 628 outlen -= priv->cx.avail_out; 629 630 /* Decompress protocol. */ 631 if ((priv->outbuf[1] & 0x01) != 0) { 632 priv->outbuf[0] = 0; 633 /* Return packet in an mbuf. */ 634 m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); 635 } else { 636 outlen--; 637 /* Return packet in an mbuf. */ 638 m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1)); 639 } 640 if (m->m_pkthdr.len < outlen) { 641 m_freem(m); 642 priv->stats.Errors++; 643 priv->seqnum = 0; 644 return (ENOMEM); 645 } else if (outlen < m->m_pkthdr.len) 646 m_adj(m, outlen - m->m_pkthdr.len); 647 *resultp = m; 648 priv->stats.FramesPlain++; 649 priv->stats.OutOctets+=outlen; 650 651 } else { /* Packet is not compressed, just update dictionary. */ 652 priv->stats.FramesUncomp++; 653 if (priv->inbuf[0] == 0) { 654 priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ 655 priv->cx.avail_in = inlen - 1; 656 } else { 657 priv->cx.next_in = priv->inbuf; 658 priv->cx.avail_in = inlen; 659 } 660 661 rtn = inflateIncomp(&priv->cx); 662 663 /* Check return value */ 664 if (rtn != Z_OK) { 665 priv->stats.Errors++; 666 log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n", 667 __func__, rtn, priv->cx.msg); 668 NG_FREE_M(m); 669 priv->seqnum = 0; 670 return (EINVAL); 671 } 672 673 *resultp = m; 674 priv->stats.FramesPlain++; 675 priv->stats.OutOctets += inlen; 676 } 677 678 /* Update sequence number. */ 679 priv->seqnum++; 680 681 return (0); 682 } 683 684 /* 685 * The peer has sent us a CCP ResetRequest, so reset our transmit state. 686 */ 687 static void 688 ng_deflate_reset_req(node_p node) 689 { 690 const priv_p priv = NG_NODE_PRIVATE(node); 691 692 priv->seqnum = 0; 693 if (priv->cfg.enable) { 694 if (priv->compress) 695 deflateReset(&priv->cx); 696 else 697 inflateReset(&priv->cx); 698 } 699 } 700 701