ng_async.c revision 52976
1
2/*
3 * ng_async.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: head/sys/netgraph/ng_async.c 52976 1999-11-08 03:10:20Z archie $
40 * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
41 */
42
43/*
44 * This node type implements a PPP style sync <-> async converter.
45 * See RFC 1661 for details of how asynchronous encoding works.
46 */
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/kernel.h>
51#include <sys/conf.h>
52#include <sys/proc.h>
53#include <sys/mbuf.h>
54#include <sys/malloc.h>
55#include <sys/socket.h>
56#include <sys/file.h>
57#include <sys/tty.h>
58#include <sys/syslog.h>
59#include <sys/errno.h>
60
61#include <netgraph/ng_message.h>
62#include <netgraph/netgraph.h>
63#include <netgraph/ng_async.h>
64
65#include <net/ppp_defs.h>
66
67/* LCP protocol number */
68#define PROTO_LCP	0xc021
69
70/* Async decode state */
71#define MODE_HUNT	0
72#define MODE_NORMAL	1
73#define MODE_ESC	2
74
75/* Private data structure */
76struct private {
77	node_p  	node;		/* Our node */
78	hook_p  	async;		/* Asynchronous side */
79	hook_p  	sync;		/* Synchronous side */
80	u_char  	amode;		/* Async hunt/esape mode */
81	u_int16_t	fcs;		/* Decoded async FCS (so far) */
82	u_char	       *abuf;		/* Buffer to encode sync into */
83	u_char	       *sbuf;		/* Buffer to decode async into */
84	u_int		slen;		/* Length of data in sbuf */
85	long		lasttime;	/* Time of last async packet sent */
86	struct		ng_async_cfg	cfg;	/* Configuration */
87	struct		ng_async_stat	stats;	/* Statistics */
88};
89typedef struct private *sc_p;
90
91/* Useful macros */
92#define ASYNC_BUF_SIZE(smru)	(2 * (smru) + 10)
93#define SYNC_BUF_SIZE(amru)	((amru) + 10)
94#define ERROUT(x)		do { error = (x); goto done; } while (0)
95
96/* Netgraph methods */
97static ng_constructor_t	nga_constructor;
98static ng_rcvdata_t		nga_rcvdata;
99static ng_rcvmsg_t		nga_rcvmsg;
100static ng_shutdown_t		nga_shutdown;
101static ng_newhook_t		nga_newhook;
102static ng_disconnect_t		nga_disconnect;
103
104/* Helper stuff */
105static int	nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta);
106static int	nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta);
107
108/* Define the netgraph node type */
109static struct ng_type typestruct = {
110	NG_VERSION,
111	NG_ASYNC_NODE_TYPE,
112	NULL,
113	nga_constructor,
114	nga_rcvmsg,
115	nga_shutdown,
116	nga_newhook,
117	NULL,
118	NULL,
119	nga_rcvdata,
120	nga_rcvdata,
121	nga_disconnect
122};
123NETGRAPH_INIT(async, &typestruct);
124
125/* CRC table */
126static const u_int16_t fcstab[];
127
128/******************************************************************
129		    NETGRAPH NODE METHODS
130******************************************************************/
131
132/*
133 * Initialize a new node
134 */
135static int
136nga_constructor(node_p *nodep)
137{
138	sc_p sc;
139	int error;
140
141	if ((error = ng_make_node_common(&typestruct, nodep)))
142		return (error);
143	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK);
144	if (sc == NULL)
145		return (ENOMEM);
146	bzero(sc, sizeof(*sc));
147	sc->amode = MODE_HUNT;
148	sc->cfg.accm = ~0;
149	sc->cfg.amru = NG_ASYNC_DEFAULT_MRU;
150	sc->cfg.smru = NG_ASYNC_DEFAULT_MRU;
151	MALLOC(sc->abuf, u_char *,
152	    ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH, M_WAITOK);
153	if (sc->abuf == NULL)
154		goto fail;
155	MALLOC(sc->sbuf, u_char *,
156	    SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH, M_WAITOK);
157	if (sc->sbuf == NULL) {
158		FREE(sc->abuf, M_NETGRAPH);
159fail:
160		FREE(sc, M_NETGRAPH);
161		return (ENOMEM);
162	}
163	(*nodep)->private = sc;
164	sc->node = *nodep;
165	return (0);
166}
167
168/*
169 * Reserve a hook for a pending connection
170 */
171static int
172nga_newhook(node_p node, hook_p hook, const char *name)
173{
174	const sc_p sc = node->private;
175	hook_p *hookp;
176
177	if (!strcmp(name, NG_ASYNC_HOOK_ASYNC))
178		hookp = &sc->async;
179	else if (!strcmp(name, NG_ASYNC_HOOK_SYNC))
180		hookp = &sc->sync;
181	else
182		return (EINVAL);
183	if (*hookp)
184		return (EISCONN);
185	*hookp = hook;
186	return (0);
187}
188
189/*
190 * Receive incoming data
191 */
192static int
193nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
194{
195	const sc_p sc = hook->node->private;
196
197	if (hook == sc->sync)
198		return (nga_rcv_sync(sc, m, meta));
199	if (hook == sc->async)
200		return (nga_rcv_async(sc, m, meta));
201	panic(__FUNCTION__);
202}
203
204/*
205 * Receive incoming control message
206 */
207static int
208nga_rcvmsg(node_p node, struct ng_mesg *msg,
209	const char *rtn, struct ng_mesg **rptr)
210{
211	const sc_p sc = (sc_p) node->private;
212	struct ng_mesg *resp = NULL;
213	int error = 0;
214
215	switch (msg->header.typecookie) {
216	case NGM_ASYNC_COOKIE:
217		switch (msg->header.cmd) {
218		case NGM_ASYNC_CMD_GET_STATS:
219			NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
220			if (resp == NULL)
221				ERROUT(ENOMEM);
222			*((struct ng_async_stat *) resp->data) = sc->stats;
223			break;
224		case NGM_ASYNC_CMD_CLR_STATS:
225			bzero(&sc->stats, sizeof(sc->stats));
226			break;
227		case NGM_ASYNC_CMD_SET_CONFIG:
228		    {
229			struct ng_async_cfg *const cfg =
230				(struct ng_async_cfg *) msg->data;
231			u_char *buf;
232
233			if (msg->header.arglen != sizeof(*cfg))
234				ERROUT(EINVAL);
235			if (cfg->amru < NG_ASYNC_MIN_MRU
236			    || cfg->amru > NG_ASYNC_MAX_MRU
237			    || cfg->smru < NG_ASYNC_MIN_MRU
238			    || cfg->smru > NG_ASYNC_MAX_MRU)
239				ERROUT(EINVAL);
240			cfg->enabled = !!cfg->enabled;	/* normalize */
241			cfg->acfcomp = !!cfg->acfcomp;	/* normalize */
242			if (cfg->smru > sc->cfg.smru) {	/* reallocate buffer */
243				MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru),
244				    M_NETGRAPH, M_NOWAIT);
245				if (!buf)
246					ERROUT(ENOMEM);
247				FREE(sc->abuf, M_NETGRAPH);
248				sc->abuf = buf;
249			}
250			if (cfg->amru > sc->cfg.amru) {	/* reallocate buffer */
251				MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru),
252				    M_NETGRAPH, M_NOWAIT);
253				if (!buf)
254					ERROUT(ENOMEM);
255				FREE(sc->sbuf, M_NETGRAPH);
256				sc->sbuf = buf;
257				sc->amode = MODE_HUNT;
258				sc->slen = 0;
259			}
260			if (!cfg->enabled) {
261				sc->amode = MODE_HUNT;
262				sc->slen = 0;
263			}
264			sc->cfg = *cfg;
265			break;
266		    }
267		case NGM_ASYNC_CMD_GET_CONFIG:
268			NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
269			if (!resp)
270				ERROUT(ENOMEM);
271			*((struct ng_async_cfg *) resp->data) = sc->cfg;
272			break;
273		default:
274			ERROUT(EINVAL);
275		}
276		break;
277	default:
278		ERROUT(EINVAL);
279	}
280	if (rptr)
281		*rptr = resp;
282	else if (resp)
283		FREE(resp, M_NETGRAPH);
284
285done:
286	FREE(msg, M_NETGRAPH);
287	return (error);
288}
289
290/*
291 * Shutdown this node
292 */
293static int
294nga_shutdown(node_p node)
295{
296	const sc_p sc = node->private;
297
298	ng_cutlinks(node);
299	ng_unname(node);
300	FREE(sc->abuf, M_NETGRAPH);
301	FREE(sc->sbuf, M_NETGRAPH);
302	bzero(sc, sizeof(*sc));
303	FREE(sc, M_NETGRAPH);
304	node->private = NULL;
305	ng_unref(node);
306	return (0);
307}
308
309/*
310 * Lose a hook. When both hooks go away, we disappear.
311 */
312static int
313nga_disconnect(hook_p hook)
314{
315	const sc_p sc = hook->node->private;
316	hook_p *hookp;
317
318	if (hook == sc->async)
319		hookp = &sc->async;
320	else if (hook == sc->sync)
321		hookp = &sc->sync;
322	else
323		panic(__FUNCTION__);
324	if (!*hookp)
325		panic(__FUNCTION__ "2");
326	*hookp = NULL;
327	bzero(&sc->stats, sizeof(sc->stats));
328	sc->lasttime = 0;
329	if (hook->node->numhooks == 0)
330		ng_rmnode(hook->node);
331	return (0);
332}
333
334/******************************************************************
335		    INTERNAL HELPER STUFF
336******************************************************************/
337
338/*
339 * Encode a byte into the async buffer
340 */
341static __inline__ void
342nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
343{
344	*fcs = PPP_FCS(*fcs, x);
345	if ((x < 32 && ((1 << x) & accm))
346	    || (x == PPP_ESCAPE)
347	    || (x == PPP_FLAG)) {
348		sc->abuf[(*len)++] = PPP_ESCAPE;
349		x ^= PPP_TRANS;
350	}
351	sc->abuf[(*len)++] = x;
352}
353
354/*
355 * Receive incoming synchronous data.
356 */
357static int
358nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta)
359{
360	struct ifnet *const rcvif = m->m_pkthdr.rcvif;
361	int acfcomp, alen, error = 0;
362	struct timeval time;
363	u_int16_t fcs, fcs0;
364	u_int32_t accm;
365
366#define ADD_BYTE(x)	nga_async_add(sc, &fcs, accm, &alen, (x))
367
368	/* Check for bypass mode */
369	if (!sc->cfg.enabled) {
370		NG_SEND_DATA(error, sc->async, m, meta);
371		return (error);
372	}
373
374	/* Defaults for ACF compression and ACCM */
375	accm = sc->cfg.accm;
376	acfcomp = sc->cfg.acfcomp;
377
378	/* Special case LCP frames: disable ACF and enable ACCM */
379	{
380		struct mbuf *n = m;
381		int off, proto;
382
383		for (proto = off = 0; (proto & 1) == 0; off++) {
384			while (n != NULL && off >= n->m_len) {
385				n = n->m_next;
386				off = 0;
387			}
388			if (n == NULL)
389				break;
390			proto = (proto << 8) | mtod(n, u_char *)[off];
391		}
392		if (proto == PROTO_LCP) {
393			accm = ~0;
394			acfcomp = 0;
395		}
396	}
397
398	/* Check for overflow */
399	if (m->m_pkthdr.len > sc->cfg.smru) {
400		sc->stats.syncOverflows++;
401		NG_FREE_DATA(m, meta);
402		return (EMSGSIZE);
403	}
404
405	/* Update stats */
406	sc->stats.syncFrames++;
407	sc->stats.syncOctets += m->m_pkthdr.len;
408
409	/* Initialize async encoded version of input mbuf */
410	alen = 0;
411	fcs = PPP_INITFCS;
412
413	/* Add beginning sync flag if it's been long enough to need one */
414	getmicrotime(&time);
415	if (time.tv_sec >= sc->lasttime + 1) {
416		sc->abuf[alen++] = PPP_FLAG;
417		sc->lasttime = time.tv_sec;
418	}
419
420	/* Add option address and control fields, then packet payload */
421	if (!acfcomp) {
422		ADD_BYTE(PPP_ALLSTATIONS);
423		ADD_BYTE(PPP_UI);
424	}
425	while (m != NULL) {
426		struct mbuf *n;
427
428		while (m->m_len > 0) {
429			ADD_BYTE(*mtod(m, u_char *));
430			m->m_data++;
431			m->m_len--;
432		}
433		MFREE(m, n);
434		m = n;
435	}
436
437	/* Add checksum and final sync flag */
438	fcs0 = fcs;
439	ADD_BYTE(~fcs0 & 0xff);
440	ADD_BYTE(~fcs0 >> 8);
441	sc->abuf[alen++] = PPP_FLAG;
442
443	/* Put frame in an mbuf and ship it off */
444	if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) {
445		NG_FREE_META(meta);
446		error = ENOBUFS;
447	} else
448		NG_SEND_DATA(error, sc->async, m, meta);
449	return (error);
450}
451
452/*
453 * Receive incoming asynchronous data
454 * XXX Technically, we should strip out incoming characters
455 *     that are in our ACCM. Not sure if this is good or not.
456 */
457static int
458nga_rcv_async(const sc_p sc, struct mbuf * m, meta_p meta)
459{
460	struct ifnet *const rcvif = m->m_pkthdr.rcvif;
461	int error;
462
463	if (!sc->cfg.enabled) {
464		NG_SEND_DATA(error, sc->sync, m, meta);
465		return (error);
466	}
467	NG_FREE_META(meta);
468	while (m) {
469		struct mbuf *n;
470
471		for (; m->m_len > 0; m->m_data++, m->m_len--) {
472			u_char  ch = *mtod(m, u_char *);
473
474			sc->stats.asyncOctets++;
475			if (ch == PPP_FLAG) {	/* Flag overrides everything */
476				int     skip = 0;
477
478				/* Check for runts */
479				if (sc->slen < 2) {
480					if (sc->slen > 0)
481						sc->stats.asyncRunts++;
482					goto reset;
483				}
484
485				/* Verify CRC */
486				if (sc->fcs != PPP_GOODFCS) {
487					sc->stats.asyncBadCheckSums++;
488					goto reset;
489				}
490				sc->slen -= 2;
491
492				/* Strip address and control fields */
493				if (sc->slen >= 2
494				    && sc->sbuf[0] == PPP_ALLSTATIONS
495				    && sc->sbuf[1] == PPP_UI)
496					skip = 2;
497
498				/* Check for frame too big */
499				if (sc->slen - skip > sc->cfg.amru) {
500					sc->stats.asyncOverflows++;
501					goto reset;
502				}
503
504				/* OK, ship it out */
505				if ((n = m_devget(sc->sbuf + skip,
506					   sc->slen - skip, 0, rcvif, NULL)))
507					NG_SEND_DATA(error, sc->sync, n, meta);
508				sc->stats.asyncFrames++;
509reset:
510				sc->amode = MODE_NORMAL;
511				sc->fcs = PPP_INITFCS;
512				sc->slen = 0;
513				continue;
514			}
515			switch (sc->amode) {
516			case MODE_NORMAL:
517				if (ch == PPP_ESCAPE) {
518					sc->amode = MODE_ESC;
519					continue;
520				}
521				break;
522			case MODE_ESC:
523				ch ^= PPP_TRANS;
524				sc->amode = MODE_NORMAL;
525				break;
526			case MODE_HUNT:
527			default:
528				continue;
529			}
530
531			/* Add byte to frame */
532			if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
533				sc->stats.asyncOverflows++;
534				sc->amode = MODE_HUNT;
535				sc->slen = 0;
536			} else {
537				sc->sbuf[sc->slen++] = ch;
538				sc->fcs = PPP_FCS(sc->fcs, ch);
539			}
540		}
541		MFREE(m, n);
542		m = n;
543	}
544	return (0);
545}
546
547/*
548 * CRC table
549 *
550 * Taken from RFC 1171 Appendix B
551 */
552static const u_int16_t fcstab[256] = {
553	 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
554	 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
555	 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
556	 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
557	 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
558	 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
559	 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
560	 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
561	 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
562	 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
563	 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
564	 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
565	 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
566	 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
567	 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
568	 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
569	 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
570	 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
571	 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
572	 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
573	 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
574	 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
575	 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
576	 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
577	 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
578	 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
579	 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
580	 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
581	 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
582	 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
583	 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
584	 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
585};
586