ng_async.c revision 70159
152419Sjulian
252419Sjulian/*
352419Sjulian * ng_async.c
452419Sjulian *
552419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
652419Sjulian * All rights reserved.
752419Sjulian *
852419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
952419Sjulian * redistribution of this software, in source or object code forms, with or
1052419Sjulian * without modifications are expressly permitted by Whistle Communications;
1152419Sjulian * provided, however, that:
1252419Sjulian * 1. Any and all reproductions of the source or object code must include the
1352419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1452419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1552419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1652419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1752419Sjulian *    such appears in the above copyright notice or in the software.
1852419Sjulian *
1952419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2052419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2152419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2252419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2352419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2452419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2552419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2652419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2752419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2852419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2952419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3052419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3152419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3252419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3352419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3452419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3552419Sjulian * OF SUCH DAMAGE.
3652419Sjulian *
3767506Sjulian * Author: Archie Cobbs <archie@freebsd.org>
3852419Sjulian *
3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_async.c 70159 2000-12-18 20:03:32Z julian $
4052752Sjulian * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
4152419Sjulian */
4252419Sjulian
4352419Sjulian/*
4452419Sjulian * This node type implements a PPP style sync <-> async converter.
4552419Sjulian * See RFC 1661 for details of how asynchronous encoding works.
4652419Sjulian */
4752419Sjulian
4852419Sjulian#include <sys/param.h>
4952419Sjulian#include <sys/systm.h>
5052419Sjulian#include <sys/kernel.h>
5152419Sjulian#include <sys/mbuf.h>
5252419Sjulian#include <sys/malloc.h>
5352419Sjulian#include <sys/errno.h>
5452419Sjulian
5552419Sjulian#include <netgraph/ng_message.h>
5652419Sjulian#include <netgraph/netgraph.h>
5752419Sjulian#include <netgraph/ng_async.h>
5853913Sarchie#include <netgraph/ng_parse.h>
5952419Sjulian
6052419Sjulian#include <net/ppp_defs.h>
6152419Sjulian
6252419Sjulian/* Async decode state */
6352419Sjulian#define MODE_HUNT	0
6452419Sjulian#define MODE_NORMAL	1
6552419Sjulian#define MODE_ESC	2
6652419Sjulian
6752419Sjulian/* Private data structure */
6853394Sarchiestruct ng_async_private {
6952419Sjulian	node_p  	node;		/* Our node */
7052419Sjulian	hook_p  	async;		/* Asynchronous side */
7152419Sjulian	hook_p  	sync;		/* Synchronous side */
7252419Sjulian	u_char  	amode;		/* Async hunt/esape mode */
7352419Sjulian	u_int16_t	fcs;		/* Decoded async FCS (so far) */
7452419Sjulian	u_char	       *abuf;		/* Buffer to encode sync into */
7552419Sjulian	u_char	       *sbuf;		/* Buffer to decode async into */
7652419Sjulian	u_int		slen;		/* Length of data in sbuf */
7752419Sjulian	long		lasttime;	/* Time of last async packet sent */
7852419Sjulian	struct		ng_async_cfg	cfg;	/* Configuration */
7952419Sjulian	struct		ng_async_stat	stats;	/* Statistics */
8052419Sjulian};
8153394Sarchietypedef struct ng_async_private *sc_p;
8252419Sjulian
8352419Sjulian/* Useful macros */
8452419Sjulian#define ASYNC_BUF_SIZE(smru)	(2 * (smru) + 10)
8552419Sjulian#define SYNC_BUF_SIZE(amru)	((amru) + 10)
8652419Sjulian#define ERROUT(x)		do { error = (x); goto done; } while (0)
8752419Sjulian
8852419Sjulian/* Netgraph methods */
8953913Sarchiestatic ng_constructor_t		nga_constructor;
9052752Sjulianstatic ng_rcvdata_t		nga_rcvdata;
9152752Sjulianstatic ng_rcvmsg_t		nga_rcvmsg;
9252752Sjulianstatic ng_shutdown_t		nga_shutdown;
9352752Sjulianstatic ng_newhook_t		nga_newhook;
9452752Sjulianstatic ng_disconnect_t		nga_disconnect;
9552419Sjulian
9652419Sjulian/* Helper stuff */
9752419Sjulianstatic int	nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta);
9852419Sjulianstatic int	nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta);
9952419Sjulian
10053913Sarchie/* Parse type for struct ng_async_cfg */
10153913Sarchiestatic const struct ng_parse_struct_info
10253913Sarchie	nga_config_type_info = NG_ASYNC_CONFIG_TYPE_INFO;
10353913Sarchiestatic const struct ng_parse_type nga_config_type = {
10453913Sarchie	&ng_parse_struct_type,
10553913Sarchie	&nga_config_type_info
10653913Sarchie};
10753913Sarchie
10853913Sarchie/* Parse type for struct ng_async_stat */
10953913Sarchiestatic const struct ng_parse_struct_info
11053913Sarchie	nga_stats_type_info = NG_ASYNC_STATS_TYPE_INFO;
11153913Sarchiestatic const struct ng_parse_type nga_stats_type = {
11253913Sarchie	&ng_parse_struct_type,
11353913Sarchie	&nga_stats_type_info,
11453913Sarchie};
11553913Sarchie
11653913Sarchie/* List of commands and how to convert arguments to/from ASCII */
11753913Sarchiestatic const struct ng_cmdlist nga_cmdlist[] = {
11853913Sarchie	{
11953913Sarchie	  NGM_ASYNC_COOKIE,
12053913Sarchie	  NGM_ASYNC_CMD_SET_CONFIG,
12153913Sarchie	  "setconfig",
12253913Sarchie	  &nga_config_type,
12353913Sarchie	  NULL
12453913Sarchie	},
12553913Sarchie	{
12653913Sarchie	  NGM_ASYNC_COOKIE,
12753913Sarchie	  NGM_ASYNC_CMD_GET_CONFIG,
12853913Sarchie	  "getconfig",
12953913Sarchie	  NULL,
13053913Sarchie	  &nga_config_type
13153913Sarchie	},
13253913Sarchie	{
13353913Sarchie	  NGM_ASYNC_COOKIE,
13453913Sarchie	  NGM_ASYNC_CMD_GET_STATS,
13553913Sarchie	  "getstats",
13653913Sarchie	  NULL,
13753913Sarchie	  &nga_stats_type
13853913Sarchie	},
13953913Sarchie	{
14053913Sarchie	  NGM_ASYNC_COOKIE,
14153913Sarchie	  NGM_ASYNC_CMD_CLR_STATS,
14253913Sarchie	  "clrstats",
14353913Sarchie	  &nga_stats_type,
14453913Sarchie	  NULL
14553913Sarchie	},
14653913Sarchie	{ 0 }
14753913Sarchie};
14853913Sarchie
14952419Sjulian/* Define the netgraph node type */
15052419Sjulianstatic struct ng_type typestruct = {
15170159Sjulian	NG_ABI_VERSION,
15252419Sjulian	NG_ASYNC_NODE_TYPE,
15352419Sjulian	NULL,
15452419Sjulian	nga_constructor,
15552419Sjulian	nga_rcvmsg,
15652419Sjulian	nga_shutdown,
15752419Sjulian	nga_newhook,
15852419Sjulian	NULL,
15952419Sjulian	NULL,
16052419Sjulian	nga_rcvdata,
16153913Sarchie	nga_disconnect,
16253913Sarchie	nga_cmdlist
16352419Sjulian};
16452419SjulianNETGRAPH_INIT(async, &typestruct);
16552419Sjulian
16652419Sjulian/* CRC table */
16752419Sjulianstatic const u_int16_t fcstab[];
16852419Sjulian
16952419Sjulian/******************************************************************
17052419Sjulian		    NETGRAPH NODE METHODS
17152419Sjulian******************************************************************/
17252419Sjulian
17352419Sjulian/*
17452419Sjulian * Initialize a new node
17552419Sjulian */
17652419Sjulianstatic int
17752419Sjuliannga_constructor(node_p *nodep)
17852419Sjulian{
17952419Sjulian	sc_p sc;
18052419Sjulian	int error;
18152419Sjulian
18252419Sjulian	if ((error = ng_make_node_common(&typestruct, nodep)))
18352419Sjulian		return (error);
18468876Sdwmalone	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
18552419Sjulian	if (sc == NULL)
18652419Sjulian		return (ENOMEM);
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;
19152419Sjulian	MALLOC(sc->abuf, u_char *,
19266182Sarchie	    ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH, M_NOWAIT);
19352419Sjulian	if (sc->abuf == NULL)
19452419Sjulian		goto fail;
19552419Sjulian	MALLOC(sc->sbuf, u_char *,
19666182Sarchie	    SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH, M_NOWAIT);
19752419Sjulian	if (sc->sbuf == NULL) {
19852419Sjulian		FREE(sc->abuf, M_NETGRAPH);
19952419Sjulianfail:
20052419Sjulian		FREE(sc, M_NETGRAPH);
20152419Sjulian		return (ENOMEM);
20252419Sjulian	}
20352419Sjulian	(*nodep)->private = sc;
20452419Sjulian	sc->node = *nodep;
20552419Sjulian	return (0);
20652419Sjulian}
20752419Sjulian
20852419Sjulian/*
20952419Sjulian * Reserve a hook for a pending connection
21052419Sjulian */
21152419Sjulianstatic int
21252419Sjuliannga_newhook(node_p node, hook_p hook, const char *name)
21352419Sjulian{
21452419Sjulian	const sc_p sc = node->private;
21552419Sjulian	hook_p *hookp;
21652419Sjulian
21752419Sjulian	if (!strcmp(name, NG_ASYNC_HOOK_ASYNC))
21852419Sjulian		hookp = &sc->async;
21952419Sjulian	else if (!strcmp(name, NG_ASYNC_HOOK_SYNC))
22052419Sjulian		hookp = &sc->sync;
22152419Sjulian	else
22252419Sjulian		return (EINVAL);
22352419Sjulian	if (*hookp)
22452419Sjulian		return (EISCONN);
22552419Sjulian	*hookp = hook;
22652419Sjulian	return (0);
22752419Sjulian}
22852419Sjulian
22952419Sjulian/*
23052419Sjulian * Receive incoming data
23152419Sjulian */
23252419Sjulianstatic int
23359728Sjuliannga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
23469922Sjulian		struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp)
23552419Sjulian{
23652419Sjulian	const sc_p sc = hook->node->private;
23752419Sjulian
23852419Sjulian	if (hook == sc->sync)
23952419Sjulian		return (nga_rcv_sync(sc, m, meta));
24052976Sarchie	if (hook == sc->async)
24152419Sjulian		return (nga_rcv_async(sc, m, meta));
24252419Sjulian	panic(__FUNCTION__);
24352419Sjulian}
24452419Sjulian
24552419Sjulian/*
24652419Sjulian * Receive incoming control message
24752419Sjulian */
24852419Sjulianstatic int
24952419Sjuliannga_rcvmsg(node_p node, struct ng_mesg *msg,
25059728Sjulian	const char *rtn, struct ng_mesg **rptr, hook_p lasthook)
25152419Sjulian{
25252419Sjulian	const sc_p sc = (sc_p) node->private;
25352419Sjulian	struct ng_mesg *resp = NULL;
25452419Sjulian	int error = 0;
25552419Sjulian
25652419Sjulian	switch (msg->header.typecookie) {
25752419Sjulian	case NGM_ASYNC_COOKIE:
25852419Sjulian		switch (msg->header.cmd) {
25952419Sjulian		case NGM_ASYNC_CMD_GET_STATS:
26052419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
26152419Sjulian			if (resp == NULL)
26252419Sjulian				ERROUT(ENOMEM);
26352419Sjulian			*((struct ng_async_stat *) resp->data) = sc->stats;
26452419Sjulian			break;
26552419Sjulian		case NGM_ASYNC_CMD_CLR_STATS:
26652419Sjulian			bzero(&sc->stats, sizeof(sc->stats));
26752419Sjulian			break;
26852419Sjulian		case NGM_ASYNC_CMD_SET_CONFIG:
26952419Sjulian		    {
27052419Sjulian			struct ng_async_cfg *const cfg =
27152419Sjulian				(struct ng_async_cfg *) msg->data;
27252419Sjulian			u_char *buf;
27352419Sjulian
27452419Sjulian			if (msg->header.arglen != sizeof(*cfg))
27552419Sjulian				ERROUT(EINVAL);
27652419Sjulian			if (cfg->amru < NG_ASYNC_MIN_MRU
27752419Sjulian			    || cfg->amru > NG_ASYNC_MAX_MRU
27852419Sjulian			    || cfg->smru < NG_ASYNC_MIN_MRU
27952419Sjulian			    || cfg->smru > NG_ASYNC_MAX_MRU)
28052419Sjulian				ERROUT(EINVAL);
28152419Sjulian			cfg->enabled = !!cfg->enabled;	/* normalize */
28252419Sjulian			if (cfg->smru > sc->cfg.smru) {	/* reallocate buffer */
28352419Sjulian				MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru),
28452419Sjulian				    M_NETGRAPH, M_NOWAIT);
28552419Sjulian				if (!buf)
28652419Sjulian					ERROUT(ENOMEM);
28752419Sjulian				FREE(sc->abuf, M_NETGRAPH);
28852419Sjulian				sc->abuf = buf;
28952419Sjulian			}
29052419Sjulian			if (cfg->amru > sc->cfg.amru) {	/* reallocate buffer */
29152419Sjulian				MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru),
29252419Sjulian				    M_NETGRAPH, M_NOWAIT);
29352419Sjulian				if (!buf)
29452419Sjulian					ERROUT(ENOMEM);
29552419Sjulian				FREE(sc->sbuf, M_NETGRAPH);
29652419Sjulian				sc->sbuf = buf;
29752419Sjulian				sc->amode = MODE_HUNT;
29852419Sjulian				sc->slen = 0;
29952419Sjulian			}
30052419Sjulian			if (!cfg->enabled) {
30152419Sjulian				sc->amode = MODE_HUNT;
30252419Sjulian				sc->slen = 0;
30352419Sjulian			}
30452419Sjulian			sc->cfg = *cfg;
30552419Sjulian			break;
30652419Sjulian		    }
30752419Sjulian		case NGM_ASYNC_CMD_GET_CONFIG:
30852419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
30952419Sjulian			if (!resp)
31052419Sjulian				ERROUT(ENOMEM);
31152419Sjulian			*((struct ng_async_cfg *) resp->data) = sc->cfg;
31252419Sjulian			break;
31352419Sjulian		default:
31452419Sjulian			ERROUT(EINVAL);
31552419Sjulian		}
31652419Sjulian		break;
31752419Sjulian	default:
31852419Sjulian		ERROUT(EINVAL);
31952419Sjulian	}
32052419Sjulian	if (rptr)
32152419Sjulian		*rptr = resp;
32252419Sjulian	else if (resp)
32352419Sjulian		FREE(resp, M_NETGRAPH);
32452419Sjulian
32552419Sjuliandone:
32652419Sjulian	FREE(msg, M_NETGRAPH);
32752419Sjulian	return (error);
32852419Sjulian}
32952419Sjulian
33052419Sjulian/*
33152419Sjulian * Shutdown this node
33252419Sjulian */
33352419Sjulianstatic int
33452419Sjuliannga_shutdown(node_p node)
33552419Sjulian{
33652419Sjulian	const sc_p sc = node->private;
33752419Sjulian
33852419Sjulian	ng_cutlinks(node);
33952419Sjulian	ng_unname(node);
34052419Sjulian	FREE(sc->abuf, M_NETGRAPH);
34152419Sjulian	FREE(sc->sbuf, M_NETGRAPH);
34252419Sjulian	bzero(sc, sizeof(*sc));
34352419Sjulian	FREE(sc, M_NETGRAPH);
34452419Sjulian	node->private = NULL;
34552419Sjulian	ng_unref(node);
34652419Sjulian	return (0);
34752419Sjulian}
34852419Sjulian
34952419Sjulian/*
35052419Sjulian * Lose a hook. When both hooks go away, we disappear.
35152419Sjulian */
35252419Sjulianstatic int
35352419Sjuliannga_disconnect(hook_p hook)
35452419Sjulian{
35552419Sjulian	const sc_p sc = hook->node->private;
35652419Sjulian	hook_p *hookp;
35752419Sjulian
35852419Sjulian	if (hook == sc->async)
35952419Sjulian		hookp = &sc->async;
36052419Sjulian	else if (hook == sc->sync)
36152419Sjulian		hookp = &sc->sync;
36252419Sjulian	else
36352419Sjulian		panic(__FUNCTION__);
36452419Sjulian	if (!*hookp)
36552419Sjulian		panic(__FUNCTION__ "2");
36652419Sjulian	*hookp = NULL;
36752419Sjulian	bzero(&sc->stats, sizeof(sc->stats));
36852419Sjulian	sc->lasttime = 0;
36952419Sjulian	if (hook->node->numhooks == 0)
37052419Sjulian		ng_rmnode(hook->node);
37152419Sjulian	return (0);
37252419Sjulian}
37352419Sjulian
37452419Sjulian/******************************************************************
37552419Sjulian		    INTERNAL HELPER STUFF
37652419Sjulian******************************************************************/
37752419Sjulian
37852419Sjulian/*
37952419Sjulian * Encode a byte into the async buffer
38052419Sjulian */
38152419Sjulianstatic __inline__ void
38252419Sjuliannga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
38352419Sjulian{
38452419Sjulian	*fcs = PPP_FCS(*fcs, x);
38552419Sjulian	if ((x < 32 && ((1 << x) & accm))
38652419Sjulian	    || (x == PPP_ESCAPE)
38752419Sjulian	    || (x == PPP_FLAG)) {
38852419Sjulian		sc->abuf[(*len)++] = PPP_ESCAPE;
38952419Sjulian		x ^= PPP_TRANS;
39052419Sjulian	}
39152419Sjulian	sc->abuf[(*len)++] = x;
39252419Sjulian}
39352419Sjulian
39452419Sjulian/*
39552976Sarchie * Receive incoming synchronous data.
39652419Sjulian */
39752419Sjulianstatic int
39852419Sjuliannga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta)
39952419Sjulian{
40052419Sjulian	struct ifnet *const rcvif = m->m_pkthdr.rcvif;
40153394Sarchie	int alen, error = 0;
40252976Sarchie	struct timeval time;
40352419Sjulian	u_int16_t fcs, fcs0;
40452976Sarchie	u_int32_t accm;
40552419Sjulian
40652976Sarchie#define ADD_BYTE(x)	nga_async_add(sc, &fcs, accm, &alen, (x))
40752419Sjulian
40852976Sarchie	/* Check for bypass mode */
40952419Sjulian	if (!sc->cfg.enabled) {
41052419Sjulian		NG_SEND_DATA(error, sc->async, m, meta);
41152419Sjulian		return (error);
41252419Sjulian	}
41352976Sarchie
41453394Sarchie	/* Get ACCM; special case LCP frames, which use full ACCM */
41552976Sarchie	accm = sc->cfg.accm;
41653394Sarchie	if (m->m_pkthdr.len >= 4) {
41753394Sarchie		static const u_char lcphdr[4] = {
41853394Sarchie		    PPP_ALLSTATIONS,
41953394Sarchie		    PPP_UI,
42053394Sarchie		    (u_char)(PPP_LCP >> 8),
42153394Sarchie		    (u_char)(PPP_LCP & 0xff)
42253394Sarchie		};
42353394Sarchie		u_char buf[4];
42452976Sarchie
42553394Sarchie		m_copydata(m, 0, 4, (caddr_t)buf);
42653394Sarchie		if (bcmp(buf, &lcphdr, 4) == 0)
42752976Sarchie			accm = ~0;
42852976Sarchie	}
42952976Sarchie
43052976Sarchie	/* Check for overflow */
43152419Sjulian	if (m->m_pkthdr.len > sc->cfg.smru) {
43252419Sjulian		sc->stats.syncOverflows++;
43352419Sjulian		NG_FREE_DATA(m, meta);
43452419Sjulian		return (EMSGSIZE);
43552419Sjulian	}
43652976Sarchie
43752976Sarchie	/* Update stats */
43852419Sjulian	sc->stats.syncFrames++;
43952419Sjulian	sc->stats.syncOctets += m->m_pkthdr.len;
44052419Sjulian
44152419Sjulian	/* Initialize async encoded version of input mbuf */
44252419Sjulian	alen = 0;
44352419Sjulian	fcs = PPP_INITFCS;
44452419Sjulian
44552419Sjulian	/* Add beginning sync flag if it's been long enough to need one */
44652976Sarchie	getmicrotime(&time);
44752976Sarchie	if (time.tv_sec >= sc->lasttime + 1) {
44852976Sarchie		sc->abuf[alen++] = PPP_FLAG;
44952976Sarchie		sc->lasttime = time.tv_sec;
45052419Sjulian	}
45152419Sjulian
45253394Sarchie	/* Add packet payload */
45352976Sarchie	while (m != NULL) {
45452419Sjulian		struct mbuf *n;
45552419Sjulian
45652419Sjulian		while (m->m_len > 0) {
45752976Sarchie			ADD_BYTE(*mtod(m, u_char *));
45852419Sjulian			m->m_data++;
45952419Sjulian			m->m_len--;
46052419Sjulian		}
46152419Sjulian		MFREE(m, n);
46252419Sjulian		m = n;
46352419Sjulian	}
46452419Sjulian
46552419Sjulian	/* Add checksum and final sync flag */
46652419Sjulian	fcs0 = fcs;
46752419Sjulian	ADD_BYTE(~fcs0 & 0xff);
46852419Sjulian	ADD_BYTE(~fcs0 >> 8);
46952419Sjulian	sc->abuf[alen++] = PPP_FLAG;
47052419Sjulian
47152419Sjulian	/* Put frame in an mbuf and ship it off */
47252976Sarchie	if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) {
47352976Sarchie		NG_FREE_META(meta);
47452419Sjulian		error = ENOBUFS;
47552976Sarchie	} else
47652419Sjulian		NG_SEND_DATA(error, sc->async, m, meta);
47752419Sjulian	return (error);
47852419Sjulian}
47952419Sjulian
48052419Sjulian/*
48152419Sjulian * Receive incoming asynchronous data
48252976Sarchie * XXX Technically, we should strip out incoming characters
48352976Sarchie *     that are in our ACCM. Not sure if this is good or not.
48452419Sjulian */
48552419Sjulianstatic int
48652419Sjuliannga_rcv_async(const sc_p sc, struct mbuf * m, meta_p meta)
48752419Sjulian{
48852419Sjulian	struct ifnet *const rcvif = m->m_pkthdr.rcvif;
48952419Sjulian	int error;
49052419Sjulian
49152419Sjulian	if (!sc->cfg.enabled) {
49252419Sjulian		NG_SEND_DATA(error, sc->sync, m, meta);
49352419Sjulian		return (error);
49452419Sjulian	}
49552419Sjulian	NG_FREE_META(meta);
49652419Sjulian	while (m) {
49752419Sjulian		struct mbuf *n;
49852419Sjulian
49952419Sjulian		for (; m->m_len > 0; m->m_data++, m->m_len--) {
50052419Sjulian			u_char  ch = *mtod(m, u_char *);
50152419Sjulian
50252419Sjulian			sc->stats.asyncOctets++;
50352419Sjulian			if (ch == PPP_FLAG) {	/* Flag overrides everything */
50452419Sjulian				int     skip = 0;
50552419Sjulian
50652419Sjulian				/* Check for runts */
50752419Sjulian				if (sc->slen < 2) {
50852419Sjulian					if (sc->slen > 0)
50952419Sjulian						sc->stats.asyncRunts++;
51052419Sjulian					goto reset;
51152419Sjulian				}
51252419Sjulian
51352419Sjulian				/* Verify CRC */
51452419Sjulian				if (sc->fcs != PPP_GOODFCS) {
51552419Sjulian					sc->stats.asyncBadCheckSums++;
51652419Sjulian					goto reset;
51752419Sjulian				}
51852419Sjulian				sc->slen -= 2;
51952419Sjulian
52052419Sjulian				/* Strip address and control fields */
52152419Sjulian				if (sc->slen >= 2
52252419Sjulian				    && sc->sbuf[0] == PPP_ALLSTATIONS
52352419Sjulian				    && sc->sbuf[1] == PPP_UI)
52452419Sjulian					skip = 2;
52552419Sjulian
52652419Sjulian				/* Check for frame too big */
52752419Sjulian				if (sc->slen - skip > sc->cfg.amru) {
52852419Sjulian					sc->stats.asyncOverflows++;
52952419Sjulian					goto reset;
53052419Sjulian				}
53152419Sjulian
53252419Sjulian				/* OK, ship it out */
53352419Sjulian				if ((n = m_devget(sc->sbuf + skip,
53452419Sjulian					   sc->slen - skip, 0, rcvif, NULL)))
53552419Sjulian					NG_SEND_DATA(error, sc->sync, n, meta);
53652419Sjulian				sc->stats.asyncFrames++;
53752419Sjulianreset:
53852419Sjulian				sc->amode = MODE_NORMAL;
53952419Sjulian				sc->fcs = PPP_INITFCS;
54052419Sjulian				sc->slen = 0;
54152419Sjulian				continue;
54252419Sjulian			}
54352419Sjulian			switch (sc->amode) {
54452419Sjulian			case MODE_NORMAL:
54552419Sjulian				if (ch == PPP_ESCAPE) {
54652419Sjulian					sc->amode = MODE_ESC;
54752419Sjulian					continue;
54852419Sjulian				}
54952419Sjulian				break;
55052419Sjulian			case MODE_ESC:
55152419Sjulian				ch ^= PPP_TRANS;
55252419Sjulian				sc->amode = MODE_NORMAL;
55352419Sjulian				break;
55452419Sjulian			case MODE_HUNT:
55552419Sjulian			default:
55652419Sjulian				continue;
55752419Sjulian			}
55852419Sjulian
55952419Sjulian			/* Add byte to frame */
56052419Sjulian			if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
56152419Sjulian				sc->stats.asyncOverflows++;
56252419Sjulian				sc->amode = MODE_HUNT;
56352419Sjulian				sc->slen = 0;
56452419Sjulian			} else {
56552419Sjulian				sc->sbuf[sc->slen++] = ch;
56652419Sjulian				sc->fcs = PPP_FCS(sc->fcs, ch);
56752419Sjulian			}
56852419Sjulian		}
56952419Sjulian		MFREE(m, n);
57052419Sjulian		m = n;
57152419Sjulian	}
57252419Sjulian	return (0);
57352419Sjulian}
57452419Sjulian
57552419Sjulian/*
57652419Sjulian * CRC table
57752419Sjulian *
57852419Sjulian * Taken from RFC 1171 Appendix B
57952419Sjulian */
58052419Sjulianstatic const u_int16_t fcstab[256] = {
58152419Sjulian	 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
58252419Sjulian	 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
58352419Sjulian	 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
58452419Sjulian	 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
58552419Sjulian	 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
58652419Sjulian	 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
58752419Sjulian	 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
58852419Sjulian	 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
58952419Sjulian	 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
59052419Sjulian	 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
59152419Sjulian	 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
59252419Sjulian	 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
59352419Sjulian	 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
59452419Sjulian	 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
59552419Sjulian	 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
59652419Sjulian	 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
59752419Sjulian	 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
59852419Sjulian	 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
59952419Sjulian	 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
60052419Sjulian	 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
60152419Sjulian	 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
60252419Sjulian	 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
60352419Sjulian	 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
60452419Sjulian	 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
60552419Sjulian	 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
60652419Sjulian	 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
60752419Sjulian	 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
60852419Sjulian	 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
60952419Sjulian	 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
61052419Sjulian	 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
61152419Sjulian	 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
61252419Sjulian	 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
61352419Sjulian};
614