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$
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>
39206049Smav#include <sys/endian.h>
40165581Sglebius#include <sys/errno.h>
41165581Sglebius#include <sys/syslog.h>
42165581Sglebius
43165581Sglebius#include <net/zlib.h>
44165581Sglebius
45165581Sglebius#include <netgraph/ng_message.h>
46165581Sglebius#include <netgraph/netgraph.h>
47165581Sglebius#include <netgraph/ng_parse.h>
48165581Sglebius#include <netgraph/ng_deflate.h>
49165581Sglebius
50165581Sglebius#include "opt_netgraph.h"
51165581Sglebius
52227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate",
53227293Sed    "netgraph deflate node");
54165581Sglebius
55165581Sglebius/* DEFLATE header length */
56165581Sglebius#define DEFLATE_HDRLEN		2
57165581Sglebius
58165581Sglebius#define PROT_COMPD		0x00fd
59165581Sglebius
60165581Sglebius#define DEFLATE_BUF_SIZE	4096
61165581Sglebius
62165581Sglebius/* Node private data */
63165581Sglebiusstruct ng_deflate_private {
64165581Sglebius	struct ng_deflate_config cfg;		/* configuration */
65165581Sglebius	u_char		inbuf[DEFLATE_BUF_SIZE];	/* input buffer */
66165581Sglebius	u_char		outbuf[DEFLATE_BUF_SIZE];	/* output buffer */
67165581Sglebius	z_stream 	cx;			/* compression context */
68165581Sglebius	struct ng_deflate_stats stats;		/* statistics */
69165581Sglebius	ng_ID_t		ctrlnode;		/* path to controlling node */
70165581Sglebius	uint16_t	seqnum;			/* sequence number */
71165581Sglebius	u_char		compress;		/* compress/decompress flag */
72165581Sglebius};
73165581Sglebiustypedef struct ng_deflate_private *priv_p;
74165581Sglebius
75165581Sglebius/* Netgraph node methods */
76165581Sglebiusstatic ng_constructor_t	ng_deflate_constructor;
77165581Sglebiusstatic ng_rcvmsg_t	ng_deflate_rcvmsg;
78165581Sglebiusstatic ng_shutdown_t	ng_deflate_shutdown;
79165581Sglebiusstatic ng_newhook_t	ng_deflate_newhook;
80165581Sglebiusstatic ng_rcvdata_t	ng_deflate_rcvdata;
81165581Sglebiusstatic ng_disconnect_t	ng_deflate_disconnect;
82165581Sglebius
83165581Sglebius/* Helper functions */
84165581Sglebiusstatic void	*z_alloc(void *, u_int items, u_int size);
85165581Sglebiusstatic void	z_free(void *, void *ptr);
86165581Sglebiusstatic int	ng_deflate_compress(node_p node,
87165581Sglebius		    struct mbuf *m, struct mbuf **resultp);
88165581Sglebiusstatic int	ng_deflate_decompress(node_p node,
89165581Sglebius		    struct mbuf *m, struct mbuf **resultp);
90165581Sglebiusstatic void	ng_deflate_reset_req(node_p node);
91165581Sglebius
92165581Sglebius/* Parse type for struct ng_deflate_config. */
93165581Sglebiusstatic const struct ng_parse_struct_field ng_deflate_config_type_fields[]
94165581Sglebius	= NG_DEFLATE_CONFIG_INFO;
95165581Sglebiusstatic const struct ng_parse_type ng_deflate_config_type = {
96165581Sglebius	&ng_parse_struct_type,
97165581Sglebius	ng_deflate_config_type_fields
98165581Sglebius};
99165581Sglebius
100165581Sglebius/* Parse type for struct ng_deflate_stat. */
101165581Sglebiusstatic const struct ng_parse_struct_field ng_deflate_stats_type_fields[]
102165581Sglebius	= NG_DEFLATE_STATS_INFO;
103165581Sglebiusstatic const struct ng_parse_type ng_deflate_stat_type = {
104165581Sglebius	&ng_parse_struct_type,
105165581Sglebius	ng_deflate_stats_type_fields
106165581Sglebius};
107165581Sglebius
108165581Sglebius/* List of commands and how to convert arguments to/from ASCII. */
109165581Sglebiusstatic const struct ng_cmdlist ng_deflate_cmds[] = {
110165581Sglebius	{
111165581Sglebius	  NGM_DEFLATE_COOKIE,
112165581Sglebius	  NGM_DEFLATE_CONFIG,
113165581Sglebius	  "config",
114165581Sglebius	  &ng_deflate_config_type,
115165581Sglebius	  NULL
116165581Sglebius	},
117165581Sglebius	{
118165581Sglebius	  NGM_DEFLATE_COOKIE,
119165581Sglebius	  NGM_DEFLATE_RESETREQ,
120165581Sglebius	  "resetreq",
121165581Sglebius	  NULL,
122165581Sglebius	  NULL
123165581Sglebius	},
124165581Sglebius	{
125165581Sglebius	  NGM_DEFLATE_COOKIE,
126165581Sglebius	  NGM_DEFLATE_GET_STATS,
127165581Sglebius	  "getstats",
128165581Sglebius	  NULL,
129165581Sglebius	  &ng_deflate_stat_type
130165581Sglebius	},
131165581Sglebius	{
132165581Sglebius	  NGM_DEFLATE_COOKIE,
133165581Sglebius	  NGM_DEFLATE_CLR_STATS,
134165581Sglebius	  "clrstats",
135165581Sglebius	  NULL,
136165581Sglebius	  NULL
137165581Sglebius	},
138165581Sglebius	{
139165581Sglebius	  NGM_DEFLATE_COOKIE,
140165581Sglebius	  NGM_DEFLATE_GETCLR_STATS,
141165581Sglebius	  "getclrstats",
142165581Sglebius	  NULL,
143165581Sglebius	  &ng_deflate_stat_type
144165581Sglebius	},
145165581Sglebius	{ 0 }
146165581Sglebius};
147165581Sglebius
148165581Sglebius/* Node type descriptor */
149165581Sglebiusstatic struct ng_type ng_deflate_typestruct = {
150165581Sglebius	.version =	NG_ABI_VERSION,
151165581Sglebius	.name =		NG_DEFLATE_NODE_TYPE,
152165581Sglebius	.constructor =	ng_deflate_constructor,
153165581Sglebius	.rcvmsg =	ng_deflate_rcvmsg,
154165581Sglebius	.shutdown =	ng_deflate_shutdown,
155165581Sglebius	.newhook =	ng_deflate_newhook,
156165581Sglebius	.rcvdata =	ng_deflate_rcvdata,
157165581Sglebius	.disconnect =	ng_deflate_disconnect,
158165581Sglebius	.cmdlist =	ng_deflate_cmds,
159165581Sglebius};
160165581SglebiusNETGRAPH_INIT(deflate, &ng_deflate_typestruct);
161165581Sglebius
162165581Sglebius/* Depend on separate zlib module. */
163165581SglebiusMODULE_DEPEND(ng_deflate, zlib, 1, 1, 1);
164165581Sglebius
165165581Sglebius#define ERROUT(x)	do { error = (x); goto done; } while (0)
166165581Sglebius
167165581Sglebius/************************************************************************
168165581Sglebius			NETGRAPH NODE STUFF
169165581Sglebius ************************************************************************/
170165581Sglebius
171165581Sglebius/*
172165581Sglebius * Node type constructor
173165581Sglebius */
174165581Sglebiusstatic int
175165581Sglebiusng_deflate_constructor(node_p node)
176165581Sglebius{
177165581Sglebius	priv_p priv;
178165581Sglebius
179165581Sglebius	/* Allocate private structure. */
180165581Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO);
181165581Sglebius
182165581Sglebius	NG_NODE_SET_PRIVATE(node, priv);
183165581Sglebius
184165581Sglebius	/* This node is not thread safe. */
185165581Sglebius	NG_NODE_FORCE_WRITER(node);
186165581Sglebius
187165581Sglebius	/* Done */
188165581Sglebius	return (0);
189165581Sglebius}
190165581Sglebius
191165581Sglebius/*
192165581Sglebius * Give our OK for a hook to be added.
193165581Sglebius */
194165581Sglebiusstatic int
195165581Sglebiusng_deflate_newhook(node_p node, hook_p hook, const char *name)
196165581Sglebius{
197165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
198165581Sglebius
199165581Sglebius	if (NG_NODE_NUMHOOKS(node) > 0)
200165581Sglebius		return (EINVAL);
201165581Sglebius
202165581Sglebius	if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0)
203165581Sglebius		priv->compress = 1;
204165581Sglebius	else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0)
205165581Sglebius		priv->compress = 0;
206165581Sglebius	else
207165581Sglebius		return (EINVAL);
208165581Sglebius
209165581Sglebius	return (0);
210165581Sglebius}
211165581Sglebius
212165581Sglebius/*
213165581Sglebius * Receive a control message
214165581Sglebius */
215165581Sglebiusstatic int
216165581Sglebiusng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook)
217165581Sglebius{
218165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
219165581Sglebius	struct ng_mesg *resp = NULL;
220165581Sglebius	int error = 0;
221165581Sglebius	struct ng_mesg *msg;
222165581Sglebius
223165581Sglebius	NGI_GET_MSG(item, msg);
224165581Sglebius
225165581Sglebius	if (msg->header.typecookie != NGM_DEFLATE_COOKIE)
226165581Sglebius		ERROUT(EINVAL);
227166019Sglebius
228165581Sglebius	switch (msg->header.cmd) {
229165581Sglebius	case NGM_DEFLATE_CONFIG:
230165581Sglebius	    {
231165581Sglebius		struct ng_deflate_config *const cfg
232165581Sglebius		    = (struct ng_deflate_config *)msg->data;
233165581Sglebius
234165581Sglebius		/* Check configuration. */
235165581Sglebius		if (msg->header.arglen != sizeof(*cfg))
236165581Sglebius			ERROUT(EINVAL);
237165581Sglebius		if (cfg->enable) {
238165581Sglebius		    if (cfg->windowBits < 8 || cfg->windowBits > 15)
239165581Sglebius			ERROUT(EINVAL);
240165581Sglebius		} else
241165581Sglebius		    cfg->windowBits = 0;
242165581Sglebius
243165581Sglebius		/* Clear previous state. */
244165581Sglebius		if (priv->cfg.enable) {
245165581Sglebius			if (priv->compress)
246165581Sglebius				deflateEnd(&priv->cx);
247165581Sglebius			else
248165581Sglebius				inflateEnd(&priv->cx);
249165581Sglebius			priv->cfg.enable = 0;
250165581Sglebius		}
251166019Sglebius
252165581Sglebius		/* Configuration is OK, reset to it. */
253165581Sglebius		priv->cfg = *cfg;
254165581Sglebius
255165581Sglebius		if (priv->cfg.enable) {
256165581Sglebius			priv->cx.next_in = NULL;
257165581Sglebius			priv->cx.zalloc = z_alloc;
258165581Sglebius			priv->cx.zfree = z_free;
259165581Sglebius			int res;
260165581Sglebius			if (priv->compress) {
261165581Sglebius				if ((res = deflateInit2(&priv->cx,
262165581Sglebius				    Z_DEFAULT_COMPRESSION, Z_DEFLATED,
263165581Sglebius				    -cfg->windowBits, 8,
264165581Sglebius				    Z_DEFAULT_STRATEGY)) != Z_OK) {
265165581Sglebius					log(LOG_NOTICE,
266165581Sglebius					    "deflateInit2: error %d, %s\n",
267165581Sglebius					    res, priv->cx.msg);
268165581Sglebius					priv->cfg.enable = 0;
269165581Sglebius					ERROUT(ENOMEM);
270165581Sglebius				}
271165581Sglebius			} else {
272165581Sglebius				if ((res = inflateInit2(&priv->cx,
273165581Sglebius				    -cfg->windowBits)) != Z_OK) {
274165581Sglebius					log(LOG_NOTICE,
275165581Sglebius					    "inflateInit2: error %d, %s\n",
276165581Sglebius					    res, priv->cx.msg);
277165581Sglebius					priv->cfg.enable = 0;
278165581Sglebius					ERROUT(ENOMEM);
279165581Sglebius				}
280165581Sglebius			}
281165581Sglebius		}
282165581Sglebius
283165581Sglebius		/* Initialize other state. */
284165581Sglebius		priv->seqnum = 0;
285165581Sglebius
286165581Sglebius		/* Save return address so we can send reset-req's */
287165581Sglebius		priv->ctrlnode = NGI_RETADDR(item);
288165581Sglebius		break;
289165581Sglebius	    }
290165581Sglebius
291165581Sglebius	case NGM_DEFLATE_RESETREQ:
292165581Sglebius		ng_deflate_reset_req(node);
293165581Sglebius		break;
294165581Sglebius
295165581Sglebius	case NGM_DEFLATE_GET_STATS:
296165581Sglebius	case NGM_DEFLATE_CLR_STATS:
297165581Sglebius	case NGM_DEFLATE_GETCLR_STATS:
298165581Sglebius		/* Create response if requested. */
299165581Sglebius		if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) {
300165581Sglebius			NG_MKRESPONSE(resp, msg,
301165581Sglebius			    sizeof(struct ng_deflate_stats), M_NOWAIT);
302165581Sglebius			if (resp == NULL)
303165581Sglebius				ERROUT(ENOMEM);
304165581Sglebius			bcopy(&priv->stats, resp->data,
305166019Sglebius			    sizeof(struct ng_deflate_stats));
306165581Sglebius		}
307165581Sglebius
308165581Sglebius		/* Clear stats if requested. */
309165581Sglebius		if (msg->header.cmd != NGM_DEFLATE_GET_STATS)
310165581Sglebius			bzero(&priv->stats,
311165581Sglebius			    sizeof(struct ng_deflate_stats));
312165581Sglebius		break;
313165581Sglebius
314165581Sglebius	default:
315165581Sglebius		error = EINVAL;
316165581Sglebius		break;
317165581Sglebius	}
318165581Sglebiusdone:
319165581Sglebius	NG_RESPOND_MSG(error, node, item, resp);
320165581Sglebius	NG_FREE_MSG(msg);
321165581Sglebius	return (error);
322165581Sglebius}
323165581Sglebius
324165581Sglebius/*
325165581Sglebius * Receive incoming data on our hook.
326165581Sglebius */
327165581Sglebiusstatic int
328165581Sglebiusng_deflate_rcvdata(hook_p hook, item_p item)
329165581Sglebius{
330165581Sglebius	const node_p node = NG_HOOK_NODE(hook);
331165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
332165581Sglebius	struct mbuf *m, *out;
333165581Sglebius	int error;
334165581Sglebius
335165581Sglebius	if (!priv->cfg.enable) {
336165581Sglebius		NG_FREE_ITEM(item);
337165581Sglebius		return (ENXIO);
338165581Sglebius	}
339166019Sglebius
340165581Sglebius	NGI_GET_M(item, m);
341165581Sglebius	/* Compress */
342165581Sglebius	if (priv->compress) {
343165581Sglebius		if ((error = ng_deflate_compress(node, m, &out)) != 0) {
344165581Sglebius			NG_FREE_ITEM(item);
345165581Sglebius			log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
346165581Sglebius			return (error);
347165581Sglebius		}
348165581Sglebius
349165581Sglebius	} else { /* Decompress */
350165581Sglebius		if ((error = ng_deflate_decompress(node, m, &out)) != 0) {
351165581Sglebius			NG_FREE_ITEM(item);
352165581Sglebius			log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
353165581Sglebius			if (priv->ctrlnode != 0) {
354165581Sglebius				struct ng_mesg *msg;
355165581Sglebius
356165581Sglebius				/* Need to send a reset-request. */
357165581Sglebius				NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE,
358165581Sglebius				    NGM_DEFLATE_RESETREQ, 0, M_NOWAIT);
359165581Sglebius				if (msg == NULL)
360165581Sglebius					return (error);
361165581Sglebius				NG_SEND_MSG_ID(error, node, msg,
362165581Sglebius					priv->ctrlnode, 0);
363165581Sglebius			}
364165581Sglebius			return (error);
365165581Sglebius		}
366165581Sglebius	}
367165581Sglebius
368165581Sglebius	NG_FWD_NEW_DATA(error, item, hook, out);
369165581Sglebius	return (error);
370165581Sglebius}
371165581Sglebius
372165581Sglebius/*
373165581Sglebius * Destroy node.
374165581Sglebius */
375165581Sglebiusstatic int
376165581Sglebiusng_deflate_shutdown(node_p node)
377165581Sglebius{
378165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
379165581Sglebius
380165581Sglebius	/* Take down netgraph node. */
381165581Sglebius	if (priv->cfg.enable) {
382165581Sglebius	    if (priv->compress)
383165581Sglebius		deflateEnd(&priv->cx);
384165581Sglebius	    else
385165581Sglebius		inflateEnd(&priv->cx);
386165581Sglebius	}
387165581Sglebius
388165581Sglebius	free(priv, M_NETGRAPH_DEFLATE);
389165581Sglebius	NG_NODE_SET_PRIVATE(node, NULL);
390165581Sglebius	NG_NODE_UNREF(node);		/* let the node escape */
391165581Sglebius	return (0);
392165581Sglebius}
393165581Sglebius
394165581Sglebius/*
395165581Sglebius * Hook disconnection
396165581Sglebius */
397165581Sglebiusstatic int
398165581Sglebiusng_deflate_disconnect(hook_p hook)
399165581Sglebius{
400165581Sglebius	const node_p node = NG_HOOK_NODE(hook);
401165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
402165581Sglebius
403165581Sglebius	if (priv->cfg.enable) {
404165581Sglebius	    if (priv->compress)
405165581Sglebius		deflateEnd(&priv->cx);
406165581Sglebius	    else
407165581Sglebius		inflateEnd(&priv->cx);
408165581Sglebius	    priv->cfg.enable = 0;
409165581Sglebius	}
410165581Sglebius
411165581Sglebius	/* Go away if no longer connected. */
412165581Sglebius	if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node))
413165581Sglebius		ng_rmnode_self(node);
414165581Sglebius	return (0);
415165581Sglebius}
416165581Sglebius
417165581Sglebius/************************************************************************
418165581Sglebius			HELPER STUFF
419165581Sglebius ************************************************************************/
420165581Sglebius
421165581Sglebius/*
422165581Sglebius * Space allocation and freeing routines for use by zlib routines.
423165581Sglebius */
424165581Sglebius
425165581Sglebiusstatic void *
426165581Sglebiusz_alloc(void *notused, u_int items, u_int size)
427165581Sglebius{
428165581Sglebius
429165581Sglebius	return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT));
430165581Sglebius}
431165581Sglebius
432165581Sglebiusstatic void
433165581Sglebiusz_free(void *notused, void *ptr)
434165581Sglebius{
435165581Sglebius
436165581Sglebius	free(ptr, M_NETGRAPH_DEFLATE);
437165581Sglebius}
438165581Sglebius
439165581Sglebius/*
440165581Sglebius * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
441165581Sglebius * The original mbuf is not free'd.
442165581Sglebius */
443165581Sglebiusstatic int
444165581Sglebiusng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
445165581Sglebius{
446165581Sglebius	const priv_p 	priv = NG_NODE_PRIVATE(node);
447165581Sglebius	int 		outlen, inlen;
448165581Sglebius	int 		rtn;
449165581Sglebius
450165581Sglebius	/* Initialize. */
451165581Sglebius	*resultp = NULL;
452165581Sglebius
453165581Sglebius	inlen = m->m_pkthdr.len;
454165581Sglebius
455165581Sglebius	priv->stats.FramesPlain++;
456165581Sglebius	priv->stats.InOctets+=inlen;
457165581Sglebius
458165581Sglebius	if (inlen > DEFLATE_BUF_SIZE) {
459165581Sglebius		priv->stats.Errors++;
460165581Sglebius		NG_FREE_M(m);
461165581Sglebius		return (ENOMEM);
462165581Sglebius	}
463166019Sglebius
464187405Smav	/* We must own the mbuf chain exclusively to modify it. */
465243882Sglebius	m = m_unshare(m, M_NOWAIT);
466187405Smav	if (m == NULL) {
467187405Smav		priv->stats.Errors++;
468187405Smav		return (ENOMEM);
469187405Smav	}
470187405Smav
471165581Sglebius	/* Work with contiguous regions of memory. */
472165581Sglebius	m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
473165581Sglebius	outlen = DEFLATE_BUF_SIZE;
474165581Sglebius
475165581Sglebius	/* Compress "inbuf" into "outbuf". */
476165581Sglebius	/* Prepare to compress. */
477165581Sglebius	if (priv->inbuf[0] != 0) {
478165581Sglebius		priv->cx.next_in = priv->inbuf;
479165581Sglebius		priv->cx.avail_in = inlen;
480165581Sglebius	} else {
481165581Sglebius		priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
482165581Sglebius		priv->cx.avail_in = inlen - 1;
483165581Sglebius	}
484165581Sglebius	priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN;
485165581Sglebius	priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN;
486165581Sglebius
487165581Sglebius	/* Compress. */
488165581Sglebius	rtn = deflate(&priv->cx, Z_PACKET_FLUSH);
489165581Sglebius
490165581Sglebius	/* Check return value. */
491165581Sglebius	if (rtn != Z_OK) {
492165581Sglebius		priv->stats.Errors++;
493165581Sglebius		log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n",
494165581Sglebius		    rtn, priv->cx.msg);
495165581Sglebius		NG_FREE_M(m);
496165581Sglebius		return (EINVAL);
497165581Sglebius	}
498165581Sglebius
499165581Sglebius	/* Calculate resulting size. */
500165581Sglebius	outlen -= priv->cx.avail_out;
501165581Sglebius
502165581Sglebius	/* If we can't compress this packet, send it as-is. */
503165581Sglebius	if (outlen > inlen) {
504165581Sglebius		/* Return original packet uncompressed. */
505165581Sglebius		*resultp = m;
506165581Sglebius		priv->stats.FramesUncomp++;
507165581Sglebius		priv->stats.OutOctets+=inlen;
508165581Sglebius	} else {
509165581Sglebius		/* Install header. */
510206049Smav		be16enc(priv->outbuf, PROT_COMPD);
511206049Smav		be16enc(priv->outbuf + 2, priv->seqnum);
512165581Sglebius
513165581Sglebius		/* Return packet in an mbuf. */
514187405Smav		m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
515187405Smav		if (m->m_pkthdr.len < outlen) {
516187405Smav			m_freem(m);
517165581Sglebius			priv->stats.Errors++;
518165581Sglebius			return (ENOMEM);
519187405Smav		} else if (outlen < m->m_pkthdr.len)
520187405Smav			m_adj(m, outlen - m->m_pkthdr.len);
521187405Smav		*resultp = m;
522165581Sglebius		priv->stats.FramesComp++;
523165581Sglebius		priv->stats.OutOctets+=outlen;
524165581Sglebius	}
525165581Sglebius
526165581Sglebius	/* Update sequence number. */
527165581Sglebius	priv->seqnum++;
528165581Sglebius
529165581Sglebius	return (0);
530165581Sglebius}
531165581Sglebius
532165581Sglebius/*
533165581Sglebius * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
534165581Sglebius * The original mbuf is not free'd.
535165581Sglebius */
536165581Sglebiusstatic int
537165581Sglebiusng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp)
538165581Sglebius{
539165581Sglebius	const priv_p 	priv = NG_NODE_PRIVATE(node);
540165581Sglebius	int 		outlen, inlen;
541165581Sglebius	int 		rtn;
542165581Sglebius	uint16_t	proto;
543165581Sglebius	int		offset;
544165581Sglebius	uint16_t	rseqnum;
545165581Sglebius
546165581Sglebius	/* Initialize. */
547165581Sglebius	*resultp = NULL;
548165581Sglebius
549165581Sglebius	inlen = m->m_pkthdr.len;
550166019Sglebius
551165581Sglebius	if (inlen > DEFLATE_BUF_SIZE) {
552165581Sglebius		priv->stats.Errors++;
553165581Sglebius		NG_FREE_M(m);
554165581Sglebius		priv->seqnum = 0;
555165581Sglebius		return (ENOMEM);
556165581Sglebius	}
557165581Sglebius
558187405Smav	/* We must own the mbuf chain exclusively to modify it. */
559243882Sglebius	m = m_unshare(m, M_NOWAIT);
560187405Smav	if (m == NULL) {
561187405Smav		priv->stats.Errors++;
562187405Smav		return (ENOMEM);
563187405Smav	}
564187405Smav
565165581Sglebius	/* Work with contiguous regions of memory. */
566165581Sglebius	m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
567165581Sglebius
568165581Sglebius	/* Separate proto. */
569165581Sglebius	if ((priv->inbuf[0] & 0x01) != 0) {
570165581Sglebius		proto = priv->inbuf[0];
571165581Sglebius		offset = 1;
572165581Sglebius	} else {
573206049Smav		proto = be16dec(priv->inbuf);
574165581Sglebius		offset = 2;
575165581Sglebius	}
576165581Sglebius
577165925Sglebius	priv->stats.InOctets += inlen;
578165925Sglebius
579165581Sglebius	/* Packet is compressed, so decompress. */
580165581Sglebius	if (proto == PROT_COMPD) {
581165581Sglebius		priv->stats.FramesComp++;
582166019Sglebius
583165581Sglebius		/* Check sequence number. */
584206049Smav		rseqnum = be16dec(priv->inbuf + offset);
585165581Sglebius		offset += 2;
586165581Sglebius		if (rseqnum != priv->seqnum) {
587165581Sglebius			priv->stats.Errors++;
588165581Sglebius			log(LOG_NOTICE, "ng_deflate: wrong sequence: %u "
589165581Sglebius			    "instead of %u\n", rseqnum, priv->seqnum);
590165581Sglebius			NG_FREE_M(m);
591165581Sglebius			priv->seqnum = 0;
592165581Sglebius			return (EPIPE);
593165581Sglebius		}
594165581Sglebius
595165581Sglebius		outlen = DEFLATE_BUF_SIZE;
596165581Sglebius
597165581Sglebius    		/* Decompress "inbuf" into "outbuf". */
598165581Sglebius		/* Prepare to decompress. */
599165581Sglebius		priv->cx.next_in = priv->inbuf + offset;
600165581Sglebius		priv->cx.avail_in = inlen - offset;
601165581Sglebius		/* Reserve space for protocol decompression. */
602165581Sglebius		priv->cx.next_out = priv->outbuf + 1;
603165581Sglebius		priv->cx.avail_out = outlen - 1;
604165581Sglebius
605165581Sglebius		/* Decompress. */
606165581Sglebius		rtn = inflate(&priv->cx, Z_PACKET_FLUSH);
607165581Sglebius
608165581Sglebius		/* Check return value. */
609165581Sglebius		if (rtn != Z_OK && rtn != Z_STREAM_END) {
610165581Sglebius			priv->stats.Errors++;
611165581Sglebius			NG_FREE_M(m);
612165581Sglebius			priv->seqnum = 0;
613165581Sglebius			log(LOG_NOTICE, "%s: decompression error: %d (%s)\n",
614165581Sglebius			    __func__, rtn, priv->cx.msg);
615165581Sglebius
616165581Sglebius			switch (rtn) {
617165581Sglebius			case Z_MEM_ERROR:
618165581Sglebius				return (ENOMEM);
619165581Sglebius			case Z_DATA_ERROR:
620165581Sglebius				return (EIO);
621165581Sglebius			default:
622165581Sglebius				return (EINVAL);
623165581Sglebius			}
624165581Sglebius		}
625165581Sglebius
626165581Sglebius		/* Calculate resulting size. */
627165581Sglebius		outlen -= priv->cx.avail_out;
628165581Sglebius
629165581Sglebius		/* Decompress protocol. */
630165581Sglebius		if ((priv->outbuf[1] & 0x01) != 0) {
631165581Sglebius			priv->outbuf[0] = 0;
632165581Sglebius			/* Return packet in an mbuf. */
633187405Smav			m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
634165581Sglebius		} else {
635165581Sglebius			outlen--;
636165581Sglebius			/* Return packet in an mbuf. */
637187405Smav			m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1));
638165581Sglebius		}
639187405Smav		if (m->m_pkthdr.len < outlen) {
640187405Smav			m_freem(m);
641165581Sglebius			priv->stats.Errors++;
642165581Sglebius			priv->seqnum = 0;
643165581Sglebius			return (ENOMEM);
644187405Smav		} else if (outlen < m->m_pkthdr.len)
645187405Smav			m_adj(m, outlen - m->m_pkthdr.len);
646187405Smav		*resultp = m;
647165581Sglebius		priv->stats.FramesPlain++;
648165581Sglebius		priv->stats.OutOctets+=outlen;
649165581Sglebius
650165581Sglebius	} else { /* Packet is not compressed, just update dictionary. */
651165581Sglebius		priv->stats.FramesUncomp++;
652165581Sglebius		if (priv->inbuf[0] == 0) {
653165581Sglebius		    priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
654165581Sglebius		    priv->cx.avail_in = inlen - 1;
655165581Sglebius		} else {
656165581Sglebius		    priv->cx.next_in = priv->inbuf;
657165581Sglebius		    priv->cx.avail_in = inlen;
658165581Sglebius		}
659165581Sglebius
660165581Sglebius		rtn = inflateIncomp(&priv->cx);
661165581Sglebius
662165581Sglebius		/* Check return value */
663165581Sglebius		if (rtn != Z_OK) {
664165581Sglebius			priv->stats.Errors++;
665165581Sglebius			log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n",
666165581Sglebius			    __func__, rtn, priv->cx.msg);
667165581Sglebius			NG_FREE_M(m);
668165581Sglebius			priv->seqnum = 0;
669165581Sglebius			return (EINVAL);
670165581Sglebius		}
671165581Sglebius
672165581Sglebius		*resultp = m;
673165581Sglebius		priv->stats.FramesPlain++;
674165581Sglebius		priv->stats.OutOctets += inlen;
675165581Sglebius	}
676165581Sglebius
677165581Sglebius	/* Update sequence number. */
678165581Sglebius	priv->seqnum++;
679165581Sglebius
680165581Sglebius	return (0);
681165581Sglebius}
682165581Sglebius
683165581Sglebius/*
684165581Sglebius * The peer has sent us a CCP ResetRequest, so reset our transmit state.
685165581Sglebius */
686165581Sglebiusstatic void
687165581Sglebiusng_deflate_reset_req(node_p node)
688165581Sglebius{
689165581Sglebius	const priv_p priv = NG_NODE_PRIVATE(node);
690165581Sglebius
691165581Sglebius	priv->seqnum = 0;
692165581Sglebius	if (priv->cfg.enable) {
693165581Sglebius	    if (priv->compress)
694165581Sglebius		deflateReset(&priv->cx);
695165581Sglebius	    else
696165581Sglebius		inflateReset(&priv->cx);
697165581Sglebius	}
698165581Sglebius}
699165581Sglebius
700