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