xref: /dragonfly/sys/netgraph/async/ng_async.c (revision 05d02a3813e2bef176c69d68035311fd2efbd031)
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
nga_constructor(node_p * nodep)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
nga_newhook(node_p node,hook_p hook,const char * name)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
nga_rcvdata(hook_p hook,struct mbuf * m,meta_p meta)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
nga_rcvmsg(node_p node,struct ng_mesg * msg,const char * rtn,struct ng_mesg ** rptr)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
nga_shutdown(node_p node)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
nga_disconnect(hook_p hook)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
nga_async_add(const sc_p sc,u_int16_t * fcs,u_int32_t accm,int * len,u_char x)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
nga_rcv_sync(const sc_p sc,struct mbuf * m,meta_p meta)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
nga_rcv_async(const sc_p sc,struct mbuf * m,meta_p meta)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