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