xref: /freebsd/sys/netgraph/ng_deflate.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
181ccbd95SGleb Smirnoff /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
481ccbd95SGleb Smirnoff  * Copyright (c) 2006 Alexander Motin <mav@alkar.net>
581ccbd95SGleb Smirnoff  * All rights reserved.
681ccbd95SGleb Smirnoff  *
781ccbd95SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
881ccbd95SGleb Smirnoff  * modification, are permitted provided that the following conditions
981ccbd95SGleb Smirnoff  * are met:
1081ccbd95SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
1181ccbd95SGleb Smirnoff  *    notice unmodified, this list of conditions, and the following
1281ccbd95SGleb Smirnoff  *    disclaimer.
1381ccbd95SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
1481ccbd95SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
1581ccbd95SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
1681ccbd95SGleb Smirnoff  *
1781ccbd95SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1881ccbd95SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1981ccbd95SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2081ccbd95SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2181ccbd95SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2281ccbd95SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2381ccbd95SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2481ccbd95SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2581ccbd95SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2681ccbd95SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2781ccbd95SGleb Smirnoff  * SUCH DAMAGE.
2881ccbd95SGleb Smirnoff  */
2981ccbd95SGleb Smirnoff 
3081ccbd95SGleb Smirnoff /*
3181ccbd95SGleb Smirnoff  * Deflate PPP compression netgraph node type.
3281ccbd95SGleb Smirnoff  */
3381ccbd95SGleb Smirnoff 
3481ccbd95SGleb Smirnoff #include <sys/param.h>
3581ccbd95SGleb Smirnoff #include <sys/systm.h>
3681ccbd95SGleb Smirnoff #include <sys/kernel.h>
3781ccbd95SGleb Smirnoff #include <sys/mbuf.h>
3881ccbd95SGleb Smirnoff #include <sys/malloc.h>
39b652a5faSAlexander Motin #include <sys/endian.h>
4081ccbd95SGleb Smirnoff #include <sys/errno.h>
4181ccbd95SGleb Smirnoff #include <sys/syslog.h>
4234ff55b6SXin LI #include <contrib/zlib/zlib.h>
4381ccbd95SGleb Smirnoff 
4481ccbd95SGleb Smirnoff #include <netgraph/ng_message.h>
4581ccbd95SGleb Smirnoff #include <netgraph/netgraph.h>
4681ccbd95SGleb Smirnoff #include <netgraph/ng_parse.h>
4781ccbd95SGleb Smirnoff #include <netgraph/ng_deflate.h>
4881ccbd95SGleb Smirnoff 
4981ccbd95SGleb Smirnoff #include "opt_netgraph.h"
5081ccbd95SGleb Smirnoff 
51d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate",
52d745c852SEd Schouten     "netgraph deflate node");
5381ccbd95SGleb Smirnoff 
5481ccbd95SGleb Smirnoff /* DEFLATE header length */
5581ccbd95SGleb Smirnoff #define DEFLATE_HDRLEN		2
5681ccbd95SGleb Smirnoff 
5781ccbd95SGleb Smirnoff #define PROT_COMPD		0x00fd
5881ccbd95SGleb Smirnoff 
5981ccbd95SGleb Smirnoff #define DEFLATE_BUF_SIZE	4096
6081ccbd95SGleb Smirnoff 
6181ccbd95SGleb Smirnoff /* Node private data */
6281ccbd95SGleb Smirnoff struct ng_deflate_private {
6381ccbd95SGleb Smirnoff 	struct ng_deflate_config cfg;		/* configuration */
6481ccbd95SGleb Smirnoff 	u_char		inbuf[DEFLATE_BUF_SIZE];	/* input buffer */
6581ccbd95SGleb Smirnoff 	u_char		outbuf[DEFLATE_BUF_SIZE];	/* output buffer */
6681ccbd95SGleb Smirnoff 	z_stream 	cx;			/* compression context */
6781ccbd95SGleb Smirnoff 	struct ng_deflate_stats stats;		/* statistics */
6881ccbd95SGleb Smirnoff 	ng_ID_t		ctrlnode;		/* path to controlling node */
6981ccbd95SGleb Smirnoff 	uint16_t	seqnum;			/* sequence number */
7081ccbd95SGleb Smirnoff 	u_char		compress;		/* compress/decompress flag */
7181ccbd95SGleb Smirnoff };
7281ccbd95SGleb Smirnoff typedef struct ng_deflate_private *priv_p;
7381ccbd95SGleb Smirnoff 
7481ccbd95SGleb Smirnoff /* Netgraph node methods */
7581ccbd95SGleb Smirnoff static ng_constructor_t	ng_deflate_constructor;
7681ccbd95SGleb Smirnoff static ng_rcvmsg_t	ng_deflate_rcvmsg;
7781ccbd95SGleb Smirnoff static ng_shutdown_t	ng_deflate_shutdown;
7881ccbd95SGleb Smirnoff static ng_newhook_t	ng_deflate_newhook;
7981ccbd95SGleb Smirnoff static ng_rcvdata_t	ng_deflate_rcvdata;
8081ccbd95SGleb Smirnoff static ng_disconnect_t	ng_deflate_disconnect;
8181ccbd95SGleb Smirnoff 
8281ccbd95SGleb Smirnoff /* Helper functions */
8334ff55b6SXin LI static int	ng_deflate_compress(node_p, struct mbuf *, struct mbuf **);
8434ff55b6SXin LI static int	ng_deflate_decompress(node_p, struct mbuf *, struct mbuf **);
8534ff55b6SXin LI static void	ng_deflate_reset_req(node_p);
8681ccbd95SGleb Smirnoff 
8781ccbd95SGleb Smirnoff /* Parse type for struct ng_deflate_config. */
8881ccbd95SGleb Smirnoff static const struct ng_parse_struct_field ng_deflate_config_type_fields[]
8981ccbd95SGleb Smirnoff 	= NG_DEFLATE_CONFIG_INFO;
9081ccbd95SGleb Smirnoff static const struct ng_parse_type ng_deflate_config_type = {
9181ccbd95SGleb Smirnoff 	&ng_parse_struct_type,
9281ccbd95SGleb Smirnoff 	ng_deflate_config_type_fields
9381ccbd95SGleb Smirnoff };
9481ccbd95SGleb Smirnoff 
9581ccbd95SGleb Smirnoff /* Parse type for struct ng_deflate_stat. */
9681ccbd95SGleb Smirnoff static const struct ng_parse_struct_field ng_deflate_stats_type_fields[]
9781ccbd95SGleb Smirnoff 	= NG_DEFLATE_STATS_INFO;
9881ccbd95SGleb Smirnoff static const struct ng_parse_type ng_deflate_stat_type = {
9981ccbd95SGleb Smirnoff 	&ng_parse_struct_type,
10081ccbd95SGleb Smirnoff 	ng_deflate_stats_type_fields
10181ccbd95SGleb Smirnoff };
10281ccbd95SGleb Smirnoff 
10381ccbd95SGleb Smirnoff /* List of commands and how to convert arguments to/from ASCII. */
10481ccbd95SGleb Smirnoff static const struct ng_cmdlist ng_deflate_cmds[] = {
10581ccbd95SGleb Smirnoff 	{
10681ccbd95SGleb Smirnoff 	  NGM_DEFLATE_COOKIE,
10781ccbd95SGleb Smirnoff 	  NGM_DEFLATE_CONFIG,
10881ccbd95SGleb Smirnoff 	  "config",
10981ccbd95SGleb Smirnoff 	  &ng_deflate_config_type,
11081ccbd95SGleb Smirnoff 	  NULL
11181ccbd95SGleb Smirnoff 	},
11281ccbd95SGleb Smirnoff 	{
11381ccbd95SGleb Smirnoff 	  NGM_DEFLATE_COOKIE,
11481ccbd95SGleb Smirnoff 	  NGM_DEFLATE_RESETREQ,
11581ccbd95SGleb Smirnoff 	  "resetreq",
11681ccbd95SGleb Smirnoff 	  NULL,
11781ccbd95SGleb Smirnoff 	  NULL
11881ccbd95SGleb Smirnoff 	},
11981ccbd95SGleb Smirnoff 	{
12081ccbd95SGleb Smirnoff 	  NGM_DEFLATE_COOKIE,
12181ccbd95SGleb Smirnoff 	  NGM_DEFLATE_GET_STATS,
12281ccbd95SGleb Smirnoff 	  "getstats",
12381ccbd95SGleb Smirnoff 	  NULL,
12481ccbd95SGleb Smirnoff 	  &ng_deflate_stat_type
12581ccbd95SGleb Smirnoff 	},
12681ccbd95SGleb Smirnoff 	{
12781ccbd95SGleb Smirnoff 	  NGM_DEFLATE_COOKIE,
12881ccbd95SGleb Smirnoff 	  NGM_DEFLATE_CLR_STATS,
12981ccbd95SGleb Smirnoff 	  "clrstats",
13081ccbd95SGleb Smirnoff 	  NULL,
13181ccbd95SGleb Smirnoff 	  NULL
13281ccbd95SGleb Smirnoff 	},
13381ccbd95SGleb Smirnoff 	{
13481ccbd95SGleb Smirnoff 	  NGM_DEFLATE_COOKIE,
13581ccbd95SGleb Smirnoff 	  NGM_DEFLATE_GETCLR_STATS,
13681ccbd95SGleb Smirnoff 	  "getclrstats",
13781ccbd95SGleb Smirnoff 	  NULL,
13881ccbd95SGleb Smirnoff 	  &ng_deflate_stat_type
13981ccbd95SGleb Smirnoff 	},
14081ccbd95SGleb Smirnoff 	{ 0 }
14181ccbd95SGleb Smirnoff };
14281ccbd95SGleb Smirnoff 
14381ccbd95SGleb Smirnoff /* Node type descriptor */
14481ccbd95SGleb Smirnoff static struct ng_type ng_deflate_typestruct = {
14581ccbd95SGleb Smirnoff 	.version =	NG_ABI_VERSION,
14681ccbd95SGleb Smirnoff 	.name =		NG_DEFLATE_NODE_TYPE,
14781ccbd95SGleb Smirnoff 	.constructor =	ng_deflate_constructor,
14881ccbd95SGleb Smirnoff 	.rcvmsg =	ng_deflate_rcvmsg,
14981ccbd95SGleb Smirnoff 	.shutdown =	ng_deflate_shutdown,
15081ccbd95SGleb Smirnoff 	.newhook =	ng_deflate_newhook,
15181ccbd95SGleb Smirnoff 	.rcvdata =	ng_deflate_rcvdata,
15281ccbd95SGleb Smirnoff 	.disconnect =	ng_deflate_disconnect,
15381ccbd95SGleb Smirnoff 	.cmdlist =	ng_deflate_cmds,
15481ccbd95SGleb Smirnoff };
15581ccbd95SGleb Smirnoff NETGRAPH_INIT(deflate, &ng_deflate_typestruct);
15681ccbd95SGleb Smirnoff 
15781ccbd95SGleb Smirnoff /* Depend on separate zlib module. */
15881ccbd95SGleb Smirnoff MODULE_DEPEND(ng_deflate, zlib, 1, 1, 1);
15981ccbd95SGleb Smirnoff 
16081ccbd95SGleb Smirnoff #define ERROUT(x)	do { error = (x); goto done; } while (0)
16181ccbd95SGleb Smirnoff 
16281ccbd95SGleb Smirnoff /************************************************************************
16381ccbd95SGleb Smirnoff 			NETGRAPH NODE STUFF
16481ccbd95SGleb Smirnoff  ************************************************************************/
16581ccbd95SGleb Smirnoff 
16681ccbd95SGleb Smirnoff /*
16781ccbd95SGleb Smirnoff  * Node type constructor
16881ccbd95SGleb Smirnoff  */
16981ccbd95SGleb Smirnoff static int
ng_deflate_constructor(node_p node)17081ccbd95SGleb Smirnoff ng_deflate_constructor(node_p node)
17181ccbd95SGleb Smirnoff {
17281ccbd95SGleb Smirnoff 	priv_p priv;
17381ccbd95SGleb Smirnoff 
17481ccbd95SGleb Smirnoff 	/* Allocate private structure. */
17581ccbd95SGleb Smirnoff 	priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO);
17681ccbd95SGleb Smirnoff 
17781ccbd95SGleb Smirnoff 	NG_NODE_SET_PRIVATE(node, priv);
17881ccbd95SGleb Smirnoff 
17981ccbd95SGleb Smirnoff 	/* This node is not thread safe. */
18081ccbd95SGleb Smirnoff 	NG_NODE_FORCE_WRITER(node);
18181ccbd95SGleb Smirnoff 
18281ccbd95SGleb Smirnoff 	/* Done */
18381ccbd95SGleb Smirnoff 	return (0);
18481ccbd95SGleb Smirnoff }
18581ccbd95SGleb Smirnoff 
18681ccbd95SGleb Smirnoff /*
18781ccbd95SGleb Smirnoff  * Give our OK for a hook to be added.
18881ccbd95SGleb Smirnoff  */
18981ccbd95SGleb Smirnoff static int
ng_deflate_newhook(node_p node,hook_p hook,const char * name)19081ccbd95SGleb Smirnoff ng_deflate_newhook(node_p node, hook_p hook, const char *name)
19181ccbd95SGleb Smirnoff {
19281ccbd95SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
19381ccbd95SGleb Smirnoff 
19481ccbd95SGleb Smirnoff 	if (NG_NODE_NUMHOOKS(node) > 0)
19581ccbd95SGleb Smirnoff 		return (EINVAL);
19681ccbd95SGleb Smirnoff 
19781ccbd95SGleb Smirnoff 	if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0)
19881ccbd95SGleb Smirnoff 		priv->compress = 1;
19981ccbd95SGleb Smirnoff 	else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0)
20081ccbd95SGleb Smirnoff 		priv->compress = 0;
20181ccbd95SGleb Smirnoff 	else
20281ccbd95SGleb Smirnoff 		return (EINVAL);
20381ccbd95SGleb Smirnoff 
20481ccbd95SGleb Smirnoff 	return (0);
20581ccbd95SGleb Smirnoff }
20681ccbd95SGleb Smirnoff 
20781ccbd95SGleb Smirnoff /*
20881ccbd95SGleb Smirnoff  * Receive a control message
20981ccbd95SGleb Smirnoff  */
21081ccbd95SGleb Smirnoff static int
ng_deflate_rcvmsg(node_p node,item_p item,hook_p lasthook)21181ccbd95SGleb Smirnoff ng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook)
21281ccbd95SGleb Smirnoff {
21381ccbd95SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
21481ccbd95SGleb Smirnoff 	struct ng_mesg *resp = NULL;
21581ccbd95SGleb Smirnoff 	int error = 0;
21681ccbd95SGleb Smirnoff 	struct ng_mesg *msg;
21781ccbd95SGleb Smirnoff 
21881ccbd95SGleb Smirnoff 	NGI_GET_MSG(item, msg);
21981ccbd95SGleb Smirnoff 
22081ccbd95SGleb Smirnoff 	if (msg->header.typecookie != NGM_DEFLATE_COOKIE)
22181ccbd95SGleb Smirnoff 		ERROUT(EINVAL);
22281ccbd95SGleb Smirnoff 
22381ccbd95SGleb Smirnoff 	switch (msg->header.cmd) {
22481ccbd95SGleb Smirnoff 	case NGM_DEFLATE_CONFIG:
22581ccbd95SGleb Smirnoff 	    {
22681ccbd95SGleb Smirnoff 		struct ng_deflate_config *const cfg
22781ccbd95SGleb Smirnoff 		    = (struct ng_deflate_config *)msg->data;
22881ccbd95SGleb Smirnoff 
22981ccbd95SGleb Smirnoff 		/* Check configuration. */
23081ccbd95SGleb Smirnoff 		if (msg->header.arglen != sizeof(*cfg))
23181ccbd95SGleb Smirnoff 			ERROUT(EINVAL);
23281ccbd95SGleb Smirnoff 		if (cfg->enable) {
23381ccbd95SGleb Smirnoff 		    if (cfg->windowBits < 8 || cfg->windowBits > 15)
23481ccbd95SGleb Smirnoff 			ERROUT(EINVAL);
23581ccbd95SGleb Smirnoff 		} else
23681ccbd95SGleb Smirnoff 		    cfg->windowBits = 0;
23781ccbd95SGleb Smirnoff 
23881ccbd95SGleb Smirnoff 		/* Clear previous state. */
23981ccbd95SGleb Smirnoff 		if (priv->cfg.enable) {
24081ccbd95SGleb Smirnoff 			if (priv->compress)
24181ccbd95SGleb Smirnoff 				deflateEnd(&priv->cx);
24281ccbd95SGleb Smirnoff 			else
24381ccbd95SGleb Smirnoff 				inflateEnd(&priv->cx);
24481ccbd95SGleb Smirnoff 			priv->cfg.enable = 0;
24581ccbd95SGleb Smirnoff 		}
24681ccbd95SGleb Smirnoff 
24781ccbd95SGleb Smirnoff 		/* Configuration is OK, reset to it. */
24881ccbd95SGleb Smirnoff 		priv->cfg = *cfg;
24981ccbd95SGleb Smirnoff 
25081ccbd95SGleb Smirnoff 		if (priv->cfg.enable) {
25181ccbd95SGleb Smirnoff 			priv->cx.next_in = NULL;
25281ccbd95SGleb Smirnoff 			int res;
25381ccbd95SGleb Smirnoff 			if (priv->compress) {
25481ccbd95SGleb Smirnoff 				if ((res = deflateInit2(&priv->cx,
25581ccbd95SGleb Smirnoff 				    Z_DEFAULT_COMPRESSION, Z_DEFLATED,
25681ccbd95SGleb Smirnoff 				    -cfg->windowBits, 8,
25781ccbd95SGleb Smirnoff 				    Z_DEFAULT_STRATEGY)) != Z_OK) {
25881ccbd95SGleb Smirnoff 					log(LOG_NOTICE,
25981ccbd95SGleb Smirnoff 					    "deflateInit2: error %d, %s\n",
26081ccbd95SGleb Smirnoff 					    res, priv->cx.msg);
26181ccbd95SGleb Smirnoff 					priv->cfg.enable = 0;
26281ccbd95SGleb Smirnoff 					ERROUT(ENOMEM);
26381ccbd95SGleb Smirnoff 				}
26481ccbd95SGleb Smirnoff 			} else {
26581ccbd95SGleb Smirnoff 				if ((res = inflateInit2(&priv->cx,
26681ccbd95SGleb Smirnoff 				    -cfg->windowBits)) != Z_OK) {
26781ccbd95SGleb Smirnoff 					log(LOG_NOTICE,
26881ccbd95SGleb Smirnoff 					    "inflateInit2: error %d, %s\n",
26981ccbd95SGleb Smirnoff 					    res, priv->cx.msg);
27081ccbd95SGleb Smirnoff 					priv->cfg.enable = 0;
27181ccbd95SGleb Smirnoff 					ERROUT(ENOMEM);
27281ccbd95SGleb Smirnoff 				}
27381ccbd95SGleb Smirnoff 			}
27481ccbd95SGleb Smirnoff 		}
27581ccbd95SGleb Smirnoff 
27681ccbd95SGleb Smirnoff 		/* Initialize other state. */
27781ccbd95SGleb Smirnoff 		priv->seqnum = 0;
27881ccbd95SGleb Smirnoff 
27981ccbd95SGleb Smirnoff 		/* Save return address so we can send reset-req's */
28081ccbd95SGleb Smirnoff 		priv->ctrlnode = NGI_RETADDR(item);
28181ccbd95SGleb Smirnoff 		break;
28281ccbd95SGleb Smirnoff 	    }
28381ccbd95SGleb Smirnoff 
28481ccbd95SGleb Smirnoff 	case NGM_DEFLATE_RESETREQ:
28581ccbd95SGleb Smirnoff 		ng_deflate_reset_req(node);
28681ccbd95SGleb Smirnoff 		break;
28781ccbd95SGleb Smirnoff 
28881ccbd95SGleb Smirnoff 	case NGM_DEFLATE_GET_STATS:
28981ccbd95SGleb Smirnoff 	case NGM_DEFLATE_CLR_STATS:
29081ccbd95SGleb Smirnoff 	case NGM_DEFLATE_GETCLR_STATS:
29181ccbd95SGleb Smirnoff 		/* Create response if requested. */
29281ccbd95SGleb Smirnoff 		if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) {
29381ccbd95SGleb Smirnoff 			NG_MKRESPONSE(resp, msg,
29481ccbd95SGleb Smirnoff 			    sizeof(struct ng_deflate_stats), M_NOWAIT);
29581ccbd95SGleb Smirnoff 			if (resp == NULL)
29681ccbd95SGleb Smirnoff 				ERROUT(ENOMEM);
29781ccbd95SGleb Smirnoff 			bcopy(&priv->stats, resp->data,
29881ccbd95SGleb Smirnoff 			    sizeof(struct ng_deflate_stats));
29981ccbd95SGleb Smirnoff 		}
30081ccbd95SGleb Smirnoff 
30181ccbd95SGleb Smirnoff 		/* Clear stats if requested. */
30281ccbd95SGleb Smirnoff 		if (msg->header.cmd != NGM_DEFLATE_GET_STATS)
30381ccbd95SGleb Smirnoff 			bzero(&priv->stats,
30481ccbd95SGleb Smirnoff 			    sizeof(struct ng_deflate_stats));
30581ccbd95SGleb Smirnoff 		break;
30681ccbd95SGleb Smirnoff 
30781ccbd95SGleb Smirnoff 	default:
30881ccbd95SGleb Smirnoff 		error = EINVAL;
30981ccbd95SGleb Smirnoff 		break;
31081ccbd95SGleb Smirnoff 	}
31181ccbd95SGleb Smirnoff done:
31281ccbd95SGleb Smirnoff 	NG_RESPOND_MSG(error, node, item, resp);
31381ccbd95SGleb Smirnoff 	NG_FREE_MSG(msg);
31481ccbd95SGleb Smirnoff 	return (error);
31581ccbd95SGleb Smirnoff }
31681ccbd95SGleb Smirnoff 
31781ccbd95SGleb Smirnoff /*
31881ccbd95SGleb Smirnoff  * Receive incoming data on our hook.
31981ccbd95SGleb Smirnoff  */
32081ccbd95SGleb Smirnoff static int
ng_deflate_rcvdata(hook_p hook,item_p item)32181ccbd95SGleb Smirnoff ng_deflate_rcvdata(hook_p hook, item_p item)
32281ccbd95SGleb Smirnoff {
32381ccbd95SGleb Smirnoff 	const node_p node = NG_HOOK_NODE(hook);
32481ccbd95SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
32581ccbd95SGleb Smirnoff 	struct mbuf *m, *out;
32681ccbd95SGleb Smirnoff 	int error;
32781ccbd95SGleb Smirnoff 
32881ccbd95SGleb Smirnoff 	if (!priv->cfg.enable) {
32981ccbd95SGleb Smirnoff 		NG_FREE_ITEM(item);
33081ccbd95SGleb Smirnoff 		return (ENXIO);
33181ccbd95SGleb Smirnoff 	}
33281ccbd95SGleb Smirnoff 
33381ccbd95SGleb Smirnoff 	NGI_GET_M(item, m);
33481ccbd95SGleb Smirnoff 	/* Compress */
33581ccbd95SGleb Smirnoff 	if (priv->compress) {
33681ccbd95SGleb Smirnoff 		if ((error = ng_deflate_compress(node, m, &out)) != 0) {
33781ccbd95SGleb Smirnoff 			NG_FREE_ITEM(item);
33881ccbd95SGleb Smirnoff 			log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
33981ccbd95SGleb Smirnoff 			return (error);
34081ccbd95SGleb Smirnoff 		}
34181ccbd95SGleb Smirnoff 	} else { /* Decompress */
34281ccbd95SGleb Smirnoff 		if ((error = ng_deflate_decompress(node, m, &out)) != 0) {
34381ccbd95SGleb Smirnoff 			NG_FREE_ITEM(item);
34481ccbd95SGleb Smirnoff 			log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
34581ccbd95SGleb Smirnoff 			if (priv->ctrlnode != 0) {
34681ccbd95SGleb Smirnoff 				struct ng_mesg *msg;
34781ccbd95SGleb Smirnoff 
34881ccbd95SGleb Smirnoff 				/* Need to send a reset-request. */
34981ccbd95SGleb Smirnoff 				NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE,
35081ccbd95SGleb Smirnoff 				    NGM_DEFLATE_RESETREQ, 0, M_NOWAIT);
35181ccbd95SGleb Smirnoff 				if (msg == NULL)
35281ccbd95SGleb Smirnoff 					return (error);
35381ccbd95SGleb Smirnoff 				NG_SEND_MSG_ID(error, node, msg,
35481ccbd95SGleb Smirnoff 					priv->ctrlnode, 0);
35581ccbd95SGleb Smirnoff 			}
35681ccbd95SGleb Smirnoff 			return (error);
35781ccbd95SGleb Smirnoff 		}
35881ccbd95SGleb Smirnoff 	}
35981ccbd95SGleb Smirnoff 
36081ccbd95SGleb Smirnoff 	NG_FWD_NEW_DATA(error, item, hook, out);
36181ccbd95SGleb Smirnoff 	return (error);
36281ccbd95SGleb Smirnoff }
36381ccbd95SGleb Smirnoff 
36481ccbd95SGleb Smirnoff /*
36581ccbd95SGleb Smirnoff  * Destroy node.
36681ccbd95SGleb Smirnoff  */
36781ccbd95SGleb Smirnoff static int
ng_deflate_shutdown(node_p node)36881ccbd95SGleb Smirnoff ng_deflate_shutdown(node_p node)
36981ccbd95SGleb Smirnoff {
37081ccbd95SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
37181ccbd95SGleb Smirnoff 
37281ccbd95SGleb Smirnoff 	/* Take down netgraph node. */
37381ccbd95SGleb Smirnoff 	if (priv->cfg.enable) {
37481ccbd95SGleb Smirnoff 	    if (priv->compress)
37581ccbd95SGleb Smirnoff 		deflateEnd(&priv->cx);
37681ccbd95SGleb Smirnoff 	    else
37781ccbd95SGleb Smirnoff 		inflateEnd(&priv->cx);
37881ccbd95SGleb Smirnoff 	}
37981ccbd95SGleb Smirnoff 
38081ccbd95SGleb Smirnoff 	free(priv, M_NETGRAPH_DEFLATE);
38181ccbd95SGleb Smirnoff 	NG_NODE_SET_PRIVATE(node, NULL);
38281ccbd95SGleb Smirnoff 	NG_NODE_UNREF(node);		/* let the node escape */
38381ccbd95SGleb Smirnoff 	return (0);
38481ccbd95SGleb Smirnoff }
38581ccbd95SGleb Smirnoff 
38681ccbd95SGleb Smirnoff /*
38781ccbd95SGleb Smirnoff  * Hook disconnection
38881ccbd95SGleb Smirnoff  */
38981ccbd95SGleb Smirnoff static int
ng_deflate_disconnect(hook_p hook)39081ccbd95SGleb Smirnoff ng_deflate_disconnect(hook_p hook)
39181ccbd95SGleb Smirnoff {
39281ccbd95SGleb Smirnoff 	const node_p node = NG_HOOK_NODE(hook);
39381ccbd95SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
39481ccbd95SGleb Smirnoff 
39581ccbd95SGleb Smirnoff 	if (priv->cfg.enable) {
39681ccbd95SGleb Smirnoff 	    if (priv->compress)
39781ccbd95SGleb Smirnoff 		deflateEnd(&priv->cx);
39881ccbd95SGleb Smirnoff 	    else
39981ccbd95SGleb Smirnoff 		inflateEnd(&priv->cx);
40081ccbd95SGleb Smirnoff 	    priv->cfg.enable = 0;
40181ccbd95SGleb Smirnoff 	}
40281ccbd95SGleb Smirnoff 
40381ccbd95SGleb Smirnoff 	/* Go away if no longer connected. */
40481ccbd95SGleb Smirnoff 	if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node))
40581ccbd95SGleb Smirnoff 		ng_rmnode_self(node);
40681ccbd95SGleb Smirnoff 	return (0);
40781ccbd95SGleb Smirnoff }
40881ccbd95SGleb Smirnoff 
40981ccbd95SGleb Smirnoff /************************************************************************
41081ccbd95SGleb Smirnoff 			HELPER STUFF
41181ccbd95SGleb Smirnoff  ************************************************************************/
41281ccbd95SGleb Smirnoff 
41381ccbd95SGleb Smirnoff /*
41481ccbd95SGleb Smirnoff  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
41581ccbd95SGleb Smirnoff  * The original mbuf is not free'd.
41681ccbd95SGleb Smirnoff  */
41781ccbd95SGleb Smirnoff static int
ng_deflate_compress(node_p node,struct mbuf * m,struct mbuf ** resultp)41881ccbd95SGleb Smirnoff ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
41981ccbd95SGleb Smirnoff {
42081ccbd95SGleb Smirnoff 	const priv_p 	priv = NG_NODE_PRIVATE(node);
42181ccbd95SGleb Smirnoff 	int 		outlen, inlen;
42281ccbd95SGleb Smirnoff 	int 		rtn;
42381ccbd95SGleb Smirnoff 
42481ccbd95SGleb Smirnoff 	/* Initialize. */
42581ccbd95SGleb Smirnoff 	*resultp = NULL;
42681ccbd95SGleb Smirnoff 
42781ccbd95SGleb Smirnoff 	inlen = m->m_pkthdr.len;
42881ccbd95SGleb Smirnoff 
42981ccbd95SGleb Smirnoff 	priv->stats.FramesPlain++;
43081ccbd95SGleb Smirnoff 	priv->stats.InOctets+=inlen;
43181ccbd95SGleb Smirnoff 
43281ccbd95SGleb Smirnoff 	if (inlen > DEFLATE_BUF_SIZE) {
43381ccbd95SGleb Smirnoff 		priv->stats.Errors++;
43481ccbd95SGleb Smirnoff 		NG_FREE_M(m);
43581ccbd95SGleb Smirnoff 		return (ENOMEM);
43681ccbd95SGleb Smirnoff 	}
43781ccbd95SGleb Smirnoff 
438e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
439eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
440e4651e05SAlexander Motin 	if (m == NULL) {
441e4651e05SAlexander Motin 		priv->stats.Errors++;
442e4651e05SAlexander Motin 		return (ENOMEM);
443e4651e05SAlexander Motin 	}
444e4651e05SAlexander Motin 
44581ccbd95SGleb Smirnoff 	/* Work with contiguous regions of memory. */
44681ccbd95SGleb Smirnoff 	m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
44781ccbd95SGleb Smirnoff 	outlen = DEFLATE_BUF_SIZE;
44881ccbd95SGleb Smirnoff 
44981ccbd95SGleb Smirnoff 	/* Compress "inbuf" into "outbuf". */
45081ccbd95SGleb Smirnoff 	/* Prepare to compress. */
45181ccbd95SGleb Smirnoff 	if (priv->inbuf[0] != 0) {
45281ccbd95SGleb Smirnoff 		priv->cx.next_in = priv->inbuf;
45381ccbd95SGleb Smirnoff 		priv->cx.avail_in = inlen;
45481ccbd95SGleb Smirnoff 	} else {
45581ccbd95SGleb Smirnoff 		priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
45681ccbd95SGleb Smirnoff 		priv->cx.avail_in = inlen - 1;
45781ccbd95SGleb Smirnoff 	}
45881ccbd95SGleb Smirnoff 	priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN;
45981ccbd95SGleb Smirnoff 	priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN;
46081ccbd95SGleb Smirnoff 
46181ccbd95SGleb Smirnoff 	/* Compress. */
46234ff55b6SXin LI 	rtn = deflate(&priv->cx, Z_SYNC_FLUSH);
46381ccbd95SGleb Smirnoff 
46481ccbd95SGleb Smirnoff 	/* Check return value. */
46581ccbd95SGleb Smirnoff 	if (rtn != Z_OK) {
46681ccbd95SGleb Smirnoff 		priv->stats.Errors++;
46781ccbd95SGleb Smirnoff 		log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n",
46881ccbd95SGleb Smirnoff 		    rtn, priv->cx.msg);
46981ccbd95SGleb Smirnoff 		NG_FREE_M(m);
47081ccbd95SGleb Smirnoff 		return (EINVAL);
47181ccbd95SGleb Smirnoff 	}
47281ccbd95SGleb Smirnoff 
47381ccbd95SGleb Smirnoff 	/* Calculate resulting size. */
47481ccbd95SGleb Smirnoff 	outlen -= priv->cx.avail_out;
47534ff55b6SXin LI 	/*
47634ff55b6SXin LI 	 * Z_SYNC_FLUSH completes the current deflate block and follows
47734ff55b6SXin LI 	 * it with an empty stored block that is three bits plus filler
47834ff55b6SXin LI 	 * bits to the next byte, followed by four bytes (00 00 ff ff).
47934ff55b6SXin LI 	 * RFC 1979 Section 2.1, "Data" requires the four bytes be
48034ff55b6SXin LI 	 * removed before transmission.
48134ff55b6SXin LI 	 */
48234ff55b6SXin LI 	outlen -= 4;
48334ff55b6SXin LI 	MPASS(outlen > 0);
48434ff55b6SXin LI 	MPASS(priv->outbuf[outlen + 0] == 0x00);
48534ff55b6SXin LI 	MPASS(priv->outbuf[outlen + 1] == 0x00);
48634ff55b6SXin LI 	MPASS(priv->outbuf[outlen + 2] == 0xff);
48734ff55b6SXin LI 	MPASS(priv->outbuf[outlen + 3] == 0xff);
48881ccbd95SGleb Smirnoff 
48981ccbd95SGleb Smirnoff 	/* If we can't compress this packet, send it as-is. */
49081ccbd95SGleb Smirnoff 	if (outlen > inlen) {
49181ccbd95SGleb Smirnoff 		/* Return original packet uncompressed. */
49281ccbd95SGleb Smirnoff 		*resultp = m;
49381ccbd95SGleb Smirnoff 		priv->stats.FramesUncomp++;
49481ccbd95SGleb Smirnoff 		priv->stats.OutOctets+=inlen;
49581ccbd95SGleb Smirnoff 	} else {
49681ccbd95SGleb Smirnoff 		/* Install header. */
497b652a5faSAlexander Motin 		be16enc(priv->outbuf, PROT_COMPD);
498b652a5faSAlexander Motin 		be16enc(priv->outbuf + 2, priv->seqnum);
49981ccbd95SGleb Smirnoff 
50081ccbd95SGleb Smirnoff 		/* Return packet in an mbuf. */
501e4651e05SAlexander Motin 		m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
502e4651e05SAlexander Motin 		if (m->m_pkthdr.len < outlen) {
503e4651e05SAlexander Motin 			m_freem(m);
50481ccbd95SGleb Smirnoff 			priv->stats.Errors++;
50581ccbd95SGleb Smirnoff 			return (ENOMEM);
506e4651e05SAlexander Motin 		} else if (outlen < m->m_pkthdr.len)
507e4651e05SAlexander Motin 			m_adj(m, outlen - m->m_pkthdr.len);
508e4651e05SAlexander Motin 		*resultp = m;
50981ccbd95SGleb Smirnoff 		priv->stats.FramesComp++;
51081ccbd95SGleb Smirnoff 		priv->stats.OutOctets+=outlen;
51181ccbd95SGleb Smirnoff 	}
51281ccbd95SGleb Smirnoff 
51381ccbd95SGleb Smirnoff 	/* Update sequence number. */
51481ccbd95SGleb Smirnoff 	priv->seqnum++;
51581ccbd95SGleb Smirnoff 
51681ccbd95SGleb Smirnoff 	return (0);
51781ccbd95SGleb Smirnoff }
51881ccbd95SGleb Smirnoff 
51981ccbd95SGleb Smirnoff /*
52081ccbd95SGleb Smirnoff  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
52181ccbd95SGleb Smirnoff  * The original mbuf is not free'd.
52281ccbd95SGleb Smirnoff  */
52381ccbd95SGleb Smirnoff static int
ng_deflate_decompress(node_p node,struct mbuf * m,struct mbuf ** resultp)52481ccbd95SGleb Smirnoff ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp)
52581ccbd95SGleb Smirnoff {
52681ccbd95SGleb Smirnoff 	const priv_p 	priv = NG_NODE_PRIVATE(node);
52734ff55b6SXin LI 	int 		outlen, inlen, datalen;
52881ccbd95SGleb Smirnoff 	int 		rtn;
52981ccbd95SGleb Smirnoff 	uint16_t	proto;
53081ccbd95SGleb Smirnoff 	int		offset;
53181ccbd95SGleb Smirnoff 	uint16_t	rseqnum;
53234ff55b6SXin LI 	u_char		headbuf[5];
53334ff55b6SXin LI 	static u_char	EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff };
53481ccbd95SGleb Smirnoff 
53581ccbd95SGleb Smirnoff 	/* Initialize. */
53681ccbd95SGleb Smirnoff 	*resultp = NULL;
53781ccbd95SGleb Smirnoff 
53881ccbd95SGleb Smirnoff 	inlen = m->m_pkthdr.len;
53981ccbd95SGleb Smirnoff 
54081ccbd95SGleb Smirnoff 	if (inlen > DEFLATE_BUF_SIZE) {
54181ccbd95SGleb Smirnoff 		priv->stats.Errors++;
54281ccbd95SGleb Smirnoff 		NG_FREE_M(m);
54381ccbd95SGleb Smirnoff 		priv->seqnum = 0;
54481ccbd95SGleb Smirnoff 		return (ENOMEM);
54581ccbd95SGleb Smirnoff 	}
54681ccbd95SGleb Smirnoff 
547e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
548eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
549e4651e05SAlexander Motin 	if (m == NULL) {
550e4651e05SAlexander Motin 		priv->stats.Errors++;
551e4651e05SAlexander Motin 		return (ENOMEM);
552e4651e05SAlexander Motin 	}
553e4651e05SAlexander Motin 
55481ccbd95SGleb Smirnoff 	/* Work with contiguous regions of memory. */
55581ccbd95SGleb Smirnoff 	m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
55681ccbd95SGleb Smirnoff 
55781ccbd95SGleb Smirnoff 	/* Separate proto. */
55881ccbd95SGleb Smirnoff 	if ((priv->inbuf[0] & 0x01) != 0) {
55981ccbd95SGleb Smirnoff 		proto = priv->inbuf[0];
56081ccbd95SGleb Smirnoff 		offset = 1;
56181ccbd95SGleb Smirnoff 	} else {
562b652a5faSAlexander Motin 		proto = be16dec(priv->inbuf);
56381ccbd95SGleb Smirnoff 		offset = 2;
56481ccbd95SGleb Smirnoff 	}
56581ccbd95SGleb Smirnoff 
566833c4a01SGleb Smirnoff 	priv->stats.InOctets += inlen;
567833c4a01SGleb Smirnoff 
56881ccbd95SGleb Smirnoff 	/* Packet is compressed, so decompress. */
56981ccbd95SGleb Smirnoff 	if (proto == PROT_COMPD) {
57081ccbd95SGleb Smirnoff 		priv->stats.FramesComp++;
57181ccbd95SGleb Smirnoff 
57281ccbd95SGleb Smirnoff 		/* Check sequence number. */
573b652a5faSAlexander Motin 		rseqnum = be16dec(priv->inbuf + offset);
57481ccbd95SGleb Smirnoff 		offset += 2;
57581ccbd95SGleb Smirnoff 		if (rseqnum != priv->seqnum) {
57681ccbd95SGleb Smirnoff 			priv->stats.Errors++;
57781ccbd95SGleb Smirnoff 			log(LOG_NOTICE, "ng_deflate: wrong sequence: %u "
57881ccbd95SGleb Smirnoff 			    "instead of %u\n", rseqnum, priv->seqnum);
57981ccbd95SGleb Smirnoff 			NG_FREE_M(m);
58081ccbd95SGleb Smirnoff 			priv->seqnum = 0;
58181ccbd95SGleb Smirnoff 			return (EPIPE);
58281ccbd95SGleb Smirnoff 		}
58381ccbd95SGleb Smirnoff 
58481ccbd95SGleb Smirnoff 		outlen = DEFLATE_BUF_SIZE;
58581ccbd95SGleb Smirnoff 
58681ccbd95SGleb Smirnoff     		/* Decompress "inbuf" into "outbuf". */
58781ccbd95SGleb Smirnoff 		/* Prepare to decompress. */
58881ccbd95SGleb Smirnoff 		priv->cx.next_in = priv->inbuf + offset;
58981ccbd95SGleb Smirnoff 		priv->cx.avail_in = inlen - offset;
59081ccbd95SGleb Smirnoff 		/* Reserve space for protocol decompression. */
59181ccbd95SGleb Smirnoff 		priv->cx.next_out = priv->outbuf + 1;
59281ccbd95SGleb Smirnoff 		priv->cx.avail_out = outlen - 1;
59381ccbd95SGleb Smirnoff 
59481ccbd95SGleb Smirnoff 		/* Decompress. */
59534ff55b6SXin LI 		rtn = inflate(&priv->cx, Z_SYNC_FLUSH);
59681ccbd95SGleb Smirnoff 
59781ccbd95SGleb Smirnoff 		/* Check return value. */
59881ccbd95SGleb Smirnoff 		if (rtn != Z_OK && rtn != Z_STREAM_END) {
59981ccbd95SGleb Smirnoff 			priv->stats.Errors++;
60081ccbd95SGleb Smirnoff 			NG_FREE_M(m);
60181ccbd95SGleb Smirnoff 			priv->seqnum = 0;
60281ccbd95SGleb Smirnoff 			log(LOG_NOTICE, "%s: decompression error: %d (%s)\n",
60381ccbd95SGleb Smirnoff 			    __func__, rtn, priv->cx.msg);
60481ccbd95SGleb Smirnoff 
60581ccbd95SGleb Smirnoff 			switch (rtn) {
60681ccbd95SGleb Smirnoff 			case Z_MEM_ERROR:
60781ccbd95SGleb Smirnoff 				return (ENOMEM);
60881ccbd95SGleb Smirnoff 			case Z_DATA_ERROR:
60981ccbd95SGleb Smirnoff 				return (EIO);
61081ccbd95SGleb Smirnoff 			default:
61181ccbd95SGleb Smirnoff 				return (EINVAL);
61281ccbd95SGleb Smirnoff 			}
61381ccbd95SGleb Smirnoff 		}
61481ccbd95SGleb Smirnoff 
61534ff55b6SXin LI 		/* Handle the EMPTY_BLOCK omitted by sender */
61634ff55b6SXin LI 		if (inflateSyncPoint(&priv->cx)) {
61734ff55b6SXin LI 			priv->cx.avail_in = 4;
61834ff55b6SXin LI 			priv->cx.next_in = EMPTY_BLOCK;
61934ff55b6SXin LI 			inflate(&priv->cx, Z_SYNC_FLUSH);
62034ff55b6SXin LI 		}
62134ff55b6SXin LI 
62281ccbd95SGleb Smirnoff 		/* Calculate resulting size. */
62381ccbd95SGleb Smirnoff 		outlen -= priv->cx.avail_out;
62481ccbd95SGleb Smirnoff 
62581ccbd95SGleb Smirnoff 		/* Decompress protocol. */
62681ccbd95SGleb Smirnoff 		if ((priv->outbuf[1] & 0x01) != 0) {
62781ccbd95SGleb Smirnoff 			priv->outbuf[0] = 0;
62881ccbd95SGleb Smirnoff 			/* Return packet in an mbuf. */
629e4651e05SAlexander Motin 			m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
63081ccbd95SGleb Smirnoff 		} else {
63181ccbd95SGleb Smirnoff 			outlen--;
63281ccbd95SGleb Smirnoff 			/* Return packet in an mbuf. */
633e4651e05SAlexander Motin 			m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1));
63481ccbd95SGleb Smirnoff 		}
635e4651e05SAlexander Motin 		if (m->m_pkthdr.len < outlen) {
636e4651e05SAlexander Motin 			m_freem(m);
63781ccbd95SGleb Smirnoff 			priv->stats.Errors++;
63881ccbd95SGleb Smirnoff 			priv->seqnum = 0;
63981ccbd95SGleb Smirnoff 			return (ENOMEM);
640e4651e05SAlexander Motin 		} else if (outlen < m->m_pkthdr.len)
641e4651e05SAlexander Motin 			m_adj(m, outlen - m->m_pkthdr.len);
642e4651e05SAlexander Motin 		*resultp = m;
64381ccbd95SGleb Smirnoff 		priv->stats.FramesPlain++;
64481ccbd95SGleb Smirnoff 		priv->stats.OutOctets+=outlen;
64581ccbd95SGleb Smirnoff 
64634ff55b6SXin LI 	} else {
64734ff55b6SXin LI 		/* Packet is not compressed, just update dictionary. */
64881ccbd95SGleb Smirnoff 		priv->stats.FramesUncomp++;
64934ff55b6SXin LI 
65034ff55b6SXin LI 		/*
65134ff55b6SXin LI 		 * Fake a header for uncompressed data block
65234ff55b6SXin LI 		 */
65334ff55b6SXin LI 		datalen = inlen - offset + 1;
65434ff55b6SXin LI 		headbuf[0] = 0x80;
65534ff55b6SXin LI 		headbuf[1] = datalen & 0xff;
65634ff55b6SXin LI 		headbuf[2] = datalen >> 8;
65734ff55b6SXin LI 		headbuf[3] = (~datalen) & 0xff;
65834ff55b6SXin LI 		headbuf[4] = (~datalen) >> 8;
65934ff55b6SXin LI 
66034ff55b6SXin LI 		priv->cx.next_in = headbuf;
66134ff55b6SXin LI 		priv->cx.avail_in = sizeof(headbuf);
66234ff55b6SXin LI 		priv->cx.next_out = priv->outbuf;
66334ff55b6SXin LI 		priv->cx.avail_out = DEFLATE_BUF_SIZE;
66434ff55b6SXin LI 
66534ff55b6SXin LI 		rtn = inflate(&priv->cx, Z_NO_FLUSH);
66634ff55b6SXin LI 
66781ccbd95SGleb Smirnoff 		if (priv->inbuf[0] == 0) {
66834ff55b6SXin LI 			priv->cx.next_in =
66934ff55b6SXin LI 			    priv->inbuf + 1; /* compress protocol */
67081ccbd95SGleb Smirnoff 			priv->cx.avail_in = inlen - 1;
67181ccbd95SGleb Smirnoff 		} else {
67281ccbd95SGleb Smirnoff 			priv->cx.next_in = priv->inbuf;
67381ccbd95SGleb Smirnoff 			priv->cx.avail_in = inlen;
67481ccbd95SGleb Smirnoff 		}
67534ff55b6SXin LI 		priv->cx.next_out = priv->outbuf;
67634ff55b6SXin LI 		priv->cx.avail_out = DEFLATE_BUF_SIZE;
67781ccbd95SGleb Smirnoff 
67834ff55b6SXin LI 		rtn = inflate(&priv->cx, Z_SYNC_FLUSH);
67981ccbd95SGleb Smirnoff 
68081ccbd95SGleb Smirnoff 		/* Check return value */
68181ccbd95SGleb Smirnoff 		if (rtn != Z_OK) {
68281ccbd95SGleb Smirnoff 			priv->stats.Errors++;
68334ff55b6SXin LI 			log(LOG_NOTICE, "%s: inflate error: %d (%s)\n",
68481ccbd95SGleb Smirnoff 			    __func__, rtn, priv->cx.msg);
68581ccbd95SGleb Smirnoff 			NG_FREE_M(m);
68681ccbd95SGleb Smirnoff 			priv->seqnum = 0;
68781ccbd95SGleb Smirnoff 			return (EINVAL);
68881ccbd95SGleb Smirnoff 		}
68981ccbd95SGleb Smirnoff 
69081ccbd95SGleb Smirnoff 		*resultp = m;
69181ccbd95SGleb Smirnoff 		priv->stats.FramesPlain++;
69281ccbd95SGleb Smirnoff 		priv->stats.OutOctets += inlen;
69381ccbd95SGleb Smirnoff 	}
69481ccbd95SGleb Smirnoff 
69581ccbd95SGleb Smirnoff 	/* Update sequence number. */
69681ccbd95SGleb Smirnoff 	priv->seqnum++;
69781ccbd95SGleb Smirnoff 
69881ccbd95SGleb Smirnoff 	return (0);
69981ccbd95SGleb Smirnoff }
70081ccbd95SGleb Smirnoff 
70181ccbd95SGleb Smirnoff /*
70281ccbd95SGleb Smirnoff  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
70381ccbd95SGleb Smirnoff  */
70481ccbd95SGleb Smirnoff static void
ng_deflate_reset_req(node_p node)70581ccbd95SGleb Smirnoff ng_deflate_reset_req(node_p node)
70681ccbd95SGleb Smirnoff {
70781ccbd95SGleb Smirnoff 	const priv_p priv = NG_NODE_PRIVATE(node);
70881ccbd95SGleb Smirnoff 
70981ccbd95SGleb Smirnoff 	priv->seqnum = 0;
71081ccbd95SGleb Smirnoff 	if (priv->cfg.enable) {
71181ccbd95SGleb Smirnoff 	    if (priv->compress)
71281ccbd95SGleb Smirnoff 		deflateReset(&priv->cx);
71381ccbd95SGleb Smirnoff 	    else
71481ccbd95SGleb Smirnoff 		inflateReset(&priv->cx);
71581ccbd95SGleb Smirnoff 	}
71681ccbd95SGleb Smirnoff }
717