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