ng_async.c revision 139823
197403Sobrien/*
297403Sobrien * ng_async.c
397403Sobrien */
497403Sobrien
597403Sobrien/*-
697403Sobrien * Copyright (c) 1996-1999 Whistle Communications, Inc.
797403Sobrien * All rights reserved.
897403Sobrien *
997403Sobrien * Subject to the following obligations and disclaimer of warranty, use and
1097403Sobrien * redistribution of this software, in source or object code forms, with or
1197403Sobrien * without modifications are expressly permitted by Whistle Communications;
1297403Sobrien * provided, however, that:
1397403Sobrien * 1. Any and all reproductions of the source or object code must include the
1497403Sobrien *    copyright notice above and the following disclaimer of warranties; and
1597403Sobrien * 2. No rights are granted, in any manner or form, to use Whistle
1697403Sobrien *    Communications, Inc. trademarks, including the mark "WHISTLE
1797403Sobrien *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1897403Sobrien *    such appears in the above copyright notice or in the software.
1997403Sobrien *
2097403Sobrien * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2197403Sobrien * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2297403Sobrien * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2397403Sobrien * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2497403Sobrien * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2597403Sobrien * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2697403Sobrien * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2797403Sobrien * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28132720Skan * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29132720Skan * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3097403Sobrien * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3197403Sobrien * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3297403Sobrien * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3397403Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34132720Skan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3597403Sobrien * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3697403Sobrien * OF SUCH DAMAGE.
3797403Sobrien *
3897403Sobrien * Author: Archie Cobbs <archie@freebsd.org>
39 *
40 * $FreeBSD: head/sys/netgraph/ng_async.c 139823 2005-01-07 01:45:51Z imp $
41 * $Whistle: ng_async.c,v 1.17 1999/11/01 09:24:51 julian Exp $
42 */
43
44/*
45 * This node type implements a PPP style sync <-> async converter.
46 * See RFC 1661 for details of how asynchronous encoding works.
47 */
48
49#include <sys/param.h>
50#include <sys/systm.h>
51#include <sys/kernel.h>
52#include <sys/mbuf.h>
53#include <sys/malloc.h>
54#include <sys/errno.h>
55
56#include <netgraph/ng_message.h>
57#include <netgraph/netgraph.h>
58#include <netgraph/ng_async.h>
59#include <netgraph/ng_parse.h>
60
61#include <net/ppp_defs.h>
62
63#ifdef NG_SEPARATE_MALLOC
64MALLOC_DEFINE(M_NETGRAPH_ASYNC, "netgraph_async", "netgraph async node ");
65#else
66#define M_NETGRAPH_ASYNC M_NETGRAPH
67#endif
68
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 ng_async_private {
77	node_p  	node;		/* Our node */
78	hook_p  	async;		/* Asynchronous side */
79	hook_p  	sync;		/* Synchronous side */
80	u_char  	amode;		/* Async hunt/esape mode */
81	u_int16_t	fcs;		/* Decoded async FCS (so far) */
82	u_char	       *abuf;		/* Buffer to encode sync into */
83	u_char	       *sbuf;		/* Buffer to decode async into */
84	u_int		slen;		/* Length of data in sbuf */
85	long		lasttime;	/* Time of last async packet sent */
86	struct		ng_async_cfg	cfg;	/* Configuration */
87	struct		ng_async_stat	stats;	/* Statistics */
88};
89typedef struct ng_async_private *sc_p;
90
91/* Useful macros */
92#define ASYNC_BUF_SIZE(smru)	(2 * (smru) + 10)
93#define SYNC_BUF_SIZE(amru)	((amru) + 10)
94#define ERROUT(x)		do { error = (x); goto done; } while (0)
95
96/* Netgraph methods */
97static ng_constructor_t		nga_constructor;
98static ng_rcvdata_t		nga_rcvdata;
99static ng_rcvmsg_t		nga_rcvmsg;
100static ng_shutdown_t		nga_shutdown;
101static ng_newhook_t		nga_newhook;
102static ng_disconnect_t		nga_disconnect;
103
104/* Helper stuff */
105static int	nga_rcv_sync(const sc_p sc, item_p item);
106static int	nga_rcv_async(const sc_p sc, item_p item);
107
108/* Parse type for struct ng_async_cfg */
109static const struct ng_parse_struct_field nga_config_type_fields[]
110	= NG_ASYNC_CONFIG_TYPE_INFO;
111static const struct ng_parse_type nga_config_type = {
112	&ng_parse_struct_type,
113	&nga_config_type_fields
114};
115
116/* Parse type for struct ng_async_stat */
117static const struct ng_parse_struct_field nga_stats_type_fields[]
118	= NG_ASYNC_STATS_TYPE_INFO;
119static const struct ng_parse_type nga_stats_type = {
120	&ng_parse_struct_type,
121	&nga_stats_type_fields
122};
123
124/* List of commands and how to convert arguments to/from ASCII */
125static const struct ng_cmdlist nga_cmdlist[] = {
126	{
127	  NGM_ASYNC_COOKIE,
128	  NGM_ASYNC_CMD_SET_CONFIG,
129	  "setconfig",
130	  &nga_config_type,
131	  NULL
132	},
133	{
134	  NGM_ASYNC_COOKIE,
135	  NGM_ASYNC_CMD_GET_CONFIG,
136	  "getconfig",
137	  NULL,
138	  &nga_config_type
139	},
140	{
141	  NGM_ASYNC_COOKIE,
142	  NGM_ASYNC_CMD_GET_STATS,
143	  "getstats",
144	  NULL,
145	  &nga_stats_type
146	},
147	{
148	  NGM_ASYNC_COOKIE,
149	  NGM_ASYNC_CMD_CLR_STATS,
150	  "clrstats",
151	  &nga_stats_type,
152	  NULL
153	},
154	{ 0 }
155};
156
157/* Define the netgraph node type */
158static struct ng_type typestruct = {
159	.version =	NG_ABI_VERSION,
160	.name =		NG_ASYNC_NODE_TYPE,
161	.constructor =	nga_constructor,
162	.rcvmsg =	nga_rcvmsg,
163	.shutdown = 	nga_shutdown,
164	.newhook =	nga_newhook,
165	.rcvdata =	nga_rcvdata,
166	.disconnect =	nga_disconnect,
167	.cmdlist =	nga_cmdlist
168};
169NETGRAPH_INIT(async, &typestruct);
170
171/* CRC table */
172static const u_int16_t fcstab[];
173
174/******************************************************************
175		    NETGRAPH NODE METHODS
176******************************************************************/
177
178/*
179 * Initialize a new node
180 */
181static int
182nga_constructor(node_p node)
183{
184	sc_p sc;
185
186	MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH_ASYNC, M_NOWAIT | M_ZERO);
187	if (sc == NULL)
188		return (ENOMEM);
189	sc->amode = MODE_HUNT;
190	sc->cfg.accm = ~0;
191	sc->cfg.amru = NG_ASYNC_DEFAULT_MRU;
192	sc->cfg.smru = NG_ASYNC_DEFAULT_MRU;
193	MALLOC(sc->abuf, u_char *,
194	    ASYNC_BUF_SIZE(sc->cfg.smru), M_NETGRAPH_ASYNC, M_NOWAIT);
195	if (sc->abuf == NULL)
196		goto fail;
197	MALLOC(sc->sbuf, u_char *,
198	    SYNC_BUF_SIZE(sc->cfg.amru), M_NETGRAPH_ASYNC, M_NOWAIT);
199	if (sc->sbuf == NULL) {
200		FREE(sc->abuf, M_NETGRAPH_ASYNC);
201fail:
202		FREE(sc, M_NETGRAPH_ASYNC);
203		return (ENOMEM);
204	}
205	NG_NODE_SET_PRIVATE(node, sc);
206	sc->node = node;
207	return (0);
208}
209
210/*
211 * Reserve a hook for a pending connection
212 */
213static int
214nga_newhook(node_p node, hook_p hook, const char *name)
215{
216	const sc_p sc = NG_NODE_PRIVATE(node);
217	hook_p *hookp;
218
219	if (!strcmp(name, NG_ASYNC_HOOK_ASYNC)) {
220		/*
221		 * We use a static buffer here so only one packet
222		 * at a time can be allowed to travel in this direction.
223		 * Force Writer semantics.
224		 */
225		NG_HOOK_FORCE_WRITER(hook);
226		hookp = &sc->async;
227	} else if (!strcmp(name, NG_ASYNC_HOOK_SYNC)) {
228		/*
229		 * We use a static state here so only one packet
230		 * at a time can be allowed to travel in this direction.
231		 * Force Writer semantics.
232		 * Since we set this for both directions
233		 * we might as well set it for the whole node
234		 * bit I haven;t done that (yet).
235		 */
236		NG_HOOK_FORCE_WRITER(hook);
237		hookp = &sc->sync;
238	} else {
239		return (EINVAL);
240	}
241	if (*hookp) /* actually can't happen I think [JRE] */
242		return (EISCONN);
243	*hookp = hook;
244	return (0);
245}
246
247/*
248 * Receive incoming data
249 */
250static int
251nga_rcvdata(hook_p hook, item_p item)
252{
253	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
254
255	if (hook == sc->sync)
256		return (nga_rcv_sync(sc, item));
257	if (hook == sc->async)
258		return (nga_rcv_async(sc, item));
259	panic(__func__);
260}
261
262/*
263 * Receive incoming control message
264 */
265static int
266nga_rcvmsg(node_p node, item_p item, hook_p lasthook)
267{
268	const sc_p sc = NG_NODE_PRIVATE(node);
269	struct ng_mesg *resp = NULL;
270	int error = 0;
271	struct ng_mesg *msg;
272
273	NGI_GET_MSG(item, msg);
274	switch (msg->header.typecookie) {
275	case NGM_ASYNC_COOKIE:
276		switch (msg->header.cmd) {
277		case NGM_ASYNC_CMD_GET_STATS:
278			NG_MKRESPONSE(resp, msg, sizeof(sc->stats), M_NOWAIT);
279			if (resp == NULL)
280				ERROUT(ENOMEM);
281			*((struct ng_async_stat *) resp->data) = sc->stats;
282			break;
283		case NGM_ASYNC_CMD_CLR_STATS:
284			bzero(&sc->stats, sizeof(sc->stats));
285			break;
286		case NGM_ASYNC_CMD_SET_CONFIG:
287		    {
288			struct ng_async_cfg *const cfg =
289				(struct ng_async_cfg *) msg->data;
290			u_char *buf;
291
292			if (msg->header.arglen != sizeof(*cfg))
293				ERROUT(EINVAL);
294			if (cfg->amru < NG_ASYNC_MIN_MRU
295			    || cfg->amru > NG_ASYNC_MAX_MRU
296			    || cfg->smru < NG_ASYNC_MIN_MRU
297			    || cfg->smru > NG_ASYNC_MAX_MRU)
298				ERROUT(EINVAL);
299			cfg->enabled = !!cfg->enabled;	/* normalize */
300			if (cfg->smru > sc->cfg.smru) {	/* reallocate buffer */
301				MALLOC(buf, u_char *, ASYNC_BUF_SIZE(cfg->smru),
302				    M_NETGRAPH_ASYNC, M_NOWAIT);
303				if (!buf)
304					ERROUT(ENOMEM);
305				FREE(sc->abuf, M_NETGRAPH_ASYNC);
306				sc->abuf = buf;
307			}
308			if (cfg->amru > sc->cfg.amru) {	/* reallocate buffer */
309				MALLOC(buf, u_char *, SYNC_BUF_SIZE(cfg->amru),
310				    M_NETGRAPH_ASYNC, M_NOWAIT);
311				if (!buf)
312					ERROUT(ENOMEM);
313				FREE(sc->sbuf, M_NETGRAPH_ASYNC);
314				sc->sbuf = buf;
315				sc->amode = MODE_HUNT;
316				sc->slen = 0;
317			}
318			if (!cfg->enabled) {
319				sc->amode = MODE_HUNT;
320				sc->slen = 0;
321			}
322			sc->cfg = *cfg;
323			break;
324		    }
325		case NGM_ASYNC_CMD_GET_CONFIG:
326			NG_MKRESPONSE(resp, msg, sizeof(sc->cfg), M_NOWAIT);
327			if (!resp)
328				ERROUT(ENOMEM);
329			*((struct ng_async_cfg *) resp->data) = sc->cfg;
330			break;
331		default:
332			ERROUT(EINVAL);
333		}
334		break;
335	default:
336		ERROUT(EINVAL);
337	}
338done:
339	NG_RESPOND_MSG(error, node, item, resp);
340	NG_FREE_MSG(msg);
341	return (error);
342}
343
344/*
345 * Shutdown this node
346 */
347static int
348nga_shutdown(node_p node)
349{
350	const sc_p sc = NG_NODE_PRIVATE(node);
351
352	FREE(sc->abuf, M_NETGRAPH_ASYNC);
353	FREE(sc->sbuf, M_NETGRAPH_ASYNC);
354	bzero(sc, sizeof(*sc));
355	FREE(sc, M_NETGRAPH_ASYNC);
356	NG_NODE_SET_PRIVATE(node, NULL);
357	NG_NODE_UNREF(node);
358	return (0);
359}
360
361/*
362 * Lose a hook. When both hooks go away, we disappear.
363 */
364static int
365nga_disconnect(hook_p hook)
366{
367	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
368	hook_p *hookp;
369
370	if (hook == sc->async)
371		hookp = &sc->async;
372	else if (hook == sc->sync)
373		hookp = &sc->sync;
374	else
375		panic(__func__);
376	if (!*hookp)
377		panic("%s 2", __func__);
378	*hookp = NULL;
379	bzero(&sc->stats, sizeof(sc->stats));
380	sc->lasttime = 0;
381	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
382	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
383		ng_rmnode_self(NG_HOOK_NODE(hook));
384	return (0);
385}
386
387/******************************************************************
388		    INTERNAL HELPER STUFF
389******************************************************************/
390
391/*
392 * Encode a byte into the async buffer
393 */
394static __inline void
395nga_async_add(const sc_p sc, u_int16_t *fcs, u_int32_t accm, int *len, u_char x)
396{
397	*fcs = PPP_FCS(*fcs, x);
398	if ((x < 32 && ((1 << x) & accm))
399	    || (x == PPP_ESCAPE)
400	    || (x == PPP_FLAG)) {
401		sc->abuf[(*len)++] = PPP_ESCAPE;
402		x ^= PPP_TRANS;
403	}
404	sc->abuf[(*len)++] = x;
405}
406
407/*
408 * Receive incoming synchronous data.
409 */
410static int
411nga_rcv_sync(const sc_p sc, item_p item)
412{
413	struct ifnet *rcvif;
414	int alen, error = 0;
415	struct timeval time;
416	u_int16_t fcs, fcs0;
417	u_int32_t accm;
418	struct mbuf *m;
419
420
421#define ADD_BYTE(x)	nga_async_add(sc, &fcs, accm, &alen, (x))
422
423	/* Check for bypass mode */
424	if (!sc->cfg.enabled) {
425		NG_FWD_ITEM_HOOK(error, item, sc->async );
426		return (error);
427	}
428	NGI_GET_M(item, m);
429
430	rcvif = m->m_pkthdr.rcvif;
431
432	/* Get ACCM; special case LCP frames, which use full ACCM */
433	accm = sc->cfg.accm;
434	if (m->m_pkthdr.len >= 4) {
435		static const u_char lcphdr[4] = {
436		    PPP_ALLSTATIONS,
437		    PPP_UI,
438		    (u_char)(PPP_LCP >> 8),
439		    (u_char)(PPP_LCP & 0xff)
440		};
441		u_char buf[4];
442
443		m_copydata(m, 0, 4, (caddr_t)buf);
444		if (bcmp(buf, &lcphdr, 4) == 0)
445			accm = ~0;
446	}
447
448	/* Check for overflow */
449	if (m->m_pkthdr.len > sc->cfg.smru) {
450		sc->stats.syncOverflows++;
451		NG_FREE_M(m);
452		NG_FREE_ITEM(item);
453		return (EMSGSIZE);
454	}
455
456	/* Update stats */
457	sc->stats.syncFrames++;
458	sc->stats.syncOctets += m->m_pkthdr.len;
459
460	/* Initialize async encoded version of input mbuf */
461	alen = 0;
462	fcs = PPP_INITFCS;
463
464	/* Add beginning sync flag if it's been long enough to need one */
465	getmicrotime(&time);
466	if (time.tv_sec >= sc->lasttime + 1) {
467		sc->abuf[alen++] = PPP_FLAG;
468		sc->lasttime = time.tv_sec;
469	}
470
471	/* Add packet payload */
472	while (m != NULL) {
473		while (m->m_len > 0) {
474			ADD_BYTE(*mtod(m, u_char *));
475			m->m_data++;
476			m->m_len--;
477		}
478		m = m_free(m);
479	}
480
481	/* Add checksum and final sync flag */
482	fcs0 = fcs;
483	ADD_BYTE(~fcs0 & 0xff);
484	ADD_BYTE(~fcs0 >> 8);
485	sc->abuf[alen++] = PPP_FLAG;
486
487	/* Put frame in an mbuf and ship it off */
488	if (!(m = m_devget(sc->abuf, alen, 0, rcvif, NULL))) {
489		NG_FREE_ITEM(item);
490		error = ENOBUFS;
491	} else {
492		NG_FWD_NEW_DATA(error, item, sc->async, m);
493	}
494	return (error);
495}
496
497/*
498 * Receive incoming asynchronous data
499 * XXX Technically, we should strip out incoming characters
500 *     that are in our ACCM. Not sure if this is good or not.
501 */
502static int
503nga_rcv_async(const sc_p sc, item_p item)
504{
505	struct ifnet *rcvif;
506	int error;
507	struct mbuf *m;
508
509	if (!sc->cfg.enabled) {
510		NG_FWD_ITEM_HOOK(error, item,  sc->sync);
511		return (error);
512	}
513	NGI_GET_M(item, m);
514	rcvif = m->m_pkthdr.rcvif;
515	while (m) {
516		struct mbuf *n;
517
518		for (; m->m_len > 0; m->m_data++, m->m_len--) {
519			u_char  ch = *mtod(m, u_char *);
520
521			sc->stats.asyncOctets++;
522			if (ch == PPP_FLAG) {	/* Flag overrides everything */
523				int     skip = 0;
524
525				/* Check for runts */
526				if (sc->slen < 2) {
527					if (sc->slen > 0)
528						sc->stats.asyncRunts++;
529					goto reset;
530				}
531
532				/* Verify CRC */
533				if (sc->fcs != PPP_GOODFCS) {
534					sc->stats.asyncBadCheckSums++;
535					goto reset;
536				}
537				sc->slen -= 2;
538
539				/* Strip address and control fields */
540				if (sc->slen >= 2
541				    && sc->sbuf[0] == PPP_ALLSTATIONS
542				    && sc->sbuf[1] == PPP_UI)
543					skip = 2;
544
545				/* Check for frame too big */
546				if (sc->slen - skip > sc->cfg.amru) {
547					sc->stats.asyncOverflows++;
548					goto reset;
549				}
550
551				/* OK, ship it out */
552				if ((n = m_devget(sc->sbuf + skip,
553					   sc->slen - skip, 0, rcvif, NULL))) {
554					if (item) { /* sets NULL -> item */
555						NG_FWD_NEW_DATA(error, item,
556							sc->sync, n);
557					} else {
558						NG_SEND_DATA_ONLY(error,
559							sc->sync ,n);
560					}
561				}
562				sc->stats.asyncFrames++;
563reset:
564				sc->amode = MODE_NORMAL;
565				sc->fcs = PPP_INITFCS;
566				sc->slen = 0;
567				continue;
568			}
569			switch (sc->amode) {
570			case MODE_NORMAL:
571				if (ch == PPP_ESCAPE) {
572					sc->amode = MODE_ESC;
573					continue;
574				}
575				break;
576			case MODE_ESC:
577				ch ^= PPP_TRANS;
578				sc->amode = MODE_NORMAL;
579				break;
580			case MODE_HUNT:
581			default:
582				continue;
583			}
584
585			/* Add byte to frame */
586			if (sc->slen >= SYNC_BUF_SIZE(sc->cfg.amru)) {
587				sc->stats.asyncOverflows++;
588				sc->amode = MODE_HUNT;
589				sc->slen = 0;
590			} else {
591				sc->sbuf[sc->slen++] = ch;
592				sc->fcs = PPP_FCS(sc->fcs, ch);
593			}
594		}
595		m = m_free(m);
596	}
597	if (item)
598		NG_FREE_ITEM(item);
599	return (0);
600}
601
602/*
603 * CRC table
604 *
605 * Taken from RFC 1171 Appendix B
606 */
607static const u_int16_t fcstab[256] = {
608	 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
609	 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
610	 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
611	 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
612	 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
613	 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
614	 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
615	 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
616	 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
617	 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
618	 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
619	 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
620	 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
621	 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
622	 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
623	 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
624	 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
625	 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
626	 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
627	 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
628	 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
629	 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
630	 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
631	 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
632	 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
633	 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
634	 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
635	 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
636	 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
637	 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
638	 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
639	 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
640};
641