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