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