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