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