1165581Sglebius/*- 2165581Sglebius * Copyright (c) 2006 Alexander Motin <mav@alkar.net> 3165581Sglebius * All rights reserved. 4165581Sglebius * 5165581Sglebius * Redistribution and use in source and binary forms, with or without 6165581Sglebius * modification, are permitted provided that the following conditions 7165581Sglebius * are met: 8165581Sglebius * 1. Redistributions of source code must retain the above copyright 9165581Sglebius * notice unmodified, this list of conditions, and the following 10165581Sglebius * disclaimer. 11165581Sglebius * 2. Redistributions in binary form must reproduce the above copyright 12165581Sglebius * notice, this list of conditions and the following disclaimer in the 13165581Sglebius * documentation and/or other materials provided with the distribution. 14165581Sglebius * 15165581Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16165581Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17165581Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18165581Sglebius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19165581Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20165581Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21165581Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22165581Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23165581Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24165581Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25165581Sglebius * SUCH DAMAGE. 26165581Sglebius * 27165581Sglebius * $FreeBSD$ 28165581Sglebius */ 29165581Sglebius 30165581Sglebius/* 31165581Sglebius * Deflate PPP compression netgraph node type. 32165581Sglebius */ 33165581Sglebius 34165581Sglebius#include <sys/param.h> 35165581Sglebius#include <sys/systm.h> 36165581Sglebius#include <sys/kernel.h> 37165581Sglebius#include <sys/mbuf.h> 38165581Sglebius#include <sys/malloc.h> 39206049Smav#include <sys/endian.h> 40165581Sglebius#include <sys/errno.h> 41165581Sglebius#include <sys/syslog.h> 42165581Sglebius 43165581Sglebius#include <net/zlib.h> 44165581Sglebius 45165581Sglebius#include <netgraph/ng_message.h> 46165581Sglebius#include <netgraph/netgraph.h> 47165581Sglebius#include <netgraph/ng_parse.h> 48165581Sglebius#include <netgraph/ng_deflate.h> 49165581Sglebius 50165581Sglebius#include "opt_netgraph.h" 51165581Sglebius 52227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate", 53227293Sed "netgraph deflate node"); 54165581Sglebius 55165581Sglebius/* DEFLATE header length */ 56165581Sglebius#define DEFLATE_HDRLEN 2 57165581Sglebius 58165581Sglebius#define PROT_COMPD 0x00fd 59165581Sglebius 60165581Sglebius#define DEFLATE_BUF_SIZE 4096 61165581Sglebius 62165581Sglebius/* Node private data */ 63165581Sglebiusstruct ng_deflate_private { 64165581Sglebius struct ng_deflate_config cfg; /* configuration */ 65165581Sglebius u_char inbuf[DEFLATE_BUF_SIZE]; /* input buffer */ 66165581Sglebius u_char outbuf[DEFLATE_BUF_SIZE]; /* output buffer */ 67165581Sglebius z_stream cx; /* compression context */ 68165581Sglebius struct ng_deflate_stats stats; /* statistics */ 69165581Sglebius ng_ID_t ctrlnode; /* path to controlling node */ 70165581Sglebius uint16_t seqnum; /* sequence number */ 71165581Sglebius u_char compress; /* compress/decompress flag */ 72165581Sglebius}; 73165581Sglebiustypedef struct ng_deflate_private *priv_p; 74165581Sglebius 75165581Sglebius/* Netgraph node methods */ 76165581Sglebiusstatic ng_constructor_t ng_deflate_constructor; 77165581Sglebiusstatic ng_rcvmsg_t ng_deflate_rcvmsg; 78165581Sglebiusstatic ng_shutdown_t ng_deflate_shutdown; 79165581Sglebiusstatic ng_newhook_t ng_deflate_newhook; 80165581Sglebiusstatic ng_rcvdata_t ng_deflate_rcvdata; 81165581Sglebiusstatic ng_disconnect_t ng_deflate_disconnect; 82165581Sglebius 83165581Sglebius/* Helper functions */ 84165581Sglebiusstatic void *z_alloc(void *, u_int items, u_int size); 85165581Sglebiusstatic void z_free(void *, void *ptr); 86165581Sglebiusstatic int ng_deflate_compress(node_p node, 87165581Sglebius struct mbuf *m, struct mbuf **resultp); 88165581Sglebiusstatic int ng_deflate_decompress(node_p node, 89165581Sglebius struct mbuf *m, struct mbuf **resultp); 90165581Sglebiusstatic void ng_deflate_reset_req(node_p node); 91165581Sglebius 92165581Sglebius/* Parse type for struct ng_deflate_config. */ 93165581Sglebiusstatic const struct ng_parse_struct_field ng_deflate_config_type_fields[] 94165581Sglebius = NG_DEFLATE_CONFIG_INFO; 95165581Sglebiusstatic const struct ng_parse_type ng_deflate_config_type = { 96165581Sglebius &ng_parse_struct_type, 97165581Sglebius ng_deflate_config_type_fields 98165581Sglebius}; 99165581Sglebius 100165581Sglebius/* Parse type for struct ng_deflate_stat. */ 101165581Sglebiusstatic const struct ng_parse_struct_field ng_deflate_stats_type_fields[] 102165581Sglebius = NG_DEFLATE_STATS_INFO; 103165581Sglebiusstatic const struct ng_parse_type ng_deflate_stat_type = { 104165581Sglebius &ng_parse_struct_type, 105165581Sglebius ng_deflate_stats_type_fields 106165581Sglebius}; 107165581Sglebius 108165581Sglebius/* List of commands and how to convert arguments to/from ASCII. */ 109165581Sglebiusstatic const struct ng_cmdlist ng_deflate_cmds[] = { 110165581Sglebius { 111165581Sglebius NGM_DEFLATE_COOKIE, 112165581Sglebius NGM_DEFLATE_CONFIG, 113165581Sglebius "config", 114165581Sglebius &ng_deflate_config_type, 115165581Sglebius NULL 116165581Sglebius }, 117165581Sglebius { 118165581Sglebius NGM_DEFLATE_COOKIE, 119165581Sglebius NGM_DEFLATE_RESETREQ, 120165581Sglebius "resetreq", 121165581Sglebius NULL, 122165581Sglebius NULL 123165581Sglebius }, 124165581Sglebius { 125165581Sglebius NGM_DEFLATE_COOKIE, 126165581Sglebius NGM_DEFLATE_GET_STATS, 127165581Sglebius "getstats", 128165581Sglebius NULL, 129165581Sglebius &ng_deflate_stat_type 130165581Sglebius }, 131165581Sglebius { 132165581Sglebius NGM_DEFLATE_COOKIE, 133165581Sglebius NGM_DEFLATE_CLR_STATS, 134165581Sglebius "clrstats", 135165581Sglebius NULL, 136165581Sglebius NULL 137165581Sglebius }, 138165581Sglebius { 139165581Sglebius NGM_DEFLATE_COOKIE, 140165581Sglebius NGM_DEFLATE_GETCLR_STATS, 141165581Sglebius "getclrstats", 142165581Sglebius NULL, 143165581Sglebius &ng_deflate_stat_type 144165581Sglebius }, 145165581Sglebius { 0 } 146165581Sglebius}; 147165581Sglebius 148165581Sglebius/* Node type descriptor */ 149165581Sglebiusstatic struct ng_type ng_deflate_typestruct = { 150165581Sglebius .version = NG_ABI_VERSION, 151165581Sglebius .name = NG_DEFLATE_NODE_TYPE, 152165581Sglebius .constructor = ng_deflate_constructor, 153165581Sglebius .rcvmsg = ng_deflate_rcvmsg, 154165581Sglebius .shutdown = ng_deflate_shutdown, 155165581Sglebius .newhook = ng_deflate_newhook, 156165581Sglebius .rcvdata = ng_deflate_rcvdata, 157165581Sglebius .disconnect = ng_deflate_disconnect, 158165581Sglebius .cmdlist = ng_deflate_cmds, 159165581Sglebius}; 160165581SglebiusNETGRAPH_INIT(deflate, &ng_deflate_typestruct); 161165581Sglebius 162165581Sglebius/* Depend on separate zlib module. */ 163165581SglebiusMODULE_DEPEND(ng_deflate, zlib, 1, 1, 1); 164165581Sglebius 165165581Sglebius#define ERROUT(x) do { error = (x); goto done; } while (0) 166165581Sglebius 167165581Sglebius/************************************************************************ 168165581Sglebius NETGRAPH NODE STUFF 169165581Sglebius ************************************************************************/ 170165581Sglebius 171165581Sglebius/* 172165581Sglebius * Node type constructor 173165581Sglebius */ 174165581Sglebiusstatic int 175165581Sglebiusng_deflate_constructor(node_p node) 176165581Sglebius{ 177165581Sglebius priv_p priv; 178165581Sglebius 179165581Sglebius /* Allocate private structure. */ 180165581Sglebius priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO); 181165581Sglebius 182165581Sglebius NG_NODE_SET_PRIVATE(node, priv); 183165581Sglebius 184165581Sglebius /* This node is not thread safe. */ 185165581Sglebius NG_NODE_FORCE_WRITER(node); 186165581Sglebius 187165581Sglebius /* Done */ 188165581Sglebius return (0); 189165581Sglebius} 190165581Sglebius 191165581Sglebius/* 192165581Sglebius * Give our OK for a hook to be added. 193165581Sglebius */ 194165581Sglebiusstatic int 195165581Sglebiusng_deflate_newhook(node_p node, hook_p hook, const char *name) 196165581Sglebius{ 197165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 198165581Sglebius 199165581Sglebius if (NG_NODE_NUMHOOKS(node) > 0) 200165581Sglebius return (EINVAL); 201165581Sglebius 202165581Sglebius if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0) 203165581Sglebius priv->compress = 1; 204165581Sglebius else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0) 205165581Sglebius priv->compress = 0; 206165581Sglebius else 207165581Sglebius return (EINVAL); 208165581Sglebius 209165581Sglebius return (0); 210165581Sglebius} 211165581Sglebius 212165581Sglebius/* 213165581Sglebius * Receive a control message 214165581Sglebius */ 215165581Sglebiusstatic int 216165581Sglebiusng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook) 217165581Sglebius{ 218165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 219165581Sglebius struct ng_mesg *resp = NULL; 220165581Sglebius int error = 0; 221165581Sglebius struct ng_mesg *msg; 222165581Sglebius 223165581Sglebius NGI_GET_MSG(item, msg); 224165581Sglebius 225165581Sglebius if (msg->header.typecookie != NGM_DEFLATE_COOKIE) 226165581Sglebius ERROUT(EINVAL); 227166019Sglebius 228165581Sglebius switch (msg->header.cmd) { 229165581Sglebius case NGM_DEFLATE_CONFIG: 230165581Sglebius { 231165581Sglebius struct ng_deflate_config *const cfg 232165581Sglebius = (struct ng_deflate_config *)msg->data; 233165581Sglebius 234165581Sglebius /* Check configuration. */ 235165581Sglebius if (msg->header.arglen != sizeof(*cfg)) 236165581Sglebius ERROUT(EINVAL); 237165581Sglebius if (cfg->enable) { 238165581Sglebius if (cfg->windowBits < 8 || cfg->windowBits > 15) 239165581Sglebius ERROUT(EINVAL); 240165581Sglebius } else 241165581Sglebius cfg->windowBits = 0; 242165581Sglebius 243165581Sglebius /* Clear previous state. */ 244165581Sglebius if (priv->cfg.enable) { 245165581Sglebius if (priv->compress) 246165581Sglebius deflateEnd(&priv->cx); 247165581Sglebius else 248165581Sglebius inflateEnd(&priv->cx); 249165581Sglebius priv->cfg.enable = 0; 250165581Sglebius } 251166019Sglebius 252165581Sglebius /* Configuration is OK, reset to it. */ 253165581Sglebius priv->cfg = *cfg; 254165581Sglebius 255165581Sglebius if (priv->cfg.enable) { 256165581Sglebius priv->cx.next_in = NULL; 257165581Sglebius priv->cx.zalloc = z_alloc; 258165581Sglebius priv->cx.zfree = z_free; 259165581Sglebius int res; 260165581Sglebius if (priv->compress) { 261165581Sglebius if ((res = deflateInit2(&priv->cx, 262165581Sglebius Z_DEFAULT_COMPRESSION, Z_DEFLATED, 263165581Sglebius -cfg->windowBits, 8, 264165581Sglebius Z_DEFAULT_STRATEGY)) != Z_OK) { 265165581Sglebius log(LOG_NOTICE, 266165581Sglebius "deflateInit2: error %d, %s\n", 267165581Sglebius res, priv->cx.msg); 268165581Sglebius priv->cfg.enable = 0; 269165581Sglebius ERROUT(ENOMEM); 270165581Sglebius } 271165581Sglebius } else { 272165581Sglebius if ((res = inflateInit2(&priv->cx, 273165581Sglebius -cfg->windowBits)) != Z_OK) { 274165581Sglebius log(LOG_NOTICE, 275165581Sglebius "inflateInit2: error %d, %s\n", 276165581Sglebius res, priv->cx.msg); 277165581Sglebius priv->cfg.enable = 0; 278165581Sglebius ERROUT(ENOMEM); 279165581Sglebius } 280165581Sglebius } 281165581Sglebius } 282165581Sglebius 283165581Sglebius /* Initialize other state. */ 284165581Sglebius priv->seqnum = 0; 285165581Sglebius 286165581Sglebius /* Save return address so we can send reset-req's */ 287165581Sglebius priv->ctrlnode = NGI_RETADDR(item); 288165581Sglebius break; 289165581Sglebius } 290165581Sglebius 291165581Sglebius case NGM_DEFLATE_RESETREQ: 292165581Sglebius ng_deflate_reset_req(node); 293165581Sglebius break; 294165581Sglebius 295165581Sglebius case NGM_DEFLATE_GET_STATS: 296165581Sglebius case NGM_DEFLATE_CLR_STATS: 297165581Sglebius case NGM_DEFLATE_GETCLR_STATS: 298165581Sglebius /* Create response if requested. */ 299165581Sglebius if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) { 300165581Sglebius NG_MKRESPONSE(resp, msg, 301165581Sglebius sizeof(struct ng_deflate_stats), M_NOWAIT); 302165581Sglebius if (resp == NULL) 303165581Sglebius ERROUT(ENOMEM); 304165581Sglebius bcopy(&priv->stats, resp->data, 305166019Sglebius sizeof(struct ng_deflate_stats)); 306165581Sglebius } 307165581Sglebius 308165581Sglebius /* Clear stats if requested. */ 309165581Sglebius if (msg->header.cmd != NGM_DEFLATE_GET_STATS) 310165581Sglebius bzero(&priv->stats, 311165581Sglebius sizeof(struct ng_deflate_stats)); 312165581Sglebius break; 313165581Sglebius 314165581Sglebius default: 315165581Sglebius error = EINVAL; 316165581Sglebius break; 317165581Sglebius } 318165581Sglebiusdone: 319165581Sglebius NG_RESPOND_MSG(error, node, item, resp); 320165581Sglebius NG_FREE_MSG(msg); 321165581Sglebius return (error); 322165581Sglebius} 323165581Sglebius 324165581Sglebius/* 325165581Sglebius * Receive incoming data on our hook. 326165581Sglebius */ 327165581Sglebiusstatic int 328165581Sglebiusng_deflate_rcvdata(hook_p hook, item_p item) 329165581Sglebius{ 330165581Sglebius const node_p node = NG_HOOK_NODE(hook); 331165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 332165581Sglebius struct mbuf *m, *out; 333165581Sglebius int error; 334165581Sglebius 335165581Sglebius if (!priv->cfg.enable) { 336165581Sglebius NG_FREE_ITEM(item); 337165581Sglebius return (ENXIO); 338165581Sglebius } 339166019Sglebius 340165581Sglebius NGI_GET_M(item, m); 341165581Sglebius /* Compress */ 342165581Sglebius if (priv->compress) { 343165581Sglebius if ((error = ng_deflate_compress(node, m, &out)) != 0) { 344165581Sglebius NG_FREE_ITEM(item); 345165581Sglebius log(LOG_NOTICE, "%s: error: %d\n", __func__, error); 346165581Sglebius return (error); 347165581Sglebius } 348165581Sglebius 349165581Sglebius } else { /* Decompress */ 350165581Sglebius if ((error = ng_deflate_decompress(node, m, &out)) != 0) { 351165581Sglebius NG_FREE_ITEM(item); 352165581Sglebius log(LOG_NOTICE, "%s: error: %d\n", __func__, error); 353165581Sglebius if (priv->ctrlnode != 0) { 354165581Sglebius struct ng_mesg *msg; 355165581Sglebius 356165581Sglebius /* Need to send a reset-request. */ 357165581Sglebius NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE, 358165581Sglebius NGM_DEFLATE_RESETREQ, 0, M_NOWAIT); 359165581Sglebius if (msg == NULL) 360165581Sglebius return (error); 361165581Sglebius NG_SEND_MSG_ID(error, node, msg, 362165581Sglebius priv->ctrlnode, 0); 363165581Sglebius } 364165581Sglebius return (error); 365165581Sglebius } 366165581Sglebius } 367165581Sglebius 368165581Sglebius NG_FWD_NEW_DATA(error, item, hook, out); 369165581Sglebius return (error); 370165581Sglebius} 371165581Sglebius 372165581Sglebius/* 373165581Sglebius * Destroy node. 374165581Sglebius */ 375165581Sglebiusstatic int 376165581Sglebiusng_deflate_shutdown(node_p node) 377165581Sglebius{ 378165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 379165581Sglebius 380165581Sglebius /* Take down netgraph node. */ 381165581Sglebius if (priv->cfg.enable) { 382165581Sglebius if (priv->compress) 383165581Sglebius deflateEnd(&priv->cx); 384165581Sglebius else 385165581Sglebius inflateEnd(&priv->cx); 386165581Sglebius } 387165581Sglebius 388165581Sglebius free(priv, M_NETGRAPH_DEFLATE); 389165581Sglebius NG_NODE_SET_PRIVATE(node, NULL); 390165581Sglebius NG_NODE_UNREF(node); /* let the node escape */ 391165581Sglebius return (0); 392165581Sglebius} 393165581Sglebius 394165581Sglebius/* 395165581Sglebius * Hook disconnection 396165581Sglebius */ 397165581Sglebiusstatic int 398165581Sglebiusng_deflate_disconnect(hook_p hook) 399165581Sglebius{ 400165581Sglebius const node_p node = NG_HOOK_NODE(hook); 401165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 402165581Sglebius 403165581Sglebius if (priv->cfg.enable) { 404165581Sglebius if (priv->compress) 405165581Sglebius deflateEnd(&priv->cx); 406165581Sglebius else 407165581Sglebius inflateEnd(&priv->cx); 408165581Sglebius priv->cfg.enable = 0; 409165581Sglebius } 410165581Sglebius 411165581Sglebius /* Go away if no longer connected. */ 412165581Sglebius if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node)) 413165581Sglebius ng_rmnode_self(node); 414165581Sglebius return (0); 415165581Sglebius} 416165581Sglebius 417165581Sglebius/************************************************************************ 418165581Sglebius HELPER STUFF 419165581Sglebius ************************************************************************/ 420165581Sglebius 421165581Sglebius/* 422165581Sglebius * Space allocation and freeing routines for use by zlib routines. 423165581Sglebius */ 424165581Sglebius 425165581Sglebiusstatic void * 426165581Sglebiusz_alloc(void *notused, u_int items, u_int size) 427165581Sglebius{ 428165581Sglebius 429165581Sglebius return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT)); 430165581Sglebius} 431165581Sglebius 432165581Sglebiusstatic void 433165581Sglebiusz_free(void *notused, void *ptr) 434165581Sglebius{ 435165581Sglebius 436165581Sglebius free(ptr, M_NETGRAPH_DEFLATE); 437165581Sglebius} 438165581Sglebius 439165581Sglebius/* 440165581Sglebius * Compress/encrypt a packet and put the result in a new mbuf at *resultp. 441165581Sglebius * The original mbuf is not free'd. 442165581Sglebius */ 443165581Sglebiusstatic int 444165581Sglebiusng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp) 445165581Sglebius{ 446165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 447165581Sglebius int outlen, inlen; 448165581Sglebius int rtn; 449165581Sglebius 450165581Sglebius /* Initialize. */ 451165581Sglebius *resultp = NULL; 452165581Sglebius 453165581Sglebius inlen = m->m_pkthdr.len; 454165581Sglebius 455165581Sglebius priv->stats.FramesPlain++; 456165581Sglebius priv->stats.InOctets+=inlen; 457165581Sglebius 458165581Sglebius if (inlen > DEFLATE_BUF_SIZE) { 459165581Sglebius priv->stats.Errors++; 460165581Sglebius NG_FREE_M(m); 461165581Sglebius return (ENOMEM); 462165581Sglebius } 463166019Sglebius 464187405Smav /* We must own the mbuf chain exclusively to modify it. */ 465243882Sglebius m = m_unshare(m, M_NOWAIT); 466187405Smav if (m == NULL) { 467187405Smav priv->stats.Errors++; 468187405Smav return (ENOMEM); 469187405Smav } 470187405Smav 471165581Sglebius /* Work with contiguous regions of memory. */ 472165581Sglebius m_copydata(m, 0, inlen, (caddr_t)priv->inbuf); 473165581Sglebius outlen = DEFLATE_BUF_SIZE; 474165581Sglebius 475165581Sglebius /* Compress "inbuf" into "outbuf". */ 476165581Sglebius /* Prepare to compress. */ 477165581Sglebius if (priv->inbuf[0] != 0) { 478165581Sglebius priv->cx.next_in = priv->inbuf; 479165581Sglebius priv->cx.avail_in = inlen; 480165581Sglebius } else { 481165581Sglebius priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ 482165581Sglebius priv->cx.avail_in = inlen - 1; 483165581Sglebius } 484165581Sglebius priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN; 485165581Sglebius priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN; 486165581Sglebius 487165581Sglebius /* Compress. */ 488165581Sglebius rtn = deflate(&priv->cx, Z_PACKET_FLUSH); 489165581Sglebius 490165581Sglebius /* Check return value. */ 491165581Sglebius if (rtn != Z_OK) { 492165581Sglebius priv->stats.Errors++; 493165581Sglebius log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n", 494165581Sglebius rtn, priv->cx.msg); 495165581Sglebius NG_FREE_M(m); 496165581Sglebius return (EINVAL); 497165581Sglebius } 498165581Sglebius 499165581Sglebius /* Calculate resulting size. */ 500165581Sglebius outlen -= priv->cx.avail_out; 501165581Sglebius 502165581Sglebius /* If we can't compress this packet, send it as-is. */ 503165581Sglebius if (outlen > inlen) { 504165581Sglebius /* Return original packet uncompressed. */ 505165581Sglebius *resultp = m; 506165581Sglebius priv->stats.FramesUncomp++; 507165581Sglebius priv->stats.OutOctets+=inlen; 508165581Sglebius } else { 509165581Sglebius /* Install header. */ 510206049Smav be16enc(priv->outbuf, PROT_COMPD); 511206049Smav be16enc(priv->outbuf + 2, priv->seqnum); 512165581Sglebius 513165581Sglebius /* Return packet in an mbuf. */ 514187405Smav m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); 515187405Smav if (m->m_pkthdr.len < outlen) { 516187405Smav m_freem(m); 517165581Sglebius priv->stats.Errors++; 518165581Sglebius return (ENOMEM); 519187405Smav } else if (outlen < m->m_pkthdr.len) 520187405Smav m_adj(m, outlen - m->m_pkthdr.len); 521187405Smav *resultp = m; 522165581Sglebius priv->stats.FramesComp++; 523165581Sglebius priv->stats.OutOctets+=outlen; 524165581Sglebius } 525165581Sglebius 526165581Sglebius /* Update sequence number. */ 527165581Sglebius priv->seqnum++; 528165581Sglebius 529165581Sglebius return (0); 530165581Sglebius} 531165581Sglebius 532165581Sglebius/* 533165581Sglebius * Decompress/decrypt packet and put the result in a new mbuf at *resultp. 534165581Sglebius * The original mbuf is not free'd. 535165581Sglebius */ 536165581Sglebiusstatic int 537165581Sglebiusng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp) 538165581Sglebius{ 539165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 540165581Sglebius int outlen, inlen; 541165581Sglebius int rtn; 542165581Sglebius uint16_t proto; 543165581Sglebius int offset; 544165581Sglebius uint16_t rseqnum; 545165581Sglebius 546165581Sglebius /* Initialize. */ 547165581Sglebius *resultp = NULL; 548165581Sglebius 549165581Sglebius inlen = m->m_pkthdr.len; 550166019Sglebius 551165581Sglebius if (inlen > DEFLATE_BUF_SIZE) { 552165581Sglebius priv->stats.Errors++; 553165581Sglebius NG_FREE_M(m); 554165581Sglebius priv->seqnum = 0; 555165581Sglebius return (ENOMEM); 556165581Sglebius } 557165581Sglebius 558187405Smav /* We must own the mbuf chain exclusively to modify it. */ 559243882Sglebius m = m_unshare(m, M_NOWAIT); 560187405Smav if (m == NULL) { 561187405Smav priv->stats.Errors++; 562187405Smav return (ENOMEM); 563187405Smav } 564187405Smav 565165581Sglebius /* Work with contiguous regions of memory. */ 566165581Sglebius m_copydata(m, 0, inlen, (caddr_t)priv->inbuf); 567165581Sglebius 568165581Sglebius /* Separate proto. */ 569165581Sglebius if ((priv->inbuf[0] & 0x01) != 0) { 570165581Sglebius proto = priv->inbuf[0]; 571165581Sglebius offset = 1; 572165581Sglebius } else { 573206049Smav proto = be16dec(priv->inbuf); 574165581Sglebius offset = 2; 575165581Sglebius } 576165581Sglebius 577165925Sglebius priv->stats.InOctets += inlen; 578165925Sglebius 579165581Sglebius /* Packet is compressed, so decompress. */ 580165581Sglebius if (proto == PROT_COMPD) { 581165581Sglebius priv->stats.FramesComp++; 582166019Sglebius 583165581Sglebius /* Check sequence number. */ 584206049Smav rseqnum = be16dec(priv->inbuf + offset); 585165581Sglebius offset += 2; 586165581Sglebius if (rseqnum != priv->seqnum) { 587165581Sglebius priv->stats.Errors++; 588165581Sglebius log(LOG_NOTICE, "ng_deflate: wrong sequence: %u " 589165581Sglebius "instead of %u\n", rseqnum, priv->seqnum); 590165581Sglebius NG_FREE_M(m); 591165581Sglebius priv->seqnum = 0; 592165581Sglebius return (EPIPE); 593165581Sglebius } 594165581Sglebius 595165581Sglebius outlen = DEFLATE_BUF_SIZE; 596165581Sglebius 597165581Sglebius /* Decompress "inbuf" into "outbuf". */ 598165581Sglebius /* Prepare to decompress. */ 599165581Sglebius priv->cx.next_in = priv->inbuf + offset; 600165581Sglebius priv->cx.avail_in = inlen - offset; 601165581Sglebius /* Reserve space for protocol decompression. */ 602165581Sglebius priv->cx.next_out = priv->outbuf + 1; 603165581Sglebius priv->cx.avail_out = outlen - 1; 604165581Sglebius 605165581Sglebius /* Decompress. */ 606165581Sglebius rtn = inflate(&priv->cx, Z_PACKET_FLUSH); 607165581Sglebius 608165581Sglebius /* Check return value. */ 609165581Sglebius if (rtn != Z_OK && rtn != Z_STREAM_END) { 610165581Sglebius priv->stats.Errors++; 611165581Sglebius NG_FREE_M(m); 612165581Sglebius priv->seqnum = 0; 613165581Sglebius log(LOG_NOTICE, "%s: decompression error: %d (%s)\n", 614165581Sglebius __func__, rtn, priv->cx.msg); 615165581Sglebius 616165581Sglebius switch (rtn) { 617165581Sglebius case Z_MEM_ERROR: 618165581Sglebius return (ENOMEM); 619165581Sglebius case Z_DATA_ERROR: 620165581Sglebius return (EIO); 621165581Sglebius default: 622165581Sglebius return (EINVAL); 623165581Sglebius } 624165581Sglebius } 625165581Sglebius 626165581Sglebius /* Calculate resulting size. */ 627165581Sglebius outlen -= priv->cx.avail_out; 628165581Sglebius 629165581Sglebius /* Decompress protocol. */ 630165581Sglebius if ((priv->outbuf[1] & 0x01) != 0) { 631165581Sglebius priv->outbuf[0] = 0; 632165581Sglebius /* Return packet in an mbuf. */ 633187405Smav m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); 634165581Sglebius } else { 635165581Sglebius outlen--; 636165581Sglebius /* Return packet in an mbuf. */ 637187405Smav m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1)); 638165581Sglebius } 639187405Smav if (m->m_pkthdr.len < outlen) { 640187405Smav m_freem(m); 641165581Sglebius priv->stats.Errors++; 642165581Sglebius priv->seqnum = 0; 643165581Sglebius return (ENOMEM); 644187405Smav } else if (outlen < m->m_pkthdr.len) 645187405Smav m_adj(m, outlen - m->m_pkthdr.len); 646187405Smav *resultp = m; 647165581Sglebius priv->stats.FramesPlain++; 648165581Sglebius priv->stats.OutOctets+=outlen; 649165581Sglebius 650165581Sglebius } else { /* Packet is not compressed, just update dictionary. */ 651165581Sglebius priv->stats.FramesUncomp++; 652165581Sglebius if (priv->inbuf[0] == 0) { 653165581Sglebius priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ 654165581Sglebius priv->cx.avail_in = inlen - 1; 655165581Sglebius } else { 656165581Sglebius priv->cx.next_in = priv->inbuf; 657165581Sglebius priv->cx.avail_in = inlen; 658165581Sglebius } 659165581Sglebius 660165581Sglebius rtn = inflateIncomp(&priv->cx); 661165581Sglebius 662165581Sglebius /* Check return value */ 663165581Sglebius if (rtn != Z_OK) { 664165581Sglebius priv->stats.Errors++; 665165581Sglebius log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n", 666165581Sglebius __func__, rtn, priv->cx.msg); 667165581Sglebius NG_FREE_M(m); 668165581Sglebius priv->seqnum = 0; 669165581Sglebius return (EINVAL); 670165581Sglebius } 671165581Sglebius 672165581Sglebius *resultp = m; 673165581Sglebius priv->stats.FramesPlain++; 674165581Sglebius priv->stats.OutOctets += inlen; 675165581Sglebius } 676165581Sglebius 677165581Sglebius /* Update sequence number. */ 678165581Sglebius priv->seqnum++; 679165581Sglebius 680165581Sglebius return (0); 681165581Sglebius} 682165581Sglebius 683165581Sglebius/* 684165581Sglebius * The peer has sent us a CCP ResetRequest, so reset our transmit state. 685165581Sglebius */ 686165581Sglebiusstatic void 687165581Sglebiusng_deflate_reset_req(node_p node) 688165581Sglebius{ 689165581Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 690165581Sglebius 691165581Sglebius priv->seqnum = 0; 692165581Sglebius if (priv->cfg.enable) { 693165581Sglebius if (priv->compress) 694165581Sglebius deflateReset(&priv->cx); 695165581Sglebius else 696165581Sglebius inflateReset(&priv->cx); 697165581Sglebius } 698165581Sglebius} 699165581Sglebius 700