152419Sjulian
252419Sjulian/*
352419Sjulian * ng_vjc.c
4139823Simp */
5139823Simp
6139823Simp/*-
752419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
852419Sjulian * All rights reserved.
952419Sjulian *
1052419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
1152419Sjulian * redistribution of this software, in source or object code forms, with or
1252419Sjulian * without modifications are expressly permitted by Whistle Communications;
1352419Sjulian * provided, however, that:
1452419Sjulian * 1. Any and all reproductions of the source or object code must include the
1552419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1652419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1752419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1852419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1952419Sjulian *    such appears in the above copyright notice or in the software.
2052419Sjulian *
2152419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2252419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2352419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2452419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2552419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2652419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2752419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2852419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2952419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
3052419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3152419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3252419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3352419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3452419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3552419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3652419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3752419Sjulian * OF SUCH DAMAGE.
3852419Sjulian *
3967506Sjulian * Author: Archie Cobbs <archie@freebsd.org>
4052419Sjulian *
4152419Sjulian * $FreeBSD$
4252752Sjulian * $Whistle: ng_vjc.c,v 1.17 1999/11/01 09:24:52 julian Exp $
4352419Sjulian */
4452419Sjulian
4552419Sjulian/*
4664287Sarchie * This node performs Van Jacobson IP header (de)compression.
4752419Sjulian * You must have included net/slcompress.c in your kernel compilation.
4852419Sjulian */
4952419Sjulian
5052419Sjulian#include <sys/param.h>
5152419Sjulian#include <sys/systm.h>
5252419Sjulian#include <sys/errno.h>
5352419Sjulian#include <sys/kernel.h>
5452419Sjulian#include <sys/mbuf.h>
5552419Sjulian#include <sys/malloc.h>
5652419Sjulian#include <sys/errno.h>
5752419Sjulian
5852419Sjulian#include <netgraph/ng_message.h>
5952419Sjulian#include <netgraph/netgraph.h>
6064509Sarchie#include <netgraph/ng_parse.h>
6152419Sjulian#include <netgraph/ng_vjc.h>
6252419Sjulian
6352419Sjulian#include <netinet/in.h>
6452419Sjulian#include <netinet/in_systm.h>
6552419Sjulian#include <netinet/ip.h>
6652419Sjulian#include <netinet/tcp.h>
6752419Sjulian
6852419Sjulian#include <net/slcompress.h>
6952419Sjulian
7052419Sjulian/* Check agreement with slcompress.c */
7152419Sjulian#if MAX_STATES != NG_VJC_MAX_CHANNELS
7252419Sjulian#error NG_VJC_MAX_CHANNELS must be the same as MAX_STATES
7352419Sjulian#endif
7452419Sjulian
7553190Sarchie/* Maximum length of a compressed TCP VJ header */
7653190Sarchie#define MAX_VJHEADER		19
7752419Sjulian
7852419Sjulian/* Node private data */
7953406Sarchiestruct ng_vjc_private {
8052419Sjulian	struct	ngm_vjc_config conf;
8152419Sjulian	struct	slcompress slc;
8252419Sjulian	hook_p	ip;
8352419Sjulian	hook_p	vjcomp;
8452419Sjulian	hook_p	vjuncomp;
8552419Sjulian	hook_p	vjip;
8652419Sjulian};
8753406Sarchietypedef struct ng_vjc_private *priv_p;
8852419Sjulian
8952419Sjulian#define ERROUT(x)	do { error = (x); goto done; } while (0)
9052419Sjulian
9152419Sjulian/* Netgraph node methods */
9252752Sjulianstatic ng_constructor_t	ng_vjc_constructor;
9352752Sjulianstatic ng_rcvmsg_t	ng_vjc_rcvmsg;
9470700Sjulianstatic ng_shutdown_t	ng_vjc_shutdown;
9552752Sjulianstatic ng_newhook_t	ng_vjc_newhook;
9652752Sjulianstatic ng_rcvdata_t	ng_vjc_rcvdata;
9752752Sjulianstatic ng_disconnect_t	ng_vjc_disconnect;
9852419Sjulian
9952419Sjulian/* Helper stuff */
10053190Sarchiestatic struct mbuf *ng_vjc_pulluphdrs(struct mbuf *m, int knownTCP);
10152419Sjulian
10264509Sarchie/* Parse type for struct ngm_vjc_config */
10397685Sarchiestatic const struct ng_parse_struct_field ng_vjc_config_type_fields[]
10464509Sarchie	= NG_VJC_CONFIG_TYPE_INFO;
10564509Sarchiestatic const struct ng_parse_type ng_vjc_config_type = {
10664509Sarchie	&ng_parse_struct_type,
10797685Sarchie	&ng_vjc_config_type_fields
10864509Sarchie};
10964509Sarchie
11064509Sarchie/* Parse type for the 'last_cs' and 'cs_next' fields in struct slcompress,
11164509Sarchie   which are pointers converted to integer indices, so parse them that way. */
112153069Sru#ifndef __LP64__
11364509Sarchie#define NG_VJC_TSTATE_PTR_TYPE	&ng_parse_uint32_type
114153069Sru#else
11564509Sarchie#define NG_VJC_TSTATE_PTR_TYPE	&ng_parse_uint64_type
11664509Sarchie#endif
11764509Sarchie
11864509Sarchie/* Parse type for the 'cs_hdr' field in a struct cstate. Ideally we would
11964509Sarchie   like to use a 'struct ip' type instead of a simple array of bytes. */
12064509Sarchiestatic const struct ng_parse_fixedarray_info ng_vjc_cs_hdr_type_info = {
12164509Sarchie	&ng_parse_hint8_type,
12264509Sarchie	MAX_HDR
12364509Sarchie};
12464509Sarchiestatic const struct ng_parse_type ng_vjc_cs_hdr_type = {
12564509Sarchie	&ng_parse_fixedarray_type,
12664509Sarchie	&ng_vjc_cs_hdr_type_info
12764509Sarchie};
12864509Sarchie
12964509Sarchie/* Parse type for a struct cstate */
13097685Sarchiestatic const struct ng_parse_struct_field ng_vjc_cstate_type_fields[] = {
13164509Sarchie	{ "cs_next",		NG_VJC_TSTATE_PTR_TYPE		},
13264509Sarchie	{ "cs_hlen",		&ng_parse_uint16_type		},
13364509Sarchie	{ "cs_id",		&ng_parse_uint8_type		},
13464509Sarchie	{ "cs_filler",		&ng_parse_uint8_type		},
13564509Sarchie	{ "cs_hdr",		&ng_vjc_cs_hdr_type		},
13697685Sarchie	{ NULL }
13764509Sarchie};
13864509Sarchiestatic const struct ng_parse_type ng_vjc_cstate_type = {
13964509Sarchie	&ng_parse_struct_type,
14097685Sarchie	&ng_vjc_cstate_type_fields
14164509Sarchie};
14264509Sarchie
14364509Sarchie/* Parse type for an array of MAX_STATES struct cstate's, ie, tstate & rstate */
14464509Sarchiestatic const struct ng_parse_fixedarray_info ng_vjc_cstatearray_type_info = {
14564509Sarchie	&ng_vjc_cstate_type,
14664509Sarchie	MAX_STATES
14764509Sarchie};
14864509Sarchiestatic const struct ng_parse_type ng_vjc_cstatearray_type = {
14964509Sarchie	&ng_parse_fixedarray_type,
15064509Sarchie	&ng_vjc_cstatearray_type_info
15164509Sarchie};
15264509Sarchie
15364509Sarchie/* Parse type for struct slcompress. Keep this in sync with the
15464509Sarchie   definition of struct slcompress defined in <net/slcompress.h> */
15597685Sarchiestatic const struct ng_parse_struct_field ng_vjc_slcompress_type_fields[] = {
15664509Sarchie	{ "last_cs",		NG_VJC_TSTATE_PTR_TYPE		},
15764509Sarchie	{ "last_recv",		&ng_parse_uint8_type		},
15864509Sarchie	{ "last_xmit",		&ng_parse_uint8_type		},
15964509Sarchie	{ "flags",		&ng_parse_hint16_type		},
16064509Sarchie#ifndef SL_NO_STATS
16164509Sarchie	{ "sls_packets",	&ng_parse_uint32_type		},
16264509Sarchie	{ "sls_compressed",	&ng_parse_uint32_type		},
16364509Sarchie	{ "sls_searches",	&ng_parse_uint32_type		},
16464509Sarchie	{ "sls_misses",		&ng_parse_uint32_type		},
16564509Sarchie	{ "sls_uncompressedin",	&ng_parse_uint32_type		},
16664509Sarchie	{ "sls_compressedin",	&ng_parse_uint32_type		},
16764509Sarchie	{ "sls_errorin",	&ng_parse_uint32_type		},
16864509Sarchie	{ "sls_tossed",		&ng_parse_uint32_type		},
16964509Sarchie#endif
17064509Sarchie	{ "tstate",		&ng_vjc_cstatearray_type	},
17164509Sarchie	{ "rstate",		&ng_vjc_cstatearray_type	},
17297685Sarchie	{ NULL }
17364509Sarchie};
17464509Sarchiestatic const struct ng_parse_type ng_vjc_slcompress_type = {
17564509Sarchie	&ng_parse_struct_type,
17697685Sarchie	&ng_vjc_slcompress_type_fields
17764509Sarchie};
17864509Sarchie
17964509Sarchie/* List of commands and how to convert arguments to/from ASCII */
18064509Sarchiestatic const struct ng_cmdlist ng_vjc_cmds[] = {
18164509Sarchie	{
18264509Sarchie	  NGM_VJC_COOKIE,
18364509Sarchie	  NGM_VJC_SET_CONFIG,
18464509Sarchie	  "setconfig",
18564509Sarchie	  &ng_vjc_config_type,
18664509Sarchie	  NULL
18764509Sarchie	},
18864509Sarchie	{
18964509Sarchie	  NGM_VJC_COOKIE,
19064509Sarchie	  NGM_VJC_GET_CONFIG,
19164509Sarchie	  "getconfig",
19264509Sarchie	  NULL,
19364509Sarchie	  &ng_vjc_config_type,
19464509Sarchie	},
19564509Sarchie	{
19664509Sarchie	  NGM_VJC_COOKIE,
19764509Sarchie	  NGM_VJC_GET_STATE,
19864509Sarchie	  "getstate",
19964509Sarchie	  NULL,
20064509Sarchie	  &ng_vjc_slcompress_type,
20164509Sarchie	},
20264509Sarchie	{
20364509Sarchie	  NGM_VJC_COOKIE,
20464509Sarchie	  NGM_VJC_CLR_STATS,
20564509Sarchie	  "clrstats",
20664509Sarchie	  NULL,
20764509Sarchie	  NULL,
20864509Sarchie	},
20964509Sarchie	{
21064509Sarchie	  NGM_VJC_COOKIE,
21164509Sarchie	  NGM_VJC_RECV_ERROR,
21264509Sarchie	  "recverror",
21364509Sarchie	  NULL,
21464509Sarchie	  NULL,
21564509Sarchie	},
21664509Sarchie	{ 0 }
21764509Sarchie};
21864509Sarchie
21952419Sjulian/* Node type descriptor */
22064509Sarchiestatic struct ng_type ng_vjc_typestruct = {
221129823Sjulian	.version =	NG_ABI_VERSION,
222129823Sjulian	.name =		NG_VJC_NODE_TYPE,
223129823Sjulian	.constructor =	ng_vjc_constructor,
224129823Sjulian	.rcvmsg =	ng_vjc_rcvmsg,
225129823Sjulian	.shutdown =	ng_vjc_shutdown,
226129823Sjulian	.newhook =	ng_vjc_newhook,
227129823Sjulian	.rcvdata =	ng_vjc_rcvdata,
228129823Sjulian	.disconnect =	ng_vjc_disconnect,
229129823Sjulian	.cmdlist =	ng_vjc_cmds,
23052419Sjulian};
23164509SarchieNETGRAPH_INIT(vjc, &ng_vjc_typestruct);
23252419Sjulian
23352419Sjulian/************************************************************************
23452419Sjulian			NETGRAPH NODE METHODS
23552419Sjulian ************************************************************************/
23652419Sjulian
23752419Sjulian/*
23852419Sjulian * Create a new node
23952419Sjulian */
24052419Sjulianstatic int
24170700Sjulianng_vjc_constructor(node_p node)
24252419Sjulian{
24352419Sjulian	priv_p priv;
24452419Sjulian
24552419Sjulian	/* Allocate private structure */
246220768Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
24752419Sjulian
24870784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
24952419Sjulian
250186907Smav	/* slcompress is not thread-safe. Protect it's state here. */
251186907Smav	NG_NODE_FORCE_WRITER(node);
252186907Smav
25352419Sjulian	/* Done */
25452419Sjulian	return (0);
25552419Sjulian}
25652419Sjulian
25752419Sjulian/*
25852419Sjulian * Add a new hook
25952419Sjulian */
26052419Sjulianstatic int
26152419Sjulianng_vjc_newhook(node_p node, hook_p hook, const char *name)
26252419Sjulian{
26370784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
26452419Sjulian	hook_p *hookp;
26552419Sjulian
26652419Sjulian	/* Get hook */
26753076Sarchie	if (strcmp(name, NG_VJC_HOOK_IP) == 0)
26852419Sjulian		hookp = &priv->ip;
26953076Sarchie	else if (strcmp(name, NG_VJC_HOOK_VJCOMP) == 0)
27052419Sjulian		hookp = &priv->vjcomp;
27153076Sarchie	else if (strcmp(name, NG_VJC_HOOK_VJUNCOMP) == 0)
27252419Sjulian		hookp = &priv->vjuncomp;
27353076Sarchie	else if (strcmp(name, NG_VJC_HOOK_VJIP) == 0)
27452419Sjulian		hookp = &priv->vjip;
27552419Sjulian	else
27652419Sjulian		return (EINVAL);
27752419Sjulian
27852419Sjulian	/* See if already connected */
27952419Sjulian	if (*hookp)
28052419Sjulian		return (EISCONN);
28152419Sjulian
28252419Sjulian	/* OK */
28352419Sjulian	*hookp = hook;
28452419Sjulian	return (0);
28552419Sjulian}
28652419Sjulian
28752419Sjulian/*
28852419Sjulian * Receive a control message
28952419Sjulian */
29052419Sjulianstatic int
29170700Sjulianng_vjc_rcvmsg(node_p node, item_p item, hook_p lasthook)
29252419Sjulian{
29370784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
29452419Sjulian	struct ng_mesg *resp = NULL;
29552419Sjulian	int error = 0;
29670700Sjulian	struct ng_mesg *msg;
29752419Sjulian
29870700Sjulian	NGI_GET_MSG(item, msg);
29952419Sjulian	/* Check type cookie */
30052419Sjulian	switch (msg->header.typecookie) {
30152419Sjulian	case NGM_VJC_COOKIE:
30252419Sjulian		switch (msg->header.cmd) {
30352539Sjulian		case NGM_VJC_SET_CONFIG:
30452419Sjulian		    {
30552419Sjulian			struct ngm_vjc_config *const c =
30652419Sjulian				(struct ngm_vjc_config *) msg->data;
30752419Sjulian
30852539Sjulian			if (msg->header.arglen != sizeof(*c))
30952419Sjulian				ERROUT(EINVAL);
31052539Sjulian			if ((priv->conf.enableComp || priv->conf.enableDecomp)
31152539Sjulian			    && (c->enableComp || c->enableDecomp))
31252419Sjulian				ERROUT(EALREADY);
31352539Sjulian			if (c->enableComp) {
31453076Sarchie				if (c->maxChannel > NG_VJC_MAX_CHANNELS - 1
31553076Sarchie				    || c->maxChannel < NG_VJC_MIN_CHANNELS - 1)
31652539Sjulian					ERROUT(EINVAL);
31753190Sarchie			} else
31853190Sarchie				c->maxChannel = NG_VJC_MAX_CHANNELS - 1;
31952539Sjulian			if (c->enableComp != 0 || c->enableDecomp != 0) {
32052419Sjulian				bzero(&priv->slc, sizeof(priv->slc));
32153076Sarchie				sl_compress_init(&priv->slc, c->maxChannel);
32252419Sjulian			}
32352419Sjulian			priv->conf = *c;
32452419Sjulian			break;
32552419Sjulian		    }
32664509Sarchie		case NGM_VJC_GET_CONFIG:
32764509Sarchie		    {
32864509Sarchie			struct ngm_vjc_config *conf;
32964509Sarchie
33064509Sarchie			NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT);
33164509Sarchie			if (resp == NULL)
33264509Sarchie				ERROUT(ENOMEM);
33364509Sarchie			conf = (struct ngm_vjc_config *)resp->data;
33464509Sarchie			*conf = priv->conf;
33564509Sarchie			break;
33664509Sarchie		    }
33752419Sjulian		case NGM_VJC_GET_STATE:
33864509Sarchie		    {
33964509Sarchie			const struct slcompress *const sl0 = &priv->slc;
34064509Sarchie			struct slcompress *sl;
34164509Sarchie			u_int16_t index;
34264509Sarchie			int i;
34364509Sarchie
34464509Sarchie			/* Get response structure */
34564509Sarchie			NG_MKRESPONSE(resp, msg, sizeof(*sl), M_NOWAIT);
34652419Sjulian			if (resp == NULL)
34752419Sjulian				ERROUT(ENOMEM);
34864509Sarchie			sl = (struct slcompress *)resp->data;
34964509Sarchie			*sl = *sl0;
35064509Sarchie
35164509Sarchie			/* Replace pointers with integer indicies */
35264509Sarchie			if (sl->last_cs != NULL) {
35364509Sarchie				index = sl0->last_cs - sl0->tstate;
35464509Sarchie				bzero(&sl->last_cs, sizeof(sl->last_cs));
35564509Sarchie				*((u_int16_t *)&sl->last_cs) = index;
35664509Sarchie			}
35764509Sarchie			for (i = 0; i < MAX_STATES; i++) {
35864509Sarchie				struct cstate *const cs = &sl->tstate[i];
35964509Sarchie
36064509Sarchie				index = sl0->tstate[i].cs_next - sl0->tstate;
36164509Sarchie				bzero(&cs->cs_next, sizeof(cs->cs_next));
36264509Sarchie				*((u_int16_t *)&cs->cs_next) = index;
36364509Sarchie			}
36452419Sjulian			break;
36564509Sarchie		    }
36652419Sjulian		case NGM_VJC_CLR_STATS:
36752419Sjulian			priv->slc.sls_packets = 0;
36852419Sjulian			priv->slc.sls_compressed = 0;
36952419Sjulian			priv->slc.sls_searches = 0;
37052419Sjulian			priv->slc.sls_misses = 0;
37152419Sjulian			priv->slc.sls_uncompressedin = 0;
37252419Sjulian			priv->slc.sls_compressedin = 0;
37352419Sjulian			priv->slc.sls_errorin = 0;
37452419Sjulian			priv->slc.sls_tossed = 0;
37552419Sjulian			break;
37652419Sjulian		case NGM_VJC_RECV_ERROR:
37753076Sarchie			sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &priv->slc);
37852419Sjulian			break;
37952419Sjulian		default:
38052419Sjulian			error = EINVAL;
38152419Sjulian			break;
38252419Sjulian		}
38352419Sjulian		break;
38452419Sjulian	default:
38552419Sjulian		error = EINVAL;
38652419Sjulian		break;
38752419Sjulian	}
38852419Sjuliandone:
38970700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
39070700Sjulian	NG_FREE_MSG(msg);
39152419Sjulian	return (error);
39252419Sjulian}
39352419Sjulian
39452419Sjulian/*
39552419Sjulian * Receive data
39652419Sjulian */
39752419Sjulianstatic int
39870700Sjulianng_vjc_rcvdata(hook_p hook, item_p item)
39952419Sjulian{
40070784Sjulian	const node_p node = NG_HOOK_NODE(hook);
40170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
40252419Sjulian	int error = 0;
40370700Sjulian	struct mbuf *m;
40452419Sjulian
40570700Sjulian	NGI_GET_M(item, m);
40652419Sjulian	if (hook == priv->ip) {			/* outgoing packet */
40753190Sarchie		u_int type = TYPE_IP;
40852419Sjulian
40953190Sarchie		/* Compress packet if enabled and proto is TCP */
41053190Sarchie		if (priv->conf.enableComp) {
41152419Sjulian			struct ip *ip;
41252419Sjulian
41353190Sarchie			if ((m = ng_vjc_pulluphdrs(m, 0)) == NULL) {
41470700Sjulian				NG_FREE_ITEM(item);
41553190Sarchie				return (ENOBUFS);
41653190Sarchie			}
41752419Sjulian			ip = mtod(m, struct ip *);
41853190Sarchie			if (ip->ip_p == IPPROTO_TCP) {
41953190Sarchie				const int origLen = m->m_len;
42053190Sarchie
42153190Sarchie				type = sl_compress_tcp(m, ip,
42253190Sarchie				    &priv->slc, priv->conf.compressCID);
42353190Sarchie				m->m_pkthdr.len += m->m_len - origLen;
42453190Sarchie			}
42552419Sjulian		}
42653190Sarchie
42753190Sarchie		/* Dispatch to the appropriate outgoing hook */
42852419Sjulian		switch (type) {
42952419Sjulian		case TYPE_IP:
43052419Sjulian			hook = priv->vjip;
43152419Sjulian			break;
43252419Sjulian		case TYPE_UNCOMPRESSED_TCP:
43352419Sjulian			hook = priv->vjuncomp;
43452419Sjulian			break;
43552419Sjulian		case TYPE_COMPRESSED_TCP:
43652419Sjulian			hook = priv->vjcomp;
43752419Sjulian			break;
43852419Sjulian		default:
43987599Sobrien			panic("%s: type=%d", __func__, type);
44052419Sjulian		}
44152419Sjulian	} else if (hook == priv->vjcomp) {	/* incoming compressed packet */
44253190Sarchie		int vjlen, need2pullup;
44353190Sarchie		struct mbuf *hm;
44452419Sjulian		u_char *hdr;
44553190Sarchie		u_int hlen;
44652419Sjulian
44752539Sjulian		/* Are we decompressing? */
44852539Sjulian		if (!priv->conf.enableDecomp) {
44970700Sjulian			NG_FREE_M(m);
45070700Sjulian			NG_FREE_ITEM(item);
45153190Sarchie			return (ENXIO);
45252419Sjulian		}
45352419Sjulian
45453190Sarchie		/* Pull up the necessary amount from the mbuf */
45553190Sarchie		need2pullup = MAX_VJHEADER;
45653190Sarchie		if (need2pullup > m->m_pkthdr.len)
45753190Sarchie			need2pullup = m->m_pkthdr.len;
45853190Sarchie		if (m->m_len < need2pullup
45953190Sarchie		    && (m = m_pullup(m, need2pullup)) == NULL) {
46053190Sarchie			priv->slc.sls_errorin++;
46170700Sjulian			NG_FREE_ITEM(item);
46253190Sarchie			return (ENOBUFS);
46353076Sarchie		}
46453190Sarchie
46553190Sarchie		/* Uncompress packet to reconstruct TCP/IP header */
46652419Sjulian		vjlen = sl_uncompress_tcp_core(mtod(m, u_char *),
46752419Sjulian		    m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP,
46852419Sjulian		    &priv->slc, &hdr, &hlen);
46952419Sjulian		if (vjlen <= 0) {
47070700Sjulian			NG_FREE_M(m);
47170700Sjulian			NG_FREE_ITEM(item);
47253190Sarchie			return (EINVAL);
47352419Sjulian		}
47453076Sarchie		m_adj(m, vjlen);
47552419Sjulian
47652419Sjulian		/* Copy the reconstructed TCP/IP headers into a new mbuf */
477111119Simp		MGETHDR(hm, M_DONTWAIT, MT_DATA);
47853190Sarchie		if (hm == NULL) {
47953190Sarchie			priv->slc.sls_errorin++;
48070700Sjulian			NG_FREE_M(m);
48170700Sjulian			NG_FREE_ITEM(item);
48253190Sarchie			return (ENOBUFS);
48353190Sarchie		}
48453190Sarchie		hm->m_len = 0;
48553284Sarchie		hm->m_pkthdr.rcvif = NULL;
48653190Sarchie		if (hlen > MHLEN) {		/* unlikely, but can happen */
487111119Simp			MCLGET(hm, M_DONTWAIT);
48853190Sarchie			if ((hm->m_flags & M_EXT) == 0) {
48953190Sarchie				m_freem(hm);
49053190Sarchie				priv->slc.sls_errorin++;
49170700Sjulian				NG_FREE_M(m);
49270700Sjulian				NG_FREE_ITEM(item);
49353190Sarchie				return (ENOBUFS);
49452419Sjulian			}
49552419Sjulian		}
49653190Sarchie		bcopy(hdr, mtod(hm, u_char *), hlen);
49753190Sarchie		hm->m_len = hlen;
49852419Sjulian
49953190Sarchie		/* Glue TCP/IP headers and rest of packet together */
50053190Sarchie		hm->m_next = m;
50153190Sarchie		hm->m_pkthdr.len = hlen + m->m_pkthdr.len;
50253190Sarchie		m = hm;
50352419Sjulian		hook = priv->ip;
50452419Sjulian	} else if (hook == priv->vjuncomp) {	/* incoming uncompressed pkt */
50552419Sjulian		u_char *hdr;
50652539Sjulian		u_int hlen;
50752419Sjulian
50852539Sjulian		/* Are we decompressing? */
50952539Sjulian		if (!priv->conf.enableDecomp) {
51070700Sjulian			NG_FREE_M(m);
51170700Sjulian			NG_FREE_ITEM(item);
51253190Sarchie			return (ENXIO);
51352419Sjulian		}
51452419Sjulian
51553190Sarchie		/* Pull up IP+TCP headers */
51653190Sarchie		if ((m = ng_vjc_pulluphdrs(m, 1)) == NULL) {
51770700Sjulian			NG_FREE_ITEM(item);
51853190Sarchie			return (ENOBUFS);
51953190Sarchie		}
52053190Sarchie
52152419Sjulian		/* Run packet through uncompressor */
52252419Sjulian		if (sl_uncompress_tcp_core(mtod(m, u_char *),
52352419Sjulian		    m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP,
52452419Sjulian		    &priv->slc, &hdr, &hlen) < 0) {
52570700Sjulian			NG_FREE_M(m);
52670700Sjulian			NG_FREE_ITEM(item);
52753190Sarchie			return (EINVAL);
52852419Sjulian		}
52952419Sjulian		hook = priv->ip;
53052419Sjulian	} else if (hook == priv->vjip)	/* incoming regular packet (bypass) */
53152419Sjulian		hook = priv->ip;
53252419Sjulian	else
53387599Sobrien		panic("%s: unknown hook", __func__);
53452419Sjulian
53553190Sarchie	/* Send result back out */
53670700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
53752419Sjulian	return (error);
53852419Sjulian}
53952419Sjulian
54052419Sjulian/*
54152419Sjulian * Shutdown node
54252419Sjulian */
54352419Sjulianstatic int
54470700Sjulianng_vjc_shutdown(node_p node)
54552419Sjulian{
54670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
54752419Sjulian
54852419Sjulian	bzero(priv, sizeof(*priv));
549184205Sdes	free(priv, M_NETGRAPH);
55070784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
55170784Sjulian	NG_NODE_UNREF(node);
55252419Sjulian	return (0);
55352419Sjulian}
55452419Sjulian
55552419Sjulian/*
55652419Sjulian * Hook disconnection
55752419Sjulian */
55852419Sjulianstatic int
55952419Sjulianng_vjc_disconnect(hook_p hook)
56052419Sjulian{
56170784Sjulian	const node_p node = NG_HOOK_NODE(hook);
56270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
56353406Sarchie
56453406Sarchie	/* Zero out hook pointer */
56553406Sarchie	if (hook == priv->ip)
56653406Sarchie		priv->ip = NULL;
56753406Sarchie	else if (hook == priv->vjcomp)
56853406Sarchie		priv->vjcomp = NULL;
56953406Sarchie	else if (hook == priv->vjuncomp)
57053406Sarchie		priv->vjuncomp = NULL;
57153406Sarchie	else if (hook == priv->vjip)
57253406Sarchie		priv->vjip = NULL;
57353406Sarchie	else
57487599Sobrien		panic("%s: unknown hook", __func__);
57553406Sarchie
57653406Sarchie	/* Go away if no hooks left */
57770784Sjulian	if ((NG_NODE_NUMHOOKS(node) == 0)
57870784Sjulian	&& (NG_NODE_IS_VALID(node)))
57970700Sjulian		ng_rmnode_self(node);
58052419Sjulian	return (0);
58152419Sjulian}
58252419Sjulian
58352419Sjulian/************************************************************************
58452419Sjulian			HELPER STUFF
58552419Sjulian ************************************************************************/
58652419Sjulian
58752419Sjulian/*
58852539Sjulian * Pull up the full IP and TCP headers of a packet. If packet is not
58952419Sjulian * a TCP packet, just pull up the IP header.
59052419Sjulian */
59152419Sjulianstatic struct mbuf *
59253190Sarchieng_vjc_pulluphdrs(struct mbuf *m, int knownTCP)
59352419Sjulian{
59452419Sjulian	struct ip *ip;
59552419Sjulian	struct tcphdr *tcp;
59652419Sjulian	int ihlen, thlen;
59752419Sjulian
59853076Sarchie	if (m->m_len < sizeof(*ip) && (m = m_pullup(m, sizeof(*ip))) == NULL)
59952419Sjulian		return (NULL);
60052419Sjulian	ip = mtod(m, struct ip *);
60153190Sarchie	if (!knownTCP && ip->ip_p != IPPROTO_TCP)
60252419Sjulian		return (m);
60352539Sjulian	ihlen = ip->ip_hl << 2;
60452539Sjulian	if (m->m_len < ihlen + sizeof(*tcp)) {
60553076Sarchie		if ((m = m_pullup(m, ihlen + sizeof(*tcp))) == NULL)
60652419Sjulian			return (NULL);
60752419Sjulian		ip = mtod(m, struct ip *);
60852419Sjulian	}
60953190Sarchie	tcp = (struct tcphdr *)((u_char *)ip + ihlen);
61052539Sjulian	thlen = tcp->th_off << 2;
61152539Sjulian	if (m->m_len < ihlen + thlen)
61252419Sjulian		m = m_pullup(m, ihlen + thlen);
61352419Sjulian	return (m);
61452419Sjulian}
61552419Sjulian
616