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