ng_async.c revision 52419
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 52419 1999-10-21 09:06:11Z julian $
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 */
76struct 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};
92typedef 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 */
100static int	nga_constructor(node_p *node);
101static int	nga_rcvdata(hook_p hook, struct mbuf *m, meta_p meta);
102static int	nga_rcvmsg(node_p node, struct ng_mesg *msg,
103		    const char *rtn, struct ng_mesg **resp);
104static int	nga_shutdown(node_p node);
105static int	nga_newhook(node_p node, hook_p hook, const char *name);
106static int	nga_disconnect(hook_p hook);
107
108/* Helper stuff */
109static int	nga_rcv_sync(const sc_p sc, struct mbuf *m, meta_p meta);
110static int	nga_rcv_async(const sc_p sc, struct mbuf *m, meta_p meta);
111
112/* Define the netgraph node type */
113static 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};
127NETGRAPH_INIT(async, &typestruct);
128
129/* CRC table */
130static const u_int16_t fcstab[];
131
132/******************************************************************
133		    NETGRAPH NODE METHODS
134******************************************************************/
135
136/*
137 * Initialize a new node
138 */
139static int
140nga_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);
163fail:
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 */
175static int
176nga_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 */
198static int
199nga_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 */
224static int
225nga_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
302done:
303	FREE(msg, M_NETGRAPH);
304	return (error);
305}
306
307/*
308 * Shutdown this node
309 */
310static int
311nga_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 */
329static int
330nga_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 */
362static __inline__ void
363nga_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 */
379static int
380nga_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 */
458static int
459nga_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++;
510reset:
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 */
553static 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