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