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