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