xref: /dragonfly/sys/netgraph/async/ng_async.c (revision 5c694678)
1 
2 /*
3  * ng_async.c
4  *
5  * Copyright (c) 1996-1999 Whistle Communications, Inc.
6  * All rights reserved.
7  *
8  * Subject to the following obligations and disclaimer of warranty, use and
9  * redistribution of this software, in source or object code forms, with or
10  * without modifications are expressly permitted by Whistle Communications;
11  * provided, however, that:
12  * 1. Any and all reproductions of the source or object code must include the
13  *    copyright notice above and the following disclaimer of warranties; and
14  * 2. No rights are granted, in any manner or form, to use Whistle
15  *    Communications, Inc. trademarks, including the mark "WHISTLE
16  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17  *    such appears in the above copyright notice or in the software.
18  *
19  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35  * OF SUCH DAMAGE.
36  *
37  * Author: Archie Cobbs <archie@freebsd.org>
38  *
39  * $FreeBSD: src/sys/netgraph/ng_async.c,v 1.6.2.5 2002/07/02 23:44:02 archie Exp $
40  * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
41  */
42 
43 /*
44  * This node type implements a PPP style sync <-> async converter.
45  * See RFC 1661 for details of how asynchronous encoding works.
46  */
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
51 #include <sys/mbuf.h>
52 #include <sys/malloc.h>
53 #include <sys/errno.h>
54 
55 #include <netgraph/ng_message.h>
56 #include <netgraph/netgraph.h>
57 #include "ng_async.h"
58 #include <netgraph/ng_parse.h>
59 
60 #include <net/ppp_layer/ppp_defs.h>
61 
62 /* Async decode state */
63 #define MODE_HUNT	0
64 #define MODE_NORMAL	1
65 #define MODE_ESC	2
66 
67 /* Private data structure */
68 struct ng_async_private {
69 	node_p  	node;		/* Our node */
70 	hook_p  	async;		/* Asynchronous side */
71 	hook_p  	sync;		/* Synchronous side */
72 	u_char  	amode;		/* Async hunt/esape mode */
73 	u_int16_t	fcs;		/* Decoded async FCS (so far) */
74 	u_char	       *abuf;		/* Buffer to encode sync into */
75 	u_char	       *sbuf;		/* Buffer to decode async into */
76 	u_int		slen;		/* Length of data in sbuf */
77 	long		lasttime;	/* Time of last async packet sent */
78 	struct		ng_async_cfg	cfg;	/* Configuration */
79 	struct		ng_async_stat	stats;	/* Statistics */
80 };
81 typedef struct ng_async_private *sc_p;
82 
83 /* Useful macros */
84 #define ASYNC_BUF_SIZE(smru)	(2 * (smru) + 10)
85 #define SYNC_BUF_SIZE(amru)	((amru) + 10)
86 #define ERROUT(x)		do { error = (x); goto done; } while (0)
87 
88 /* Netgraph methods */
89 static ng_constructor_t		nga_constructor;
90 static ng_rcvdata_t		nga_rcvdata;
91 static ng_rcvmsg_t		nga_rcvmsg;
92 static ng_shutdown_t		nga_shutdown;
93 static ng_newhook_t		nga_newhook;
94 static ng_disconnect_t		nga_disconnect;
95 
96 /* Helper stuff */
97 static int	nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta);
98 static int	nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta);
99 
100 /* Parse type for struct ng_async_cfg */
101 static const struct ng_parse_struct_field nga_config_type_fields[]
102 	= NG_ASYNC_CONFIG_TYPE_INFO;
103 static const struct ng_parse_type nga_config_type = {
104 	&ng_parse_struct_type,
105 	&nga_config_type_fields
106 };
107 
108 /* Parse type for struct ng_async_stat */
109 static const struct ng_parse_struct_field nga_stats_type_fields[]
110 	= NG_ASYNC_STATS_TYPE_INFO;
111 static const struct ng_parse_type nga_stats_type = {
112 	&ng_parse_struct_type,
113 	&nga_stats_type_fields
114 };
115 
116 /* List of commands and how to convert arguments to/from ASCII */
117 static const struct ng_cmdlist nga_cmdlist[] = {
118 	{
119 	  NGM_ASYNC_COOKIE,
120 	  NGM_ASYNC_CMD_SET_CONFIG,
121 	  "setconfig",
122 	  &nga_config_type,
123 	  NULL
124 	},
125 	{
126 	  NGM_ASYNC_COOKIE,
127 	  NGM_ASYNC_CMD_GET_CONFIG,
128 	  "getconfig",
129 	  NULL,
130 	  &nga_config_type
131 	},
132 	{
133 	  NGM_ASYNC_COOKIE,
134 	  NGM_ASYNC_CMD_GET_STATS,
135 	  "getstats",
136 	  NULL,
137 	  &nga_stats_type
138 	},
139 	{
140 	  NGM_ASYNC_COOKIE,
141 	  NGM_ASYNC_CMD_CLR_STATS,
142 	  "clrstats",
143 	  &nga_stats_type,
144 	  NULL
145 	},
146 	{ 0 }
147 };
148 
149 /* Define the netgraph node type */
150 static struct ng_type typestruct = {
151 	NG_VERSION,
152 	NG_ASYNC_NODE_TYPE,
153 	NULL,
154 	nga_constructor,
155 	nga_rcvmsg,
156 	nga_shutdown,
157 	nga_newhook,
158 	NULL,
159 	NULL,
160 	nga_rcvdata,
161 	nga_rcvdata,
162 	nga_disconnect,
163 	nga_cmdlist
164 };
165 NETGRAPH_INIT(async, &typestruct);
166 
167 /*
168  * CRC table
169  *
170  * Taken from RFC 1171 Appendix B
171  */
172 static const u_int16_t fcstab[256] = {
173 	 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
174 	 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
175 	 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
176 	 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
177 	 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
178 	 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
179 	 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
180 	 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
181 	 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
182 	 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
183 	 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
184 	 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
185 	 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
186 	 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
187 	 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
188 	 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
189 	 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
190 	 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
191 	 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
192 	 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
193 	 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
194 	 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
195 	 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
196 	 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
197 	 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
198 	 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
199 	 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
200 	 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
201 	 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
202 	 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
203 	 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
204 	 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
205 };
206 
207 /******************************************************************
208 		    NETGRAPH NODE METHODS
209 ******************************************************************/
210 
211 /*
212  * Initialize a new node
213  */
214 static int
215 nga_constructor(node_p *nodep)
216 {
217 	sc_p sc;
218 	int error;
219 
220 	if ((error = ng_make_node_common(&typestruct, nodep)))
221 		return (error);
222 	sc = kmalloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO);
223 	if (sc == NULL)
224 		return (ENOMEM);
225 	sc->amode = MODE_HUNT;
226 	sc->cfg.accm = ~0;
227 	sc->cfg.amru = NG_ASYNC_DEFAULT_MRU;
228 	sc->cfg.smru = NG_ASYNC_DEFAULT_MRU;
229 	sc->abuf = kmalloc(ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH, M_NOWAIT);
230 	if (sc->abuf == NULL)
231 		goto fail;
232 	sc->sbuf = kmalloc(SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH, M_NOWAIT);
233 	if (sc->sbuf == NULL) {
234 		kfree(sc->abuf, M_NETGRAPH);
235 fail:
236 		kfree(sc, M_NETGRAPH);
237 		return (ENOMEM);
238 	}
239 	(*nodep)->private = sc;
240 	sc->node = *nodep;
241 	return (0);
242 }
243 
244 /*
245  * Reserve a hook for a pending connection
246  */
247 static int
248 nga_newhook(node_p node, hook_p hook, const char *name)
249 {
250 	const sc_p sc = node->private;
251 	hook_p *hookp;
252 
253 	if (!strcmp(name, NG_ASYNC_HOOK_ASYNC))
254 		hookp = &sc->async;
255 	else if (!strcmp(name, NG_ASYNC_HOOK_SYNC))
256 		hookp = &sc->sync;
257 	else
258 		return (EINVAL);
259 	if (*hookp)
260 		return (EISCONN);
261 	*hookp = hook;
262 	return (0);
263 }
264 
265 /*
266  * Receive incoming data
267  */
268 static int
269 nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
270 {
271 	const sc_p sc = hook->node->private;
272 
273 	if (hook == sc->sync)
274 		return (nga_rcv_sync(sc, m, meta));
275 	if (hook == sc->async)
276 		return (nga_rcv_async(sc, m, meta));
277 	panic(__func__);
278 }
279 
280 /*
281  * Receive incoming control message
282  */
283 static int
284 nga_rcvmsg(node_p node, struct ng_mesg *msg,
285 	const char *rtn, struct ng_mesg **rptr)
286 {
287 	const sc_p sc = (sc_p) node->private;
288 	struct ng_mesg *resp = NULL;
289 	int error = 0;
290 
291 	switch (msg->header.typecookie) {
292 	case NGM_ASYNC_COOKIE:
293 		switch (msg->header.cmd) {
294 		case NGM_ASYNC_CMD_GET_STATS:
295 			NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
296 			if (resp == NULL)
297 				ERROUT(ENOMEM);
298 			*((struct ng_async_stat *) resp->data) = sc->stats;
299 			break;
300 		case NGM_ASYNC_CMD_CLR_STATS:
301 			bzero(&sc->stats, sizeof(sc->stats));
302 			break;
303 		case NGM_ASYNC_CMD_SET_CONFIG:
304 		    {
305 			struct ng_async_cfg *const cfg =
306 				(struct ng_async_cfg *) msg->data;
307 			u_char *buf;
308 
309 			if (msg->header.arglen != sizeof(*cfg))
310 				ERROUT(EINVAL);
311 			if (cfg->amru < NG_ASYNC_MIN_MRU
312 			    || cfg->amru > NG_ASYNC_MAX_MRU
313 			    || cfg->smru < NG_ASYNC_MIN_MRU
314 			    || cfg->smru > NG_ASYNC_MAX_MRU)
315 				ERROUT(EINVAL);
316 			cfg->enabled = !!cfg->enabled;	/* normalize */
317 			if (cfg->smru > sc->cfg.smru) {	/* reallocate buffer */
318 				buf = kmalloc(ASYNC_BUF_SIZE(cfg->smru),
319 					      M_NETGRAPH, M_NOWAIT);
320 				if (!buf)
321 					ERROUT(ENOMEM);
322 				kfree(sc->abuf, M_NETGRAPH);
323 				sc->abuf = buf;
324 			}
325 			if (cfg->amru > sc->cfg.amru) {	/* reallocate buffer */
326 				buf = kmalloc(SYNC_BUF_SIZE(cfg->amru),
327 					      M_NETGRAPH, M_NOWAIT);
328 				if (!buf)
329 					ERROUT(ENOMEM);
330 				kfree(sc->sbuf, M_NETGRAPH);
331 				sc->sbuf = buf;
332 				sc->amode = MODE_HUNT;
333 				sc->slen = 0;
334 			}
335 			if (!cfg->enabled) {
336 				sc->amode = MODE_HUNT;
337 				sc->slen = 0;
338 			}
339 			sc->cfg = *cfg;
340 			break;
341 		    }
342 		case NGM_ASYNC_CMD_GET_CONFIG:
343 			NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
344 			if (!resp)
345 				ERROUT(ENOMEM);
346 			*((struct ng_async_cfg *) resp->data) = sc->cfg;
347 			break;
348 		default:
349 			ERROUT(EINVAL);
350 		}
351 		break;
352 	default:
353 		ERROUT(EINVAL);
354 	}
355 	if (rptr)
356 		*rptr = resp;
357 	else if (resp)
358 		kfree(resp, M_NETGRAPH);
359 
360 done:
361 	kfree(msg, M_NETGRAPH);
362 	return (error);
363 }
364 
365 /*
366  * Shutdown this node
367  */
368 static int
369 nga_shutdown(node_p node)
370 {
371 	const sc_p sc = node->private;
372 
373 	ng_cutlinks(node);
374 	ng_unname(node);
375 	kfree(sc->abuf, M_NETGRAPH);
376 	kfree(sc->sbuf, M_NETGRAPH);
377 	bzero(sc, sizeof(*sc));
378 	kfree(sc, M_NETGRAPH);
379 	node->private = NULL;
380 	ng_unref(node);
381 	return (0);
382 }
383 
384 /*
385  * Lose a hook. When both hooks go away, we disappear.
386  */
387 static int
388 nga_disconnect(hook_p hook)
389 {
390 	const sc_p sc = hook->node->private;
391 	hook_p *hookp;
392 
393 	if (hook == sc->async)
394 		hookp = &sc->async;
395 	else if (hook == sc->sync)
396 		hookp = &sc->sync;
397 	else
398 		panic(__func__);
399 	if (!*hookp)
400 		panic("%s2", __func__);
401 	*hookp = NULL;
402 	bzero(&sc->stats, sizeof(sc->stats));
403 	sc->lasttime = 0;
404 	if (hook->node->numhooks == 0)
405 		ng_rmnode(hook->node);
406 	return (0);
407 }
408 
409 /******************************************************************
410 		    INTERNAL HELPER STUFF
411 ******************************************************************/
412 
413 /*
414  * Encode a byte into the async buffer
415  */
416 static __inline__ void
417 nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
418 {
419 	*fcs = PPP_FCS(*fcs, x);
420 	if ((x < 32 && ((1 << x) & accm))
421 	    || (x == PPP_ESCAPE)
422 	    || (x == PPP_FLAG)) {
423 		sc->abuf[(*len)++] = PPP_ESCAPE;
424 		x ^= PPP_TRANS;
425 	}
426 	sc->abuf[(*len)++] = x;
427 }
428 
429 /*
430  * Receive incoming synchronous data.
431  */
432 static int
433 nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta)
434 {
435 	struct ifnet *const rcvif = m->m_pkthdr.rcvif;
436 	int alen, error = 0;
437 	struct timeval time;
438 	u_int16_t fcs, fcs0;
439 	u_int32_t accm;
440 
441 #define ADD_BYTE(x)	nga_async_add(sc, &fcs, accm, &alen, (x))
442 
443 	/* Check for bypass mode */
444 	if (!sc->cfg.enabled) {
445 		NG_SEND_DATA(error, sc->async, m, meta);
446 		return (error);
447 	}
448 
449 	/* Get ACCM; special case LCP frames, which use full ACCM */
450 	accm = sc->cfg.accm;
451 	if (m->m_pkthdr.len >= 4) {
452 		static const u_char lcphdr[4] = {
453 		    PPP_ALLSTATIONS,
454 		    PPP_UI,
455 		    (u_char)(PPP_LCP >> 8),
456 		    (u_char)(PPP_LCP & 0xff)
457 		};
458 		u_char buf[4];
459 
460 		m_copydata(m, 0, 4, buf);
461 		if (bcmp(buf, &lcphdr, 4) == 0)
462 			accm = ~0;
463 	}
464 
465 	/* Check for overflow */
466 	if (m->m_pkthdr.len > sc->cfg.smru) {
467 		sc->stats.syncOverflows++;
468 		NG_FREE_DATA(m, meta);
469 		return (EMSGSIZE);
470 	}
471 
472 	/* Update stats */
473 	sc->stats.syncFrames++;
474 	sc->stats.syncOctets += m->m_pkthdr.len;
475 
476 	/* Initialize async encoded version of input mbuf */
477 	alen = 0;
478 	fcs = PPP_INITFCS;
479 
480 	/* Add beginning sync flag if it's been long enough to need one */
481 	getmicrotime(&time);
482 	if (time.tv_sec >= sc->lasttime + 1) {
483 		sc->abuf[alen++] = PPP_FLAG;
484 		sc->lasttime = time.tv_sec;
485 	}
486 
487 	/* Add packet payload */
488 	while (m != NULL) {
489 		while (m->m_len > 0) {
490 			ADD_BYTE(*mtod(m, u_char *));
491 			m->m_data++;
492 			m->m_len--;
493 		}
494 		m = m_free(m);
495 	}
496 
497 	/* Add checksum and final sync flag */
498 	fcs0 = fcs;
499 	ADD_BYTE(~fcs0 & 0xff);
500 	ADD_BYTE(~fcs0 >> 8);
501 	sc->abuf[alen++] = PPP_FLAG;
502 
503 	/* Put frame in an mbuf and ship it off */
504 	if (!(m = m_devget(sc->abuf, alen, 0, rcvif))) {
505 		NG_FREE_META(meta);
506 		error = ENOBUFS;
507 	} else {
508 		NG_SEND_DATA(error, sc->async, m, meta);
509 	}
510 	return (error);
511 }
512 
513 /*
514  * Receive incoming asynchronous data
515  * XXX Technically, we should strip out incoming characters
516  *     that are in our ACCM. Not sure if this is good or not.
517  */
518 static int
519 nga_rcv_async(const sc_p sc, struct mbuf * m, meta_p meta)
520 {
521 	struct ifnet *const rcvif = m->m_pkthdr.rcvif;
522 	int error;
523 
524 	if (!sc->cfg.enabled) {
525 		NG_SEND_DATA(error, sc->sync, m, meta);
526 		return (error);
527 	}
528 	NG_FREE_META(meta);
529 	while (m) {
530 		struct mbuf *n;
531 
532 		for (; m->m_len > 0; m->m_data++, m->m_len--) {
533 			u_char  ch = *mtod(m, u_char *);
534 
535 			sc->stats.asyncOctets++;
536 			if (ch == PPP_FLAG) {	/* Flag overrides everything */
537 				int     skip = 0;
538 
539 				/* Check for runts */
540 				if (sc->slen < 2) {
541 					if (sc->slen > 0)
542 						sc->stats.asyncRunts++;
543 					goto reset;
544 				}
545 
546 				/* Verify CRC */
547 				if (sc->fcs != PPP_GOODFCS) {
548 					sc->stats.asyncBadCheckSums++;
549 					goto reset;
550 				}
551 				sc->slen -= 2;
552 
553 				/* Strip address and control fields */
554 				if (sc->slen >= 2
555 				    && sc->sbuf[0] == PPP_ALLSTATIONS
556 				    && sc->sbuf[1] == PPP_UI)
557 					skip = 2;
558 
559 				/* Check for frame too big */
560 				if (sc->slen - skip > sc->cfg.amru) {
561 					sc->stats.asyncOverflows++;
562 					goto reset;
563 				}
564 
565 				/* OK, ship it out */
566 				if ((n = m_devget(sc->sbuf + skip,
567 						  sc->slen - skip,
568 						  0, rcvif))) {
569 					NG_SEND_DATA(error, sc->sync, n, meta);
570 				}
571 				sc->stats.asyncFrames++;
572 reset:
573 				sc->amode = MODE_NORMAL;
574 				sc->fcs = PPP_INITFCS;
575 				sc->slen = 0;
576 				continue;
577 			}
578 			switch (sc->amode) {
579 			case MODE_NORMAL:
580 				if (ch == PPP_ESCAPE) {
581 					sc->amode = MODE_ESC;
582 					continue;
583 				}
584 				break;
585 			case MODE_ESC:
586 				ch ^= PPP_TRANS;
587 				sc->amode = MODE_NORMAL;
588 				break;
589 			case MODE_HUNT:
590 			default:
591 				continue;
592 			}
593 
594 			/* Add byte to frame */
595 			if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
596 				sc->stats.asyncOverflows++;
597 				sc->amode = MODE_HUNT;
598 				sc->slen = 0;
599 			} else {
600 				sc->sbuf[sc->slen++] = ch;
601 				sc->fcs = PPP_FCS(sc->fcs, ch);
602 			}
603 		}
604 		m = m_free(m);
605 	}
606 	return (0);
607 }
608