152419Sjulian/*
252419Sjulian * ng_async.c
3139823Simp */
4139823Simp
5139823Simp/*-
652419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
752419Sjulian * All rights reserved.
852419Sjulian *
952419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
1052419Sjulian * redistribution of this software, in source or object code forms, with or
1152419Sjulian * without modifications are expressly permitted by Whistle Communications;
1252419Sjulian * provided, however, that:
1352419Sjulian * 1. Any and all reproductions of the source or object code must include the
1452419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1552419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1652419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1752419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1852419Sjulian *    such appears in the above copyright notice or in the software.
1952419Sjulian *
2052419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2152419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2252419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2352419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2452419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2552419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2652419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2752419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2852419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2952419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3052419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3152419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3252419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3352419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3452419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3552419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3652419Sjulian * OF SUCH DAMAGE.
3752419Sjulian *
3867506Sjulian * Author: Archie Cobbs <archie@freebsd.org>
3952419Sjulian *
4052419Sjulian * $FreeBSD$
4152752Sjulian * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
4252419Sjulian */
4352419Sjulian
4452419Sjulian/*
4552419Sjulian * This node type implements a PPP style sync <-> async converter.
4652419Sjulian * See RFC 1661 for details of how asynchronous encoding works.
4752419Sjulian */
4852419Sjulian
4952419Sjulian#include <sys/param.h>
5052419Sjulian#include <sys/systm.h>
5152419Sjulian#include <sys/kernel.h>
5252419Sjulian#include <sys/mbuf.h>
5352419Sjulian#include <sys/malloc.h>
5452419Sjulian#include <sys/errno.h>
5552419Sjulian
5652419Sjulian#include <netgraph/ng_message.h>
5752419Sjulian#include <netgraph/netgraph.h>
5852419Sjulian#include <netgraph/ng_async.h>
5953913Sarchie#include <netgraph/ng_parse.h>
6052419Sjulian
6152419Sjulian#include <net/ppp_defs.h>
6252419Sjulian
6370870Sjulian#ifdef NG_SEPARATE_MALLOC
64227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_ASYNC, "netgraph_async", "netgraph async node");
6570870Sjulian#else
6670870Sjulian#define M_NETGRAPH_ASYNC M_NETGRAPH
6770870Sjulian#endif
6870870Sjulian
6970870Sjulian
7052419Sjulian/* Async decode state */
7152419Sjulian#define MODE_HUNT	0
7252419Sjulian#define MODE_NORMAL	1
7352419Sjulian#define MODE_ESC	2
7452419Sjulian
7552419Sjulian/* Private data structure */
7653394Sarchiestruct ng_async_private {
7752419Sjulian	node_p  	node;		/* Our node */
7852419Sjulian	hook_p  	async;		/* Asynchronous side */
7952419Sjulian	hook_p  	sync;		/* Synchronous side */
8052419Sjulian	u_char  	amode;		/* Async hunt/esape mode */
8152419Sjulian	u_int16_t	fcs;		/* Decoded async FCS (so far) */
8252419Sjulian	u_char	       *abuf;		/* Buffer to encode sync into */
8352419Sjulian	u_char	       *sbuf;		/* Buffer to decode async into */
8452419Sjulian	u_int		slen;		/* Length of data in sbuf */
8552419Sjulian	long		lasttime;	/* Time of last async packet sent */
8652419Sjulian	struct		ng_async_cfg	cfg;	/* Configuration */
8752419Sjulian	struct		ng_async_stat	stats;	/* Statistics */
8852419Sjulian};
8953394Sarchietypedef struct ng_async_private *sc_p;
9052419Sjulian
9152419Sjulian/* Useful macros */
9252419Sjulian#define ASYNC_BUF_SIZE(smru)	(2 * (smru) + 10)
9352419Sjulian#define SYNC_BUF_SIZE(amru)	((amru) + 10)
9452419Sjulian#define ERROUT(x)		do { error = (x); goto done; } while (0)
9552419Sjulian
9652419Sjulian/* Netgraph methods */
9753913Sarchiestatic ng_constructor_t		nga_constructor;
9852752Sjulianstatic ng_rcvdata_t		nga_rcvdata;
9952752Sjulianstatic ng_rcvmsg_t		nga_rcvmsg;
10052752Sjulianstatic ng_shutdown_t		nga_shutdown;
10152752Sjulianstatic ng_newhook_t		nga_newhook;
10252752Sjulianstatic ng_disconnect_t		nga_disconnect;
10352419Sjulian
10452419Sjulian/* Helper stuff */
10570700Sjulianstatic int	nga_rcv_sync(const sc_p sc, item_p item);
10670700Sjulianstatic int	nga_rcv_async(const sc_p sc, item_p item);
10752419Sjulian
10853913Sarchie/* Parse type for struct ng_async_cfg */
10997685Sarchiestatic const struct ng_parse_struct_field nga_config_type_fields[]
11097685Sarchie	= NG_ASYNC_CONFIG_TYPE_INFO;
11153913Sarchiestatic const struct ng_parse_type nga_config_type = {
11253913Sarchie	&ng_parse_struct_type,
11397685Sarchie	&nga_config_type_fields
11453913Sarchie};
11553913Sarchie
11653913Sarchie/* Parse type for struct ng_async_stat */
11797685Sarchiestatic const struct ng_parse_struct_field nga_stats_type_fields[]
11897685Sarchie	= NG_ASYNC_STATS_TYPE_INFO;
11953913Sarchiestatic const struct ng_parse_type nga_stats_type = {
12053913Sarchie	&ng_parse_struct_type,
12197685Sarchie	&nga_stats_type_fields
12253913Sarchie};
12353913Sarchie
12453913Sarchie/* List of commands and how to convert arguments to/from ASCII */
12553913Sarchiestatic const struct ng_cmdlist nga_cmdlist[] = {
12653913Sarchie	{
12753913Sarchie	  NGM_ASYNC_COOKIE,
12853913Sarchie	  NGM_ASYNC_CMD_SET_CONFIG,
12953913Sarchie	  "setconfig",
13053913Sarchie	  &nga_config_type,
13153913Sarchie	  NULL
13253913Sarchie	},
13353913Sarchie	{
13453913Sarchie	  NGM_ASYNC_COOKIE,
13553913Sarchie	  NGM_ASYNC_CMD_GET_CONFIG,
13653913Sarchie	  "getconfig",
13753913Sarchie	  NULL,
13853913Sarchie	  &nga_config_type
13953913Sarchie	},
14053913Sarchie	{
14153913Sarchie	  NGM_ASYNC_COOKIE,
14253913Sarchie	  NGM_ASYNC_CMD_GET_STATS,
14353913Sarchie	  "getstats",
14453913Sarchie	  NULL,
14553913Sarchie	  &nga_stats_type
14653913Sarchie	},
14753913Sarchie	{
14853913Sarchie	  NGM_ASYNC_COOKIE,
14953913Sarchie	  NGM_ASYNC_CMD_CLR_STATS,
15053913Sarchie	  "clrstats",
15153913Sarchie	  &nga_stats_type,
15253913Sarchie	  NULL
15353913Sarchie	},
15453913Sarchie	{ 0 }
15553913Sarchie};
15653913Sarchie
15752419Sjulian/* Define the netgraph node type */
15852419Sjulianstatic struct ng_type typestruct = {
159129823Sjulian	.version =	NG_ABI_VERSION,
160129823Sjulian	.name =		NG_ASYNC_NODE_TYPE,
161129823Sjulian	.constructor =	nga_constructor,
162129823Sjulian	.rcvmsg =	nga_rcvmsg,
163129823Sjulian	.shutdown = 	nga_shutdown,
164129823Sjulian	.newhook =	nga_newhook,
165129823Sjulian	.rcvdata =	nga_rcvdata,
166129823Sjulian	.disconnect =	nga_disconnect,
167129823Sjulian	.cmdlist =	nga_cmdlist
16852419Sjulian};
16952419SjulianNETGRAPH_INIT(async, &typestruct);
17052419Sjulian
17152419Sjulian/* CRC table */
17252419Sjulianstatic const u_int16_t fcstab[];
17352419Sjulian
17452419Sjulian/******************************************************************
17552419Sjulian		    NETGRAPH NODE METHODS
17652419Sjulian******************************************************************/
17752419Sjulian
17852419Sjulian/*
17952419Sjulian * Initialize a new node
18052419Sjulian */
18152419Sjulianstatic int
18270700Sjuliannga_constructor(node_p node)
18352419Sjulian{
18452419Sjulian	sc_p sc;
18552419Sjulian
186220768Sglebius	sc = malloc(sizeof(*sc), M_NETGRAPH_ASYNC, M_WAITOK | M_ZERO);
18752419Sjulian	sc->amode = MODE_HUNT;
18852419Sjulian	sc->cfg.accm = ~0;
18952419Sjulian	sc->cfg.amru = NG_ASYNC_DEFAULT_MRU;
19052419Sjulian	sc->cfg.smru = NG_ASYNC_DEFAULT_MRU;
191184214Sdes	sc->abuf = malloc(ASYNC_BUF_SIZE(sc->cfg.smru),
192220768Sglebius	    M_NETGRAPH_ASYNC, M_WAITOK);
193184214Sdes	sc->sbuf = malloc(SYNC_BUF_SIZE(sc->cfg.amru),
194220768Sglebius	    M_NETGRAPH_ASYNC, M_WAITOK);
19570784Sjulian	NG_NODE_SET_PRIVATE(node, sc);
19670700Sjulian	sc->node = node;
19752419Sjulian	return (0);
19852419Sjulian}
19952419Sjulian
20052419Sjulian/*
20152419Sjulian * Reserve a hook for a pending connection
20252419Sjulian */
20352419Sjulianstatic int
20452419Sjuliannga_newhook(node_p node, hook_p hook, const char *name)
20552419Sjulian{
20670784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
20752419Sjulian	hook_p *hookp;
20852419Sjulian
20970700Sjulian	if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) {
21070700Sjulian		/*
21170700Sjulian		 * We use a static buffer here so only one packet
21270700Sjulian		 * at a time can be allowed to travel in this direction.
21370700Sjulian		 * Force Writer semantics.
21470700Sjulian		 */
21570784Sjulian		NG_HOOK_FORCE_WRITER(hook);
21652419Sjulian		hookp = &sc->async;
21770700Sjulian	} else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) {
21870700Sjulian		/*
21970700Sjulian		 * We use a static state here so only one packet
22070700Sjulian		 * at a time can be allowed to travel in this direction.
22170700Sjulian		 * Force Writer semantics.
22270700Sjulian		 * Since we set this for both directions
22370700Sjulian		 * we might as well set it for the whole node
22470700Sjulian		 * bit I haven;t done that (yet).
22570700Sjulian		 */
22670784Sjulian		NG_HOOK_FORCE_WRITER(hook);
22752419Sjulian		hookp = &sc->sync;
22870700Sjulian	} else {
22952419Sjulian		return (EINVAL);
23070700Sjulian	}
23170700Sjulian	if (*hookp) /* actually can't happen I think [JRE] */
23252419Sjulian		return (EISCONN);
23352419Sjulian	*hookp = hook;
23452419Sjulian	return (0);
23552419Sjulian}
23652419Sjulian
23752419Sjulian/*
23852419Sjulian * Receive incoming data
23952419Sjulian */
24052419Sjulianstatic int
24170700Sjuliannga_rcvdata(hook_p hook, item_p item)
24252419Sjulian{
24370784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
24452419Sjulian
24552419Sjulian	if (hook == sc->sync)
24670700Sjulian		return (nga_rcv_sync(sc, item));
24752976Sarchie	if (hook == sc->async)
24870700Sjulian		return (nga_rcv_async(sc, item));
249213794Srpaulo	panic("%s", __func__);
25052419Sjulian}
25152419Sjulian
25252419Sjulian/*
25352419Sjulian * Receive incoming control message
25452419Sjulian */
25552419Sjulianstatic int
25670700Sjuliannga_rcvmsg(node_p node, item_p item, hook_p lasthook)
25752419Sjulian{
25870784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
25952419Sjulian	struct ng_mesg *resp = NULL;
26052419Sjulian	int error = 0;
26170700Sjulian	struct ng_mesg *msg;
26270700Sjulian
26370700Sjulian	NGI_GET_MSG(item, msg);
26452419Sjulian	switch (msg->header.typecookie) {
26552419Sjulian	case NGM_ASYNC_COOKIE:
26652419Sjulian		switch (msg->header.cmd) {
26752419Sjulian		case NGM_ASYNC_CMD_GET_STATS:
26852419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
26952419Sjulian			if (resp == NULL)
27052419Sjulian				ERROUT(ENOMEM);
27152419Sjulian			*((struct ng_async_stat *) resp->data) = sc->stats;
27252419Sjulian			break;
27352419Sjulian		case NGM_ASYNC_CMD_CLR_STATS:
27452419Sjulian			bzero(&sc->stats, sizeof(sc->stats));
27552419Sjulian			break;
27652419Sjulian		case NGM_ASYNC_CMD_SET_CONFIG:
27752419Sjulian		    {
27852419Sjulian			struct ng_async_cfg *const cfg =
27952419Sjulian				(struct ng_async_cfg *) msg->data;
28052419Sjulian			u_char *buf;
28152419Sjulian
28252419Sjulian			if (msg->header.arglen != sizeof(*cfg))
28352419Sjulian				ERROUT(EINVAL);
28452419Sjulian			if (cfg->amru < NG_ASYNC_MIN_MRU
28552419Sjulian			    || cfg->amru > NG_ASYNC_MAX_MRU
28652419Sjulian			    || cfg->smru < NG_ASYNC_MIN_MRU
28752419Sjulian			    || cfg->smru > NG_ASYNC_MAX_MRU)
28852419Sjulian				ERROUT(EINVAL);
28952419Sjulian			cfg->enabled = !!cfg->enabled;	/* normalize */
29052419Sjulian			if (cfg->smru > sc->cfg.smru) {	/* reallocate buffer */
291184205Sdes				buf = malloc(ASYNC_BUF_SIZE(cfg->smru),
29270870Sjulian				    M_NETGRAPH_ASYNC, M_NOWAIT);
29352419Sjulian				if (!buf)
29452419Sjulian					ERROUT(ENOMEM);
295184205Sdes				free(sc->abuf, M_NETGRAPH_ASYNC);
29652419Sjulian				sc->abuf = buf;
29752419Sjulian			}
29852419Sjulian			if (cfg->amru > sc->cfg.amru) {	/* reallocate buffer */
299184205Sdes				buf = malloc(SYNC_BUF_SIZE(cfg->amru),
30070870Sjulian				    M_NETGRAPH_ASYNC, M_NOWAIT);
30152419Sjulian				if (!buf)
30252419Sjulian					ERROUT(ENOMEM);
303184205Sdes				free(sc->sbuf, M_NETGRAPH_ASYNC);
30452419Sjulian				sc->sbuf = buf;
30552419Sjulian				sc->amode = MODE_HUNT;
30652419Sjulian				sc->slen = 0;
30752419Sjulian			}
30852419Sjulian			if (!cfg->enabled) {
30952419Sjulian				sc->amode = MODE_HUNT;
31052419Sjulian				sc->slen = 0;
31152419Sjulian			}
31252419Sjulian			sc->cfg = *cfg;
31352419Sjulian			break;
31452419Sjulian		    }
31552419Sjulian		case NGM_ASYNC_CMD_GET_CONFIG:
31652419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
31752419Sjulian			if (!resp)
31852419Sjulian				ERROUT(ENOMEM);
31952419Sjulian			*((struct ng_async_cfg *) resp->data) = sc->cfg;
32052419Sjulian			break;
32152419Sjulian		default:
32252419Sjulian			ERROUT(EINVAL);
32352419Sjulian		}
32452419Sjulian		break;
32552419Sjulian	default:
32652419Sjulian		ERROUT(EINVAL);
32752419Sjulian	}
32852419Sjuliandone:
32970700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
33070700Sjulian	NG_FREE_MSG(msg);
33152419Sjulian	return (error);
33252419Sjulian}
33352419Sjulian
33452419Sjulian/*
33552419Sjulian * Shutdown this node
33652419Sjulian */
33752419Sjulianstatic int
33852419Sjuliannga_shutdown(node_p node)
33952419Sjulian{
34070784Sjulian	const sc_p sc = NG_NODE_PRIVATE(node);
34152419Sjulian
342184205Sdes	free(sc->abuf, M_NETGRAPH_ASYNC);
343184205Sdes	free(sc->sbuf, M_NETGRAPH_ASYNC);
34452419Sjulian	bzero(sc, sizeof(*sc));
345184205Sdes	free(sc, M_NETGRAPH_ASYNC);
34670784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
34770784Sjulian	NG_NODE_UNREF(node);
34852419Sjulian	return (0);
34952419Sjulian}
35052419Sjulian
35152419Sjulian/*
35252419Sjulian * Lose a hook. When both hooks go away, we disappear.
35352419Sjulian */
35452419Sjulianstatic int
35552419Sjuliannga_disconnect(hook_p hook)
35652419Sjulian{
35770784Sjulian	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
35852419Sjulian	hook_p *hookp;
35952419Sjulian
36052419Sjulian	if (hook == sc->async)
36152419Sjulian		hookp = &sc->async;
36252419Sjulian	else if (hook == sc->sync)
36352419Sjulian		hookp = &sc->sync;
36452419Sjulian	else
365213794Srpaulo		panic("%s", __func__);
36652419Sjulian	if (!*hookp)
36787599Sobrien		panic("%s 2", __func__);
36852419Sjulian	*hookp = NULL;
36952419Sjulian	bzero(&sc->stats, sizeof(sc->stats));
37052419Sjulian	sc->lasttime = 0;
37170784Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
37270784Sjulian	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
37370784Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
37452419Sjulian	return (0);
37552419Sjulian}
37652419Sjulian
37752419Sjulian/******************************************************************
37852419Sjulian		    INTERNAL HELPER STUFF
37952419Sjulian******************************************************************/
38052419Sjulian
38152419Sjulian/*
38252419Sjulian * Encode a byte into the async buffer
38352419Sjulian */
384131575Sstefanfstatic __inline void
38552419Sjuliannga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
38652419Sjulian{
38752419Sjulian	*fcs = PPP_FCS(*fcs, x);
38852419Sjulian	if ((x < 32 && ((1 << x) & accm))
38952419Sjulian	    || (x == PPP_ESCAPE)
39052419Sjulian	    || (x == PPP_FLAG)) {
39152419Sjulian		sc->abuf[(*len)++] = PPP_ESCAPE;
39252419Sjulian		x ^= PPP_TRANS;
39352419Sjulian	}
39452419Sjulian	sc->abuf[(*len)++] = x;
39552419Sjulian}
39652419Sjulian
39752419Sjulian/*
39852976Sarchie * Receive incoming synchronous data.
39952419Sjulian */
40052419Sjulianstatic int
40170700Sjuliannga_rcv_sync(const sc_p sc, item_p item)
40252419Sjulian{
40370700Sjulian	struct ifnet *rcvif;
40453394Sarchie	int alen, error = 0;
40552976Sarchie	struct timeval time;
40652419Sjulian	u_int16_t fcs, fcs0;
40752976Sarchie	u_int32_t accm;
40870700Sjulian	struct mbuf *m;
40952419Sjulian
41070700Sjulian
41152976Sarchie#define ADD_BYTE(x)	nga_async_add(sc, &fcs, accm, &alen, (x))
41252419Sjulian
41352976Sarchie	/* Check for bypass mode */
41452419Sjulian	if (!sc->cfg.enabled) {
41570784Sjulian		NG_FWD_ITEM_HOOK(error, item, sc->async );
41652419Sjulian		return (error);
41752419Sjulian	}
41870700Sjulian	NGI_GET_M(item, m);
41952976Sarchie
42070700Sjulian	rcvif = m->m_pkthdr.rcvif;
42170700Sjulian
42253394Sarchie	/* Get ACCM; special case LCP frames, which use full ACCM */
42352976Sarchie	accm = sc->cfg.accm;
42453394Sarchie	if (m->m_pkthdr.len >= 4) {
42553394Sarchie		static const u_char lcphdr[4] = {
42653394Sarchie		    PPP_ALLSTATIONS,
42753394Sarchie		    PPP_UI,
42853394Sarchie		    (u_char)(PPP_LCP >> 8),
42953394Sarchie		    (u_char)(PPP_LCP & 0xff)
43053394Sarchie		};
43153394Sarchie		u_char buf[4];
43252976Sarchie
43353394Sarchie		m_copydata(m, 0, 4, (caddr_t)buf);
43453394Sarchie		if (bcmp(buf, &lcphdr, 4) == 0)
43552976Sarchie			accm = ~0;
43652976Sarchie	}
43752976Sarchie
43852976Sarchie	/* Check for overflow */
43952419Sjulian	if (m->m_pkthdr.len > sc->cfg.smru) {
44052419Sjulian		sc->stats.syncOverflows++;
44170700Sjulian		NG_FREE_M(m);
44270700Sjulian		NG_FREE_ITEM(item);
44352419Sjulian		return (EMSGSIZE);
44452419Sjulian	}
44552976Sarchie
44652976Sarchie	/* Update stats */
44752419Sjulian	sc->stats.syncFrames++;
44852419Sjulian	sc->stats.syncOctets += m->m_pkthdr.len;
44952419Sjulian
45052419Sjulian	/* Initialize async encoded version of input mbuf */
45152419Sjulian	alen = 0;
45252419Sjulian	fcs = PPP_INITFCS;
45352419Sjulian
45452419Sjulian	/* Add beginning sync flag if it's been long enough to need one */
45552976Sarchie	getmicrotime(&time);
45652976Sarchie	if (time.tv_sec >= sc->lasttime + 1) {
45752976Sarchie		sc->abuf[alen++] = PPP_FLAG;
45852976Sarchie		sc->lasttime = time.tv_sec;
45952419Sjulian	}
46052419Sjulian
46153394Sarchie	/* Add packet payload */
46252976Sarchie	while (m != NULL) {
46352419Sjulian		while (m->m_len > 0) {
46452976Sarchie			ADD_BYTE(*mtod(m, u_char *));
46552419Sjulian			m->m_data++;
46652419Sjulian			m->m_len--;
46752419Sjulian		}
46890227Sdillon		m = m_free(m);
46952419Sjulian	}
47052419Sjulian
47152419Sjulian	/* Add checksum and final sync flag */
47252419Sjulian	fcs0 = fcs;
47352419Sjulian	ADD_BYTE(~fcs0 & 0xff);
47452419Sjulian	ADD_BYTE(~fcs0 >> 8);
47552419Sjulian	sc->abuf[alen++] = PPP_FLAG;
47652419Sjulian
47752419Sjulian	/* Put frame in an mbuf and ship it off */
47852976Sarchie	if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) {
47970700Sjulian		NG_FREE_ITEM(item);
48052419Sjulian		error = ENOBUFS;
48170700Sjulian	} else {
48270700Sjulian		NG_FWD_NEW_DATA(error, item, sc->async, m);
48370700Sjulian	}
48452419Sjulian	return (error);
48552419Sjulian}
48652419Sjulian
48752419Sjulian/*
48852419Sjulian * Receive incoming asynchronous data
48952976Sarchie * XXX Technically, we should strip out incoming characters
49052976Sarchie *     that are in our ACCM. Not sure if this is good or not.
49152419Sjulian */
49252419Sjulianstatic int
49370700Sjuliannga_rcv_async(const sc_p sc, item_p item)
49452419Sjulian{
49570700Sjulian	struct ifnet *rcvif;
49652419Sjulian	int error;
49770700Sjulian	struct mbuf *m;
49852419Sjulian
49952419Sjulian	if (!sc->cfg.enabled) {
50070784Sjulian		NG_FWD_ITEM_HOOK(error, item,  sc->sync);
50152419Sjulian		return (error);
50252419Sjulian	}
50370700Sjulian	NGI_GET_M(item, m);
50470700Sjulian	rcvif = m->m_pkthdr.rcvif;
50552419Sjulian	while (m) {
50652419Sjulian		struct mbuf *n;
50752419Sjulian
50852419Sjulian		for (; m->m_len > 0; m->m_data++, m->m_len--) {
50952419Sjulian			u_char  ch = *mtod(m, u_char *);
51052419Sjulian
51152419Sjulian			sc->stats.asyncOctets++;
51252419Sjulian			if (ch == PPP_FLAG) {	/* Flag overrides everything */
51352419Sjulian				int     skip = 0;
51452419Sjulian
51552419Sjulian				/* Check for runts */
51652419Sjulian				if (sc->slen < 2) {
51752419Sjulian					if (sc->slen > 0)
51852419Sjulian						sc->stats.asyncRunts++;
51952419Sjulian					goto reset;
52052419Sjulian				}
52152419Sjulian
52252419Sjulian				/* Verify CRC */
52352419Sjulian				if (sc->fcs != PPP_GOODFCS) {
52452419Sjulian					sc->stats.asyncBadCheckSums++;
52552419Sjulian					goto reset;
52652419Sjulian				}
52752419Sjulian				sc->slen -= 2;
52852419Sjulian
52952419Sjulian				/* Strip address and control fields */
53052419Sjulian				if (sc->slen >= 2
53152419Sjulian				    && sc->sbuf[0] == PPP_ALLSTATIONS
53252419Sjulian				    && sc->sbuf[1] == PPP_UI)
53352419Sjulian					skip = 2;
53452419Sjulian
53552419Sjulian				/* Check for frame too big */
53652419Sjulian				if (sc->slen - skip > sc->cfg.amru) {
53752419Sjulian					sc->stats.asyncOverflows++;
53852419Sjulian					goto reset;
53952419Sjulian				}
54052419Sjulian
54152419Sjulian				/* OK, ship it out */
54252419Sjulian				if ((n = m_devget(sc->sbuf + skip,
54370700Sjulian					   sc->slen - skip, 0, rcvif, NULL))) {
54470700Sjulian					if (item) { /* sets NULL -> item */
54570700Sjulian						NG_FWD_NEW_DATA(error, item,
54670700Sjulian							sc->sync, n);
54770700Sjulian					} else {
54870700Sjulian						NG_SEND_DATA_ONLY(error,
54970700Sjulian							sc->sync ,n);
55070700Sjulian					}
55170700Sjulian				}
55252419Sjulian				sc->stats.asyncFrames++;
55352419Sjulianreset:
55452419Sjulian				sc->amode = MODE_NORMAL;
55552419Sjulian				sc->fcs = PPP_INITFCS;
55652419Sjulian				sc->slen = 0;
55752419Sjulian				continue;
55852419Sjulian			}
55952419Sjulian			switch (sc->amode) {
56052419Sjulian			case MODE_NORMAL:
56152419Sjulian				if (ch == PPP_ESCAPE) {
56252419Sjulian					sc->amode = MODE_ESC;
56352419Sjulian					continue;
56452419Sjulian				}
56552419Sjulian				break;
56652419Sjulian			case MODE_ESC:
56752419Sjulian				ch ^= PPP_TRANS;
56852419Sjulian				sc->amode = MODE_NORMAL;
56952419Sjulian				break;
57052419Sjulian			case MODE_HUNT:
57152419Sjulian			default:
57252419Sjulian				continue;
57352419Sjulian			}
57452419Sjulian
57552419Sjulian			/* Add byte to frame */
57652419Sjulian			if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
57752419Sjulian				sc->stats.asyncOverflows++;
57852419Sjulian				sc->amode = MODE_HUNT;
57952419Sjulian				sc->slen = 0;
58052419Sjulian			} else {
58152419Sjulian				sc->sbuf[sc->slen++] = ch;
58252419Sjulian				sc->fcs = PPP_FCS(sc->fcs, ch);
58352419Sjulian			}
58452419Sjulian		}
58590227Sdillon		m = m_free(m);
58652419Sjulian	}
58770700Sjulian	if (item)
58870700Sjulian		NG_FREE_ITEM(item);
58952419Sjulian	return (0);
59052419Sjulian}
59152419Sjulian
59252419Sjulian/*
59352419Sjulian * CRC table
59452419Sjulian *
59552419Sjulian * Taken from RFC 1171 Appendix B
59652419Sjulian */
59752419Sjulianstatic const u_int16_t fcstab[256] = {
59852419Sjulian	 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
59952419Sjulian	 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
60052419Sjulian	 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
60152419Sjulian	 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
60252419Sjulian	 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
60352419Sjulian	 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
60452419Sjulian	 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
60552419Sjulian	 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
60652419Sjulian	 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
60752419Sjulian	 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
60852419Sjulian	 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
60952419Sjulian	 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
61052419Sjulian	 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
61152419Sjulian	 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
61252419Sjulian	 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
61352419Sjulian	 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
61452419Sjulian	 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
61552419Sjulian	 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
61652419Sjulian	 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
61752419Sjulian	 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
61852419Sjulian	 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
61952419Sjulian	 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
62052419Sjulian	 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
62152419Sjulian	 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
62252419Sjulian	 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
62352419Sjulian	 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
62452419Sjulian	 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
62552419Sjulian	 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
62652419Sjulian	 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
62752419Sjulian	 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
62852419Sjulian	 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
62952419Sjulian	 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
63052419Sjulian};
631