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