ng_async.c revision 52976
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@whistle.com> 38 * 39 * $FreeBSD: head/sys/netgraph/ng_async.c 52976 1999-11-08 03:10:20Z archie $ 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/conf.h> 52#include <sys/proc.h> 53#include <sys/mbuf.h> 54#include <sys/malloc.h> 55#include <sys/socket.h> 56#include <sys/file.h> 57#include <sys/tty.h> 58#include <sys/syslog.h> 59#include <sys/errno.h> 60 61#include <netgraph/ng_message.h> 62#include <netgraph/netgraph.h> 63#include <netgraph/ng_async.h> 64 65#include <net/ppp_defs.h> 66 67/* LCP protocol number */ 68#define PROTO_LCP 0xc021 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 */ 76struct 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}; 89typedef struct 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 */ 97static ng_constructor_t nga_constructor; 98static ng_rcvdata_t nga_rcvdata; 99static ng_rcvmsg_t nga_rcvmsg; 100static ng_shutdown_t nga_shutdown; 101static ng_newhook_t nga_newhook; 102static ng_disconnect_t nga_disconnect; 103 104/* Helper stuff */ 105static int nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta); 106static int nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta); 107 108/* Define the netgraph node type */ 109static struct ng_type typestruct = { 110 NG_VERSION, 111 NG_ASYNC_NODE_TYPE, 112 NULL, 113 nga_constructor, 114 nga_rcvmsg, 115 nga_shutdown, 116 nga_newhook, 117 NULL, 118 NULL, 119 nga_rcvdata, 120 nga_rcvdata, 121 nga_disconnect 122}; 123NETGRAPH_INIT(async, &typestruct); 124 125/* CRC table */ 126static const u_int16_t fcstab[]; 127 128/****************************************************************** 129 NETGRAPH NODE METHODS 130******************************************************************/ 131 132/* 133 * Initialize a new node 134 */ 135static int 136nga_constructor(node_p *nodep) 137{ 138 sc_p sc; 139 int error; 140 141 if ((error = ng_make_node_common(&typestruct, nodep))) 142 return (error); 143 MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK); 144 if (sc == NULL) 145 return (ENOMEM); 146 bzero(sc, sizeof(*sc)); 147 sc->amode = MODE_HUNT; 148 sc->cfg.accm = ~0; 149 sc->cfg.amru = NG_ASYNC_DEFAULT_MRU; 150 sc->cfg.smru = NG_ASYNC_DEFAULT_MRU; 151 MALLOC(sc->abuf, u_char *, 152 ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH, M_WAITOK); 153 if (sc->abuf == NULL) 154 goto fail; 155 MALLOC(sc->sbuf, u_char *, 156 SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH, M_WAITOK); 157 if (sc->sbuf == NULL) { 158 FREE(sc->abuf, M_NETGRAPH); 159fail: 160 FREE(sc, M_NETGRAPH); 161 return (ENOMEM); 162 } 163 (*nodep)->private = sc; 164 sc->node = *nodep; 165 return (0); 166} 167 168/* 169 * Reserve a hook for a pending connection 170 */ 171static int 172nga_newhook(node_p node, hook_p hook, const char *name) 173{ 174 const sc_p sc = node->private; 175 hook_p *hookp; 176 177 if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) 178 hookp = &sc->async; 179 else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) 180 hookp = &sc->sync; 181 else 182 return (EINVAL); 183 if (*hookp) 184 return (EISCONN); 185 *hookp = hook; 186 return (0); 187} 188 189/* 190 * Receive incoming data 191 */ 192static int 193nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 194{ 195 const sc_p sc = hook->node->private; 196 197 if (hook == sc->sync) 198 return (nga_rcv_sync(sc, m, meta)); 199 if (hook == sc->async) 200 return (nga_rcv_async(sc, m, meta)); 201 panic(__FUNCTION__); 202} 203 204/* 205 * Receive incoming control message 206 */ 207static int 208nga_rcvmsg(node_p node, struct ng_mesg *msg, 209 const char *rtn, struct ng_mesg **rptr) 210{ 211 const sc_p sc = (sc_p) node->private; 212 struct ng_mesg *resp = NULL; 213 int error = 0; 214 215 switch (msg->header.typecookie) { 216 case NGM_ASYNC_COOKIE: 217 switch (msg->header.cmd) { 218 case NGM_ASYNC_CMD_GET_STATS: 219 NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT); 220 if (resp == NULL) 221 ERROUT(ENOMEM); 222 *((struct ng_async_stat *) resp->data) = sc->stats; 223 break; 224 case NGM_ASYNC_CMD_CLR_STATS: 225 bzero(&sc->stats, sizeof(sc->stats)); 226 break; 227 case NGM_ASYNC_CMD_SET_CONFIG: 228 { 229 struct ng_async_cfg *const cfg = 230 (struct ng_async_cfg *) msg->data; 231 u_char *buf; 232 233 if (msg->header.arglen != sizeof(*cfg)) 234 ERROUT(EINVAL); 235 if (cfg->amru < NG_ASYNC_MIN_MRU 236 || cfg->amru > NG_ASYNC_MAX_MRU 237 || cfg->smru < NG_ASYNC_MIN_MRU 238 || cfg->smru > NG_ASYNC_MAX_MRU) 239 ERROUT(EINVAL); 240 cfg->enabled = !!cfg->enabled; /* normalize */ 241 cfg->acfcomp = !!cfg->acfcomp; /* normalize */ 242 if (cfg->smru > sc->cfg.smru) { /* reallocate buffer */ 243 MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru), 244 M_NETGRAPH, M_NOWAIT); 245 if (!buf) 246 ERROUT(ENOMEM); 247 FREE(sc->abuf, M_NETGRAPH); 248 sc->abuf = buf; 249 } 250 if (cfg->amru > sc->cfg.amru) { /* reallocate buffer */ 251 MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru), 252 M_NETGRAPH, M_NOWAIT); 253 if (!buf) 254 ERROUT(ENOMEM); 255 FREE(sc->sbuf, M_NETGRAPH); 256 sc->sbuf = buf; 257 sc->amode = MODE_HUNT; 258 sc->slen = 0; 259 } 260 if (!cfg->enabled) { 261 sc->amode = MODE_HUNT; 262 sc->slen = 0; 263 } 264 sc->cfg = *cfg; 265 break; 266 } 267 case NGM_ASYNC_CMD_GET_CONFIG: 268 NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT); 269 if (!resp) 270 ERROUT(ENOMEM); 271 *((struct ng_async_cfg *) resp->data) = sc->cfg; 272 break; 273 default: 274 ERROUT(EINVAL); 275 } 276 break; 277 default: 278 ERROUT(EINVAL); 279 } 280 if (rptr) 281 *rptr = resp; 282 else if (resp) 283 FREE(resp, M_NETGRAPH); 284 285done: 286 FREE(msg, M_NETGRAPH); 287 return (error); 288} 289 290/* 291 * Shutdown this node 292 */ 293static int 294nga_shutdown(node_p node) 295{ 296 const sc_p sc = node->private; 297 298 ng_cutlinks(node); 299 ng_unname(node); 300 FREE(sc->abuf, M_NETGRAPH); 301 FREE(sc->sbuf, M_NETGRAPH); 302 bzero(sc, sizeof(*sc)); 303 FREE(sc, M_NETGRAPH); 304 node->private = NULL; 305 ng_unref(node); 306 return (0); 307} 308 309/* 310 * Lose a hook. When both hooks go away, we disappear. 311 */ 312static int 313nga_disconnect(hook_p hook) 314{ 315 const sc_p sc = hook->node->private; 316 hook_p *hookp; 317 318 if (hook == sc->async) 319 hookp = &sc->async; 320 else if (hook == sc->sync) 321 hookp = &sc->sync; 322 else 323 panic(__FUNCTION__); 324 if (!*hookp) 325 panic(__FUNCTION__ "2"); 326 *hookp = NULL; 327 bzero(&sc->stats, sizeof(sc->stats)); 328 sc->lasttime = 0; 329 if (hook->node->numhooks == 0) 330 ng_rmnode(hook->node); 331 return (0); 332} 333 334/****************************************************************** 335 INTERNAL HELPER STUFF 336******************************************************************/ 337 338/* 339 * Encode a byte into the async buffer 340 */ 341static __inline__ void 342nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x) 343{ 344 *fcs = PPP_FCS(*fcs, x); 345 if ((x < 32 && ((1 << x) & accm)) 346 || (x == PPP_ESCAPE) 347 || (x == PPP_FLAG)) { 348 sc->abuf[(*len)++] = PPP_ESCAPE; 349 x ^= PPP_TRANS; 350 } 351 sc->abuf[(*len)++] = x; 352} 353 354/* 355 * Receive incoming synchronous data. 356 */ 357static int 358nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta) 359{ 360 struct ifnet *const rcvif = m->m_pkthdr.rcvif; 361 int acfcomp, alen, error = 0; 362 struct timeval time; 363 u_int16_t fcs, fcs0; 364 u_int32_t accm; 365 366#define ADD_BYTE(x) nga_async_add(sc, &fcs, accm, &alen, (x)) 367 368 /* Check for bypass mode */ 369 if (!sc->cfg.enabled) { 370 NG_SEND_DATA(error, sc->async, m, meta); 371 return (error); 372 } 373 374 /* Defaults for ACF compression and ACCM */ 375 accm = sc->cfg.accm; 376 acfcomp = sc->cfg.acfcomp; 377 378 /* Special case LCP frames: disable ACF and enable ACCM */ 379 { 380 struct mbuf *n = m; 381 int off, proto; 382 383 for (proto = off = 0; (proto & 1) == 0; off++) { 384 while (n != NULL && off >= n->m_len) { 385 n = n->m_next; 386 off = 0; 387 } 388 if (n == NULL) 389 break; 390 proto = (proto << 8) | mtod(n, u_char *)[off]; 391 } 392 if (proto == PROTO_LCP) { 393 accm = ~0; 394 acfcomp = 0; 395 } 396 } 397 398 /* Check for overflow */ 399 if (m->m_pkthdr.len > sc->cfg.smru) { 400 sc->stats.syncOverflows++; 401 NG_FREE_DATA(m, meta); 402 return (EMSGSIZE); 403 } 404 405 /* Update stats */ 406 sc->stats.syncFrames++; 407 sc->stats.syncOctets += m->m_pkthdr.len; 408 409 /* Initialize async encoded version of input mbuf */ 410 alen = 0; 411 fcs = PPP_INITFCS; 412 413 /* Add beginning sync flag if it's been long enough to need one */ 414 getmicrotime(&time); 415 if (time.tv_sec >= sc->lasttime + 1) { 416 sc->abuf[alen++] = PPP_FLAG; 417 sc->lasttime = time.tv_sec; 418 } 419 420 /* Add option address and control fields, then packet payload */ 421 if (!acfcomp) { 422 ADD_BYTE(PPP_ALLSTATIONS); 423 ADD_BYTE(PPP_UI); 424 } 425 while (m != NULL) { 426 struct mbuf *n; 427 428 while (m->m_len > 0) { 429 ADD_BYTE(*mtod(m, u_char *)); 430 m->m_data++; 431 m->m_len--; 432 } 433 MFREE(m, n); 434 m = n; 435 } 436 437 /* Add checksum and final sync flag */ 438 fcs0 = fcs; 439 ADD_BYTE(~fcs0 & 0xff); 440 ADD_BYTE(~fcs0 >> 8); 441 sc->abuf[alen++] = PPP_FLAG; 442 443 /* Put frame in an mbuf and ship it off */ 444 if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) { 445 NG_FREE_META(meta); 446 error = ENOBUFS; 447 } else 448 NG_SEND_DATA(error, sc->async, m, meta); 449 return (error); 450} 451 452/* 453 * Receive incoming asynchronous data 454 * XXX Technically, we should strip out incoming characters 455 * that are in our ACCM. Not sure if this is good or not. 456 */ 457static int 458nga_rcv_async(const sc_p sc, struct mbuf * m, meta_p meta) 459{ 460 struct ifnet *const rcvif = m->m_pkthdr.rcvif; 461 int error; 462 463 if (!sc->cfg.enabled) { 464 NG_SEND_DATA(error, sc->sync, m, meta); 465 return (error); 466 } 467 NG_FREE_META(meta); 468 while (m) { 469 struct mbuf *n; 470 471 for (; m->m_len > 0; m->m_data++, m->m_len--) { 472 u_char ch = *mtod(m, u_char *); 473 474 sc->stats.asyncOctets++; 475 if (ch == PPP_FLAG) { /* Flag overrides everything */ 476 int skip = 0; 477 478 /* Check for runts */ 479 if (sc->slen < 2) { 480 if (sc->slen > 0) 481 sc->stats.asyncRunts++; 482 goto reset; 483 } 484 485 /* Verify CRC */ 486 if (sc->fcs != PPP_GOODFCS) { 487 sc->stats.asyncBadCheckSums++; 488 goto reset; 489 } 490 sc->slen -= 2; 491 492 /* Strip address and control fields */ 493 if (sc->slen >= 2 494 && sc->sbuf[0] == PPP_ALLSTATIONS 495 && sc->sbuf[1] == PPP_UI) 496 skip = 2; 497 498 /* Check for frame too big */ 499 if (sc->slen - skip > sc->cfg.amru) { 500 sc->stats.asyncOverflows++; 501 goto reset; 502 } 503 504 /* OK, ship it out */ 505 if ((n = m_devget(sc->sbuf + skip, 506 sc->slen - skip, 0, rcvif, NULL))) 507 NG_SEND_DATA(error, sc->sync, n, meta); 508 sc->stats.asyncFrames++; 509reset: 510 sc->amode = MODE_NORMAL; 511 sc->fcs = PPP_INITFCS; 512 sc->slen = 0; 513 continue; 514 } 515 switch (sc->amode) { 516 case MODE_NORMAL: 517 if (ch == PPP_ESCAPE) { 518 sc->amode = MODE_ESC; 519 continue; 520 } 521 break; 522 case MODE_ESC: 523 ch ^= PPP_TRANS; 524 sc->amode = MODE_NORMAL; 525 break; 526 case MODE_HUNT: 527 default: 528 continue; 529 } 530 531 /* Add byte to frame */ 532 if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) { 533 sc->stats.asyncOverflows++; 534 sc->amode = MODE_HUNT; 535 sc->slen = 0; 536 } else { 537 sc->sbuf[sc->slen++] = ch; 538 sc->fcs = PPP_FCS(sc->fcs, ch); 539 } 540 } 541 MFREE(m, n); 542 m = n; 543 } 544 return (0); 545} 546 547/* 548 * CRC table 549 * 550 * Taken from RFC 1171 Appendix B 551 */ 552static const u_int16_t fcstab[256] = { 553 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 554 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 555 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 556 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 557 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 558 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 559 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 560 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 561 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 562 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 563 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 564 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 565 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 566 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 567 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 568 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 569 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 570 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 571 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 572 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 573 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 574 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 575 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 576 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 577 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 578 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 579 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 580 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 581 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 582 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 583 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 584 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 585}; 586