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