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