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