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