ng_async.c revision 129823
1228753Smm
2228753Smm/*
3248616Smm * ng_async.c
4228753Smm *
5228753Smm * Copyright (c) 1996-1999 Whistle Communications, Inc.
6228753Smm * All rights reserved.
7228753Smm *
8228753Smm * Subject to the following obligations and disclaimer of warranty, use and
9228753Smm * redistribution of this software, in source or object code forms, with or
10228753Smm * without modifications are expressly permitted by Whistle Communications;
11228753Smm * provided, however, that:
12228753Smm * 1. Any and all reproductions of the source or object code must include the
13228753Smm *    copyright notice above and the following disclaimer of warranties; and
14228753Smm * 2. No rights are granted, in any manner or form, to use Whistle
15228753Smm *    Communications, Inc. trademarks, including the mark "WHISTLE
16228753Smm *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17228753Smm *    such appears in the above copyright notice or in the software.
18228753Smm *
19228753Smm * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20228753Smm * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21228753Smm * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22228753Smm * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23228753Smm * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24228753Smm * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25228753Smm * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26228753Smm * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27228763Smm * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28228753Smm * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29228753Smm * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30228753Smm * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31228753Smm * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34228753Smm * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35228753Smm * OF SUCH DAMAGE.
36228753Smm *
37232153Smm * Author: Archie Cobbs <archie@freebsd.org>
38232153Smm *
39232153Smm * $FreeBSD: head/sys/netgraph/ng_async.c 129823 2004-05-29 00:51:19Z julian $
40232153Smm * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
41248616Smm */
42248616Smm
43228753Smm/*
44228753Smm * This node type implements a PPP style sync <-> async converter.
45228753Smm * See RFC 1661 for details of how asynchronous encoding works.
46228753Smm */
47228753Smm
48228753Smm#include <sys/param.h>
49228753Smm#include <sys/systm.h>
50228753Smm#include <sys/kernel.h>
51228753Smm#include <sys/mbuf.h>
52228753Smm#include <sys/malloc.h>
53228753Smm#include <sys/errno.h>
54228753Smm
55228753Smm#include <netgraph/ng_message.h>
56228753Smm#include <netgraph/netgraph.h>
57232153Smm#include <netgraph/ng_async.h>
58228753Smm#include <netgraph/ng_parse.h>
59228753Smm
60248616Smm#include <net/ppp_defs.h>
61248616Smm
62228753Smm#ifdef NG_SEPARATE_MALLOC
63228753SmmMALLOC_DEFINE(M_NETGRAPH_ASYNC, "netgraph_async", "netgraph async node ");
64228753Smm#else
65228753Smm#define M_NETGRAPH_ASYNC M_NETGRAPH
66228753Smm#endif
67228753Smm
68228753Smm
69228753Smm/* Async decode state */
70228753Smm#define MODE_HUNT	0
71228753Smm#define MODE_NORMAL	1
72228753Smm#define MODE_ESC	2
73228753Smm
74228753Smm/* Private data structure */
75228753Smmstruct ng_async_private {
76228753Smm	node_p  	node;		/* Our node */
77228753Smm	hook_p  	async;		/* Asynchronous side */
78228753Smm	hook_p  	sync;		/* Synchronous side */
79228753Smm	u_char  	amode;		/* Async hunt/esape mode */
80228753Smm	u_int16_t	fcs;		/* Decoded async FCS (so far) */
81228753Smm	u_char	       *abuf;		/* Buffer to encode sync into */
82228753Smm	u_char	       *sbuf;		/* Buffer to decode async into */
83228753Smm	u_int		slen;		/* Length of data in sbuf */
84228753Smm	long		lasttime;	/* Time of last async packet sent */
85228753Smm	struct		ng_async_cfg	cfg;	/* Configuration */
86228753Smm	struct		ng_async_stat	stats;	/* Statistics */
87228753Smm};
88228753Smmtypedef struct ng_async_private *sc_p;
89228753Smm
90228753Smm/* Useful macros */
91228753Smm#define ASYNC_BUF_SIZE(smru)	(2 * (smru) + 10)
92228753Smm#define SYNC_BUF_SIZE(amru)	((amru) + 10)
93228753Smm#define ERROUT(x)		do { error = (x); goto done; } while (0)
94228753Smm
95228753Smm/* Netgraph methods */
96228753Smmstatic ng_constructor_t		nga_constructor;
97228753Smmstatic ng_rcvdata_t		nga_rcvdata;
98228753Smmstatic ng_rcvmsg_t		nga_rcvmsg;
99228753Smmstatic ng_shutdown_t		nga_shutdown;
100228753Smmstatic ng_newhook_t		nga_newhook;
101228753Smmstatic ng_disconnect_t		nga_disconnect;
102228753Smm
103228753Smm/* Helper stuff */
104228753Smmstatic int	nga_rcv_sync(const sc_p sc, item_p item);
105228753Smmstatic int	nga_rcv_async(const sc_p sc, item_p item);
106228753Smm
107228753Smm/* Parse type for struct ng_async_cfg */
108228753Smmstatic const struct ng_parse_struct_field nga_config_type_fields[]
109228753Smm	= NG_ASYNC_CONFIG_TYPE_INFO;
110228753Smmstatic const struct ng_parse_type nga_config_type = {
111228753Smm	&ng_parse_struct_type,
112228753Smm	&nga_config_type_fields
113228753Smm};
114228753Smm
115232153Smm/* Parse type for struct ng_async_stat */
116232153Smmstatic const struct ng_parse_struct_field nga_stats_type_fields[]
117232153Smm	= NG_ASYNC_STATS_TYPE_INFO;
118232153Smmstatic const struct ng_parse_type nga_stats_type = {
119232153Smm	&ng_parse_struct_type,
120232153Smm	&nga_stats_type_fields
121232153Smm};
122232153Smm
123232153Smm/* List of commands and how to convert arguments to/from ASCII */
124232153Smmstatic const struct ng_cmdlist nga_cmdlist[] = {
125232153Smm	{
126232153Smm	  NGM_ASYNC_COOKIE,
127232153Smm	  NGM_ASYNC_CMD_SET_CONFIG,
128232153Smm	  "setconfig",
129232153Smm	  &nga_config_type,
130232153Smm	  NULL
131232153Smm	},
132232153Smm	{
133232153Smm	  NGM_ASYNC_COOKIE,
134232153Smm	  NGM_ASYNC_CMD_GET_CONFIG,
135232153Smm	  "getconfig",
136232153Smm	  NULL,
137232153Smm	  &nga_config_type
138232153Smm	},
139232153Smm	{
140232153Smm	  NGM_ASYNC_COOKIE,
141232153Smm	  NGM_ASYNC_CMD_GET_STATS,
142232153Smm	  "getstats",
143232153Smm	  NULL,
144232153Smm	  &nga_stats_type
145232153Smm	},
146232153Smm	{
147232153Smm	  NGM_ASYNC_COOKIE,
148232153Smm	  NGM_ASYNC_CMD_CLR_STATS,
149232153Smm	  "clrstats",
150232153Smm	  &nga_stats_type,
151232153Smm	  NULL
152232153Smm	},
153228753Smm	{ 0 }
154232153Smm};
155228753Smm
156232153Smm/* Define the netgraph node type */
157228753Smmstatic struct ng_type typestruct = {
158228753Smm	.version =	NG_ABI_VERSION,
159228753Smm	.name =		NG_ASYNC_NODE_TYPE,
160228753Smm	.constructor =	nga_constructor,
161228753Smm	.rcvmsg =	nga_rcvmsg,
162228753Smm	.shutdown = 	nga_shutdown,
163228753Smm	.newhook =	nga_newhook,
164228753Smm	.rcvdata =	nga_rcvdata,
165228753Smm	.disconnect =	nga_disconnect,
166228753Smm	.cmdlist =	nga_cmdlist
167228753Smm};
168228753SmmNETGRAPH_INIT(async, &typestruct);
169228753Smm
170232153Smm/* CRC table */
171228753Smmstatic const u_int16_t fcstab[];
172228753Smm
173228753Smm/******************************************************************
174228753Smm		    NETGRAPH NODE METHODS
175228753Smm******************************************************************/
176228753Smm
177228753Smm/*
178228753Smm * Initialize a new node
179228753Smm */
180232153Smmstatic int
181228753Smmnga_constructor(node_p node)
182232153Smm{
183228753Smm	sc_p sc;
184228753Smm
185228753Smm	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH_ASYNC, M_NOWAIT | M_ZERO);
186228753Smm	if (sc == NULL)
187228753Smm		return (ENOMEM);
188228753Smm	sc->amode = MODE_HUNT;
189228753Smm	sc->cfg.accm = ~0;
190228753Smm	sc->cfg.amru = NG_ASYNC_DEFAULT_MRU;
191228753Smm	sc->cfg.smru = NG_ASYNC_DEFAULT_MRU;
192228753Smm	MALLOC(sc->abuf, u_char *,
193228753Smm	    ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH_ASYNC, M_NOWAIT);
194228753Smm	if (sc->abuf == NULL)
195228753Smm		goto fail;
196228753Smm	MALLOC(sc->sbuf, u_char *,
197228753Smm	    SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH_ASYNC, M_NOWAIT);
198228753Smm	if (sc->sbuf == NULL) {
199228753Smm		FREE(sc->abuf, M_NETGRAPH_ASYNC);
200228753Smmfail:
201228753Smm		FREE(sc, M_NETGRAPH_ASYNC);
202228753Smm		return (ENOMEM);
203228753Smm	}
204228753Smm	NG_NODE_SET_PRIVATE(node, sc);
205232153Smm	sc->node = node;
206228753Smm	return (0);
207228753Smm}
208228753Smm
209228753Smm/*
210228753Smm * Reserve a hook for a pending connection
211228753Smm */
212228753Smmstatic int
213228753Smmnga_newhook(node_p node, hook_p hook, const char *name)
214228753Smm{
215228753Smm	const sc_p sc = NG_NODE_PRIVATE(node);
216228753Smm	hook_p *hookp;
217228753Smm
218228753Smm	if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) {
219228753Smm		/*
220228753Smm		 * We use a static buffer here so only one packet
221232153Smm		 * at a time can be allowed to travel in this direction.
222228753Smm		 * Force Writer semantics.
223232153Smm		 */
224228753Smm		NG_HOOK_FORCE_WRITER(hook);
225228753Smm		hookp = &sc->async;
226228753Smm	} else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) {
227228753Smm		/*
228248616Smm		 * We use a static state here so only one packet
229248616Smm		 * at a time can be allowed to travel in this direction.
230248616Smm		 * Force Writer semantics.
231248616Smm		 * Since we set this for both directions
232248616Smm		 * we might as well set it for the whole node
233248616Smm		 * bit I haven;t done that (yet).
234228753Smm		 */
235248616Smm		NG_HOOK_FORCE_WRITER(hook);
236232153Smm		hookp = &sc->sync;
237248616Smm	} else {
238248616Smm		return (EINVAL);
239248616Smm	}
240248616Smm	if (*hookp) /* actually can't happen I think [JRE] */
241248616Smm		return (EISCONN);
242248616Smm	*hookp = hook;
243248616Smm	return (0);
244248616Smm}
245248616Smm
246248616Smm/*
247248616Smm * Receive incoming data
248248616Smm */
249248616Smmstatic int
250248616Smmnga_rcvdata(hook_p hook, item_p item)
251248616Smm{
252248616Smm	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
253248616Smm
254248616Smm	if (hook == sc->sync)
255248616Smm		return (nga_rcv_sync(sc, item));
256248616Smm	if (hook == sc->async)
257248616Smm		return (nga_rcv_async(sc, item));
258248616Smm	panic(__func__);
259248616Smm}
260248616Smm
261248616Smm/*
262248616Smm * Receive incoming control message
263248616Smm */
264248616Smmstatic int
265248616Smmnga_rcvmsg(node_p node, item_p item, hook_p lasthook)
266248616Smm{
267248616Smm	const sc_p sc = NG_NODE_PRIVATE(node);
268248616Smm	struct ng_mesg *resp = NULL;
269248616Smm	int error = 0;
270248616Smm	struct ng_mesg *msg;
271248616Smm
272248616Smm	NGI_GET_MSG(item, msg);
273248616Smm	switch (msg->header.typecookie) {
274248616Smm	case NGM_ASYNC_COOKIE:
275248616Smm		switch (msg->header.cmd) {
276248616Smm		case NGM_ASYNC_CMD_GET_STATS:
277248616Smm			NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
278248616Smm			if (resp == NULL)
279248616Smm				ERROUT(ENOMEM);
280248616Smm			*((struct ng_async_stat *) resp->data) = sc->stats;
281248616Smm			break;
282248616Smm		case NGM_ASYNC_CMD_CLR_STATS:
283248616Smm			bzero(&sc->stats, sizeof(sc->stats));
284248616Smm			break;
285248616Smm		case NGM_ASYNC_CMD_SET_CONFIG:
286248616Smm		    {
287248616Smm			struct ng_async_cfg *const cfg =
288248616Smm				(struct ng_async_cfg *) msg->data;
289248616Smm			u_char *buf;
290248616Smm
291248616Smm			if (msg->header.arglen != sizeof(*cfg))
292248616Smm				ERROUT(EINVAL);
293248616Smm			if (cfg->amru < NG_ASYNC_MIN_MRU
294248616Smm			    || cfg->amru > NG_ASYNC_MAX_MRU
295248616Smm			    || cfg->smru < NG_ASYNC_MIN_MRU
296248616Smm			    || cfg->smru > NG_ASYNC_MAX_MRU)
297248616Smm				ERROUT(EINVAL);
298248616Smm			cfg->enabled = !!cfg->enabled;	/* normalize */
299248616Smm			if (cfg->smru > sc->cfg.smru) {	/* reallocate buffer */
300248616Smm				MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru),
301248616Smm				    M_NETGRAPH_ASYNC, M_NOWAIT);
302248616Smm				if (!buf)
303248616Smm					ERROUT(ENOMEM);
304248616Smm				FREE(sc->abuf, M_NETGRAPH_ASYNC);
305248616Smm				sc->abuf = buf;
306248616Smm			}
307248616Smm			if (cfg->amru > sc->cfg.amru) {	/* reallocate buffer */
308248616Smm				MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru),
309248616Smm				    M_NETGRAPH_ASYNC, M_NOWAIT);
310248616Smm				if (!buf)
311248616Smm					ERROUT(ENOMEM);
312248616Smm				FREE(sc->sbuf, M_NETGRAPH_ASYNC);
313248616Smm				sc->sbuf = buf;
314248616Smm				sc->amode = MODE_HUNT;
315248616Smm				sc->slen = 0;
316248616Smm			}
317248616Smm			if (!cfg->enabled) {
318248616Smm				sc->amode = MODE_HUNT;
319248616Smm				sc->slen = 0;
320248616Smm			}
321248616Smm			sc->cfg = *cfg;
322248616Smm			break;
323248616Smm		    }
324248616Smm		case NGM_ASYNC_CMD_GET_CONFIG:
325248616Smm			NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
326248616Smm			if (!resp)
327248616Smm				ERROUT(ENOMEM);
328248616Smm			*((struct ng_async_cfg *) resp->data) = sc->cfg;
329248616Smm			break;
330248616Smm		default:
331248616Smm			ERROUT(EINVAL);
332248616Smm		}
333248616Smm		break;
334248616Smm	default:
335248616Smm		ERROUT(EINVAL);
336248616Smm	}
337248616Smmdone:
338248616Smm	NG_RESPOND_MSG(error, node, item, resp);
339248616Smm	NG_FREE_MSG(msg);
340248616Smm	return (error);
341248616Smm}
342248616Smm
343248616Smm/*
344248616Smm * Shutdown this node
345248616Smm */
346248616Smmstatic int
347248616Smmnga_shutdown(node_p node)
348248616Smm{
349248616Smm	const sc_p sc = NG_NODE_PRIVATE(node);
350248616Smm
351248616Smm	FREE(sc->abuf, M_NETGRAPH_ASYNC);
352248616Smm	FREE(sc->sbuf, M_NETGRAPH_ASYNC);
353248616Smm	bzero(sc, sizeof(*sc));
354248616Smm	FREE(sc, M_NETGRAPH_ASYNC);
355248616Smm	NG_NODE_SET_PRIVATE(node, NULL);
356248616Smm	NG_NODE_UNREF(node);
357248616Smm	return (0);
358248616Smm}
359248616Smm
360248616Smm/*
361248616Smm * Lose a hook. When both hooks go away, we disappear.
362248616Smm */
363248616Smmstatic int
364248616Smmnga_disconnect(hook_p hook)
365248616Smm{
366248616Smm	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
367248616Smm	hook_p *hookp;
368248616Smm
369248616Smm	if (hook == sc->async)
370232153Smm		hookp = &sc->async;
371232153Smm	else if (hook == sc->sync)
372232153Smm		hookp = &sc->sync;
373232153Smm	else
374232153Smm		panic(__func__);
375228753Smm	if (!*hookp)
376232153Smm		panic("%s 2", __func__);
377232153Smm	*hookp = NULL;
378232153Smm	bzero(&sc->stats, sizeof(sc->stats));
379232153Smm	sc->lasttime = 0;
380232153Smm	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
381232153Smm	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
382232153Smm		ng_rmnode_self(NG_HOOK_NODE(hook));
383248616Smm	return (0);
384248616Smm}
385232153Smm
386232153Smm/******************************************************************
387232153Smm		    INTERNAL HELPER STUFF
388232153Smm******************************************************************/
389232153Smm
390232153Smm/*
391232153Smm * Encode a byte into the async buffer
392232153Smm */
393232153Smmstatic __inline__ void
394232153Smmnga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
395232153Smm{
396232153Smm	*fcs = PPP_FCS(*fcs, x);
397232153Smm	if ((x < 32 && ((1 << x) & accm))
398232153Smm	    || (x == PPP_ESCAPE)
399232153Smm	    || (x == PPP_FLAG)) {
400232153Smm		sc->abuf[(*len)++] = PPP_ESCAPE;
401232153Smm		x ^= PPP_TRANS;
402232153Smm	}
403232153Smm	sc->abuf[(*len)++] = x;
404232153Smm}
405232153Smm
406232153Smm/*
407232153Smm * Receive incoming synchronous data.
408232153Smm */
409232153Smmstatic int
410232153Smmnga_rcv_sync(const sc_p sc, item_p item)
411232153Smm{
412232153Smm	struct ifnet *rcvif;
413232153Smm	int alen, error = 0;
414232153Smm	struct timeval time;
415232153Smm	u_int16_t fcs, fcs0;
416232153Smm	u_int32_t accm;
417232153Smm	struct mbuf *m;
418232153Smm
419232153Smm
420232153Smm#define ADD_BYTE(x)	nga_async_add(sc, &fcs, accm, &alen, (x))
421232153Smm
422232153Smm	/* Check for bypass mode */
423232153Smm	if (!sc->cfg.enabled) {
424232153Smm		NG_FWD_ITEM_HOOK(error, item, sc->async );
425232153Smm		return (error);
426232153Smm	}
427232153Smm	NGI_GET_M(item, m);
428232153Smm
429232153Smm	rcvif = m->m_pkthdr.rcvif;
430232153Smm
431232153Smm	/* Get ACCM; special case LCP frames, which use full ACCM */
432232153Smm	accm = sc->cfg.accm;
433232153Smm	if (m->m_pkthdr.len >= 4) {
434232153Smm		static const u_char lcphdr[4] = {
435232153Smm		    PPP_ALLSTATIONS,
436232153Smm		    PPP_UI,
437232153Smm		    (u_char)(PPP_LCP >> 8),
438232153Smm		    (u_char)(PPP_LCP & 0xff)
439232153Smm		};
440232153Smm		u_char buf[4];
441232153Smm
442248616Smm		m_copydata(m, 0, 4, (caddr_t)buf);
443232153Smm		if (bcmp(buf, &lcphdr, 4) == 0)
444248616Smm			accm = ~0;
445248616Smm	}
446248616Smm
447248616Smm	/* Check for overflow */
448248616Smm	if (m->m_pkthdr.len > sc->cfg.smru) {
449248616Smm		sc->stats.syncOverflows++;
450248616Smm		NG_FREE_M(m);
451248616Smm		NG_FREE_ITEM(item);
452248616Smm		return (EMSGSIZE);
453248616Smm	}
454248616Smm
455248616Smm	/* Update stats */
456248616Smm	sc->stats.syncFrames++;
457248616Smm	sc->stats.syncOctets += m->m_pkthdr.len;
458248616Smm
459248616Smm	/* Initialize async encoded version of input mbuf */
460248616Smm	alen = 0;
461248616Smm	fcs = PPP_INITFCS;
462248616Smm
463248616Smm	/* Add beginning sync flag if it's been long enough to need one */
464248616Smm	getmicrotime(&time);
465248616Smm	if (time.tv_sec >= sc->lasttime + 1) {
466248616Smm		sc->abuf[alen++] = PPP_FLAG;
467248616Smm		sc->lasttime = time.tv_sec;
468248616Smm	}
469248616Smm
470248616Smm	/* Add packet payload */
471248616Smm	while (m != NULL) {
472248616Smm		while (m->m_len > 0) {
473248616Smm			ADD_BYTE(*mtod(m, u_char *));
474248616Smm			m->m_data++;
475248616Smm			m->m_len--;
476248616Smm		}
477248616Smm		m = m_free(m);
478248616Smm	}
479248616Smm
480248616Smm	/* Add checksum and final sync flag */
481248616Smm	fcs0 = fcs;
482248616Smm	ADD_BYTE(~fcs0 & 0xff);
483248616Smm	ADD_BYTE(~fcs0 >> 8);
484248616Smm	sc->abuf[alen++] = PPP_FLAG;
485248616Smm
486248616Smm	/* Put frame in an mbuf and ship it off */
487248616Smm	if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) {
488248616Smm		NG_FREE_ITEM(item);
489248616Smm		error = ENOBUFS;
490248616Smm	} else {
491248616Smm		NG_FWD_NEW_DATA(error, item, sc->async, m);
492248616Smm	}
493248616Smm	return (error);
494248616Smm}
495248616Smm
496248616Smm/*
497248616Smm * Receive incoming asynchronous data
498248616Smm * XXX Technically, we should strip out incoming characters
499248616Smm *     that are in our ACCM. Not sure if this is good or not.
500248616Smm */
501248616Smmstatic int
502248616Smmnga_rcv_async(const sc_p sc, item_p item)
503248616Smm{
504248616Smm	struct ifnet *rcvif;
505248616Smm	int error;
506248616Smm	struct mbuf *m;
507232153Smm
508232153Smm	if (!sc->cfg.enabled) {
509232153Smm		NG_FWD_ITEM_HOOK(error, item,  sc->sync);
510248616Smm		return (error);
511248616Smm	}
512248616Smm	NGI_GET_M(item, m);
513248616Smm	rcvif = m->m_pkthdr.rcvif;
514248616Smm	while (m) {
515248616Smm		struct mbuf *n;
516248616Smm
517248616Smm		for (; m->m_len > 0; m->m_data++, m->m_len--) {
518248616Smm			u_char  ch = *mtod(m, u_char *);
519248616Smm
520248616Smm			sc->stats.asyncOctets++;
521248616Smm			if (ch == PPP_FLAG) {	/* Flag overrides everything */
522248616Smm				int     skip = 0;
523248616Smm
524248616Smm				/* Check for runts */
525248616Smm				if (sc->slen < 2) {
526248616Smm					if (sc->slen > 0)
527248616Smm						sc->stats.asyncRunts++;
528248616Smm					goto reset;
529248616Smm				}
530248616Smm
531248616Smm				/* Verify CRC */
532248616Smm				if (sc->fcs != PPP_GOODFCS) {
533248616Smm					sc->stats.asyncBadCheckSums++;
534248616Smm					goto reset;
535248616Smm				}
536248616Smm				sc->slen -= 2;
537248616Smm
538248616Smm				/* Strip address and control fields */
539248616Smm				if (sc->slen >= 2
540248616Smm				    && sc->sbuf[0] == PPP_ALLSTATIONS
541248616Smm				    && sc->sbuf[1] == PPP_UI)
542248616Smm					skip = 2;
543248616Smm
544248616Smm				/* Check for frame too big */
545248616Smm				if (sc->slen - skip > sc->cfg.amru) {
546248616Smm					sc->stats.asyncOverflows++;
547248616Smm					goto reset;
548248616Smm				}
549248616Smm
550248616Smm				/* OK, ship it out */
551248616Smm				if ((n = m_devget(sc->sbuf + skip,
552248616Smm					   sc->slen - skip, 0, rcvif, NULL))) {
553248616Smm					if (item) { /* sets NULL -> item */
554248616Smm						NG_FWD_NEW_DATA(error, item,
555248616Smm							sc->sync, n);
556248616Smm					} else {
557248616Smm						NG_SEND_DATA_ONLY(error,
558248616Smm							sc->sync ,n);
559248616Smm					}
560248616Smm				}
561248616Smm				sc->stats.asyncFrames++;
562248616Smmreset:
563248616Smm				sc->amode = MODE_NORMAL;
564248616Smm				sc->fcs = PPP_INITFCS;
565248616Smm				sc->slen = 0;
566248616Smm				continue;
567248616Smm			}
568248616Smm			switch (sc->amode) {
569248616Smm			case MODE_NORMAL:
570248616Smm				if (ch == PPP_ESCAPE) {
571248616Smm					sc->amode = MODE_ESC;
572248616Smm					continue;
573248616Smm				}
574248616Smm				break;
575232153Smm			case MODE_ESC:
576232153Smm				ch ^= PPP_TRANS;
577248616Smm				sc->amode = MODE_NORMAL;
578232153Smm				break;
579232153Smm			case MODE_HUNT:
580232153Smm			default:
581232153Smm				continue;
582232153Smm			}
583232153Smm
584232153Smm			/* Add byte to frame */
585232153Smm			if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
586232153Smm				sc->stats.asyncOverflows++;
587232153Smm				sc->amode = MODE_HUNT;
588232153Smm				sc->slen = 0;
589232153Smm			} else {
590232153Smm				sc->sbuf[sc->slen++] = ch;
591232153Smm				sc->fcs = PPP_FCS(sc->fcs, ch);
592232153Smm			}
593232153Smm		}
594232153Smm		m = m_free(m);
595232153Smm	}
596232153Smm	if (item)
597232153Smm		NG_FREE_ITEM(item);
598232153Smm	return (0);
599232153Smm}
600232153Smm
601232153Smm/*
602232153Smm * CRC table
603 *
604 * Taken from RFC 1171 Appendix B
605 */
606static const u_int16_t fcstab[256] = {
607	 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
608	 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
609	 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
610	 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
611	 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
612	 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
613	 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
614	 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
615	 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
616	 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
617	 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
618	 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
619	 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
620	 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
621	 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
622	 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
623	 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
624	 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
625	 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
626	 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
627	 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
628	 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
629	 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
630	 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
631	 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
632	 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
633	 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
634	 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
635	 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
636	 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
637	 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
638	 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
639};
640