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