ng_deflate.c revision 166019
1165581Sglebius/*-
2165581Sglebius * Copyright (c) 2006 Alexander Motin <mav@alkar.net>
3165581Sglebius * All rights reserved.
4165581Sglebius *
5165581Sglebius * Redistribution and use in source and binary forms, with or without
6165581Sglebius * modification, are permitted provided that the following conditions
7165581Sglebius * are met:
8165581Sglebius * 1. Redistributions of source code must retain the above copyright
9165581Sglebius *    notice unmodified, this list of conditions, and the following
10165581Sglebius *    disclaimer.
11165581Sglebius * 2. Redistributions in binary form must reproduce the above copyright
12165581Sglebius *    notice, this list of conditions and the following disclaimer in the
13165581Sglebius *    documentation and/or other materials provided with the distribution.
14165581Sglebius *
15165581Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16165581Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17165581Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18165581Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19165581Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20165581Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21165581Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22165581Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23165581Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24165581Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25165581Sglebius * SUCH DAMAGE.
26165581Sglebius *
27165581Sglebius * $FreeBSD: head/sys/netgraph/ng_deflate.c 166019 2007-01-15 05:55:56Z glebius $
28165581Sglebius */
29165581Sglebius
30165581Sglebius/*
31165581Sglebius * Deflate PPP compression netgraph node type.
32165581Sglebius */
33165581Sglebius
34165581Sglebius#include <sys/param.h>
35165581Sglebius#include <sys/systm.h>
36165581Sglebius#include <sys/kernel.h>
37165581Sglebius#include <sys/mbuf.h>
38165581Sglebius#include <sys/malloc.h>
39165581Sglebius#include <sys/errno.h>
40165581Sglebius#include <sys/syslog.h>
41165581Sglebius
42165581Sglebius#include <net/zlib.h>
43165581Sglebius
44165581Sglebius#include <netgraph/ng_message.h>
45165581Sglebius#include <netgraph/netgraph.h>
46165581Sglebius#include <netgraph/ng_parse.h>
47165581Sglebius#include <netgraph/ng_deflate.h>
48165581Sglebius
49165581Sglebius#include "opt_netgraph.h"
50165581Sglebius
51165581SglebiusMALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate", "netgraph deflate node ");
52165581Sglebius
53165581Sglebius/* DEFLATE header length */
54165581Sglebius#define DEFLATE_HDRLEN		2
55165581Sglebius
56165581Sglebius#define PROT_COMPD		0x00fd
57165581Sglebius
58165581Sglebius#define DEFLATE_BUF_SIZE	4096
59165581Sglebius
60165581Sglebius/* Node private data */
61165581Sglebiusstruct ng_deflate_private {
62165581Sglebius	struct ng_deflate_config cfg;		/* configuration */
63165581Sglebius	u_char		inbuf[DEFLATE_BUF_SIZE];	/* input buffer */
64165581Sglebius	u_char		outbuf[DEFLATE_BUF_SIZE];	/* output buffer */
65165581Sglebius	z_stream 	cx;			/* compression context */
66165581Sglebius	struct ng_deflate_stats stats;		/* statistics */
67165581Sglebius	ng_ID_t		ctrlnode;		/* path to controlling node */
68165581Sglebius	uint16_t	seqnum;			/* sequence number */
69165581Sglebius	u_char		compress;		/* compress/decompress flag */
70165581Sglebius};
71165581Sglebiustypedef struct ng_deflate_private *priv_p;
72165581Sglebius
73165581Sglebius/* Netgraph node methods */
74165581Sglebiusstatic ng_constructor_t	ng_deflate_constructor;
75165581Sglebiusstatic ng_rcvmsg_t	ng_deflate_rcvmsg;
76165581Sglebiusstatic ng_shutdown_t	ng_deflate_shutdown;
77165581Sglebiusstatic ng_newhook_t	ng_deflate_newhook;
78165581Sglebiusstatic ng_rcvdata_t	ng_deflate_rcvdata;
79165581Sglebiusstatic ng_disconnect_t	ng_deflate_disconnect;
80165581Sglebius
81165581Sglebius/* Helper functions */
82165581Sglebiusstatic void	*z_alloc(void *, u_int items, u_int size);
83165581Sglebiusstatic void	z_free(void *, void *ptr);
84165581Sglebiusstatic int	ng_deflate_compress(node_p node,
85165581Sglebius		    struct mbuf *m, struct mbuf **resultp);
86165581Sglebiusstatic int	ng_deflate_decompress(node_p node,
87165581Sglebius		    struct mbuf *m, struct mbuf **resultp);
88165581Sglebiusstatic void	ng_deflate_reset_req(node_p node);
89165581Sglebius
90165581Sglebius/* Parse type for struct ng_deflate_config. */
91165581Sglebiusstatic const struct ng_parse_struct_field ng_deflate_config_type_fields[]
92165581Sglebius	= NG_DEFLATE_CONFIG_INFO;
93165581Sglebiusstatic const struct ng_parse_type ng_deflate_config_type = {
94165581Sglebius	&ng_parse_struct_type,
95165581Sglebius	ng_deflate_config_type_fields
96165581Sglebius};
97165581Sglebius
98165581Sglebius/* Parse type for struct ng_deflate_stat. */
99165581Sglebiusstatic const struct ng_parse_struct_field ng_deflate_stats_type_fields[]
100165581Sglebius	= NG_DEFLATE_STATS_INFO;
101165581Sglebiusstatic const struct ng_parse_type ng_deflate_stat_type = {
102165581Sglebius	&ng_parse_struct_type,
103165581Sglebius	ng_deflate_stats_type_fields
104165581Sglebius};
105165581Sglebius
106165581Sglebius/* List of commands and how to convert arguments to/from ASCII. */
107165581Sglebiusstatic const struct ng_cmdlist ng_deflate_cmds[] = {
108165581Sglebius	{
109165581Sglebius	  NGM_DEFLATE_COOKIE,
110165581Sglebius	  NGM_DEFLATE_CONFIG,
111165581Sglebius	  "config",
112165581Sglebius	  &ng_deflate_config_type,
113165581Sglebius	  NULL
114165581Sglebius	},
115165581Sglebius	{
116165581Sglebius	  NGM_DEFLATE_COOKIE,
117165581Sglebius	  NGM_DEFLATE_RESETREQ,
118165581Sglebius	  "resetreq",
119165581Sglebius	  NULL,
120165581Sglebius	  NULL
121165581Sglebius	},
122165581Sglebius	{
123165581Sglebius	  NGM_DEFLATE_COOKIE,
124165581Sglebius	  NGM_DEFLATE_GET_STATS,
125165581Sglebius	  "getstats",
126165581Sglebius	  NULL,
127165581Sglebius	  &ng_deflate_stat_type
128165581Sglebius	},
129165581Sglebius	{
130165581Sglebius	  NGM_DEFLATE_COOKIE,
131165581Sglebius	  NGM_DEFLATE_CLR_STATS,
132165581Sglebius	  "clrstats",
133165581Sglebius	  NULL,
134165581Sglebius	  NULL
135165581Sglebius	},
136165581Sglebius	{
137165581Sglebius	  NGM_DEFLATE_COOKIE,
138165581Sglebius	  NGM_DEFLATE_GETCLR_STATS,
139165581Sglebius	  "getclrstats",
140165581Sglebius	  NULL,
141165581Sglebius	  &ng_deflate_stat_type
142165581Sglebius	},
143165581Sglebius	{ 0 }
144165581Sglebius};
145165581Sglebius
146165581Sglebius/* Node type descriptor */
147165581Sglebiusstatic struct ng_type ng_deflate_typestruct = {
148165581Sglebius	.version =	NG_ABI_VERSION,
149165581Sglebius	.name =		NG_DEFLATE_NODE_TYPE,
150165581Sglebius	.constructor =	ng_deflate_constructor,
151165581Sglebius	.rcvmsg =	ng_deflate_rcvmsg,
152165581Sglebius	.shutdown =	ng_deflate_shutdown,
153165581Sglebius	.newhook =	ng_deflate_newhook,
154165581Sglebius	.rcvdata =	ng_deflate_rcvdata,
155165581Sglebius	.disconnect =	ng_deflate_disconnect,
156165581Sglebius	.cmdlist =	ng_deflate_cmds,
157165581Sglebius};
158165581SglebiusNETGRAPH_INIT(deflate, &ng_deflate_typestruct);
159165581Sglebius
160165581Sglebius/* Depend on separate zlib module. */
161165581SglebiusMODULE_DEPEND(ng_deflate, zlib, 1, 1, 1);
162165581Sglebius
163165581Sglebius#define ERROUT(x)	do { error = (x); goto done; } while (0)
164165581Sglebius
165165581Sglebius/************************************************************************
166165581Sglebius			NETGRAPH NODE STUFF
167165581Sglebius ************************************************************************/
168165581Sglebius
169165581Sglebius/*
170165581Sglebius * Node type constructor
171165581Sglebius */
172165581Sglebiusstatic int
173165581Sglebiusng_deflate_constructor(node_p node)
174165581Sglebius{
175165581Sglebius	priv_p priv;
176165581Sglebius
177165581Sglebius	/* Allocate private structure. */
178165581Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO);
179165581Sglebius
180165581Sglebius	NG_NODE_SET_PRIVATE(node, priv);
181165581Sglebius
182165581Sglebius	/* This node is not thread safe. */
183165581Sglebius	NG_NODE_FORCE_WRITER(node);
184165581Sglebius
185165581Sglebius	/* Done */
186165581Sglebius	return (0);
187165581Sglebius}
188165581Sglebius
189165581Sglebius/*
190165581Sglebius * Give our OK for a hook to be added.
191165581Sglebius */
192165581Sglebiusstatic int
193165581Sglebiusng_deflate_newhook(node_p node, hook_p hook, const char *name)
194165581Sglebius{
195165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
196165581Sglebius
197165581Sglebius	if (NG_NODE_NUMHOOKS(node) > 0)
198165581Sglebius		return (EINVAL);
199165581Sglebius
200165581Sglebius	if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0)
201165581Sglebius		priv->compress = 1;
202165581Sglebius	else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0)
203165581Sglebius		priv->compress = 0;
204165581Sglebius	else
205165581Sglebius		return (EINVAL);
206165581Sglebius
207165581Sglebius	return (0);
208165581Sglebius}
209165581Sglebius
210165581Sglebius/*
211165581Sglebius * Receive a control message
212165581Sglebius */
213165581Sglebiusstatic int
214165581Sglebiusng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook)
215165581Sglebius{
216165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
217165581Sglebius	struct ng_mesg *resp = NULL;
218165581Sglebius	int error = 0;
219165581Sglebius	struct ng_mesg *msg;
220165581Sglebius
221165581Sglebius	NGI_GET_MSG(item, msg);
222165581Sglebius
223165581Sglebius	if (msg->header.typecookie != NGM_DEFLATE_COOKIE)
224165581Sglebius		ERROUT(EINVAL);
225166019Sglebius
226165581Sglebius	switch (msg->header.cmd) {
227165581Sglebius	case NGM_DEFLATE_CONFIG:
228165581Sglebius	    {
229165581Sglebius		struct ng_deflate_config *const cfg
230165581Sglebius		    = (struct ng_deflate_config *)msg->data;
231165581Sglebius
232165581Sglebius		/* Check configuration. */
233165581Sglebius		if (msg->header.arglen != sizeof(*cfg))
234165581Sglebius			ERROUT(EINVAL);
235165581Sglebius		if (cfg->enable) {
236165581Sglebius		    if (cfg->windowBits < 8 || cfg->windowBits > 15)
237165581Sglebius			ERROUT(EINVAL);
238165581Sglebius		} else
239165581Sglebius		    cfg->windowBits = 0;
240165581Sglebius
241165581Sglebius		/* Clear previous state. */
242165581Sglebius		if (priv->cfg.enable) {
243165581Sglebius			if (priv->compress)
244165581Sglebius				deflateEnd(&priv->cx);
245165581Sglebius			else
246165581Sglebius				inflateEnd(&priv->cx);
247165581Sglebius			priv->cfg.enable = 0;
248165581Sglebius		}
249166019Sglebius
250165581Sglebius		/* Configuration is OK, reset to it. */
251165581Sglebius		priv->cfg = *cfg;
252165581Sglebius
253165581Sglebius		if (priv->cfg.enable) {
254165581Sglebius			priv->cx.next_in = NULL;
255165581Sglebius			priv->cx.zalloc = z_alloc;
256165581Sglebius			priv->cx.zfree = z_free;
257165581Sglebius			int res;
258165581Sglebius			if (priv->compress) {
259165581Sglebius				if ((res = deflateInit2(&priv->cx,
260165581Sglebius				    Z_DEFAULT_COMPRESSION, Z_DEFLATED,
261165581Sglebius				    -cfg->windowBits, 8,
262165581Sglebius				    Z_DEFAULT_STRATEGY)) != Z_OK) {
263165581Sglebius					log(LOG_NOTICE,
264165581Sglebius					    "deflateInit2: error %d, %s\n",
265165581Sglebius					    res, priv->cx.msg);
266165581Sglebius					priv->cfg.enable = 0;
267165581Sglebius					ERROUT(ENOMEM);
268165581Sglebius				}
269165581Sglebius			} else {
270165581Sglebius				if ((res = inflateInit2(&priv->cx,
271165581Sglebius				    -cfg->windowBits)) != Z_OK) {
272165581Sglebius					log(LOG_NOTICE,
273165581Sglebius					    "inflateInit2: error %d, %s\n",
274165581Sglebius					    res, priv->cx.msg);
275165581Sglebius					priv->cfg.enable = 0;
276165581Sglebius					ERROUT(ENOMEM);
277165581Sglebius				}
278165581Sglebius			}
279165581Sglebius		}
280165581Sglebius
281165581Sglebius		/* Initialize other state. */
282165581Sglebius		priv->seqnum = 0;
283165581Sglebius
284165581Sglebius		/* Save return address so we can send reset-req's */
285165581Sglebius		priv->ctrlnode = NGI_RETADDR(item);
286165581Sglebius		break;
287165581Sglebius	    }
288165581Sglebius
289165581Sglebius	case NGM_DEFLATE_RESETREQ:
290165581Sglebius		ng_deflate_reset_req(node);
291165581Sglebius		break;
292165581Sglebius
293165581Sglebius	case NGM_DEFLATE_GET_STATS:
294165581Sglebius	case NGM_DEFLATE_CLR_STATS:
295165581Sglebius	case NGM_DEFLATE_GETCLR_STATS:
296165581Sglebius		/* Create response if requested. */
297165581Sglebius		if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) {
298165581Sglebius			NG_MKRESPONSE(resp, msg,
299165581Sglebius			    sizeof(struct ng_deflate_stats), M_NOWAIT);
300165581Sglebius			if (resp == NULL)
301165581Sglebius				ERROUT(ENOMEM);
302165581Sglebius			bcopy(&priv->stats, resp->data,
303166019Sglebius			    sizeof(struct ng_deflate_stats));
304165581Sglebius		}
305165581Sglebius
306165581Sglebius		/* Clear stats if requested. */
307165581Sglebius		if (msg->header.cmd != NGM_DEFLATE_GET_STATS)
308165581Sglebius			bzero(&priv->stats,
309165581Sglebius			    sizeof(struct ng_deflate_stats));
310165581Sglebius		break;
311165581Sglebius
312165581Sglebius	default:
313165581Sglebius		error = EINVAL;
314165581Sglebius		break;
315165581Sglebius	}
316165581Sglebiusdone:
317165581Sglebius	NG_RESPOND_MSG(error, node, item, resp);
318165581Sglebius	NG_FREE_MSG(msg);
319165581Sglebius	return (error);
320165581Sglebius}
321165581Sglebius
322165581Sglebius/*
323165581Sglebius * Receive incoming data on our hook.
324165581Sglebius */
325165581Sglebiusstatic int
326165581Sglebiusng_deflate_rcvdata(hook_p hook, item_p item)
327165581Sglebius{
328165581Sglebius	const node_p node = NG_HOOK_NODE(hook);
329165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
330165581Sglebius	struct mbuf *m, *out;
331165581Sglebius	int error;
332165581Sglebius
333165581Sglebius	if (!priv->cfg.enable) {
334165581Sglebius		NG_FREE_ITEM(item);
335165581Sglebius		return (ENXIO);
336165581Sglebius	}
337166019Sglebius
338165581Sglebius	NGI_GET_M(item, m);
339165581Sglebius	/* Compress */
340165581Sglebius	if (priv->compress) {
341165581Sglebius		if ((error = ng_deflate_compress(node, m, &out)) != 0) {
342165581Sglebius			NG_FREE_ITEM(item);
343165581Sglebius			log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
344165581Sglebius			return (error);
345165581Sglebius		}
346165581Sglebius
347165581Sglebius	} else { /* Decompress */
348165581Sglebius		if ((error = ng_deflate_decompress(node, m, &out)) != 0) {
349165581Sglebius			NG_FREE_ITEM(item);
350165581Sglebius			log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
351165581Sglebius			if (priv->ctrlnode != 0) {
352165581Sglebius				struct ng_mesg *msg;
353165581Sglebius
354165581Sglebius				/* Need to send a reset-request. */
355165581Sglebius				NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE,
356165581Sglebius				    NGM_DEFLATE_RESETREQ, 0, M_NOWAIT);
357165581Sglebius				if (msg == NULL)
358165581Sglebius					return (error);
359165581Sglebius				NG_SEND_MSG_ID(error, node, msg,
360165581Sglebius					priv->ctrlnode, 0);
361165581Sglebius			}
362165581Sglebius			return (error);
363165581Sglebius		}
364165581Sglebius	}
365165581Sglebius
366165581Sglebius	NG_FWD_NEW_DATA(error, item, hook, out);
367165581Sglebius	return (error);
368165581Sglebius}
369165581Sglebius
370165581Sglebius/*
371165581Sglebius * Destroy node.
372165581Sglebius */
373165581Sglebiusstatic int
374165581Sglebiusng_deflate_shutdown(node_p node)
375165581Sglebius{
376165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
377165581Sglebius
378165581Sglebius	/* Take down netgraph node. */
379165581Sglebius	if (priv->cfg.enable) {
380165581Sglebius	    if (priv->compress)
381165581Sglebius		deflateEnd(&priv->cx);
382165581Sglebius	    else
383165581Sglebius		inflateEnd(&priv->cx);
384165581Sglebius	}
385165581Sglebius
386165581Sglebius	free(priv, M_NETGRAPH_DEFLATE);
387165581Sglebius	NG_NODE_SET_PRIVATE(node, NULL);
388165581Sglebius	NG_NODE_UNREF(node);		/* let the node escape */
389165581Sglebius	return (0);
390165581Sglebius}
391165581Sglebius
392165581Sglebius/*
393165581Sglebius * Hook disconnection
394165581Sglebius */
395165581Sglebiusstatic int
396165581Sglebiusng_deflate_disconnect(hook_p hook)
397165581Sglebius{
398165581Sglebius	const node_p node = NG_HOOK_NODE(hook);
399165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
400165581Sglebius
401165581Sglebius	if (priv->cfg.enable) {
402165581Sglebius	    if (priv->compress)
403165581Sglebius		deflateEnd(&priv->cx);
404165581Sglebius	    else
405165581Sglebius		inflateEnd(&priv->cx);
406165581Sglebius	    priv->cfg.enable = 0;
407165581Sglebius	}
408165581Sglebius
409165581Sglebius	/* Go away if no longer connected. */
410165581Sglebius	if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node))
411165581Sglebius		ng_rmnode_self(node);
412165581Sglebius	return (0);
413165581Sglebius}
414165581Sglebius
415165581Sglebius/************************************************************************
416165581Sglebius			HELPER STUFF
417165581Sglebius ************************************************************************/
418165581Sglebius
419165581Sglebius/*
420165581Sglebius * Space allocation and freeing routines for use by zlib routines.
421165581Sglebius */
422165581Sglebius
423165581Sglebiusstatic void *
424165581Sglebiusz_alloc(void *notused, u_int items, u_int size)
425165581Sglebius{
426165581Sglebius
427165581Sglebius	return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT));
428165581Sglebius}
429165581Sglebius
430165581Sglebiusstatic void
431165581Sglebiusz_free(void *notused, void *ptr)
432165581Sglebius{
433165581Sglebius
434165581Sglebius	free(ptr, M_NETGRAPH_DEFLATE);
435165581Sglebius}
436165581Sglebius
437165581Sglebius/*
438165581Sglebius * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
439165581Sglebius * The original mbuf is not free'd.
440165581Sglebius */
441165581Sglebiusstatic int
442165581Sglebiusng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
443165581Sglebius{
444165581Sglebius	const priv_p 	priv = NG_NODE_PRIVATE(node);
445165581Sglebius	int 		outlen, inlen;
446165581Sglebius	int 		rtn;
447165581Sglebius
448165581Sglebius	/* Initialize. */
449165581Sglebius	*resultp = NULL;
450165581Sglebius
451165581Sglebius	inlen = m->m_pkthdr.len;
452165581Sglebius
453165581Sglebius	priv->stats.FramesPlain++;
454165581Sglebius	priv->stats.InOctets+=inlen;
455165581Sglebius
456165581Sglebius	if (inlen > DEFLATE_BUF_SIZE) {
457165581Sglebius		priv->stats.Errors++;
458165581Sglebius		NG_FREE_M(m);
459165581Sglebius		return (ENOMEM);
460165581Sglebius	}
461166019Sglebius
462165581Sglebius	/* Work with contiguous regions of memory. */
463165581Sglebius	m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
464165581Sglebius	outlen = DEFLATE_BUF_SIZE;
465165581Sglebius
466165581Sglebius	/* Compress "inbuf" into "outbuf". */
467165581Sglebius	/* Prepare to compress. */
468165581Sglebius	if (priv->inbuf[0] != 0) {
469165581Sglebius		priv->cx.next_in = priv->inbuf;
470165581Sglebius		priv->cx.avail_in = inlen;
471165581Sglebius	} else {
472165581Sglebius		priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
473165581Sglebius		priv->cx.avail_in = inlen - 1;
474165581Sglebius	}
475165581Sglebius	priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN;
476165581Sglebius	priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN;
477165581Sglebius
478165581Sglebius	/* Compress. */
479165581Sglebius	rtn = deflate(&priv->cx, Z_PACKET_FLUSH);
480165581Sglebius
481165581Sglebius	/* Check return value. */
482165581Sglebius	if (rtn != Z_OK) {
483165581Sglebius		priv->stats.Errors++;
484165581Sglebius		log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n",
485165581Sglebius		    rtn, priv->cx.msg);
486165581Sglebius		NG_FREE_M(m);
487165581Sglebius		return (EINVAL);
488165581Sglebius	}
489165581Sglebius
490165581Sglebius	/* Calculate resulting size. */
491165581Sglebius	outlen -= priv->cx.avail_out;
492165581Sglebius
493165581Sglebius	/* If we can't compress this packet, send it as-is. */
494165581Sglebius	if (outlen > inlen) {
495165581Sglebius		/* Return original packet uncompressed. */
496165581Sglebius		*resultp = m;
497165581Sglebius		priv->stats.FramesUncomp++;
498165581Sglebius		priv->stats.OutOctets+=inlen;
499165581Sglebius	} else {
500165581Sglebius		NG_FREE_M(m);
501165581Sglebius
502165581Sglebius		/* Install header. */
503165581Sglebius		((u_int16_t *)priv->outbuf)[0] = htons(PROT_COMPD);
504165581Sglebius		((u_int16_t *)priv->outbuf)[1] = htons(priv->seqnum);
505165581Sglebius
506165581Sglebius		/* Return packet in an mbuf. */
507165581Sglebius		*resultp = m_devget((caddr_t)priv->outbuf, outlen, 0, NULL,
508165581Sglebius		    NULL);
509165581Sglebius		if (*resultp == NULL) {
510165581Sglebius			priv->stats.Errors++;
511165581Sglebius			return (ENOMEM);
512165581Sglebius		};
513165581Sglebius		priv->stats.FramesComp++;
514165581Sglebius		priv->stats.OutOctets+=outlen;
515165581Sglebius	}
516165581Sglebius
517165581Sglebius	/* Update sequence number. */
518165581Sglebius	priv->seqnum++;
519165581Sglebius
520165581Sglebius	return (0);
521165581Sglebius}
522165581Sglebius
523165581Sglebius/*
524165581Sglebius * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
525165581Sglebius * The original mbuf is not free'd.
526165581Sglebius */
527165581Sglebiusstatic int
528165581Sglebiusng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp)
529165581Sglebius{
530165581Sglebius	const priv_p 	priv = NG_NODE_PRIVATE(node);
531165581Sglebius	int 		outlen, inlen;
532165581Sglebius	int 		rtn;
533165581Sglebius	uint16_t	proto;
534165581Sglebius	int		offset;
535165581Sglebius	uint16_t	rseqnum;
536165581Sglebius
537165581Sglebius	/* Initialize. */
538165581Sglebius	*resultp = NULL;
539165581Sglebius
540165581Sglebius	inlen = m->m_pkthdr.len;
541166019Sglebius
542165581Sglebius	if (inlen > DEFLATE_BUF_SIZE) {
543165581Sglebius		priv->stats.Errors++;
544165581Sglebius		NG_FREE_M(m);
545165581Sglebius		priv->seqnum = 0;
546165581Sglebius		return (ENOMEM);
547165581Sglebius	}
548165581Sglebius
549165581Sglebius	/* Work with contiguous regions of memory. */
550165581Sglebius	m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
551165581Sglebius
552165581Sglebius	/* Separate proto. */
553165581Sglebius	if ((priv->inbuf[0] & 0x01) != 0) {
554165581Sglebius		proto = priv->inbuf[0];
555165581Sglebius		offset = 1;
556165581Sglebius	} else {
557165581Sglebius		proto = ntohs(((uint16_t *)priv->inbuf)[0]);
558165581Sglebius		offset = 2;
559165581Sglebius	}
560165581Sglebius
561165925Sglebius	priv->stats.InOctets += inlen;
562165925Sglebius
563165581Sglebius	/* Packet is compressed, so decompress. */
564165581Sglebius	if (proto == PROT_COMPD) {
565165581Sglebius		priv->stats.FramesComp++;
566166019Sglebius
567165581Sglebius		/* Check sequence number. */
568165581Sglebius		rseqnum = ntohs(((uint16_t *)(priv->inbuf + offset))[0]);
569165581Sglebius		offset += 2;
570165581Sglebius		if (rseqnum != priv->seqnum) {
571165581Sglebius			priv->stats.Errors++;
572165581Sglebius			log(LOG_NOTICE, "ng_deflate: wrong sequence: %u "
573165581Sglebius			    "instead of %u\n", rseqnum, priv->seqnum);
574165581Sglebius			NG_FREE_M(m);
575165581Sglebius			priv->seqnum = 0;
576165581Sglebius			return (EPIPE);
577165581Sglebius		}
578165581Sglebius
579165581Sglebius		outlen = DEFLATE_BUF_SIZE;
580165581Sglebius
581165581Sglebius    		/* Decompress "inbuf" into "outbuf". */
582165581Sglebius		/* Prepare to decompress. */
583165581Sglebius		priv->cx.next_in = priv->inbuf + offset;
584165581Sglebius		priv->cx.avail_in = inlen - offset;
585165581Sglebius		/* Reserve space for protocol decompression. */
586165581Sglebius		priv->cx.next_out = priv->outbuf + 1;
587165581Sglebius		priv->cx.avail_out = outlen - 1;
588165581Sglebius
589165581Sglebius		/* Decompress. */
590165581Sglebius		rtn = inflate(&priv->cx, Z_PACKET_FLUSH);
591165581Sglebius
592165581Sglebius		/* Check return value. */
593165581Sglebius		if (rtn != Z_OK && rtn != Z_STREAM_END) {
594165581Sglebius			priv->stats.Errors++;
595165581Sglebius			NG_FREE_M(m);
596165581Sglebius			priv->seqnum = 0;
597165581Sglebius			log(LOG_NOTICE, "%s: decompression error: %d (%s)\n",
598165581Sglebius			    __func__, rtn, priv->cx.msg);
599165581Sglebius
600165581Sglebius			switch (rtn) {
601165581Sglebius			case Z_MEM_ERROR:
602165581Sglebius				return (ENOMEM);
603165581Sglebius			case Z_DATA_ERROR:
604165581Sglebius				return (EIO);
605165581Sglebius			default:
606165581Sglebius				return (EINVAL);
607165581Sglebius			}
608165581Sglebius		}
609165581Sglebius
610165581Sglebius		/* Calculate resulting size. */
611165581Sglebius		outlen -= priv->cx.avail_out;
612165581Sglebius
613165581Sglebius		NG_FREE_M(m);
614165581Sglebius
615165581Sglebius		/* Decompress protocol. */
616165581Sglebius		if ((priv->outbuf[1] & 0x01) != 0) {
617165581Sglebius			priv->outbuf[0] = 0;
618165581Sglebius			/* Return packet in an mbuf. */
619165581Sglebius			*resultp = m_devget((caddr_t)priv->outbuf, outlen, 0,
620165581Sglebius			    NULL, NULL);
621165581Sglebius		} else {
622165581Sglebius			outlen--;
623165581Sglebius			/* Return packet in an mbuf. */
624165581Sglebius			*resultp = m_devget((caddr_t)(priv->outbuf + 1),
625165581Sglebius			    outlen, 0, NULL, NULL);
626165581Sglebius		}
627165581Sglebius		if (*resultp == NULL) {
628165581Sglebius			priv->stats.Errors++;
629165581Sglebius			priv->seqnum = 0;
630165581Sglebius			return (ENOMEM);
631165581Sglebius		};
632165581Sglebius		priv->stats.FramesPlain++;
633165581Sglebius		priv->stats.OutOctets+=outlen;
634165581Sglebius
635165581Sglebius	} else { /* Packet is not compressed, just update dictionary. */
636165581Sglebius		priv->stats.FramesUncomp++;
637165581Sglebius		if (priv->inbuf[0] == 0) {
638165581Sglebius		    priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
639165581Sglebius		    priv->cx.avail_in = inlen - 1;
640165581Sglebius		} else {
641165581Sglebius		    priv->cx.next_in = priv->inbuf;
642165581Sglebius		    priv->cx.avail_in = inlen;
643165581Sglebius		}
644165581Sglebius
645165581Sglebius		rtn = inflateIncomp(&priv->cx);
646165581Sglebius
647165581Sglebius		/* Check return value */
648165581Sglebius		if (rtn != Z_OK) {
649165581Sglebius			priv->stats.Errors++;
650165581Sglebius			log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n",
651165581Sglebius			    __func__, rtn, priv->cx.msg);
652165581Sglebius			NG_FREE_M(m);
653165581Sglebius			priv->seqnum = 0;
654165581Sglebius			return (EINVAL);
655165581Sglebius		}
656165581Sglebius
657165581Sglebius		*resultp = m;
658165581Sglebius		priv->stats.FramesPlain++;
659165581Sglebius		priv->stats.OutOctets += inlen;
660165581Sglebius	}
661165581Sglebius
662165581Sglebius	/* Update sequence number. */
663165581Sglebius	priv->seqnum++;
664165581Sglebius
665165581Sglebius	return (0);
666165581Sglebius}
667165581Sglebius
668165581Sglebius/*
669165581Sglebius * The peer has sent us a CCP ResetRequest, so reset our transmit state.
670165581Sglebius */
671165581Sglebiusstatic void
672165581Sglebiusng_deflate_reset_req(node_p node)
673165581Sglebius{
674165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
675165581Sglebius
676165581Sglebius	priv->seqnum = 0;
677165581Sglebius	if (priv->cfg.enable) {
678165581Sglebius	    if (priv->compress)
679165581Sglebius		deflateReset(&priv->cx);
680165581Sglebius	    else
681165581Sglebius		inflateReset(&priv->cx);
682165581Sglebius	}
683165581Sglebius}
684165581Sglebius
685