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