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