2 * Copyright (c) 2006 Alexander Motin <mav@alkar.net>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
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.
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
31 * Deflate PPP compression netgraph node type.
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/endian.h>
40 #include <sys/errno.h>
41 #include <sys/syslog.h>
45 #include <netgraph/ng_message.h>
46 #include <netgraph/netgraph.h>
47 #include <netgraph/ng_parse.h>
48 #include <netgraph/ng_deflate.h>
50 #include "opt_netgraph.h"
52 MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate", "netgraph deflate node ");
54 /* DEFLATE header length */
55 #define DEFLATE_HDRLEN 2
57 #define PROT_COMPD 0x00fd
59 #define DEFLATE_BUF_SIZE 4096
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 */
72 typedef struct ng_deflate_private *priv_p;
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;
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);
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
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
107 /* List of commands and how to convert arguments to/from ASCII. */
108 static const struct ng_cmdlist ng_deflate_cmds[] = {
113 &ng_deflate_config_type,
118 NGM_DEFLATE_RESETREQ,
125 NGM_DEFLATE_GET_STATS,
128 &ng_deflate_stat_type
132 NGM_DEFLATE_CLR_STATS,
139 NGM_DEFLATE_GETCLR_STATS,
142 &ng_deflate_stat_type
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,
159 NETGRAPH_INIT(deflate, &ng_deflate_typestruct);
161 /* Depend on separate zlib module. */
162 MODULE_DEPEND(ng_deflate, zlib, 1, 1, 1);
164 #define ERROUT(x) do { error = (x); goto done; } while (0)
166 /************************************************************************
168 ************************************************************************/
171 * Node type constructor
174 ng_deflate_constructor(node_p node)
178 /* Allocate private structure. */
179 priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO);
181 NG_NODE_SET_PRIVATE(node, priv);
183 /* This node is not thread safe. */
184 NG_NODE_FORCE_WRITER(node);
191 * Give our OK for a hook to be added.
194 ng_deflate_newhook(node_p node, hook_p hook, const char *name)
196 const priv_p priv = NG_NODE_PRIVATE(node);
198 if (NG_NODE_NUMHOOKS(node) > 0)
201 if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0)
203 else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0)
212 * Receive a control message
215 ng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook)
217 const priv_p priv = NG_NODE_PRIVATE(node);
218 struct ng_mesg *resp = NULL;
222 NGI_GET_MSG(item, msg);
224 if (msg->header.typecookie != NGM_DEFLATE_COOKIE)
227 switch (msg->header.cmd) {
228 case NGM_DEFLATE_CONFIG:
230 struct ng_deflate_config *const cfg
231 = (struct ng_deflate_config *)msg->data;
233 /* Check configuration. */
234 if (msg->header.arglen != sizeof(*cfg))
237 if (cfg->windowBits < 8 || cfg->windowBits > 15)
242 /* Clear previous state. */
243 if (priv->cfg.enable) {
245 deflateEnd(&priv->cx);
247 inflateEnd(&priv->cx);
248 priv->cfg.enable = 0;
251 /* Configuration is OK, reset to it. */
254 if (priv->cfg.enable) {
255 priv->cx.next_in = NULL;
256 priv->cx.zalloc = z_alloc;
257 priv->cx.zfree = z_free;
259 if (priv->compress) {
260 if ((res = deflateInit2(&priv->cx,
261 Z_DEFAULT_COMPRESSION, Z_DEFLATED,
263 Z_DEFAULT_STRATEGY)) != Z_OK) {
265 "deflateInit2: error %d, %s\n",
267 priv->cfg.enable = 0;
271 if ((res = inflateInit2(&priv->cx,
272 -cfg->windowBits)) != Z_OK) {
274 "inflateInit2: error %d, %s\n",
276 priv->cfg.enable = 0;
282 /* Initialize other state. */
285 /* Save return address so we can send reset-req's */
286 priv->ctrlnode = NGI_RETADDR(item);
290 case NGM_DEFLATE_RESETREQ:
291 ng_deflate_reset_req(node);
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);
303 bcopy(&priv->stats, resp->data,
304 sizeof(struct ng_deflate_stats));
307 /* Clear stats if requested. */
308 if (msg->header.cmd != NGM_DEFLATE_GET_STATS)
310 sizeof(struct ng_deflate_stats));
318 NG_RESPOND_MSG(error, node, item, resp);
324 * Receive incoming data on our hook.
327 ng_deflate_rcvdata(hook_p hook, item_p item)
329 const node_p node = NG_HOOK_NODE(hook);
330 const priv_p priv = NG_NODE_PRIVATE(node);
331 struct mbuf *m, *out;
334 if (!priv->cfg.enable) {
341 if (priv->compress) {
342 if ((error = ng_deflate_compress(node, m, &out)) != 0) {
344 log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
348 } else { /* Decompress */
349 if ((error = ng_deflate_decompress(node, m, &out)) != 0) {
351 log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
352 if (priv->ctrlnode != 0) {
355 /* Need to send a reset-request. */
356 NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE,
357 NGM_DEFLATE_RESETREQ, 0, M_NOWAIT);
360 NG_SEND_MSG_ID(error, node, msg,
367 NG_FWD_NEW_DATA(error, item, hook, out);
375 ng_deflate_shutdown(node_p node)
377 const priv_p priv = NG_NODE_PRIVATE(node);
379 /* Take down netgraph node. */
380 if (priv->cfg.enable) {
382 deflateEnd(&priv->cx);
384 inflateEnd(&priv->cx);
387 free(priv, M_NETGRAPH_DEFLATE);
388 NG_NODE_SET_PRIVATE(node, NULL);
389 NG_NODE_UNREF(node); /* let the node escape */
397 ng_deflate_disconnect(hook_p hook)
399 const node_p node = NG_HOOK_NODE(hook);
400 const priv_p priv = NG_NODE_PRIVATE(node);
402 if (priv->cfg.enable) {
404 deflateEnd(&priv->cx);
406 inflateEnd(&priv->cx);
407 priv->cfg.enable = 0;
410 /* Go away if no longer connected. */
411 if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node))
412 ng_rmnode_self(node);
416 /************************************************************************
418 ************************************************************************/
421 * Space allocation and freeing routines for use by zlib routines.
425 z_alloc(void *notused, u_int items, u_int size)
428 return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT));
432 z_free(void *notused, void *ptr)
435 free(ptr, M_NETGRAPH_DEFLATE);
439 * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
440 * The original mbuf is not free'd.
443 ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
445 const priv_p priv = NG_NODE_PRIVATE(node);
452 inlen = m->m_pkthdr.len;
454 priv->stats.FramesPlain++;
455 priv->stats.InOctets+=inlen;
457 if (inlen > DEFLATE_BUF_SIZE) {
458 priv->stats.Errors++;
463 /* We must own the mbuf chain exclusively to modify it. */
464 m = m_unshare(m, M_DONTWAIT);
466 priv->stats.Errors++;
470 /* Work with contiguous regions of memory. */
471 m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
472 outlen = DEFLATE_BUF_SIZE;
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;
480 priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
481 priv->cx.avail_in = inlen - 1;
483 priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN;
484 priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN;
487 rtn = deflate(&priv->cx, Z_PACKET_FLUSH);
489 /* Check return value. */
491 priv->stats.Errors++;
492 log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n",
498 /* Calculate resulting size. */
499 outlen -= priv->cx.avail_out;
501 /* If we can't compress this packet, send it as-is. */
502 if (outlen > inlen) {
503 /* Return original packet uncompressed. */
505 priv->stats.FramesUncomp++;
506 priv->stats.OutOctets+=inlen;
508 /* Install header. */
509 be16enc(priv->outbuf, PROT_COMPD);
510 be16enc(priv->outbuf + 2, priv->seqnum);
512 /* Return packet in an mbuf. */
513 m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
514 if (m->m_pkthdr.len < outlen) {
516 priv->stats.Errors++;
518 } else if (outlen < m->m_pkthdr.len)
519 m_adj(m, outlen - m->m_pkthdr.len);
521 priv->stats.FramesComp++;
522 priv->stats.OutOctets+=outlen;
525 /* Update sequence number. */
532 * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
533 * The original mbuf is not free'd.
536 ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp)
538 const priv_p priv = NG_NODE_PRIVATE(node);
548 inlen = m->m_pkthdr.len;
550 if (inlen > DEFLATE_BUF_SIZE) {
551 priv->stats.Errors++;
557 /* We must own the mbuf chain exclusively to modify it. */
558 m = m_unshare(m, M_DONTWAIT);
560 priv->stats.Errors++;
564 /* Work with contiguous regions of memory. */
565 m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
567 /* Separate proto. */
568 if ((priv->inbuf[0] & 0x01) != 0) {
569 proto = priv->inbuf[0];
572 proto = be16dec(priv->inbuf);
576 priv->stats.InOctets += inlen;
578 /* Packet is compressed, so decompress. */
579 if (proto == PROT_COMPD) {
580 priv->stats.FramesComp++;
582 /* Check sequence number. */
583 rseqnum = be16dec(priv->inbuf + offset);
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);
594 outlen = DEFLATE_BUF_SIZE;
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;
605 rtn = inflate(&priv->cx, Z_PACKET_FLUSH);
607 /* Check return value. */
608 if (rtn != Z_OK && rtn != Z_STREAM_END) {
609 priv->stats.Errors++;
612 log(LOG_NOTICE, "%s: decompression error: %d (%s)\n",
613 __func__, rtn, priv->cx.msg);
625 /* Calculate resulting size. */
626 outlen -= priv->cx.avail_out;
628 /* Decompress protocol. */
629 if ((priv->outbuf[1] & 0x01) != 0) {
631 /* Return packet in an mbuf. */
632 m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
635 /* Return packet in an mbuf. */
636 m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1));
638 if (m->m_pkthdr.len < outlen) {
640 priv->stats.Errors++;
643 } else if (outlen < m->m_pkthdr.len)
644 m_adj(m, outlen - m->m_pkthdr.len);
646 priv->stats.FramesPlain++;
647 priv->stats.OutOctets+=outlen;
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;
655 priv->cx.next_in = priv->inbuf;
656 priv->cx.avail_in = inlen;
659 rtn = inflateIncomp(&priv->cx);
661 /* Check return value */
663 priv->stats.Errors++;
664 log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n",
665 __func__, rtn, priv->cx.msg);
672 priv->stats.FramesPlain++;
673 priv->stats.OutOctets += inlen;
676 /* Update sequence number. */
683 * The peer has sent us a CCP ResetRequest, so reset our transmit state.
686 ng_deflate_reset_req(node_p node)
688 const priv_p priv = NG_NODE_PRIVATE(node);
691 if (priv->cfg.enable) {
693 deflateReset(&priv->cx);
695 inflateReset(&priv->cx);