xref: /freebsd/sys/netgraph/ng_vjc.c (revision a14a0223ae1b172e96dd2a1d849e22026a98b692)
1 
2 /*
3  * ng_vjc.c
4  *
5  * Copyright (c) 1996-1999 Whistle Communications, Inc.
6  * All rights reserved.
7  *
8  * Subject to the following obligations and disclaimer of warranty, use and
9  * redistribution of this software, in source or object code forms, with or
10  * without modifications are expressly permitted by Whistle Communications;
11  * provided, however, that:
12  * 1. Any and all reproductions of the source or object code must include the
13  *    copyright notice above and the following disclaimer of warranties; and
14  * 2. No rights are granted, in any manner or form, to use Whistle
15  *    Communications, Inc. trademarks, including the mark "WHISTLE
16  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17  *    such appears in the above copyright notice or in the software.
18  *
19  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35  * OF SUCH DAMAGE.
36  *
37  * Author: Archie Cobbs <archie@whistle.com>
38  *
39  * $FreeBSD$
40  * $Whistle: ng_vjc.c,v 1.17 1999/11/01 09:24:52 julian Exp $
41  */
42 
43 /*
44  * This node performs Van Jacobsen IP header (de)compression.
45  * You must have included net/slcompress.c in your kernel compilation.
46  */
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/errno.h>
51 #include <sys/kernel.h>
52 #include <sys/mbuf.h>
53 #include <sys/malloc.h>
54 #include <sys/conf.h>
55 #include <sys/errno.h>
56 #include <sys/socket.h>
57 #include <sys/syslog.h>
58 
59 #include <netgraph/ng_message.h>
60 #include <netgraph/netgraph.h>
61 #include <netgraph/ng_vjc.h>
62 
63 #include <netinet/in.h>
64 #include <netinet/in_systm.h>
65 #include <netinet/ip.h>
66 #include <netinet/tcp.h>
67 
68 #include <net/slcompress.h>
69 
70 /* Check agreement with slcompress.c */
71 #if MAX_STATES != NG_VJC_MAX_CHANNELS
72 #error NG_VJC_MAX_CHANNELS must be the same as MAX_STATES
73 #endif
74 
75 #define MAX_VJHEADER		16
76 
77 /* Node private data */
78 struct private {
79 	struct	ngm_vjc_config conf;
80 	struct	slcompress slc;
81 	hook_p	ip;
82 	hook_p	vjcomp;
83 	hook_p	vjuncomp;
84 	hook_p	vjip;
85 };
86 typedef struct private *priv_p;
87 
88 #define ERROUT(x)	do { error = (x); goto done; } while (0)
89 
90 /* Netgraph node methods */
91 static ng_constructor_t	ng_vjc_constructor;
92 static ng_rcvmsg_t	ng_vjc_rcvmsg;
93 static ng_shutdown_t	ng_vjc_rmnode;
94 static ng_newhook_t	ng_vjc_newhook;
95 static ng_rcvdata_t	ng_vjc_rcvdata;
96 static ng_disconnect_t	ng_vjc_disconnect;
97 
98 /* Helper stuff */
99 static struct mbuf *ng_vjc_pulluphdrs(struct mbuf *m);
100 
101 /* Node type descriptor */
102 static struct ng_type typestruct = {
103 	NG_VERSION,
104 	NG_VJC_NODE_TYPE,
105 	NULL,
106 	ng_vjc_constructor,
107 	ng_vjc_rcvmsg,
108 	ng_vjc_rmnode,
109 	ng_vjc_newhook,
110 	NULL,
111 	NULL,
112 	ng_vjc_rcvdata,
113 	ng_vjc_rcvdata,
114 	ng_vjc_disconnect
115 };
116 NETGRAPH_INIT(vjc, &typestruct);
117 
118 /************************************************************************
119 			NETGRAPH NODE METHODS
120  ************************************************************************/
121 
122 /*
123  * Create a new node
124  */
125 static int
126 ng_vjc_constructor(node_p *nodep)
127 {
128 	priv_p priv;
129 	int error;
130 
131 	/* Allocate private structure */
132 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
133 	if (priv == NULL)
134 		return (ENOMEM);
135 	bzero(priv, sizeof(*priv));
136 
137 	/* Call generic node constructor */
138 	if ((error = ng_make_node_common(&typestruct, nodep))) {
139 		FREE(priv, M_NETGRAPH);
140 		return (error);
141 	}
142 	(*nodep)->private = priv;
143 
144 	/* Done */
145 	return (0);
146 }
147 
148 /*
149  * Add a new hook
150  */
151 static int
152 ng_vjc_newhook(node_p node, hook_p hook, const char *name)
153 {
154 	const priv_p priv = (priv_p) node->private;
155 	hook_p *hookp;
156 
157 	/* Get hook */
158 	if (!strcmp(name, NG_VJC_HOOK_IP))
159 		hookp = &priv->ip;
160 	else if (!strcmp(name, NG_VJC_HOOK_VJCOMP))
161 		hookp = &priv->vjcomp;
162 	else if (!strcmp(name, NG_VJC_HOOK_VJUNCOMP))
163 		hookp = &priv->vjuncomp;
164 	else if (!strcmp(name, NG_VJC_HOOK_VJIP))
165 		hookp = &priv->vjip;
166 	else
167 		return (EINVAL);
168 
169 	/* See if already connected */
170 	if (*hookp)
171 		return (EISCONN);
172 
173 	/* OK */
174 	*hookp = hook;
175 	return (0);
176 }
177 
178 /*
179  * Receive a control message
180  */
181 static int
182 ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg,
183 	      const char *raddr, struct ng_mesg **rptr)
184 {
185 	const priv_p priv = (priv_p) node->private;
186 	struct ng_mesg *resp = NULL;
187 	int error = 0;
188 
189 	/* Check type cookie */
190 	switch (msg->header.typecookie) {
191 	case NGM_VJC_COOKIE:
192 		switch (msg->header.cmd) {
193 		case NGM_VJC_SET_CONFIG:
194 		    {
195 			struct ngm_vjc_config *const c =
196 				(struct ngm_vjc_config *) msg->data;
197 
198 			if (msg->header.arglen != sizeof(*c))
199 				ERROUT(EINVAL);
200 			if ((priv->conf.enableComp || priv->conf.enableDecomp)
201 			    && (c->enableComp || c->enableDecomp))
202 				ERROUT(EALREADY);
203 			if (c->enableComp) {
204 				if (c->numChannels > NG_VJC_MAX_CHANNELS
205 				    || c->numChannels < NG_VJC_MIN_CHANNELS)
206 					ERROUT(EINVAL);
207 			} else {
208 				c->numChannels = NG_VJC_MAX_CHANNELS;
209 			}
210 			if (c->enableComp != 0 || c->enableDecomp != 0) {
211 				bzero(&priv->slc, sizeof(priv->slc));
212 				sl_compress_init(&priv->slc, c->numChannels);
213 			}
214 			priv->conf = *c;
215 			break;
216 		    }
217 		case NGM_VJC_GET_STATE:
218 			NG_MKRESPONSE(resp, msg, sizeof(priv->slc), M_NOWAIT);
219 			if (resp == NULL)
220 				ERROUT(ENOMEM);
221 			*((struct slcompress *) resp->data) = priv->slc;
222 			break;
223 		case NGM_VJC_CLR_STATS:
224 			priv->slc.sls_packets = 0;
225 			priv->slc.sls_compressed = 0;
226 			priv->slc.sls_searches = 0;
227 			priv->slc.sls_misses = 0;
228 			priv->slc.sls_uncompressedin = 0;
229 			priv->slc.sls_compressedin = 0;
230 			priv->slc.sls_errorin = 0;
231 			priv->slc.sls_tossed = 0;
232 			break;
233 		case NGM_VJC_RECV_ERROR:
234 			priv->slc.flags |= SLF_TOSS;
235 			break;
236 		default:
237 			error = EINVAL;
238 			break;
239 		}
240 		break;
241 	default:
242 		error = EINVAL;
243 		break;
244 	}
245 	if (rptr)
246 		*rptr = resp;
247 	else if (resp)
248 		FREE(resp, M_NETGRAPH);
249 
250 done:
251 	FREE(msg, M_NETGRAPH);
252 	return (error);
253 }
254 
255 /*
256  * Receive data
257  */
258 static int
259 ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
260 {
261 	const node_p node = hook->node;
262 	const priv_p priv = (priv_p) node->private;
263 	int error = 0;
264 
265 	if (hook == priv->ip) {			/* outgoing packet */
266 		u_int   type;
267 
268 		if (!priv->conf.enableComp)	/* compression not enabled */
269 			type = TYPE_IP;
270 		else {
271 			struct ip *ip;
272 
273 			if ((m = ng_vjc_pulluphdrs(m)) == NULL)
274 				ERROUT(ENOBUFS);
275 			ip = mtod(m, struct ip *);
276 			type = (ip->ip_p == IPPROTO_TCP) ?
277 			    sl_compress_tcp(m, ip,
278 				&priv->slc, priv->conf.compressCID) : TYPE_IP;
279 		}
280 		switch (type) {
281 		case TYPE_IP:
282 			hook = priv->vjip;
283 			break;
284 		case TYPE_UNCOMPRESSED_TCP:
285 			hook = priv->vjuncomp;
286 			break;
287 		case TYPE_COMPRESSED_TCP:
288 			hook = priv->vjcomp;
289 			break;
290 		default:
291 			panic(__FUNCTION__);
292 		}
293 	} else if (hook == priv->vjcomp) {	/* incoming compressed packet */
294 		int     vjlen;
295 		u_int   hlen;
296 		u_char *hdr;
297 		struct mbuf *mp;
298 
299 		/* Are we decompressing? */
300 		if (!priv->conf.enableDecomp) {
301 			m_freem(m);
302 			m = NULL;
303 			ERROUT(ENETDOWN);
304 		}
305 
306 		/* Uncompress packet to reconstruct TCP/IP header */
307 		if (m->m_len < MAX_VJHEADER && !(m = m_pullup(m, MAX_VJHEADER)))
308 			ERROUT(ENOBUFS);
309 		vjlen = sl_uncompress_tcp_core(mtod(m, u_char *),
310 		    m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP,
311 		    &priv->slc, &hdr, &hlen);
312 		if (vjlen <= 0) {
313 			m_freem(m);
314 			m = NULL;
315 			ERROUT(EINVAL);
316 		}
317 
318 		/* Copy the reconstructed TCP/IP headers into a new mbuf */
319 		MGETHDR(mp, M_DONTWAIT, MT_DATA);
320 		if (!mp)
321 			goto compfailmem;
322 		mp->m_len = 0;
323 		mp->m_next = NULL;
324 		if (hlen > MHLEN) {
325 			MCLGET(mp, M_DONTWAIT);
326 			if (M_TRAILINGSPACE(mp) < hlen) {
327 				m_freem(mp);	/* can't get a cluster, drop */
328 compfailmem:
329 				m_freem(m);
330 				m = NULL;
331 				ERROUT(ENOBUFS);
332 			}
333 		}
334 		bcopy(hdr, mtod(mp, u_char *), hlen);
335 		mp->m_len = hlen;
336 
337 		/* Stick header and rest of packet together */
338 		m->m_data += vjlen;
339 		m->m_len -= vjlen;
340 		if (m->m_len <= M_TRAILINGSPACE(mp)) {
341 			bcopy(mtod(m, u_char *),
342 			    mtod(mp, u_char *) + mp->m_len, m->m_len);
343 			mp->m_len += m->m_len;
344 			MFREE(m, mp->m_next);
345 		} else
346 			mp->m_next = m;
347 		m = mp;
348 		hook = priv->ip;
349 	} else if (hook == priv->vjuncomp) {	/* incoming uncompressed pkt */
350 		u_char *hdr;
351 		u_int hlen;
352 
353 		/* Are we decompressing? */
354 		if (!priv->conf.enableDecomp) {
355 			m_freem(m);
356 			m = NULL;
357 			ERROUT(ENETDOWN);
358 		}
359 
360 		/* Run packet through uncompressor */
361 		if ((m = ng_vjc_pulluphdrs(m)) == NULL)
362 			ERROUT(ENOBUFS);
363 		if (sl_uncompress_tcp_core(mtod(m, u_char *),
364 		    m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP,
365 		    &priv->slc, &hdr, &hlen) < 0) {
366 			m_freem(m);
367 			m = NULL;
368 			ERROUT(EINVAL);
369 		}
370 		hook = priv->ip;
371 	} else if (hook == priv->vjip)	/* incoming regular packet (bypass) */
372 		hook = priv->ip;
373 	else
374 		panic(__FUNCTION__);
375 
376 done:
377 	if (m)
378 		NG_SEND_DATA(error, hook, m, meta);
379 	else
380 		NG_FREE_META(meta);
381 	return (error);
382 }
383 
384 /*
385  * Shutdown node
386  */
387 static int
388 ng_vjc_rmnode(node_p node)
389 {
390 	const priv_p priv = (priv_p) node->private;
391 
392 	node->flags |= NG_INVALID;
393 	ng_cutlinks(node);
394 	ng_unname(node);
395 	bzero(priv, sizeof(*priv));
396 	FREE(priv, M_NETGRAPH);
397 	node->private = NULL;
398 	ng_unref(node);
399 	return (0);
400 }
401 
402 /*
403  * Hook disconnection
404  */
405 static int
406 ng_vjc_disconnect(hook_p hook)
407 {
408 	if (hook->node->numhooks == 0)
409 		ng_rmnode(hook->node);
410 	return (0);
411 }
412 
413 /************************************************************************
414 			HELPER STUFF
415  ************************************************************************/
416 
417 /*
418  * Pull up the full IP and TCP headers of a packet. If packet is not
419  * a TCP packet, just pull up the IP header.
420  */
421 static struct mbuf *
422 ng_vjc_pulluphdrs(struct mbuf *m)
423 {
424 	struct ip *ip;
425 	struct tcphdr *tcp;
426 	int ihlen, thlen;
427 
428 	if (m->m_len < sizeof(*ip) && !(m = m_pullup(m, sizeof(*ip))))
429 		return (NULL);
430 	ip = mtod(m, struct ip *);
431 	if (ip->ip_p != IPPROTO_TCP)
432 		return (m);
433 	ihlen = ip->ip_hl << 2;
434 	if (m->m_len < ihlen + sizeof(*tcp)) {
435 		if (!(m = m_pullup(m, ihlen + sizeof(*tcp))))
436 			return (NULL);
437 		ip = mtod(m, struct ip *);
438 	}
439 	tcp = (struct tcphdr *) ((u_char *) ip + ihlen);
440 	thlen = tcp->th_off << 2;
441 	if (m->m_len < ihlen + thlen)
442 		m = m_pullup(m, ihlen + thlen);
443 	return (m);
444 }
445 
446