xref: /minix3/minix/tests/test93.c (revision 3ba6090f825dfbac97538b19928b0ab1e37909d6)
1*3ba6090fSDavid van Moolenbroek /* Tests for network interfaces and routing (LWIP) - by D.C. van Moolenbroek */
2*3ba6090fSDavid van Moolenbroek /* This test needs to be run as root: it manipulates network settings. */
3*3ba6090fSDavid van Moolenbroek /*
4*3ba6090fSDavid van Moolenbroek  * TODO: due to time constraints, this test is currently absolutely minimal.
5*3ba6090fSDavid van Moolenbroek  * It does not yet test by far most of the service code it is supposed to test,
6*3ba6090fSDavid van Moolenbroek  * in particular interface management code, interface address assignment code,
7*3ba6090fSDavid van Moolenbroek  * routing sockets code, and routing code. The second subtest (test93b) in this
8*3ba6090fSDavid van Moolenbroek  * file serves as a reasonable example of how many of the future subtests
9*3ba6090fSDavid van Moolenbroek  * should operate, though: by issuing interface IOCTLs and routing commands on
10*3ba6090fSDavid van Moolenbroek  * a loopback interface created for the occasion.
11*3ba6090fSDavid van Moolenbroek  */
12*3ba6090fSDavid van Moolenbroek #include <stdlib.h>
13*3ba6090fSDavid van Moolenbroek #include <string.h>
14*3ba6090fSDavid van Moolenbroek #include <stddef.h>
15*3ba6090fSDavid van Moolenbroek #include <sys/socket.h>
16*3ba6090fSDavid van Moolenbroek #include <sys/ioctl.h>
17*3ba6090fSDavid van Moolenbroek #include <net/if.h>
18*3ba6090fSDavid van Moolenbroek #include <net/if_dl.h>
19*3ba6090fSDavid van Moolenbroek #include <net/route.h>
20*3ba6090fSDavid van Moolenbroek #include <netinet/in.h>
21*3ba6090fSDavid van Moolenbroek #include <netinet6/in6_var.h>
22*3ba6090fSDavid van Moolenbroek #include <arpa/inet.h>
23*3ba6090fSDavid van Moolenbroek 
24*3ba6090fSDavid van Moolenbroek #include "common.h"
25*3ba6090fSDavid van Moolenbroek #include "socklib.h"
26*3ba6090fSDavid van Moolenbroek 
27*3ba6090fSDavid van Moolenbroek #define TEST_IFNAME	"lo93"
28*3ba6090fSDavid van Moolenbroek 
29*3ba6090fSDavid van Moolenbroek #define ITERATIONS 2
30*3ba6090fSDavid van Moolenbroek 
31*3ba6090fSDavid van Moolenbroek static const enum state rtlnk_states[] = {
32*3ba6090fSDavid van Moolenbroek 		S_NEW,		S_N_SHUT_R,	S_N_SHUT_W,	S_N_SHUT_RW,
33*3ba6090fSDavid van Moolenbroek };
34*3ba6090fSDavid van Moolenbroek 
35*3ba6090fSDavid van Moolenbroek static const int rt_results[][__arraycount(rtlnk_states)] = {
36*3ba6090fSDavid van Moolenbroek 	[C_ACCEPT]		= {
37*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
38*3ba6090fSDavid van Moolenbroek 	},
39*3ba6090fSDavid van Moolenbroek 	[C_BIND]		= {
40*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
41*3ba6090fSDavid van Moolenbroek 	},
42*3ba6090fSDavid van Moolenbroek 	[C_CONNECT]		= {
43*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
44*3ba6090fSDavid van Moolenbroek 	},
45*3ba6090fSDavid van Moolenbroek 	[C_GETPEERNAME]		= {
46*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
47*3ba6090fSDavid van Moolenbroek 	},
48*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKNAME]		= {
49*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
50*3ba6090fSDavid van Moolenbroek 	},
51*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKOPT_ERR]	= {
52*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
53*3ba6090fSDavid van Moolenbroek 	},
54*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKOPT_KA]	= {
55*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
56*3ba6090fSDavid van Moolenbroek 	},
57*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKOPT_RB]	= {
58*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
59*3ba6090fSDavid van Moolenbroek 	},
60*3ba6090fSDavid van Moolenbroek 	[C_IOCTL_NREAD]		= {
61*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
62*3ba6090fSDavid van Moolenbroek 	},
63*3ba6090fSDavid van Moolenbroek 	[C_LISTEN]		= {
64*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
65*3ba6090fSDavid van Moolenbroek 	},
66*3ba6090fSDavid van Moolenbroek 	[C_RECV]		= {
67*3ba6090fSDavid van Moolenbroek 		-EAGAIN,	0,		-EAGAIN,	0,
68*3ba6090fSDavid van Moolenbroek 	},
69*3ba6090fSDavid van Moolenbroek 	[C_RECVFROM]		= {
70*3ba6090fSDavid van Moolenbroek 		-EAGAIN,	0,		-EAGAIN,	0,
71*3ba6090fSDavid van Moolenbroek 	},
72*3ba6090fSDavid van Moolenbroek 	[C_SEND]		= {
73*3ba6090fSDavid van Moolenbroek 		-ENOBUFS,	-ENOBUFS,	-EPIPE,		-EPIPE,
74*3ba6090fSDavid van Moolenbroek 	},
75*3ba6090fSDavid van Moolenbroek 	[C_SENDTO]		= {
76*3ba6090fSDavid van Moolenbroek 		-EISCONN,	-EISCONN,	-EPIPE,		-EPIPE,
77*3ba6090fSDavid van Moolenbroek 	},
78*3ba6090fSDavid van Moolenbroek 	[C_SELECT_R]		= {
79*3ba6090fSDavid van Moolenbroek 		0,		1,		0,		1,
80*3ba6090fSDavid van Moolenbroek 	},
81*3ba6090fSDavid van Moolenbroek 	[C_SELECT_W]		= {
82*3ba6090fSDavid van Moolenbroek 		1,		1,		1,		1,
83*3ba6090fSDavid van Moolenbroek 	},
84*3ba6090fSDavid van Moolenbroek 	[C_SELECT_X]		= {
85*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
86*3ba6090fSDavid van Moolenbroek 	},
87*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_BC]	= {
88*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
89*3ba6090fSDavid van Moolenbroek 	},
90*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_KA]	= {
91*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
92*3ba6090fSDavid van Moolenbroek 	},
93*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_L]	= {
94*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
95*3ba6090fSDavid van Moolenbroek 	},
96*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_RA]	= {
97*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
98*3ba6090fSDavid van Moolenbroek 	},
99*3ba6090fSDavid van Moolenbroek 	[C_SHUTDOWN_R]		= {
100*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
101*3ba6090fSDavid van Moolenbroek 	},
102*3ba6090fSDavid van Moolenbroek 	[C_SHUTDOWN_RW]		= {
103*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
104*3ba6090fSDavid van Moolenbroek 	},
105*3ba6090fSDavid van Moolenbroek 	[C_SHUTDOWN_W]		= {
106*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
107*3ba6090fSDavid van Moolenbroek 	},
108*3ba6090fSDavid van Moolenbroek };
109*3ba6090fSDavid van Moolenbroek 
110*3ba6090fSDavid van Moolenbroek static const int lnk_results[][__arraycount(rtlnk_states)] = {
111*3ba6090fSDavid van Moolenbroek 	[C_ACCEPT]		= {
112*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
113*3ba6090fSDavid van Moolenbroek 	},
114*3ba6090fSDavid van Moolenbroek 	[C_BIND]		= {
115*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
116*3ba6090fSDavid van Moolenbroek 	},
117*3ba6090fSDavid van Moolenbroek 	[C_CONNECT]		= {
118*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
119*3ba6090fSDavid van Moolenbroek 	},
120*3ba6090fSDavid van Moolenbroek 	[C_GETPEERNAME]		= {
121*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
122*3ba6090fSDavid van Moolenbroek 	},
123*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKNAME]		= {
124*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
125*3ba6090fSDavid van Moolenbroek 	},
126*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKOPT_ERR]	= {
127*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
128*3ba6090fSDavid van Moolenbroek 	},
129*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKOPT_KA]	= {
130*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
131*3ba6090fSDavid van Moolenbroek 	},
132*3ba6090fSDavid van Moolenbroek 	[C_GETSOCKOPT_RB]	= {
133*3ba6090fSDavid van Moolenbroek 		-ENOPROTOOPT,	-ENOPROTOOPT,	-ENOPROTOOPT,	-ENOPROTOOPT,
134*3ba6090fSDavid van Moolenbroek 	},
135*3ba6090fSDavid van Moolenbroek 	[C_IOCTL_NREAD]		= {
136*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
137*3ba6090fSDavid van Moolenbroek 	},
138*3ba6090fSDavid van Moolenbroek 	[C_LISTEN]		= {
139*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,	-EOPNOTSUPP,
140*3ba6090fSDavid van Moolenbroek 	},
141*3ba6090fSDavid van Moolenbroek 	[C_RECV]		= {
142*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	0,		-EOPNOTSUPP,	0,
143*3ba6090fSDavid van Moolenbroek 	},
144*3ba6090fSDavid van Moolenbroek 	[C_RECVFROM]		= {
145*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	0,		-EOPNOTSUPP,	0,
146*3ba6090fSDavid van Moolenbroek 	},
147*3ba6090fSDavid van Moolenbroek 	[C_SEND]		= {
148*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EPIPE,		-EPIPE,
149*3ba6090fSDavid van Moolenbroek 	},
150*3ba6090fSDavid van Moolenbroek 	[C_SENDTO]		= {
151*3ba6090fSDavid van Moolenbroek 		-EOPNOTSUPP,	-EOPNOTSUPP,	-EPIPE,		-EPIPE,
152*3ba6090fSDavid van Moolenbroek 	},
153*3ba6090fSDavid van Moolenbroek 	[C_SELECT_R]		= {
154*3ba6090fSDavid van Moolenbroek 		1,		1,		1,		1,
155*3ba6090fSDavid van Moolenbroek 	},
156*3ba6090fSDavid van Moolenbroek 	[C_SELECT_W]		= {
157*3ba6090fSDavid van Moolenbroek 		1,		1,		1,		1,
158*3ba6090fSDavid van Moolenbroek 	},
159*3ba6090fSDavid van Moolenbroek 	[C_SELECT_X]		= {
160*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
161*3ba6090fSDavid van Moolenbroek 	},
162*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_BC]	= {
163*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
164*3ba6090fSDavid van Moolenbroek 	},
165*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_KA]	= {
166*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
167*3ba6090fSDavid van Moolenbroek 	},
168*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_L]	= {
169*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
170*3ba6090fSDavid van Moolenbroek 	},
171*3ba6090fSDavid van Moolenbroek 	[C_SETSOCKOPT_RA]	= {
172*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
173*3ba6090fSDavid van Moolenbroek 	},
174*3ba6090fSDavid van Moolenbroek 	[C_SHUTDOWN_R]		= {
175*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
176*3ba6090fSDavid van Moolenbroek 	},
177*3ba6090fSDavid van Moolenbroek 	[C_SHUTDOWN_RW]		= {
178*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
179*3ba6090fSDavid van Moolenbroek 	},
180*3ba6090fSDavid van Moolenbroek 	[C_SHUTDOWN_W]		= {
181*3ba6090fSDavid van Moolenbroek 		0,		0,		0,		0,
182*3ba6090fSDavid van Moolenbroek 	},
183*3ba6090fSDavid van Moolenbroek };
184*3ba6090fSDavid van Moolenbroek 
185*3ba6090fSDavid van Moolenbroek /*
186*3ba6090fSDavid van Moolenbroek  * Set up a routing or link socket file descriptor in the requested state and
187*3ba6090fSDavid van Moolenbroek  * pass it to socklib_sweep_call() along with local and remote addresses and
188*3ba6090fSDavid van Moolenbroek  * their lengths.
189*3ba6090fSDavid van Moolenbroek  */
190*3ba6090fSDavid van Moolenbroek static int
rtlnk_sweep(int domain,int type,int protocol,enum state state,enum call call)191*3ba6090fSDavid van Moolenbroek rtlnk_sweep(int domain, int type, int protocol, enum state state,
192*3ba6090fSDavid van Moolenbroek 	enum call call)
193*3ba6090fSDavid van Moolenbroek {
194*3ba6090fSDavid van Moolenbroek 	struct sockaddr sa;
195*3ba6090fSDavid van Moolenbroek 	int r, fd;
196*3ba6090fSDavid van Moolenbroek 
197*3ba6090fSDavid van Moolenbroek 	memset(&sa, 0, sizeof(sa));
198*3ba6090fSDavid van Moolenbroek 	sa.sa_family = domain;
199*3ba6090fSDavid van Moolenbroek 
200*3ba6090fSDavid van Moolenbroek 	if ((fd = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0);
201*3ba6090fSDavid van Moolenbroek 
202*3ba6090fSDavid van Moolenbroek 	switch (state) {
203*3ba6090fSDavid van Moolenbroek 	case S_NEW: break;
204*3ba6090fSDavid van Moolenbroek 	case S_N_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break;
205*3ba6090fSDavid van Moolenbroek 	case S_N_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break;
206*3ba6090fSDavid van Moolenbroek 	case S_N_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break;
207*3ba6090fSDavid van Moolenbroek 	default: e(0);
208*3ba6090fSDavid van Moolenbroek 	}
209*3ba6090fSDavid van Moolenbroek 
210*3ba6090fSDavid van Moolenbroek 	r = socklib_sweep_call(call, fd, &sa, &sa,
211*3ba6090fSDavid van Moolenbroek 	    offsetof(struct sockaddr, sa_data));
212*3ba6090fSDavid van Moolenbroek 
213*3ba6090fSDavid van Moolenbroek 	if (close(fd) != 0) e(0);
214*3ba6090fSDavid van Moolenbroek 
215*3ba6090fSDavid van Moolenbroek 	return r;
216*3ba6090fSDavid van Moolenbroek }
217*3ba6090fSDavid van Moolenbroek 
218*3ba6090fSDavid van Moolenbroek /*
219*3ba6090fSDavid van Moolenbroek  * Sweep test for socket calls versus socket states of routing and link
220*3ba6090fSDavid van Moolenbroek  * sockets.
221*3ba6090fSDavid van Moolenbroek  */
222*3ba6090fSDavid van Moolenbroek static void
test93a(void)223*3ba6090fSDavid van Moolenbroek test93a(void)
224*3ba6090fSDavid van Moolenbroek {
225*3ba6090fSDavid van Moolenbroek 
226*3ba6090fSDavid van Moolenbroek 	subtest = 1;
227*3ba6090fSDavid van Moolenbroek 
228*3ba6090fSDavid van Moolenbroek 	socklib_sweep(AF_ROUTE, SOCK_RAW, 0, rtlnk_states,
229*3ba6090fSDavid van Moolenbroek 	    __arraycount(rtlnk_states), (const int *)rt_results, rtlnk_sweep);
230*3ba6090fSDavid van Moolenbroek 
231*3ba6090fSDavid van Moolenbroek 	/*
232*3ba6090fSDavid van Moolenbroek 	 * Our implementation of link sockets currently serves only one
233*3ba6090fSDavid van Moolenbroek 	 * purpose, and that is to pass on ioctl() calls issued on the socket.
234*3ba6090fSDavid van Moolenbroek 	 * As such, the results here are not too important.  The test mostly
235*3ba6090fSDavid van Moolenbroek 	 * ensures that all calls actually complete--for example, that there is
236*3ba6090fSDavid van Moolenbroek 	 * no function pointer NULL check missing in libsockevent.
237*3ba6090fSDavid van Moolenbroek 	 */
238*3ba6090fSDavid van Moolenbroek 	socklib_sweep(AF_LINK, SOCK_DGRAM, 0, rtlnk_states,
239*3ba6090fSDavid van Moolenbroek 	    __arraycount(rtlnk_states), (const int *)lnk_results, rtlnk_sweep);
240*3ba6090fSDavid van Moolenbroek }
241*3ba6090fSDavid van Moolenbroek 
242*3ba6090fSDavid van Moolenbroek /*
243*3ba6090fSDavid van Moolenbroek  * Attempt to destroy the test loopback interface.  Return 0 if destruction was
244*3ba6090fSDavid van Moolenbroek  * successful, or -1 if no such interface existed.
245*3ba6090fSDavid van Moolenbroek  */
246*3ba6090fSDavid van Moolenbroek static int
test93_destroy_if(void)247*3ba6090fSDavid van Moolenbroek test93_destroy_if(void)
248*3ba6090fSDavid van Moolenbroek {
249*3ba6090fSDavid van Moolenbroek 	struct ifreq ifr;
250*3ba6090fSDavid van Moolenbroek 	int r, fd;
251*3ba6090fSDavid van Moolenbroek 
252*3ba6090fSDavid van Moolenbroek 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0);
253*3ba6090fSDavid van Moolenbroek 
254*3ba6090fSDavid van Moolenbroek 	memset(&ifr, 0, sizeof(ifr));
255*3ba6090fSDavid van Moolenbroek 	strlcpy(ifr.ifr_name, TEST_IFNAME, sizeof(ifr.ifr_name));
256*3ba6090fSDavid van Moolenbroek 
257*3ba6090fSDavid van Moolenbroek 	r = ioctl(fd, SIOCIFDESTROY, &ifr);
258*3ba6090fSDavid van Moolenbroek 	if (r != 0 && (r != -1 || errno != ENXIO)) e(0);
259*3ba6090fSDavid van Moolenbroek 
260*3ba6090fSDavid van Moolenbroek 	if (close(fd) != 0) e(0);
261*3ba6090fSDavid van Moolenbroek 
262*3ba6090fSDavid van Moolenbroek 	return r;
263*3ba6090fSDavid van Moolenbroek }
264*3ba6090fSDavid van Moolenbroek 
265*3ba6090fSDavid van Moolenbroek /*
266*3ba6090fSDavid van Moolenbroek  * Destroy the test interface at exit.  It is always safe to do so as its name
267*3ba6090fSDavid van Moolenbroek  * is sufficiently unique, and we do not want to leave it around.
268*3ba6090fSDavid van Moolenbroek  */
269*3ba6090fSDavid van Moolenbroek static void
test93_destroy_if_atexit(void)270*3ba6090fSDavid van Moolenbroek test93_destroy_if_atexit(void)
271*3ba6090fSDavid van Moolenbroek {
272*3ba6090fSDavid van Moolenbroek 	static int atexit_set = 0;
273*3ba6090fSDavid van Moolenbroek 
274*3ba6090fSDavid van Moolenbroek 	if (!atexit_set) {
275*3ba6090fSDavid van Moolenbroek 		(void)test93_destroy_if();
276*3ba6090fSDavid van Moolenbroek 
277*3ba6090fSDavid van Moolenbroek 		atexit_set = 1;
278*3ba6090fSDavid van Moolenbroek 	}
279*3ba6090fSDavid van Moolenbroek }
280*3ba6090fSDavid van Moolenbroek 
281*3ba6090fSDavid van Moolenbroek /*
282*3ba6090fSDavid van Moolenbroek  * Attempt to create a test loopback interface.  Return 0 if creation was
283*3ba6090fSDavid van Moolenbroek  * successful, or -1 if no more interfaces could be created.
284*3ba6090fSDavid van Moolenbroek  */
285*3ba6090fSDavid van Moolenbroek static int
test93_create_if(void)286*3ba6090fSDavid van Moolenbroek test93_create_if(void)
287*3ba6090fSDavid van Moolenbroek {
288*3ba6090fSDavid van Moolenbroek 	struct ifreq ifr;
289*3ba6090fSDavid van Moolenbroek 	int r, fd;
290*3ba6090fSDavid van Moolenbroek 
291*3ba6090fSDavid van Moolenbroek 	(void)test93_destroy_if();
292*3ba6090fSDavid van Moolenbroek 
293*3ba6090fSDavid van Moolenbroek 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0);
294*3ba6090fSDavid van Moolenbroek 
295*3ba6090fSDavid van Moolenbroek 	memset(&ifr, 0, sizeof(ifr));
296*3ba6090fSDavid van Moolenbroek 	strlcpy(ifr.ifr_name, TEST_IFNAME, sizeof(ifr.ifr_name));
297*3ba6090fSDavid van Moolenbroek 
298*3ba6090fSDavid van Moolenbroek 	r = ioctl(fd, SIOCIFCREATE, &ifr);
299*3ba6090fSDavid van Moolenbroek 	if (r != 0 && (r != -1 || errno != ENOBUFS)) e(0);
300*3ba6090fSDavid van Moolenbroek 
301*3ba6090fSDavid van Moolenbroek 	if (close(fd) != 0) e(0);
302*3ba6090fSDavid van Moolenbroek 
303*3ba6090fSDavid van Moolenbroek 	atexit(test93_destroy_if_atexit);
304*3ba6090fSDavid van Moolenbroek 
305*3ba6090fSDavid van Moolenbroek 	return r;
306*3ba6090fSDavid van Moolenbroek }
307*3ba6090fSDavid van Moolenbroek 
308*3ba6090fSDavid van Moolenbroek /*
309*3ba6090fSDavid van Moolenbroek  * Set the interface-up value for an interface to the given boolean value.
310*3ba6090fSDavid van Moolenbroek  */
311*3ba6090fSDavid van Moolenbroek static void
test93_set_if_up(const char * ifname,int up)312*3ba6090fSDavid van Moolenbroek test93_set_if_up(const char * ifname, int up)
313*3ba6090fSDavid van Moolenbroek {
314*3ba6090fSDavid van Moolenbroek 	struct ifreq ifr;
315*3ba6090fSDavid van Moolenbroek 	int fd;
316*3ba6090fSDavid van Moolenbroek 
317*3ba6090fSDavid van Moolenbroek 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0);
318*3ba6090fSDavid van Moolenbroek 
319*3ba6090fSDavid van Moolenbroek 	memset(&ifr, 0, sizeof(ifr));
320*3ba6090fSDavid van Moolenbroek 	strlcpy(ifr.ifr_name, TEST_IFNAME, sizeof(ifr.ifr_name));
321*3ba6090fSDavid van Moolenbroek 
322*3ba6090fSDavid van Moolenbroek 	if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) e(0);
323*3ba6090fSDavid van Moolenbroek 
324*3ba6090fSDavid van Moolenbroek 	if (up)
325*3ba6090fSDavid van Moolenbroek 		ifr.ifr_flags |= IFF_UP;
326*3ba6090fSDavid van Moolenbroek 	else
327*3ba6090fSDavid van Moolenbroek 		ifr.ifr_flags &= ~IFF_UP;
328*3ba6090fSDavid van Moolenbroek 
329*3ba6090fSDavid van Moolenbroek 	if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) e(0);
330*3ba6090fSDavid van Moolenbroek 
331*3ba6090fSDavid van Moolenbroek 	if (close(fd) != 0) e(0);
332*3ba6090fSDavid van Moolenbroek }
333*3ba6090fSDavid van Moolenbroek 
334*3ba6090fSDavid van Moolenbroek /*
335*3ba6090fSDavid van Moolenbroek  * Construct an IPv6 network mask for a certain prefix length.
336*3ba6090fSDavid van Moolenbroek  */
337*3ba6090fSDavid van Moolenbroek static void
test93_make_netmask6(struct sockaddr_in6 * sin6,unsigned int prefix)338*3ba6090fSDavid van Moolenbroek test93_make_netmask6(struct sockaddr_in6 * sin6, unsigned int prefix)
339*3ba6090fSDavid van Moolenbroek {
340*3ba6090fSDavid van Moolenbroek 	unsigned int byte, bit;
341*3ba6090fSDavid van Moolenbroek 
342*3ba6090fSDavid van Moolenbroek 	if (prefix > 128) e(0);
343*3ba6090fSDavid van Moolenbroek 	memset(sin6, 0, sizeof(*sin6));
344*3ba6090fSDavid van Moolenbroek 	sin6->sin6_family = AF_INET6;
345*3ba6090fSDavid van Moolenbroek 
346*3ba6090fSDavid van Moolenbroek 	byte = prefix / NBBY;
347*3ba6090fSDavid van Moolenbroek 	bit = prefix % NBBY;
348*3ba6090fSDavid van Moolenbroek 
349*3ba6090fSDavid van Moolenbroek 	if (byte > 0)
350*3ba6090fSDavid van Moolenbroek 		memset(sin6->sin6_addr.s6_addr, 0xff, byte);
351*3ba6090fSDavid van Moolenbroek 	if (bit != 0)
352*3ba6090fSDavid van Moolenbroek 		sin6->sin6_addr.s6_addr[byte] = 0xff << (NBBY - bit);
353*3ba6090fSDavid van Moolenbroek }
354*3ba6090fSDavid van Moolenbroek 
355*3ba6090fSDavid van Moolenbroek /*
356*3ba6090fSDavid van Moolenbroek  * Issue a modifying routing command, which must be one of RTM_ADD, RTM_CHANGE,
357*3ba6090fSDavid van Moolenbroek  * RTM_DELETE, or RTM_LOCK.  The destination address (IPv4 or IPv6) and netmask
358*3ba6090fSDavid van Moolenbroek  * prefix are required.  The flags (RTF_), interface name, and gateway are
359*3ba6090fSDavid van Moolenbroek  * optional depending on the command (and flags) being issued.  Return 0 on
360*3ba6090fSDavid van Moolenbroek  * success, and -1 with errno set on failure.
361*3ba6090fSDavid van Moolenbroek  */
362*3ba6090fSDavid van Moolenbroek static int
test93_route_cmd(int cmd,const struct sockaddr * dest,socklen_t dest_len,unsigned int prefix,int flags,const char * ifname,const struct sockaddr * gw,socklen_t gw_len)363*3ba6090fSDavid van Moolenbroek test93_route_cmd(int cmd, const struct sockaddr * dest, socklen_t dest_len,
364*3ba6090fSDavid van Moolenbroek 	unsigned int prefix, int flags, const char * ifname,
365*3ba6090fSDavid van Moolenbroek 	const struct sockaddr * gw, socklen_t gw_len)
366*3ba6090fSDavid van Moolenbroek {
367*3ba6090fSDavid van Moolenbroek 	static unsigned int seq = 0;
368*3ba6090fSDavid van Moolenbroek 	struct sockaddr_storage destss, maskss, ifpss, gwss;
369*3ba6090fSDavid van Moolenbroek 	struct sockaddr_in mask4;
370*3ba6090fSDavid van Moolenbroek 	struct sockaddr_in6 mask6;
371*3ba6090fSDavid van Moolenbroek 	struct sockaddr_dl ifp;
372*3ba6090fSDavid van Moolenbroek 	struct rt_msghdr rtm;
373*3ba6090fSDavid van Moolenbroek 	struct iovec iov[5];
374*3ba6090fSDavid van Moolenbroek 	struct msghdr msg;
375*3ba6090fSDavid van Moolenbroek 	unsigned int i, iovlen;
376*3ba6090fSDavid van Moolenbroek 	int r, fd, err;
377*3ba6090fSDavid van Moolenbroek 
378*3ba6090fSDavid van Moolenbroek 	memset(&rtm, 0, sizeof(rtm));
379*3ba6090fSDavid van Moolenbroek 	rtm.rtm_version = RTM_VERSION;
380*3ba6090fSDavid van Moolenbroek 	rtm.rtm_type = cmd;
381*3ba6090fSDavid van Moolenbroek 	rtm.rtm_flags = flags;
382*3ba6090fSDavid van Moolenbroek 	rtm.rtm_addrs = RTA_DST | RTA_NETMASK;
383*3ba6090fSDavid van Moolenbroek 	rtm.rtm_seq = ++seq;
384*3ba6090fSDavid van Moolenbroek 
385*3ba6090fSDavid van Moolenbroek 	iovlen = 0;
386*3ba6090fSDavid van Moolenbroek 	iov[iovlen].iov_base = &rtm;
387*3ba6090fSDavid van Moolenbroek 	iov[iovlen++].iov_len = sizeof(rtm);
388*3ba6090fSDavid van Moolenbroek 
389*3ba6090fSDavid van Moolenbroek 	memset(&destss, 0, sizeof(destss));
390*3ba6090fSDavid van Moolenbroek 	memcpy(&destss, dest, dest_len);
391*3ba6090fSDavid van Moolenbroek 	destss.ss_len = dest_len;
392*3ba6090fSDavid van Moolenbroek 
393*3ba6090fSDavid van Moolenbroek 	iov[iovlen].iov_base = &destss;
394*3ba6090fSDavid van Moolenbroek 	iov[iovlen++].iov_len = RT_ROUNDUP(dest_len);
395*3ba6090fSDavid van Moolenbroek 
396*3ba6090fSDavid van Moolenbroek 	/* Do this in RTA order. */
397*3ba6090fSDavid van Moolenbroek 	memset(&gwss, 0, sizeof(gwss));
398*3ba6090fSDavid van Moolenbroek 	if (gw != NULL) {
399*3ba6090fSDavid van Moolenbroek 		memcpy(&gwss, gw, gw_len);
400*3ba6090fSDavid van Moolenbroek 		gwss.ss_len = gw_len;
401*3ba6090fSDavid van Moolenbroek 
402*3ba6090fSDavid van Moolenbroek 		rtm.rtm_addrs |= RTA_GATEWAY;
403*3ba6090fSDavid van Moolenbroek 		iov[iovlen].iov_base = &gwss;
404*3ba6090fSDavid van Moolenbroek 		iov[iovlen++].iov_len = RT_ROUNDUP(gwss.ss_len);
405*3ba6090fSDavid van Moolenbroek 	}
406*3ba6090fSDavid van Moolenbroek 
407*3ba6090fSDavid van Moolenbroek 	memset(&maskss, 0, sizeof(maskss));
408*3ba6090fSDavid van Moolenbroek 	switch (dest->sa_family) {
409*3ba6090fSDavid van Moolenbroek 	case AF_INET:
410*3ba6090fSDavid van Moolenbroek 		if (prefix > 32) e(0);
411*3ba6090fSDavid van Moolenbroek 		memset(&mask4, 0, sizeof(mask4));
412*3ba6090fSDavid van Moolenbroek 		mask4.sin_family = AF_INET;
413*3ba6090fSDavid van Moolenbroek 		if (prefix < 32)
414*3ba6090fSDavid van Moolenbroek 			mask4.sin_addr.s_addr = htonl(0xffffffffUL << prefix);
415*3ba6090fSDavid van Moolenbroek 
416*3ba6090fSDavid van Moolenbroek 		memcpy(&maskss, &mask4, sizeof(mask4));
417*3ba6090fSDavid van Moolenbroek 		maskss.ss_len = sizeof(mask4);
418*3ba6090fSDavid van Moolenbroek 
419*3ba6090fSDavid van Moolenbroek 		break;
420*3ba6090fSDavid van Moolenbroek 
421*3ba6090fSDavid van Moolenbroek 	case AF_INET6:
422*3ba6090fSDavid van Moolenbroek 		test93_make_netmask6(&mask6, prefix);
423*3ba6090fSDavid van Moolenbroek 
424*3ba6090fSDavid van Moolenbroek 		memcpy(&maskss, &mask6, sizeof(mask6));
425*3ba6090fSDavid van Moolenbroek 		maskss.ss_len = sizeof(mask6);
426*3ba6090fSDavid van Moolenbroek 
427*3ba6090fSDavid van Moolenbroek 		break;
428*3ba6090fSDavid van Moolenbroek 
429*3ba6090fSDavid van Moolenbroek 	default:
430*3ba6090fSDavid van Moolenbroek 		e(0);
431*3ba6090fSDavid van Moolenbroek 	}
432*3ba6090fSDavid van Moolenbroek 
433*3ba6090fSDavid van Moolenbroek 	iov[iovlen].iov_base = &maskss;
434*3ba6090fSDavid van Moolenbroek 	iov[iovlen++].iov_len = RT_ROUNDUP(maskss.ss_len);
435*3ba6090fSDavid van Moolenbroek 
436*3ba6090fSDavid van Moolenbroek 	if (ifname != NULL) {
437*3ba6090fSDavid van Moolenbroek 		memset(&ifp, 0, sizeof(ifp));
438*3ba6090fSDavid van Moolenbroek 		ifp.sdl_nlen = strlen(ifname);
439*3ba6090fSDavid van Moolenbroek 		ifp.sdl_len = offsetof(struct sockaddr_dl, sdl_data) +
440*3ba6090fSDavid van Moolenbroek 		    ifp.sdl_nlen;
441*3ba6090fSDavid van Moolenbroek 		ifp.sdl_family = AF_LINK;
442*3ba6090fSDavid van Moolenbroek 
443*3ba6090fSDavid van Moolenbroek 		memset(&ifpss, 0, sizeof(ifpss));
444*3ba6090fSDavid van Moolenbroek 		memcpy(&ifpss, &ifp, ifp.sdl_len);
445*3ba6090fSDavid van Moolenbroek 		memcpy(&((struct sockaddr_dl *)&ifpss)->sdl_data, ifname,
446*3ba6090fSDavid van Moolenbroek 		    ifp.sdl_nlen);
447*3ba6090fSDavid van Moolenbroek 
448*3ba6090fSDavid van Moolenbroek 		rtm.rtm_addrs |= RTA_IFP;
449*3ba6090fSDavid van Moolenbroek 		iov[iovlen].iov_base = &ifpss;
450*3ba6090fSDavid van Moolenbroek 		iov[iovlen++].iov_len = RT_ROUNDUP(ifpss.ss_len);
451*3ba6090fSDavid van Moolenbroek 	}
452*3ba6090fSDavid van Moolenbroek 
453*3ba6090fSDavid van Moolenbroek 	memset(&msg, 0, sizeof(msg));
454*3ba6090fSDavid van Moolenbroek 	msg.msg_iov = iov;
455*3ba6090fSDavid van Moolenbroek 	msg.msg_iovlen = iovlen;
456*3ba6090fSDavid van Moolenbroek 
457*3ba6090fSDavid van Moolenbroek 	if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) < 0) e(0);
458*3ba6090fSDavid van Moolenbroek 
459*3ba6090fSDavid van Moolenbroek 	for (i = 0; i < iovlen; i++)
460*3ba6090fSDavid van Moolenbroek 		rtm.rtm_msglen += iov[i].iov_len;
461*3ba6090fSDavid van Moolenbroek 
462*3ba6090fSDavid van Moolenbroek 	r = sendmsg(fd, &msg, 0);
463*3ba6090fSDavid van Moolenbroek 	if (r != rtm.rtm_msglen && r != -1) e(0);
464*3ba6090fSDavid van Moolenbroek 	err = errno;
465*3ba6090fSDavid van Moolenbroek 
466*3ba6090fSDavid van Moolenbroek 	/*
467*3ba6090fSDavid van Moolenbroek 	 * We could just shut down the socket for reading, but this is just an
468*3ba6090fSDavid van Moolenbroek 	 * extra test we can do basically for free.
469*3ba6090fSDavid van Moolenbroek 	 */
470*3ba6090fSDavid van Moolenbroek 	rtm.rtm_seq = 0;
471*3ba6090fSDavid van Moolenbroek 	do {
472*3ba6090fSDavid van Moolenbroek 		iov[0].iov_base = &rtm;
473*3ba6090fSDavid van Moolenbroek 		iov[0].iov_len = sizeof(rtm);
474*3ba6090fSDavid van Moolenbroek 
475*3ba6090fSDavid van Moolenbroek 		if (recvmsg(fd, &msg, 0) <= 0) e(0);
476*3ba6090fSDavid van Moolenbroek 	} while (rtm.rtm_pid != getpid() || rtm.rtm_seq != seq);
477*3ba6090fSDavid van Moolenbroek 
478*3ba6090fSDavid van Moolenbroek 	if (r == -1) {
479*3ba6090fSDavid van Moolenbroek 		if (rtm.rtm_errno != err) e(0);
480*3ba6090fSDavid van Moolenbroek 		if (rtm.rtm_flags & RTF_DONE) e(0);
481*3ba6090fSDavid van Moolenbroek 	} else {
482*3ba6090fSDavid van Moolenbroek 		if (rtm.rtm_errno != 0) e(0);
483*3ba6090fSDavid van Moolenbroek 		if (!(rtm.rtm_flags & RTF_DONE)) e(0);
484*3ba6090fSDavid van Moolenbroek 	}
485*3ba6090fSDavid van Moolenbroek 
486*3ba6090fSDavid van Moolenbroek 	if (close(fd) != 0) e(0);
487*3ba6090fSDavid van Moolenbroek 
488*3ba6090fSDavid van Moolenbroek 	errno = err;
489*3ba6090fSDavid van Moolenbroek 	return (r > 0) ? 0 : -1;
490*3ba6090fSDavid van Moolenbroek }
491*3ba6090fSDavid van Moolenbroek 
492*3ba6090fSDavid van Moolenbroek /*
493*3ba6090fSDavid van Moolenbroek  * Add or delete an IPv6 address to or from an interface.  The interface name,
494*3ba6090fSDavid van Moolenbroek  * address, and prefix length must always be given.  When adding, a set of
495*3ba6090fSDavid van Moolenbroek  * flags (IN6_IFF) and lifetimes must be given as well.
496*3ba6090fSDavid van Moolenbroek  */
497*3ba6090fSDavid van Moolenbroek static void
test93_ipv6_addr(int add,const char * ifname,const struct sockaddr_in6 * sin6,unsigned int prefix,int flags,uint32_t valid_life,uint32_t pref_life)498*3ba6090fSDavid van Moolenbroek test93_ipv6_addr(int add, const char * ifname,
499*3ba6090fSDavid van Moolenbroek 	const struct sockaddr_in6 * sin6, unsigned int prefix, int flags,
500*3ba6090fSDavid van Moolenbroek 	uint32_t valid_life, uint32_t pref_life)
501*3ba6090fSDavid van Moolenbroek {
502*3ba6090fSDavid van Moolenbroek 	struct in6_aliasreq ifra;
503*3ba6090fSDavid van Moolenbroek 	int fd;
504*3ba6090fSDavid van Moolenbroek 
505*3ba6090fSDavid van Moolenbroek 	memset(&ifra, 0, sizeof(ifra));
506*3ba6090fSDavid van Moolenbroek 	strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
507*3ba6090fSDavid van Moolenbroek 	memcpy(&ifra.ifra_addr, sin6, sizeof(ifra.ifra_addr));
508*3ba6090fSDavid van Moolenbroek 	/* leave ifra_dstaddr blank */
509*3ba6090fSDavid van Moolenbroek 	test93_make_netmask6(&ifra.ifra_prefixmask, prefix);
510*3ba6090fSDavid van Moolenbroek 	ifra.ifra_flags = flags;
511*3ba6090fSDavid van Moolenbroek 	ifra.ifra_lifetime.ia6t_vltime = valid_life;
512*3ba6090fSDavid van Moolenbroek 	ifra.ifra_lifetime.ia6t_pltime = pref_life;
513*3ba6090fSDavid van Moolenbroek 
514*3ba6090fSDavid van Moolenbroek 	if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0);
515*3ba6090fSDavid van Moolenbroek 
516*3ba6090fSDavid van Moolenbroek 	if (ioctl(fd, (add) ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6, &ifra) != 0)
517*3ba6090fSDavid van Moolenbroek 		e(0);
518*3ba6090fSDavid van Moolenbroek 
519*3ba6090fSDavid van Moolenbroek 	if (close(fd) != 0) e(0);
520*3ba6090fSDavid van Moolenbroek }
521*3ba6090fSDavid van Moolenbroek 
522*3ba6090fSDavid van Moolenbroek static const struct {
523*3ba6090fSDavid van Moolenbroek 	int result; /* 0..2 = prefer srcN, -1 = no preference */
524*3ba6090fSDavid van Moolenbroek 	const char *dest_addr;
525*3ba6090fSDavid van Moolenbroek 	const char *src0_addr;
526*3ba6090fSDavid van Moolenbroek 	unsigned int src0_prefix;
527*3ba6090fSDavid van Moolenbroek 	int src0_flags;
528*3ba6090fSDavid van Moolenbroek 	const char *src1_addr;
529*3ba6090fSDavid van Moolenbroek 	unsigned int src1_prefix;
530*3ba6090fSDavid van Moolenbroek 	int src1_flags;
531*3ba6090fSDavid van Moolenbroek 	const char *src2_addr;
532*3ba6090fSDavid van Moolenbroek 	unsigned int src2_prefix;
533*3ba6090fSDavid van Moolenbroek 	int src2_flags;
534*3ba6090fSDavid van Moolenbroek } test93b_table[] = {
535*3ba6090fSDavid van Moolenbroek 	/*
536*3ba6090fSDavid van Moolenbroek 	 * These are all the applicable tests from RFC 6724 Sec. 10.1, slightly
537*3ba6090fSDavid van Moolenbroek 	 * changed not to use the default link-local address of lo0.
538*3ba6090fSDavid van Moolenbroek 	 */
539*3ba6090fSDavid van Moolenbroek 	/* Prefer appropriate scope: */
540*3ba6090fSDavid van Moolenbroek 	{ 0, "2001:db8:1::1", "2001:db8:3::1", 64, 0, "fe80::93:1", 64, 0 },
541*3ba6090fSDavid van Moolenbroek 	/* Prefer appropriate scope: */
542*3ba6090fSDavid van Moolenbroek 	{ 0, "ff05::1", "2001:db8:3::1", 64, 0, "fe80::93:1", 64, 0 },
543*3ba6090fSDavid van Moolenbroek 	/* Prefer same address: */
544*3ba6090fSDavid van Moolenbroek 	{ 0, "2001:db8:1::1", "2001:db8:1::1", 64, IN6_IFF_DEPRECATED,
545*3ba6090fSDavid van Moolenbroek 	    "2001:db8:2::1", 64, 0 },
546*3ba6090fSDavid van Moolenbroek 	/* Prefer appropriate scope: */
547*3ba6090fSDavid van Moolenbroek 	{ 0, "fe80::93:1", "fe80::93:2", 64, IN6_IFF_DEPRECATED,
548*3ba6090fSDavid van Moolenbroek 	    "2001:db8:2::1", 64, 0 },
549*3ba6090fSDavid van Moolenbroek 	/* Longest matching prefix: */
550*3ba6090fSDavid van Moolenbroek 	{ 0, "2001:db8:1::1", "2001:db8:1::2", 64, 0, "2001:db8:3::2", 64, 0 },
551*3ba6090fSDavid van Moolenbroek 	/* Prefer matching label: */
552*3ba6090fSDavid van Moolenbroek 	{ 0, "2002:c633:6401::1", "2002:c633:6401::d5e3:7953:13eb:22e8", 64,
553*3ba6090fSDavid van Moolenbroek 	    IN6_IFF_TEMPORARY, "2001:db8:1::2", 64, 0 },
554*3ba6090fSDavid van Moolenbroek 	/* Prefer temporary address: */
555*3ba6090fSDavid van Moolenbroek 	{ 1, "2001:db8:1::d5e3:0:0:1", "2001:db8:1::2", 64, 0,
556*3ba6090fSDavid van Moolenbroek 	    "2001:db8:1::d5e3:7953:13eb:22e8", 64, IN6_IFF_TEMPORARY },
557*3ba6090fSDavid van Moolenbroek 	/*
558*3ba6090fSDavid van Moolenbroek 	 * Our own additional tests.
559*3ba6090fSDavid van Moolenbroek 	 */
560*3ba6090fSDavid van Moolenbroek 	/* Prefer same address: */
561*3ba6090fSDavid van Moolenbroek 	{ 1, "4000:93::1", "2001:db8:3::1", 64, 0, "4000:93::1", 64, 0 },
562*3ba6090fSDavid van Moolenbroek 	{ 2, "2001:db8:1::1", "2001:db8:3::1", 64, 0, "fe80::93:1", 64, 0,
563*3ba6090fSDavid van Moolenbroek 	    "2001:db8:1::1", 64, 0 },
564*3ba6090fSDavid van Moolenbroek 	/* Prefer appropriate scope: */
565*3ba6090fSDavid van Moolenbroek 	{ 1, "ff01::1", "2001:db8:3::1", 64, 0, "fe80::93:1", 64, 0 },
566*3ba6090fSDavid van Moolenbroek 	{ 1, "ff02::1", "2001:db8:3::1", 64, 0, "fe80::93:1", 64, 0 },
567*3ba6090fSDavid van Moolenbroek 	{ 0, "ff0e::1", "2001:db8:3::1", 64, 0, "fe80::93:1", 64, 0 },
568*3ba6090fSDavid van Moolenbroek 	{ 1, "fd00:93::1", "2001:db8:3::1", 64, 0, "fd00::93:2", 64, 0 },
569*3ba6090fSDavid van Moolenbroek 	{ 1, "fd00:93::1", "fe80::93:1", 64, 0, "fd00::93:2", 64, 0 },
570*3ba6090fSDavid van Moolenbroek 	{ 0, "fd00:93::1", "2001:db8:3::1", 64, 0, "fe80::93:1", 64, 0 },
571*3ba6090fSDavid van Moolenbroek 	{ 1, "2001:db8:1::1", "fe80::93:1", 64, 0, "fd00::93:2", 64, 0 },
572*3ba6090fSDavid van Moolenbroek 	{ 0, "2001:db8:1::1", "2001:db8:3::1", 64, 0, "4000:93::1", 64, 0 },
573*3ba6090fSDavid van Moolenbroek 	{ 0, "4000:93::2", "2001:db8:3::1", 64, 0, "4000:93::1", 64, 0 },
574*3ba6090fSDavid van Moolenbroek 	{ 2, "2001:db8:1::1", "fe80::93:1", 64, 0, "fd00::93:1", 64, 0,
575*3ba6090fSDavid van Moolenbroek 	    "2001:db8:3::1", 64, 0 },
576*3ba6090fSDavid van Moolenbroek 	{ 2, "2001:db8:1::1", "fe80::93:1", 64, IN6_IFF_DEPRECATED,
577*3ba6090fSDavid van Moolenbroek 	    "fe80::93:2", 64, 0, "2001:db8:3::1", 64, 0 },
578*3ba6090fSDavid van Moolenbroek 	/* Avoid deprecated address: */
579*3ba6090fSDavid van Moolenbroek 	{ 1, "2002:c633:6401::1", "2002:c633:6401::d5e3:7953:13eb:22e8", 64,
580*3ba6090fSDavid van Moolenbroek 	    IN6_IFF_DEPRECATED, "2001:db8:1::2", 64, 0 },
581*3ba6090fSDavid van Moolenbroek 	{ 2, "2001:db8:1::1", "2001:db8:1::3", 64, IN6_IFF_DEPRECATED,
582*3ba6090fSDavid van Moolenbroek 	    "2001:db8:2::1", 64, IN6_IFF_DEPRECATED, "2001:db8:3::1", 64, 0 },
583*3ba6090fSDavid van Moolenbroek 	{ 2, "2001:db8:1::1", "2002:db8:1::3", 64, IN6_IFF_DEPRECATED,
584*3ba6090fSDavid van Moolenbroek 	    "2001:db8:2::1", 64, IN6_IFF_DEPRECATED, "2001:db8:3::1", 64, 0 },
585*3ba6090fSDavid van Moolenbroek 	/* Prefer matching label: */
586*3ba6090fSDavid van Moolenbroek 	{ 0, "2002:c633:6401::1", "2002:c633:6401::d5e3:7953:13eb:22e8", 64, 0,
587*3ba6090fSDavid van Moolenbroek 	    "2001:db8:1::2", 64, IN6_IFF_TEMPORARY },
588*3ba6090fSDavid van Moolenbroek 	{ 2, "2002:c633:6401::1", "2001:db8:3::2", 64, 0, "2001:db8:1::2", 64,
589*3ba6090fSDavid van Moolenbroek 	    IN6_IFF_TEMPORARY, "2002:c633:6401::d5e3:7953:13eb:22e8", 64, 0 },
590*3ba6090fSDavid van Moolenbroek 	{ 2, "2001:db8:1::1", "2003:db8::1", 64, 0, "3ffe:db8::1", 64, 0,
591*3ba6090fSDavid van Moolenbroek 	    "2001:db8:3::1", 64, 0 },
592*3ba6090fSDavid van Moolenbroek 	/* Prefer temporary address: */
593*3ba6090fSDavid van Moolenbroek 	{ 0, "2001:db8:1::d5e3:0:0:1", "2001:db8:1::2", 96, IN6_IFF_TEMPORARY,
594*3ba6090fSDavid van Moolenbroek 	    "2001:db8:1::d5e3:7953:13eb:22e8", 96, 0 },
595*3ba6090fSDavid van Moolenbroek 	{ 2, "2002:c633:6401::1", "2001:db8:3::2", 64, 0, "2002:c633:6401::2",
596*3ba6090fSDavid van Moolenbroek 	    64, 0, "2002:c633:6401::d5e3:7953:13eb:22e8", 64,
597*3ba6090fSDavid van Moolenbroek 	    IN6_IFF_TEMPORARY },
598*3ba6090fSDavid van Moolenbroek 	/* Longest matching prefix: */
599*3ba6090fSDavid van Moolenbroek 	{ 1, "2001:db8:1::d5e3:0:0:1", "2001:db8:1::2", 96, 0,
600*3ba6090fSDavid van Moolenbroek 	    "2001:db8:1::d5e3:7953:13eb:22e8", 96, 0 },
601*3ba6090fSDavid van Moolenbroek 	{ 2, "2001:db8:1:1::1", "2001:db8:2:1::2", 64, 0, "2001:db8:1:2::2",
602*3ba6090fSDavid van Moolenbroek 	    64, 0, "2001:db8:1:1::2", 64, 0 },
603*3ba6090fSDavid van Moolenbroek 	{ 0, "2001:db8:1::1", "2001:db8:1::2", 47, 0, "2001:db8:3::2", 47, 0 },
604*3ba6090fSDavid van Moolenbroek 	/* No preference (a tie): */
605*3ba6090fSDavid van Moolenbroek 	{ -1, "2001:db8:1::1", "2001:db8:1::2", 46, 0, "2001:db8:3::2", 46,
606*3ba6090fSDavid van Moolenbroek 	    0 },
607*3ba6090fSDavid van Moolenbroek 	{ -1, "2001:db8::1:0:0:1", "2001:db8::1:0:0:2", 64, 0,
608*3ba6090fSDavid van Moolenbroek 	    "2001:db8::2:0:0:2", 64, 0, "2001:db8::3:0:0:2", 64, 0 },
609*3ba6090fSDavid van Moolenbroek };
610*3ba6090fSDavid van Moolenbroek 
611*3ba6090fSDavid van Moolenbroek struct src_addr {
612*3ba6090fSDavid van Moolenbroek 	struct sockaddr_in6 addr;
613*3ba6090fSDavid van Moolenbroek 	unsigned int prefix;
614*3ba6090fSDavid van Moolenbroek 	int flags;
615*3ba6090fSDavid van Moolenbroek };
616*3ba6090fSDavid van Moolenbroek 
617*3ba6090fSDavid van Moolenbroek /*
618*3ba6090fSDavid van Moolenbroek  * Test source address selection with a particular destination address and two
619*3ba6090fSDavid van Moolenbroek  * or three source addresses.
620*3ba6090fSDavid van Moolenbroek  */
621*3ba6090fSDavid van Moolenbroek static void
sub93b(int result,const struct sockaddr_in6 * dest,unsigned int ifindex,const struct src_addr * src0,const struct src_addr * src1,const struct src_addr * src2)622*3ba6090fSDavid van Moolenbroek sub93b(int result, const struct sockaddr_in6 * dest, unsigned int ifindex,
623*3ba6090fSDavid van Moolenbroek 	const struct src_addr * src0, const struct src_addr * src1,
624*3ba6090fSDavid van Moolenbroek 	const struct src_addr * src2)
625*3ba6090fSDavid van Moolenbroek {
626*3ba6090fSDavid van Moolenbroek 	struct sockaddr_in6 dest_copy, src;
627*3ba6090fSDavid van Moolenbroek 	socklen_t len;
628*3ba6090fSDavid van Moolenbroek 	int fd, rt_res;
629*3ba6090fSDavid van Moolenbroek 
630*3ba6090fSDavid van Moolenbroek 	/* Add the candidate source addresses. */
631*3ba6090fSDavid van Moolenbroek 	test93_ipv6_addr(1, TEST_IFNAME, &src0->addr, src0->prefix,
632*3ba6090fSDavid van Moolenbroek 	    src0->flags, 0xffffffffUL, 0xffffffffUL);
633*3ba6090fSDavid van Moolenbroek 
634*3ba6090fSDavid van Moolenbroek 	test93_ipv6_addr(1, TEST_IFNAME, &src1->addr, src1->prefix,
635*3ba6090fSDavid van Moolenbroek 	    src1->flags, 0xffffffffUL, 0xffffffffUL);
636*3ba6090fSDavid van Moolenbroek 
637*3ba6090fSDavid van Moolenbroek 	if (src2 != NULL)
638*3ba6090fSDavid van Moolenbroek 		test93_ipv6_addr(1, TEST_IFNAME, &src2->addr, src2->prefix,
639*3ba6090fSDavid van Moolenbroek 		    src2->flags, 0xffffffffUL, 0xffffffffUL);
640*3ba6090fSDavid van Moolenbroek 
641*3ba6090fSDavid van Moolenbroek 	/*
642*3ba6090fSDavid van Moolenbroek 	 * We need to make sure that packets to the destination are routed to
643*3ba6090fSDavid van Moolenbroek 	 * our test interface at all, so create a route for it.  Creating the
644*3ba6090fSDavid van Moolenbroek 	 * route may fail if the destination address is equal to either of the
645*3ba6090fSDavid van Moolenbroek 	 * source addresses, but that is fine.  We use a blackhole route here,
646*3ba6090fSDavid van Moolenbroek 	 * but this test should not generate any traffic anyway.
647*3ba6090fSDavid van Moolenbroek 	 */
648*3ba6090fSDavid van Moolenbroek 	rt_res = test93_route_cmd(RTM_ADD, (struct sockaddr *)dest,
649*3ba6090fSDavid van Moolenbroek 	    sizeof(*dest), 128, RTF_UP | RTF_BLACKHOLE | RTF_STATIC,
650*3ba6090fSDavid van Moolenbroek 	    TEST_IFNAME, NULL, 0);
651*3ba6090fSDavid van Moolenbroek 	if (rt_res != 0 && (rt_res != -1 || errno != EEXIST)) e(0);
652*3ba6090fSDavid van Moolenbroek 
653*3ba6090fSDavid van Moolenbroek 	if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0);
654*3ba6090fSDavid van Moolenbroek 
655*3ba6090fSDavid van Moolenbroek 	/* Set a scope ID if necessary. */
656*3ba6090fSDavid van Moolenbroek 	memcpy(&dest_copy, dest, sizeof(dest_copy));
657*3ba6090fSDavid van Moolenbroek 	dest_copy.sin6_port = 1; /* anything that is not zero */
658*3ba6090fSDavid van Moolenbroek 	if (IN6_IS_ADDR_LINKLOCAL(&dest_copy.sin6_addr) ||
659*3ba6090fSDavid van Moolenbroek 	    IN6_IS_ADDR_MC_NODELOCAL(&dest_copy.sin6_addr) ||
660*3ba6090fSDavid van Moolenbroek 	    IN6_IS_ADDR_MC_LINKLOCAL(&dest_copy.sin6_addr))
661*3ba6090fSDavid van Moolenbroek 		dest_copy.sin6_scope_id = ifindex;
662*3ba6090fSDavid van Moolenbroek 
663*3ba6090fSDavid van Moolenbroek 	/* Connecting also selects a source address. */
664*3ba6090fSDavid van Moolenbroek 	if (connect(fd, (struct sockaddr *)&dest_copy, sizeof(dest_copy)) != 0)
665*3ba6090fSDavid van Moolenbroek 		e(0);
666*3ba6090fSDavid van Moolenbroek 
667*3ba6090fSDavid van Moolenbroek 	/* Obtain the selected source address. */
668*3ba6090fSDavid van Moolenbroek 	len = sizeof(src);
669*3ba6090fSDavid van Moolenbroek 	if (getsockname(fd, (struct sockaddr *)&src, &len) != 0) e(0);
670*3ba6090fSDavid van Moolenbroek 
671*3ba6090fSDavid van Moolenbroek 	/*
672*3ba6090fSDavid van Moolenbroek 	 * If the chosen destination address has a scope ID, it must be for our
673*3ba6090fSDavid van Moolenbroek 	 * test interface.
674*3ba6090fSDavid van Moolenbroek 	 */
675*3ba6090fSDavid van Moolenbroek 	if (src.sin6_scope_id != 0 && src.sin6_scope_id != ifindex) e(0);
676*3ba6090fSDavid van Moolenbroek 
677*3ba6090fSDavid van Moolenbroek 	/* Is it the expected candidate source address? */
678*3ba6090fSDavid van Moolenbroek 	if (!memcmp(&src.sin6_addr, &src0->addr.sin6_addr,
679*3ba6090fSDavid van Moolenbroek 	    sizeof(src.sin6_addr))) {
680*3ba6090fSDavid van Moolenbroek 		if (result != 0) e(0);
681*3ba6090fSDavid van Moolenbroek 	} else if (!memcmp(&src.sin6_addr, &src1->addr.sin6_addr,
682*3ba6090fSDavid van Moolenbroek 	    sizeof(src.sin6_addr))) {
683*3ba6090fSDavid van Moolenbroek 		if (result != 1) e(0);
684*3ba6090fSDavid van Moolenbroek 	} else if (src2 != NULL && !memcmp(&src.sin6_addr,
685*3ba6090fSDavid van Moolenbroek 	    &src2->addr.sin6_addr, sizeof(src.sin6_addr))) {
686*3ba6090fSDavid van Moolenbroek 		if (result != 2) e(0);
687*3ba6090fSDavid van Moolenbroek 	} else
688*3ba6090fSDavid van Moolenbroek 		e(0);
689*3ba6090fSDavid van Moolenbroek 
690*3ba6090fSDavid van Moolenbroek 	/* Clean up. */
691*3ba6090fSDavid van Moolenbroek 	if (close(fd) != 0) e(0);
692*3ba6090fSDavid van Moolenbroek 
693*3ba6090fSDavid van Moolenbroek 	if (rt_res == 0) {
694*3ba6090fSDavid van Moolenbroek 		if (test93_route_cmd(RTM_DELETE, (struct sockaddr *)dest,
695*3ba6090fSDavid van Moolenbroek 		    sizeof(*dest), 128, 0, NULL, NULL, 0) != 0) e(0);
696*3ba6090fSDavid van Moolenbroek 	}
697*3ba6090fSDavid van Moolenbroek 
698*3ba6090fSDavid van Moolenbroek 	if (src2 != NULL)
699*3ba6090fSDavid van Moolenbroek 		test93_ipv6_addr(0, TEST_IFNAME, &src2->addr, src2->prefix, 0,
700*3ba6090fSDavid van Moolenbroek 		    0, 0);
701*3ba6090fSDavid van Moolenbroek 
702*3ba6090fSDavid van Moolenbroek 	test93_ipv6_addr(0, TEST_IFNAME, &src1->addr, src1->prefix, 0, 0, 0);
703*3ba6090fSDavid van Moolenbroek 
704*3ba6090fSDavid van Moolenbroek 	test93_ipv6_addr(0, TEST_IFNAME, &src0->addr, src0->prefix, 0, 0, 0);
705*3ba6090fSDavid van Moolenbroek }
706*3ba6090fSDavid van Moolenbroek 
707*3ba6090fSDavid van Moolenbroek /*
708*3ba6090fSDavid van Moolenbroek  * IPv6 source address selection algorithm test.
709*3ba6090fSDavid van Moolenbroek  */
710*3ba6090fSDavid van Moolenbroek static void
test93b(void)711*3ba6090fSDavid van Moolenbroek test93b(void)
712*3ba6090fSDavid van Moolenbroek {
713*3ba6090fSDavid van Moolenbroek 	static const int order[][3] = {
714*3ba6090fSDavid van Moolenbroek 		{ 0, 1, 2 },
715*3ba6090fSDavid van Moolenbroek 		{ 1, 0, 2 },
716*3ba6090fSDavid van Moolenbroek 		{ 0, 2, 1 },
717*3ba6090fSDavid van Moolenbroek 		{ 1, 2, 0 },
718*3ba6090fSDavid van Moolenbroek 		{ 2, 0, 1 },
719*3ba6090fSDavid van Moolenbroek 		{ 2, 1, 0 }
720*3ba6090fSDavid van Moolenbroek 	};
721*3ba6090fSDavid van Moolenbroek 	struct sockaddr_in6 dest;
722*3ba6090fSDavid van Moolenbroek 	struct src_addr src[3];
723*3ba6090fSDavid van Moolenbroek 	unsigned int i, j, k, count, ifindex;
724*3ba6090fSDavid van Moolenbroek 	int result;
725*3ba6090fSDavid van Moolenbroek 
726*3ba6090fSDavid van Moolenbroek 	subtest = 2;
727*3ba6090fSDavid van Moolenbroek 
728*3ba6090fSDavid van Moolenbroek 	if (test93_create_if() != 0)
729*3ba6090fSDavid van Moolenbroek 		return; /* skip this test */
730*3ba6090fSDavid van Moolenbroek 
731*3ba6090fSDavid van Moolenbroek 	if ((ifindex = if_nametoindex(TEST_IFNAME)) == 0) e(0);
732*3ba6090fSDavid van Moolenbroek 
733*3ba6090fSDavid van Moolenbroek 	test93_set_if_up(TEST_IFNAME, 1);
734*3ba6090fSDavid van Moolenbroek 
735*3ba6090fSDavid van Moolenbroek 	for (i = 0; i < __arraycount(test93b_table); i++) {
736*3ba6090fSDavid van Moolenbroek 		memset(&dest, 0, sizeof(dest));
737*3ba6090fSDavid van Moolenbroek 		dest.sin6_family = AF_INET6;
738*3ba6090fSDavid van Moolenbroek 		if (inet_pton(AF_INET6, test93b_table[i].dest_addr,
739*3ba6090fSDavid van Moolenbroek 		    &dest.sin6_addr) != 1) e(0);
740*3ba6090fSDavid van Moolenbroek 
741*3ba6090fSDavid van Moolenbroek 		memset(&src[0].addr, 0, sizeof(src[0].addr));
742*3ba6090fSDavid van Moolenbroek 		src[0].addr.sin6_family = AF_INET6;
743*3ba6090fSDavid van Moolenbroek 		if (inet_pton(AF_INET6, test93b_table[i].src0_addr,
744*3ba6090fSDavid van Moolenbroek 		    &src[0].addr.sin6_addr) != 1) e(0);
745*3ba6090fSDavid van Moolenbroek 		src[0].prefix = test93b_table[i].src0_prefix;
746*3ba6090fSDavid van Moolenbroek 		src[0].flags = test93b_table[i].src0_flags;
747*3ba6090fSDavid van Moolenbroek 
748*3ba6090fSDavid van Moolenbroek 		memset(&src[1].addr, 0, sizeof(src[1].addr));
749*3ba6090fSDavid van Moolenbroek 		src[1].addr.sin6_family = AF_INET6;
750*3ba6090fSDavid van Moolenbroek 		if (inet_pton(AF_INET6, test93b_table[i].src1_addr,
751*3ba6090fSDavid van Moolenbroek 		    &src[1].addr.sin6_addr) != 1) e(0);
752*3ba6090fSDavid van Moolenbroek 		src[1].prefix = test93b_table[i].src1_prefix;
753*3ba6090fSDavid van Moolenbroek 		src[1].flags = test93b_table[i].src1_flags;
754*3ba6090fSDavid van Moolenbroek 
755*3ba6090fSDavid van Moolenbroek 		if (test93b_table[i].src2_addr != NULL) {
756*3ba6090fSDavid van Moolenbroek 			memset(&src[2].addr, 0, sizeof(src[2].addr));
757*3ba6090fSDavid van Moolenbroek 			src[2].addr.sin6_family = AF_INET6;
758*3ba6090fSDavid van Moolenbroek 			if (inet_pton(AF_INET6, test93b_table[i].src2_addr,
759*3ba6090fSDavid van Moolenbroek 			    &src[2].addr.sin6_addr) != 1) e(0);
760*3ba6090fSDavid van Moolenbroek 			src[2].prefix = test93b_table[i].src2_prefix;
761*3ba6090fSDavid van Moolenbroek 			src[2].flags = test93b_table[i].src2_flags;
762*3ba6090fSDavid van Moolenbroek 
763*3ba6090fSDavid van Moolenbroek 			count = 6;
764*3ba6090fSDavid van Moolenbroek 		} else
765*3ba6090fSDavid van Moolenbroek 			count = 2;
766*3ba6090fSDavid van Moolenbroek 
767*3ba6090fSDavid van Moolenbroek 		result = test93b_table[i].result;
768*3ba6090fSDavid van Moolenbroek 
769*3ba6090fSDavid van Moolenbroek 		/*
770*3ba6090fSDavid van Moolenbroek 		 * Try all orders for the source addresses.  The permutation
771*3ba6090fSDavid van Moolenbroek 		 * part can be done much better, but it really does not matter.
772*3ba6090fSDavid van Moolenbroek 		 */
773*3ba6090fSDavid van Moolenbroek 		for (j = 0; j < count; j++) {
774*3ba6090fSDavid van Moolenbroek 			for (k = 0; k < count; k++)
775*3ba6090fSDavid van Moolenbroek 				if (result == -1 || order[j][k] == result)
776*3ba6090fSDavid van Moolenbroek 					break;
777*3ba6090fSDavid van Moolenbroek 
778*3ba6090fSDavid van Moolenbroek 			sub93b((result != -1) ? k : 0, &dest, ifindex,
779*3ba6090fSDavid van Moolenbroek 			    &src[order[j][0]], &src[order[j][1]],
780*3ba6090fSDavid van Moolenbroek 			    (count > 2) ? &src[order[j][2]] : NULL);
781*3ba6090fSDavid van Moolenbroek 		}
782*3ba6090fSDavid van Moolenbroek 	}
783*3ba6090fSDavid van Moolenbroek 
784*3ba6090fSDavid van Moolenbroek 	if (test93_destroy_if() != 0) e(0);
785*3ba6090fSDavid van Moolenbroek }
786*3ba6090fSDavid van Moolenbroek 
787*3ba6090fSDavid van Moolenbroek /*
788*3ba6090fSDavid van Moolenbroek  * Interface index number wrapping test.
789*3ba6090fSDavid van Moolenbroek  */
790*3ba6090fSDavid van Moolenbroek static void
test93c(void)791*3ba6090fSDavid van Moolenbroek test93c(void)
792*3ba6090fSDavid van Moolenbroek {
793*3ba6090fSDavid van Moolenbroek 	unsigned int i;
794*3ba6090fSDavid van Moolenbroek 
795*3ba6090fSDavid van Moolenbroek 	subtest = 3;
796*3ba6090fSDavid van Moolenbroek 
797*3ba6090fSDavid van Moolenbroek 	/* There might not be an available loopback interface at all. */
798*3ba6090fSDavid van Moolenbroek 	if (test93_create_if() != 0)
799*3ba6090fSDavid van Moolenbroek 		return; /* skip this test */
800*3ba6090fSDavid van Moolenbroek 
801*3ba6090fSDavid van Moolenbroek 	if (test93_destroy_if() != 0) e(0);
802*3ba6090fSDavid van Moolenbroek 
803*3ba6090fSDavid van Moolenbroek 	/*
804*3ba6090fSDavid van Moolenbroek 	 * During the development of the LWIP service, the lwIP library's
805*3ba6090fSDavid van Moolenbroek 	 * interface index assignment was still in its infancy.  This test aims
806*3ba6090fSDavid van Moolenbroek 	 * to ensure that future changes in the library do not break our
807*3ba6090fSDavid van Moolenbroek 	 * service.
808*3ba6090fSDavid van Moolenbroek 	 */
809*3ba6090fSDavid van Moolenbroek 	for (i = 0; i < UINT8_MAX + 1; i++) {
810*3ba6090fSDavid van Moolenbroek 		if (test93_create_if() != 0) e(0);
811*3ba6090fSDavid van Moolenbroek 
812*3ba6090fSDavid van Moolenbroek 		if (test93_destroy_if() != 0) e(0);
813*3ba6090fSDavid van Moolenbroek 	}
814*3ba6090fSDavid van Moolenbroek }
815*3ba6090fSDavid van Moolenbroek 
816*3ba6090fSDavid van Moolenbroek /*
817*3ba6090fSDavid van Moolenbroek  * Test program for LWIP interface and routing management.
818*3ba6090fSDavid van Moolenbroek  */
819*3ba6090fSDavid van Moolenbroek int
main(int argc,char ** argv)820*3ba6090fSDavid van Moolenbroek main(int argc, char ** argv)
821*3ba6090fSDavid van Moolenbroek {
822*3ba6090fSDavid van Moolenbroek 	int i, m;
823*3ba6090fSDavid van Moolenbroek 
824*3ba6090fSDavid van Moolenbroek 	start(93);
825*3ba6090fSDavid van Moolenbroek 
826*3ba6090fSDavid van Moolenbroek 	if (argc == 2)
827*3ba6090fSDavid van Moolenbroek 		m = atoi(argv[1]);
828*3ba6090fSDavid van Moolenbroek 	else
829*3ba6090fSDavid van Moolenbroek 		m = 0xFF;
830*3ba6090fSDavid van Moolenbroek 
831*3ba6090fSDavid van Moolenbroek 	for (i = 0; i < ITERATIONS; i++) {
832*3ba6090fSDavid van Moolenbroek 		if (m & 0x01) test93a();
833*3ba6090fSDavid van Moolenbroek 		if (m & 0x02) test93b();
834*3ba6090fSDavid van Moolenbroek 		if (m & 0x04) test93c();
835*3ba6090fSDavid van Moolenbroek 	}
836*3ba6090fSDavid van Moolenbroek 
837*3ba6090fSDavid van Moolenbroek 	quit();
838*3ba6090fSDavid van Moolenbroek 	/* NOTREACHED */
839*3ba6090fSDavid van Moolenbroek }
840