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