ng_async.c revision 227293
1218885Sdim/*
2218885Sdim * ng_async.c
3218885Sdim */
4218885Sdim
5218885Sdim/*-
6218885Sdim * Copyright (c) 1996-1999 Whistle Communications, Inc.
7218885Sdim * All rights reserved.
8218885Sdim *
9218885Sdim * Subject to the following obligations and disclaimer of warranty, use and
10218885Sdim * redistribution of this software, in source or object code forms, with or
11218885Sdim * without modifications are expressly permitted by Whistle Communications;
12218885Sdim * provided, however, that:
13218885Sdim * 1. Any and all reproductions of the source or object code must include the
14218885Sdim *    copyright notice above and the following disclaimer of warranties; and
15218885Sdim * 2. No rights are granted, in any manner or form, to use Whistle
16218885Sdim *    Communications, Inc. trademarks, including the mark "WHISTLE
17218885Sdim *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18218885Sdim *    such appears in the above copyright notice or in the software.
19218885Sdim *
20218885Sdim * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21249423Sdim * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22218885Sdim * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23218885Sdim * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24218885Sdim * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25218885Sdim * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26218885Sdim * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27218885Sdim * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28218885Sdim * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29218885Sdim * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30263508Sdim * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31218885Sdim * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32218885Sdim * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33218885Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34218885Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35218885Sdim * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36218885Sdim * OF SUCH DAMAGE.
37218885Sdim *
38218885Sdim * Author: Archie Cobbs <archie@freebsd.org>
39218885Sdim *
40218885Sdim * $FreeBSD: head/sys/netgraph/ng_async.c 227293 2011-11-07 06:44:47Z ed $
41218885Sdim * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
42263508Sdim */
43263508Sdim
44218885Sdim/*
45218885Sdim * This node type implements a PPP style sync <-> async converter.
46218885Sdim * See RFC 1661 for details of how asynchronous encoding works.
47218885Sdim */
48218885Sdim
49218885Sdim#include <sys/param.h>
50263508Sdim#include <sys/systm.h>
51263508Sdim#include <sys/kernel.h>
52218885Sdim#include <sys/mbuf.h>
53218885Sdim#include <sys/malloc.h>
54218885Sdim#include <sys/errno.h>
55218885Sdim
56218885Sdim#include <netgraph/ng_message.h>
57218885Sdim#include <netgraph/netgraph.h>
58218885Sdim#include <netgraph/ng_async.h>
59218885Sdim#include <netgraph/ng_parse.h>
60218885Sdim
61218885Sdim#include <net/ppp_defs.h>
62218885Sdim
63263508Sdim#ifdef NG_SEPARATE_MALLOC
64263508Sdimstatic MALLOC_DEFINE(M_NETGRAPH_ASYNC, "netgraph_async", "netgraph async node");
65263508Sdim#else
66218885Sdim#define M_NETGRAPH_ASYNC M_NETGRAPH
67263508Sdim#endif
68263508Sdim
69218885Sdim
70218885Sdim/* Async decode state */
71218885Sdim#define MODE_HUNT	0
72218885Sdim#define MODE_NORMAL	1
73218885Sdim#define MODE_ESC	2
74218885Sdim
75218885Sdim/* Private data structure */
76218885Sdimstruct ng_async_private {
77218885Sdim	node_p  	node;		/* Our node */
78218885Sdim	hook_p  	async;		/* Asynchronous side */
79218885Sdim	hook_p  	sync;		/* Synchronous side */
80263508Sdim	u_char  	amode;		/* Async hunt/esape mode */
81218885Sdim	u_int16_t	fcs;		/* Decoded async FCS (so far) */
82218885Sdim	u_char	       *abuf;		/* Buffer to encode sync into */
83263508Sdim	u_char	       *sbuf;		/* Buffer to decode async into */
84218885Sdim	u_int		slen;		/* Length of data in sbuf */
85218885Sdim	long		lasttime;	/* Time of last async packet sent */
86218885Sdim	struct		ng_async_cfg	cfg;	/* Configuration */
87218885Sdim	struct		ng_async_stat	stats;	/* Statistics */
88218885Sdim};
89218885Sdimtypedef struct ng_async_private *sc_p;
90218885Sdim
91218885Sdim/* Useful macros */
92218885Sdim#define ASYNC_BUF_SIZE(smru)	(2 * (smru) + 10)
93218885Sdim#define SYNC_BUF_SIZE(amru)	((amru) + 10)
94218885Sdim#define ERROUT(x)		do { error = (x); goto done; } while (0)
95218885Sdim
96218885Sdim/* Netgraph methods */
97218885Sdimstatic ng_constructor_t		nga_constructor;
98218885Sdimstatic ng_rcvdata_t		nga_rcvdata;
99218885Sdimstatic ng_rcvmsg_t		nga_rcvmsg;
100218885Sdimstatic ng_shutdown_t		nga_shutdown;
101218885Sdimstatic ng_newhook_t		nga_newhook;
102218885Sdimstatic ng_disconnect_t		nga_disconnect;
103218885Sdim
104218885Sdim/* Helper stuff */
105218885Sdimstatic int	nga_rcv_sync(const sc_p sc, item_p item);
106218885Sdimstatic int	nga_rcv_async(const sc_p sc, item_p item);
107218885Sdim
108218885Sdim/* Parse type for struct ng_async_cfg */
109218885Sdimstatic const struct ng_parse_struct_field nga_config_type_fields[]
110218885Sdim	= NG_ASYNC_CONFIG_TYPE_INFO;
111218885Sdimstatic const struct ng_parse_type nga_config_type = {
112218885Sdim	&ng_parse_struct_type,
113218885Sdim	&nga_config_type_fields
114218885Sdim};
115218885Sdim
116218885Sdim/* Parse type for struct ng_async_stat */
117263508Sdimstatic const struct ng_parse_struct_field nga_stats_type_fields[]
118218885Sdim	= NG_ASYNC_STATS_TYPE_INFO;
119218885Sdimstatic const struct ng_parse_type nga_stats_type = {
120218885Sdim	&ng_parse_struct_type,
121218885Sdim	&nga_stats_type_fields
122218885Sdim};
123218885Sdim
124218885Sdim/* List of commands and how to convert arguments to/from ASCII */
125218885Sdimstatic const struct ng_cmdlist nga_cmdlist[] = {
126263508Sdim	{
127263508Sdim	  NGM_ASYNC_COOKIE,
128218885Sdim	  NGM_ASYNC_CMD_SET_CONFIG,
129218885Sdim	  "setconfig",
130218885Sdim	  &nga_config_type,
131218885Sdim	  NULL
132218885Sdim	},
133218885Sdim	{
134218885Sdim	  NGM_ASYNC_COOKIE,
135263508Sdim	  NGM_ASYNC_CMD_GET_CONFIG,
136263508Sdim	  "getconfig",
137263508Sdim	  NULL,
138263508Sdim	  &nga_config_type
139218885Sdim	},
140218885Sdim	{
141263508Sdim	  NGM_ASYNC_COOKIE,
142218885Sdim	  NGM_ASYNC_CMD_GET_STATS,
143263508Sdim	  "getstats",
144218885Sdim	  NULL,
145218885Sdim	  &nga_stats_type
146218885Sdim	},
147218885Sdim	{
148218885Sdim	  NGM_ASYNC_COOKIE,
149218885Sdim	  NGM_ASYNC_CMD_CLR_STATS,
150218885Sdim	  "clrstats",
151218885Sdim	  &nga_stats_type,
152218885Sdim	  NULL
153218885Sdim	},
154218885Sdim	{ 0 }
155218885Sdim};
156218885Sdim
157218885Sdim/* Define the netgraph node type */
158218885Sdimstatic struct ng_type typestruct = {
159218885Sdim	.version =	NG_ABI_VERSION,
160218885Sdim	.name =		NG_ASYNC_NODE_TYPE,
161218885Sdim	.constructor =	nga_constructor,
162263508Sdim	.rcvmsg =	nga_rcvmsg,
163218885Sdim	.shutdown = 	nga_shutdown,
164263508Sdim	.newhook =	nga_newhook,
165263508Sdim	.rcvdata =	nga_rcvdata,
166218885Sdim	.disconnect =	nga_disconnect,
167218885Sdim	.cmdlist =	nga_cmdlist
168218885Sdim};
169218885SdimNETGRAPH_INIT(async, &typestruct);
170218885Sdim
171263508Sdim/* CRC table */
172218885Sdimstatic const u_int16_t fcstab[];
173263508Sdim
174263508Sdim/******************************************************************
175218885Sdim		    NETGRAPH NODE METHODS
176218885Sdim******************************************************************/
177218885Sdim
178218885Sdim/*
179218885Sdim * Initialize a new node
180218885Sdim */
181218885Sdimstatic int
182263508Sdimnga_constructor(node_p node)
183263508Sdim{
184218885Sdim	sc_p sc;
185263508Sdim
186263508Sdim	sc = malloc(sizeof(*sc), M_NETGRAPH_ASYNC, M_WAITOK | M_ZERO);
187263508Sdim	sc->amode = MODE_HUNT;
188263508Sdim	sc->cfg.accm = ~0;
189218885Sdim	sc->cfg.amru = NG_ASYNC_DEFAULT_MRU;
190218885Sdim	sc->cfg.smru = NG_ASYNC_DEFAULT_MRU;
191263508Sdim	sc->abuf = malloc(ASYNC_BUF_SIZE(sc->cfg.smru),
192218885Sdim	    M_NETGRAPH_ASYNC, M_WAITOK);
193263508Sdim	sc->sbuf = malloc(SYNC_BUF_SIZE(sc->cfg.amru),
194218885Sdim	    M_NETGRAPH_ASYNC, M_WAITOK);
195263508Sdim	NG_NODE_SET_PRIVATE(node, sc);
196218885Sdim	sc->node = node;
197218885Sdim	return (0);
198218885Sdim}
199218885Sdim
200263508Sdim/*
201263508Sdim * Reserve a hook for a pending connection
202218885Sdim */
203218885Sdimstatic int
204263508Sdimnga_newhook(node_p node, hook_p hook, const char *name)
205218885Sdim{
206218885Sdim	const sc_p sc = NG_NODE_PRIVATE(node);
207218885Sdim	hook_p *hookp;
208263508Sdim
209218885Sdim	if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) {
210218885Sdim		/*
211218885Sdim		 * We use a static buffer here so only one packet
212218885Sdim		 * at a time can be allowed to travel in this direction.
213263508Sdim		 * Force Writer semantics.
214218885Sdim		 */
215263508Sdim		NG_HOOK_FORCE_WRITER(hook);
216263508Sdim		hookp = &sc->async;
217263508Sdim	} else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) {
218263508Sdim		/*
219218885Sdim		 * We use a static state here so only one packet
220218885Sdim		 * at a time can be allowed to travel in this direction.
221218885Sdim		 * Force Writer semantics.
222218885Sdim		 * Since we set this for both directions
223218885Sdim		 * we might as well set it for the whole node
224218885Sdim		 * bit I haven;t done that (yet).
225218885Sdim		 */
226263508Sdim		NG_HOOK_FORCE_WRITER(hook);
227218885Sdim		hookp = &sc->sync;
228218885Sdim	} else {
229218885Sdim		return (EINVAL);
230218885Sdim	}
231263508Sdim	if (*hookp) /* actually can't happen I think [JRE] */
232263508Sdim		return (EISCONN);
233218885Sdim	*hookp = hook;
234218885Sdim	return (0);
235218885Sdim}
236263508Sdim
237218885Sdim/*
238218885Sdim * Receive incoming data
239218885Sdim */
240218885Sdimstatic int
241218885Sdimnga_rcvdata(hook_p hook, item_p item)
242218885Sdim{
243263508Sdim	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
244263508Sdim
245218885Sdim	if (hook == sc->sync)
246218885Sdim		return (nga_rcv_sync(sc, item));
247218885Sdim	if (hook == sc->async)
248263508Sdim		return (nga_rcv_async(sc, item));
249218885Sdim	panic("%s", __func__);
250218885Sdim}
251218885Sdim
252218885Sdim/*
253218885Sdim * Receive incoming control message
254263508Sdim */
255263508Sdimstatic int
256218885Sdimnga_rcvmsg(node_p node, item_p item, hook_p lasthook)
257218885Sdim{
258218885Sdim	const sc_p sc = NG_NODE_PRIVATE(node);
259263508Sdim	struct ng_mesg *resp = NULL;
260263508Sdim	int error = 0;
261218885Sdim	struct ng_mesg *msg;
262218885Sdim
263218885Sdim	NGI_GET_MSG(item, msg);
264218885Sdim	switch (msg->header.typecookie) {
265218885Sdim	case NGM_ASYNC_COOKIE:
266218885Sdim		switch (msg->header.cmd) {
267263508Sdim		case NGM_ASYNC_CMD_GET_STATS:
268218885Sdim			NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
269218885Sdim			if (resp == NULL)
270218885Sdim				ERROUT(ENOMEM);
271218885Sdim			*((struct ng_async_stat *) resp->data) = sc->stats;
272218885Sdim			break;
273263508Sdim		case NGM_ASYNC_CMD_CLR_STATS:
274263508Sdim			bzero(&sc->stats, sizeof(sc->stats));
275218885Sdim			break;
276218885Sdim		case NGM_ASYNC_CMD_SET_CONFIG:
277263508Sdim		    {
278218885Sdim			struct ng_async_cfg *const cfg =
279263508Sdim				(struct ng_async_cfg *) msg->data;
280218885Sdim			u_char *buf;
281218885Sdim
282218885Sdim			if (msg->header.arglen != sizeof(*cfg))
283218885Sdim				ERROUT(EINVAL);
284218885Sdim			if (cfg->amru < NG_ASYNC_MIN_MRU
285218885Sdim			    || cfg->amru > NG_ASYNC_MAX_MRU
286218885Sdim			    || cfg->smru < NG_ASYNC_MIN_MRU
287218885Sdim			    || cfg->smru > NG_ASYNC_MAX_MRU)
288218885Sdim				ERROUT(EINVAL);
289218885Sdim			cfg->enabled = !!cfg->enabled;	/* normalize */
290263508Sdim			if (cfg->smru > sc->cfg.smru) {	/* reallocate buffer */
291218885Sdim				buf = malloc(ASYNC_BUF_SIZE(cfg->smru),
292218885Sdim				    M_NETGRAPH_ASYNC, M_NOWAIT);
293218885Sdim				if (!buf)
294218885Sdim					ERROUT(ENOMEM);
295218885Sdim				free(sc->abuf, M_NETGRAPH_ASYNC);
296218885Sdim				sc->abuf = buf;
297263508Sdim			}
298263508Sdim			if (cfg->amru > sc->cfg.amru) {	/* reallocate buffer */
299218885Sdim				buf = malloc(SYNC_BUF_SIZE(cfg->amru),
300218885Sdim				    M_NETGRAPH_ASYNC, M_NOWAIT);
301263508Sdim				if (!buf)
302263508Sdim					ERROUT(ENOMEM);
303218885Sdim				free(sc->sbuf, M_NETGRAPH_ASYNC);
304218885Sdim				sc->sbuf = buf;
305218885Sdim				sc->amode = MODE_HUNT;
306218885Sdim				sc->slen = 0;
307263508Sdim			}
308263508Sdim			if (!cfg->enabled) {
309218885Sdim				sc->amode = MODE_HUNT;
310218885Sdim				sc->slen = 0;
311218885Sdim			}
312218885Sdim			sc->cfg = *cfg;
313263508Sdim			break;
314218885Sdim		    }
315218885Sdim		case NGM_ASYNC_CMD_GET_CONFIG:
316218885Sdim			NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
317218885Sdim			if (!resp)
318218885Sdim				ERROUT(ENOMEM);
319263508Sdim			*((struct ng_async_cfg *) resp->data) = sc->cfg;
320218885Sdim			break;
321218885Sdim		default:
322218885Sdim			ERROUT(EINVAL);
323218885Sdim		}
324218885Sdim		break;
325218885Sdim	default:
326218885Sdim		ERROUT(EINVAL);
327218885Sdim	}
328218885Sdimdone:
329218885Sdim	NG_RESPOND_MSG(error, node, item, resp);
330218885Sdim	NG_FREE_MSG(msg);
331218885Sdim	return (error);
332218885Sdim}
333218885Sdim
334218885Sdim/*
335218885Sdim * Shutdown this node
336218885Sdim */
337218885Sdimstatic int
338218885Sdimnga_shutdown(node_p node)
339218885Sdim{
340218885Sdim	const sc_p sc = NG_NODE_PRIVATE(node);
341218885Sdim
342218885Sdim	free(sc->abuf, M_NETGRAPH_ASYNC);
343218885Sdim	free(sc->sbuf, M_NETGRAPH_ASYNC);
344218885Sdim	bzero(sc, sizeof(*sc));
345218885Sdim	free(sc, M_NETGRAPH_ASYNC);
346218885Sdim	NG_NODE_SET_PRIVATE(node, NULL);
347218885Sdim	NG_NODE_UNREF(node);
348218885Sdim	return (0);
349218885Sdim}
350218885Sdim
351263508Sdim/*
352218885Sdim * Lose a hook. When both hooks go away, we disappear.
353218885Sdim */
354218885Sdimstatic int
355263508Sdimnga_disconnect(hook_p hook)
356263508Sdim{
357218885Sdim	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
358218885Sdim	hook_p *hookp;
359218885Sdim
360263508Sdim	if (hook == sc->async)
361263508Sdim		hookp = &sc->async;
362263508Sdim	else if (hook == sc->sync)
363263508Sdim		hookp = &sc->sync;
364218885Sdim	else
365218885Sdim		panic("%s", __func__);
366218885Sdim	if (!*hookp)
367218885Sdim		panic("%s 2", __func__);
368218885Sdim	*hookp = NULL;
369218885Sdim	bzero(&sc->stats, sizeof(sc->stats));
370218885Sdim	sc->lasttime = 0;
371218885Sdim	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
372218885Sdim	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
373218885Sdim		ng_rmnode_self(NG_HOOK_NODE(hook));
374218885Sdim	return (0);
375218885Sdim}
376218885Sdim
377218885Sdim/******************************************************************
378263508Sdim		    INTERNAL HELPER STUFF
379218885Sdim******************************************************************/
380218885Sdim
381218885Sdim/*
382263508Sdim * Encode a byte into the async buffer
383263508Sdim */
384218885Sdimstatic __inline void
385218885Sdimnga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
386218885Sdim{
387263508Sdim	*fcs = PPP_FCS(*fcs, x);
388263508Sdim	if ((x < 32 && ((1 << x) & accm))
389263508Sdim	    || (x == PPP_ESCAPE)
390263508Sdim	    || (x == PPP_FLAG)) {
391218885Sdim		sc->abuf[(*len)++] = PPP_ESCAPE;
392218885Sdim		x ^= PPP_TRANS;
393218885Sdim	}
394218885Sdim	sc->abuf[(*len)++] = x;
395218885Sdim}
396263508Sdim
397218885Sdim/*
398218885Sdim * Receive incoming synchronous data.
399218885Sdim */
400218885Sdimstatic int
401218885Sdimnga_rcv_sync(const sc_p sc, item_p item)
402218885Sdim{
403218885Sdim	struct ifnet *rcvif;
404218885Sdim	int alen, error = 0;
405263508Sdim	struct timeval time;
406263508Sdim	u_int16_t fcs, fcs0;
407263508Sdim	u_int32_t accm;
408218885Sdim	struct mbuf *m;
409218885Sdim
410218885Sdim
411263508Sdim#define ADD_BYTE(x)	nga_async_add(sc, &fcs, accm, &alen, (x))
412263508Sdim
413218885Sdim	/* Check for bypass mode */
414218885Sdim	if (!sc->cfg.enabled) {
415218885Sdim		NG_FWD_ITEM_HOOK(error, item, sc->async );
416218885Sdim		return (error);
417263508Sdim	}
418218885Sdim	NGI_GET_M(item, m);
419218885Sdim
420263508Sdim	rcvif = m->m_pkthdr.rcvif;
421234353Sdim
422234353Sdim	/* Get ACCM; special case LCP frames, which use full ACCM */
423234353Sdim	accm = sc->cfg.accm;
424234353Sdim	if (m->m_pkthdr.len >= 4) {
425234353Sdim		static const u_char lcphdr[4] = {
426218885Sdim		    PPP_ALLSTATIONS,
427218885Sdim		    PPP_UI,
428218885Sdim		    (u_char)(PPP_LCP >> 8),
429218885Sdim		    (u_char)(PPP_LCP & 0xff)
430218885Sdim		};
431263508Sdim		u_char buf[4];
432263508Sdim
433218885Sdim		m_copydata(m, 0, 4, (caddr_t)buf);
434263508Sdim		if (bcmp(buf, &lcphdr, 4) == 0)
435263508Sdim			accm = ~0;
436263508Sdim	}
437263508Sdim
438218885Sdim	/* Check for overflow */
439218885Sdim	if (m->m_pkthdr.len > sc->cfg.smru) {
440263508Sdim		sc->stats.syncOverflows++;
441218885Sdim		NG_FREE_M(m);
442218885Sdim		NG_FREE_ITEM(item);
443218885Sdim		return (EMSGSIZE);
444263508Sdim	}
445218885Sdim
446263508Sdim	/* Update stats */
447263508Sdim	sc->stats.syncFrames++;
448263508Sdim	sc->stats.syncOctets += m->m_pkthdr.len;
449218885Sdim
450263508Sdim	/* Initialize async encoded version of input mbuf */
451263508Sdim	alen = 0;
452218885Sdim	fcs = PPP_INITFCS;
453218885Sdim
454218885Sdim	/* Add beginning sync flag if it's been long enough to need one */
455218885Sdim	getmicrotime(&time);
456218885Sdim	if (time.tv_sec >= sc->lasttime + 1) {
457218885Sdim		sc->abuf[alen++] = PPP_FLAG;
458218885Sdim		sc->lasttime = time.tv_sec;
459218885Sdim	}
460218885Sdim
461218885Sdim	/* Add packet payload */
462218885Sdim	while (m != NULL) {
463218885Sdim		while (m->m_len > 0) {
464218885Sdim			ADD_BYTE(*mtod(m, u_char *));
465218885Sdim			m->m_data++;
466218885Sdim			m->m_len--;
467218885Sdim		}
468218885Sdim		m = m_free(m);
469	}
470
471	/* Add checksum and final sync flag */
472	fcs0 = fcs;
473	ADD_BYTE(~fcs0 & 0xff);
474	ADD_BYTE(~fcs0 >> 8);
475	sc->abuf[alen++] = PPP_FLAG;
476
477	/* Put frame in an mbuf and ship it off */
478	if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) {
479		NG_FREE_ITEM(item);
480		error = ENOBUFS;
481	} else {
482		NG_FWD_NEW_DATA(error, item, sc->async, m);
483	}
484	return (error);
485}
486
487/*
488 * Receive incoming asynchronous data
489 * XXX Technically, we should strip out incoming characters
490 *     that are in our ACCM. Not sure if this is good or not.
491 */
492static int
493nga_rcv_async(const sc_p sc, item_p item)
494{
495	struct ifnet *rcvif;
496	int error;
497	struct mbuf *m;
498
499	if (!sc->cfg.enabled) {
500		NG_FWD_ITEM_HOOK(error, item,  sc->sync);
501		return (error);
502	}
503	NGI_GET_M(item, m);
504	rcvif = m->m_pkthdr.rcvif;
505	while (m) {
506		struct mbuf *n;
507
508		for (; m->m_len > 0; m->m_data++, m->m_len--) {
509			u_char  ch = *mtod(m, u_char *);
510
511			sc->stats.asyncOctets++;
512			if (ch == PPP_FLAG) {	/* Flag overrides everything */
513				int     skip = 0;
514
515				/* Check for runts */
516				if (sc->slen < 2) {
517					if (sc->slen > 0)
518						sc->stats.asyncRunts++;
519					goto reset;
520				}
521
522				/* Verify CRC */
523				if (sc->fcs != PPP_GOODFCS) {
524					sc->stats.asyncBadCheckSums++;
525					goto reset;
526				}
527				sc->slen -= 2;
528
529				/* Strip address and control fields */
530				if (sc->slen >= 2
531				    && sc->sbuf[0] == PPP_ALLSTATIONS
532				    && sc->sbuf[1] == PPP_UI)
533					skip = 2;
534
535				/* Check for frame too big */
536				if (sc->slen - skip > sc->cfg.amru) {
537					sc->stats.asyncOverflows++;
538					goto reset;
539				}
540
541				/* OK, ship it out */
542				if ((n = m_devget(sc->sbuf + skip,
543					   sc->slen - skip, 0, rcvif, NULL))) {
544					if (item) { /* sets NULL -> item */
545						NG_FWD_NEW_DATA(error, item,
546							sc->sync, n);
547					} else {
548						NG_SEND_DATA_ONLY(error,
549							sc->sync ,n);
550					}
551				}
552				sc->stats.asyncFrames++;
553reset:
554				sc->amode = MODE_NORMAL;
555				sc->fcs = PPP_INITFCS;
556				sc->slen = 0;
557				continue;
558			}
559			switch (sc->amode) {
560			case MODE_NORMAL:
561				if (ch == PPP_ESCAPE) {
562					sc->amode = MODE_ESC;
563					continue;
564				}
565				break;
566			case MODE_ESC:
567				ch ^= PPP_TRANS;
568				sc->amode = MODE_NORMAL;
569				break;
570			case MODE_HUNT:
571			default:
572				continue;
573			}
574
575			/* Add byte to frame */
576			if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
577				sc->stats.asyncOverflows++;
578				sc->amode = MODE_HUNT;
579				sc->slen = 0;
580			} else {
581				sc->sbuf[sc->slen++] = ch;
582				sc->fcs = PPP_FCS(sc->fcs, ch);
583			}
584		}
585		m = m_free(m);
586	}
587	if (item)
588		NG_FREE_ITEM(item);
589	return (0);
590}
591
592/*
593 * CRC table
594 *
595 * Taken from RFC 1171 Appendix B
596 */
597static const u_int16_t fcstab[256] = {
598	 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
599	 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
600	 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
601	 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
602	 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
603	 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
604	 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
605	 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
606	 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
607	 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
608	 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
609	 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
610	 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
611	 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
612	 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
613	 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
614	 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
615	 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
616	 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
617	 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
618	 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
619	 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
620	 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
621	 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
622	 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
623	 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
624	 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
625	 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
626	 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
627	 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
628	 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
629	 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
630};
631