5 * Copyright (c) 1996-1999 Whistle Communications, Inc.
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.
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
37 * Author: Archie Cobbs <archie@freebsd.org>
40 * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
44 * This node type implements a PPP style sync <-> async converter.
45 * See RFC 1661 for details of how asynchronous encoding works.
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
52 #include <sys/malloc.h>
53 #include <sys/errno.h>
55 #include <netgraph/ng_message.h>
56 #include <netgraph/netgraph.h>
57 #include <netgraph/ng_async.h>
58 #include <netgraph/ng_parse.h>
60 #include <net/ppp_defs.h>
62 #ifdef NG_SEPARATE_MALLOC
63 MALLOC_DEFINE(M_NETGRAPH_ASYNC, "netgraph_async", "netgraph async node ");
65 #define M_NETGRAPH_ASYNC M_NETGRAPH
69 /* Async decode state */
74 /* Private data structure */
75 struct ng_async_private {
76 node_p node; /* Our node */
77 hook_p async; /* Asynchronous side */
78 hook_p sync; /* Synchronous side */
79 u_char amode; /* Async hunt/esape mode */
80 u_int16_t fcs; /* Decoded async FCS (so far) */
81 u_char *abuf; /* Buffer to encode sync into */
82 u_char *sbuf; /* Buffer to decode async into */
83 u_int slen; /* Length of data in sbuf */
84 long lasttime; /* Time of last async packet sent */
85 struct ng_async_cfg cfg; /* Configuration */
86 struct ng_async_stat stats; /* Statistics */
88 typedef struct ng_async_private *sc_p;
91 #define ASYNC_BUF_SIZE(smru) (2 * (smru) + 10)
92 #define SYNC_BUF_SIZE(amru) ((amru) + 10)
93 #define ERROUT(x) do { error = (x); goto done; } while (0)
95 /* Netgraph methods */
96 static ng_constructor_t nga_constructor;
97 static ng_rcvdata_t nga_rcvdata;
98 static ng_rcvmsg_t nga_rcvmsg;
99 static ng_shutdown_t nga_shutdown;
100 static ng_newhook_t nga_newhook;
101 static ng_disconnect_t nga_disconnect;
104 static int nga_rcv_sync(const sc_p sc, item_p item);
105 static int nga_rcv_async(const sc_p sc, item_p item);
107 /* Parse type for struct ng_async_cfg */
108 static const struct ng_parse_struct_info
109 nga_config_type_info = NG_ASYNC_CONFIG_TYPE_INFO;
110 static const struct ng_parse_type nga_config_type = {
111 &ng_parse_struct_type,
112 &nga_config_type_info
115 /* Parse type for struct ng_async_stat */
116 static const struct ng_parse_struct_info
117 nga_stats_type_info = NG_ASYNC_STATS_TYPE_INFO;
118 static const struct ng_parse_type nga_stats_type = {
119 &ng_parse_struct_type,
120 &nga_stats_type_info,
123 /* List of commands and how to convert arguments to/from ASCII */
124 static const struct ng_cmdlist nga_cmdlist[] = {
127 NGM_ASYNC_CMD_SET_CONFIG,
134 NGM_ASYNC_CMD_GET_CONFIG,
141 NGM_ASYNC_CMD_GET_STATS,
148 NGM_ASYNC_CMD_CLR_STATS,
156 /* Define the netgraph node type */
157 static struct ng_type typestruct = {
171 NETGRAPH_INIT(async, &typestruct);
174 static const u_int16_t fcstab[];
176 /******************************************************************
177 NETGRAPH NODE METHODS
178 ******************************************************************/
181 * Initialize a new node
184 nga_constructor(node_p node)
188 MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH_ASYNC, M_NOWAIT | M_ZERO);
191 sc->amode = MODE_HUNT;
193 sc->cfg.amru = NG_ASYNC_DEFAULT_MRU;
194 sc->cfg.smru = NG_ASYNC_DEFAULT_MRU;
195 MALLOC(sc->abuf, u_char *,
196 ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH_ASYNC, M_NOWAIT);
197 if (sc->abuf == NULL)
199 MALLOC(sc->sbuf, u_char *,
200 SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH_ASYNC, M_NOWAIT);
201 if (sc->sbuf == NULL) {
202 FREE(sc->abuf, M_NETGRAPH_ASYNC);
204 FREE(sc, M_NETGRAPH_ASYNC);
207 NG_NODE_SET_PRIVATE(node, sc);
213 * Reserve a hook for a pending connection
216 nga_newhook(node_p node, hook_p hook, const char *name)
218 const sc_p sc = NG_NODE_PRIVATE(node);
221 if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) {
223 * We use a static buffer here so only one packet
224 * at a time can be allowed to travel in this direction.
225 * Force Writer semantics.
227 NG_HOOK_FORCE_WRITER(hook);
229 } else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) {
231 * We use a static state here so only one packet
232 * at a time can be allowed to travel in this direction.
233 * Force Writer semantics.
234 * Since we set this for both directions
235 * we might as well set it for the whole node
236 * bit I haven;t done that (yet).
238 NG_HOOK_FORCE_WRITER(hook);
243 if (*hookp) /* actually can't happen I think [JRE] */
250 * Receive incoming data
253 nga_rcvdata(hook_p hook, item_p item)
255 const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
257 if (hook == sc->sync)
258 return (nga_rcv_sync(sc, item));
259 if (hook == sc->async)
260 return (nga_rcv_async(sc, item));
265 * Receive incoming control message
268 nga_rcvmsg(node_p node, item_p item, hook_p lasthook)
270 const sc_p sc = NG_NODE_PRIVATE(node);
271 struct ng_mesg *resp = NULL;
275 NGI_GET_MSG(item, msg);
276 switch (msg->header.typecookie) {
277 case NGM_ASYNC_COOKIE:
278 switch (msg->header.cmd) {
279 case NGM_ASYNC_CMD_GET_STATS:
280 NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
283 *((struct ng_async_stat *) resp->data) = sc->stats;
285 case NGM_ASYNC_CMD_CLR_STATS:
286 bzero(&sc->stats, sizeof(sc->stats));
288 case NGM_ASYNC_CMD_SET_CONFIG:
290 struct ng_async_cfg *const cfg =
291 (struct ng_async_cfg *) msg->data;
294 if (msg->header.arglen != sizeof(*cfg))
296 if (cfg->amru < NG_ASYNC_MIN_MRU
297 || cfg->amru > NG_ASYNC_MAX_MRU
298 || cfg->smru < NG_ASYNC_MIN_MRU
299 || cfg->smru > NG_ASYNC_MAX_MRU)
301 cfg->enabled = !!cfg->enabled; /* normalize */
302 if (cfg->smru > sc->cfg.smru) { /* reallocate buffer */
303 MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru),
304 M_NETGRAPH_ASYNC, M_NOWAIT);
307 FREE(sc->abuf, M_NETGRAPH_ASYNC);
310 if (cfg->amru > sc->cfg.amru) { /* reallocate buffer */
311 MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru),
312 M_NETGRAPH_ASYNC, M_NOWAIT);
315 FREE(sc->sbuf, M_NETGRAPH_ASYNC);
317 sc->amode = MODE_HUNT;
321 sc->amode = MODE_HUNT;
327 case NGM_ASYNC_CMD_GET_CONFIG:
328 NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
331 *((struct ng_async_cfg *) resp->data) = sc->cfg;
341 NG_RESPOND_MSG(error, node, item, resp);
350 nga_shutdown(node_p node)
352 const sc_p sc = NG_NODE_PRIVATE(node);
354 FREE(sc->abuf, M_NETGRAPH_ASYNC);
355 FREE(sc->sbuf, M_NETGRAPH_ASYNC);
356 bzero(sc, sizeof(*sc));
357 FREE(sc, M_NETGRAPH_ASYNC);
358 NG_NODE_SET_PRIVATE(node, NULL);
364 * Lose a hook. When both hooks go away, we disappear.
367 nga_disconnect(hook_p hook)
369 const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
372 if (hook == sc->async)
374 else if (hook == sc->sync)
379 panic("%s 2", __func__);
381 bzero(&sc->stats, sizeof(sc->stats));
383 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
384 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
385 ng_rmnode_self(NG_HOOK_NODE(hook));
389 /******************************************************************
390 INTERNAL HELPER STUFF
391 ******************************************************************/
394 * Encode a byte into the async buffer
396 static __inline__ void
397 nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
399 *fcs = PPP_FCS(*fcs, x);
400 if ((x < 32 && ((1 << x) & accm))
402 || (x == PPP_FLAG)) {
403 sc->abuf[(*len)++] = PPP_ESCAPE;
406 sc->abuf[(*len)++] = x;
410 * Receive incoming synchronous data.
413 nga_rcv_sync(const sc_p sc, item_p item)
423 #define ADD_BYTE(x) nga_async_add(sc, &fcs, accm, &alen, (x))
425 /* Check for bypass mode */
426 if (!sc->cfg.enabled) {
427 NG_FWD_ITEM_HOOK(error, item, sc->async );
432 rcvif = m->m_pkthdr.rcvif;
434 /* Get ACCM; special case LCP frames, which use full ACCM */
436 if (m->m_pkthdr.len >= 4) {
437 static const u_char lcphdr[4] = {
440 (u_char)(PPP_LCP >> 8),
441 (u_char)(PPP_LCP & 0xff)
445 m_copydata(m, 0, 4, (caddr_t)buf);
446 if (bcmp(buf, &lcphdr, 4) == 0)
450 /* Check for overflow */
451 if (m->m_pkthdr.len > sc->cfg.smru) {
452 sc->stats.syncOverflows++;
459 sc->stats.syncFrames++;
460 sc->stats.syncOctets += m->m_pkthdr.len;
462 /* Initialize async encoded version of input mbuf */
466 /* Add beginning sync flag if it's been long enough to need one */
468 if (time.tv_sec >= sc->lasttime + 1) {
469 sc->abuf[alen++] = PPP_FLAG;
470 sc->lasttime = time.tv_sec;
473 /* Add packet payload */
475 while (m->m_len > 0) {
476 ADD_BYTE(*mtod(m, u_char *));
483 /* Add checksum and final sync flag */
485 ADD_BYTE(~fcs0 & 0xff);
486 ADD_BYTE(~fcs0 >> 8);
487 sc->abuf[alen++] = PPP_FLAG;
489 /* Put frame in an mbuf and ship it off */
490 if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) {
494 NG_FWD_NEW_DATA(error, item, sc->async, m);
500 * Receive incoming asynchronous data
501 * XXX Technically, we should strip out incoming characters
502 * that are in our ACCM. Not sure if this is good or not.
505 nga_rcv_async(const sc_p sc, item_p item)
511 if (!sc->cfg.enabled) {
512 NG_FWD_ITEM_HOOK(error, item, sc->sync);
516 rcvif = m->m_pkthdr.rcvif;
520 for (; m->m_len > 0; m->m_data++, m->m_len--) {
521 u_char ch = *mtod(m, u_char *);
523 sc->stats.asyncOctets++;
524 if (ch == PPP_FLAG) { /* Flag overrides everything */
527 /* Check for runts */
530 sc->stats.asyncRunts++;
535 if (sc->fcs != PPP_GOODFCS) {
536 sc->stats.asyncBadCheckSums++;
541 /* Strip address and control fields */
543 && sc->sbuf[0] == PPP_ALLSTATIONS
544 && sc->sbuf[1] == PPP_UI)
547 /* Check for frame too big */
548 if (sc->slen - skip > sc->cfg.amru) {
549 sc->stats.asyncOverflows++;
553 /* OK, ship it out */
554 if ((n = m_devget(sc->sbuf + skip,
555 sc->slen - skip, 0, rcvif, NULL))) {
556 if (item) { /* sets NULL -> item */
557 NG_FWD_NEW_DATA(error, item,
560 NG_SEND_DATA_ONLY(error,
564 sc->stats.asyncFrames++;
566 sc->amode = MODE_NORMAL;
567 sc->fcs = PPP_INITFCS;
573 if (ch == PPP_ESCAPE) {
574 sc->amode = MODE_ESC;
580 sc->amode = MODE_NORMAL;
587 /* Add byte to frame */
588 if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
589 sc->stats.asyncOverflows++;
590 sc->amode = MODE_HUNT;
593 sc->sbuf[sc->slen++] = ch;
594 sc->fcs = PPP_FCS(sc->fcs, ch);
607 * Taken from RFC 1171 Appendix B
609 static const u_int16_t fcstab[256] = {
610 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
611 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
612 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
613 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
614 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
615 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
616 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
617 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
618 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
619 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
620 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
621 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
622 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
623 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
624 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
625 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
626 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
627 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
628 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
629 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
630 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
631 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
632 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
633 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
634 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
635 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
636 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
637 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
638 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
639 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
640 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
641 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78