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