xref: /xnu-11215.41.3/tests/skywalk/skywalk_mptest_driver.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
1 /*
2  * Copyright (c) 2017-2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 /*
30  * Testing Framework for skywalk wrappers and syscalls
31  *
32  * This version forks many subchildren and sequences them via
33  * control messages on fd 3 (MPTEST_SEQ_FILENO)
34  */
35 
36 #include <assert.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <spawn.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <signal.h>
44 #include <sys/types.h>
45 #include <sys/sysctl.h>
46 #include <sys/socket.h>
47 #include <fcntl.h>
48 #include <darwintest.h>
49 #include "skywalk_test_driver.h"
50 #include "skywalk_test_common.h"
51 #include "skywalk_test_utils.h"
52 
53 static uint64_t skywalk_features;
54 extern char **environ;
55 bool skywalk_in_driver;
56 static struct timeval inittime;
57 static struct skywalk_mptest *curr_test;
58 
59 #define test_exit(ecode)                                                                           \
60 	{                                                                                          \
61 	        T_LOG("%s:%d := Test exiting with error code %d\n", __func__, __LINE__, (ecode)); \
62 	        T_END;                                                                            \
63 	}
64 
65 /*
66  * Print extra stats (e.g. AQM, ARP) that doesn't have to be done by the child.
67  */
68 static void
print_extra_stats(struct skywalk_mptest * test)69 print_extra_stats(struct skywalk_mptest *test)
70 {
71 	struct protox *tp;
72 
73 	skt_aqstatpr("feth0");  /* netstat -qq -I feth0 */
74 	skt_aqstatpr("feth1");  /* netstat -qq -I feth1 */
75 	if ((!strncmp(test->skt_testname, "xferrdudpping",
76 	    strlen("xferrdudpping")))) {
77 		skt_aqstatpr("rd0");  /* netstat -qq -I rd0 */
78 	}
79 
80 	/* netstat -sp arp */
81 	tp = &protox[0];
82 	skt_printproto(tp, tp->pr_name);
83 }
84 
85 void
skywalk_mptest_driver_SIGINT_handler(int sig)86 skywalk_mptest_driver_SIGINT_handler(int sig)
87 {
88 	signal(sig, SIG_IGN);
89 
90 	if (curr_test != NULL) {
91 		if (curr_test->skt_fini != NULL) {
92 			curr_test->skt_fini();
93 		}
94 	}
95 
96 	exit(0);
97 }
98 
99 void
skywalk_mptest_driver_init(void)100 skywalk_mptest_driver_init(void)
101 {
102 	size_t len;
103 
104 	assert(!skywalk_in_driver); // only call init once
105 
106 	skywalk_in_driver = true;
107 
108 	/* Query the kernel for available skywalk features */
109 	len = sizeof(skywalk_features);
110 	sysctlbyname("kern.skywalk.features", &skywalk_features, &len, NULL, 0);
111 
112 	gettimeofday(&inittime, NULL);
113 
114 	curr_test = NULL;
115 
116 	signal(SIGINT, skywalk_mptest_driver_SIGINT_handler);
117 }
118 
119 
120 static struct skywalk_mptest_check *sk_checks[] = {
121 	&skt_filternative_check,
122 	&skt_filtercompat_check,
123 };
124 
125 bool
skywalk_mptest_supported(const char * name)126 skywalk_mptest_supported(const char *name)
127 {
128 	int i;
129 
130 	for (i = 0; i < sizeof(sk_checks) / sizeof(sk_checks[0]); i++) {
131 		if (strcmp(name, sk_checks[i]->skt_testname) == 0) {
132 			return sk_checks[i]->skt_supported();
133 		}
134 	}
135 	return TRUE;
136 }
137 
138 int
skywalk_mptest_driver_run(struct skywalk_mptest * skt,bool ignoreskip)139 skywalk_mptest_driver_run(struct skywalk_mptest *skt, bool ignoreskip)
140 {
141 	posix_spawnattr_t attrs;
142 	int error;
143 	int childfail;
144 	bool childarg;
145 
146 	skywalk_mptest_driver_init();
147 
148 	/* Initialize posix_spawn attributes */
149 	posix_spawnattr_init(&attrs);
150 
151 	if ((error = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) {
152 		T_LOG("posix_spawnattr_setflags: %s", strerror(error));
153 		test_exit(1);
154 	}
155 
156 	/* Run Tests */
157 	T_LOG("Test \"%s\":\t%s", skt->skt_testname, skt->skt_testdesc);
158 
159 	if ((skt->skt_required_features & skywalk_features) !=
160 	    skt->skt_required_features) {
161 		T_LOG("Required features: 0x%llx, actual features 0x%llx",
162 		    skt->skt_required_features, skywalk_features);
163 		if (!ignoreskip) {
164 			T_LOG("Test Result: SKIPPED\n-------------------");
165 			return 0;
166 		} else {
167 			T_LOG("Proceeding with skipped test");
168 		}
169 	}
170 	if (!skywalk_mptest_supported(skt->skt_testname)) {
171 		T_LOG("Test is not supported on this device");
172 		if (!ignoreskip) {
173 			T_LOG("Test Result: SKIPPED\n-------------------");
174 			return 0;
175 		} else {
176 			T_LOG("Proceeding with skipped test");
177 		}
178 	}
179 	if (skt->skt_init) {
180 		skt->skt_init();
181 	}
182 	curr_test = skt;
183 
184 	pid_t pids[skt->skt_nchildren];
185 	int fds[skt->skt_nchildren];
186 
187 	/* If the child arg isn't set, then set it */
188 	childarg = !skt->skt_argv[3];
189 
190 	T_LOG("Spawning %d children", skt->skt_nchildren);
191 
192 	for (int j = 0; j < skt->skt_nchildren; j++) {
193 		int pfd[2];
194 		char argbuf[11];
195 
196 		if (childarg) {
197 			snprintf(argbuf, sizeof(argbuf), "%d", j);
198 			skt->skt_argv[3] = "--child";
199 			skt->skt_argv[4] = argbuf;
200 		}
201 
202 		//T_LOG("Spawning:");
203 		//for (int k = 0; skt->skt_argv[k]; k++) {
204 		//	T_LOG(" %s", skt->skt_argv[k]);
205 		//}
206 		//T_LOG("\n");
207 
208 		if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pfd) == -1) {
209 			SKT_LOG("socketpair: %s", strerror(errno));
210 			test_exit(1);
211 		}
212 
213 		/* Fork and exec child */
214 		if ((pids[j] = fork()) == -1) {
215 			SKT_LOG("fork: %s", strerror(errno));
216 			test_exit(1);
217 		}
218 
219 		if (pids[j] == 0) {
220 			/* XXX If a parent process test init expects to share an fd with a
221 			 * child process, care must be taken to make sure this doesn't
222 			 * accidentally close it.  So far, no test does this.  Another
223 			 * option would be to pass an fd number to the child's argv instead
224 			 * of assuming the hard coded fd number.
225 			 */
226 			error = close(pfd[1]);
227 			assert(!error);
228 			dup2(pfd[0], MPTEST_SEQ_FILENO);
229 			if (pfd[0] != MPTEST_SEQ_FILENO) {
230 				error = close(pfd[0]);
231 				assert(!error);
232 			}
233 
234 			int argc = 0;  /* XXX */
235 			int ret = skt->skt_main(argc, skt->skt_argv);
236 			exit(ret);
237 			/* Not reached */
238 			abort();
239 		}
240 
241 		error = close(pfd[0]);
242 		assert(!error);
243 		int fdflags = fcntl(pfd[1], F_GETFD);
244 		if (fdflags == -1) {
245 			SKT_LOG("fcntl(F_GETFD): %s", strerror(errno));
246 			test_exit(1);
247 		}
248 		error = fcntl(pfd[1], F_SETFD, fdflags | FD_CLOEXEC);
249 		assert(!error);
250 		fds[j] = pfd[1];
251 	}
252 
253 	/* Wait for input from children */
254 	for (int j = 0; j < skt->skt_nchildren; j++) {
255 		ssize_t ret;
256 		char buf[1];
257 		if ((ret = read(fds[j], buf, sizeof(buf))) == -1) {
258 			SKT_LOG("read: %s", strerror(errno));
259 			test_exit(1);
260 		}
261 		assert(ret == 1);
262 	}
263 
264 	// sleep for debug introspection
265 	//T_LOG("sleep 100\n");
266 	//sleep(100);
267 
268 	/* Send output to children */
269 	for (int j = 0; j < skt->skt_nchildren; j++) {
270 		ssize_t ret;
271 		char buf[1] = { 0 };
272 		if ((ret = write(fds[j], buf, sizeof(buf))) == -1) {
273 			SKT_LOG("write: %s", strerror(errno));
274 			test_exit(1);
275 		}
276 		assert(ret == 1);
277 
278 		error = close(fds[j]);
279 		assert(!error);
280 	}
281 
282 	childfail = 0;
283 	for (int j = 0; j < skt->skt_nchildren; j++) {
284 		int child;
285 		pid_t childpid;
286 		int child_status;
287 
288 		/* Wait for children and check results */
289 		if ((childpid = wait(&child_status)) == -1) {
290 			SKT_LOG("wait: %s", strerror(errno));
291 			test_exit(1);
292 		}
293 
294 		/* Which child was this? */
295 		for (child = 0; child < skt->skt_nchildren; child++) {
296 			if (childpid == pids[child]) {
297 				pids[child] = 0;
298 				break;
299 			}
300 		}
301 		if (child == skt->skt_nchildren) {
302 			T_LOG("Received unexpected child status from pid %d\n", childpid);
303 			test_exit(1);
304 		}
305 
306 		if (WIFEXITED(child_status)) {
307 			if (WEXITSTATUS(child_status)) {
308 				T_LOG("Child %d (pid %d) exited with status %d",
309 				    child, childpid, WEXITSTATUS(child_status));
310 			}
311 		}
312 		if (WIFSIGNALED(child_status)) {
313 			T_LOG("Child %d (pid %d) signaled with signal %d coredump %d",
314 			    child, childpid, WTERMSIG(child_status), WCOREDUMP(child_status));
315 		}
316 
317 		if (!WIFEXITED(child_status) || WEXITSTATUS(child_status)) {
318 			childfail++;
319 			/* Wait for a second and then send sigterm to remaining children
320 			 * in case they may be stuck
321 			 */
322 			if (childfail == 1) {
323 				print_extra_stats(skt);
324 				sleep(1);
325 				for (int k = 0; k < skt->skt_nchildren; k++) {
326 					if (pids[k]) {
327 						error = kill(pids[k], SIGTERM);
328 						if (error == 0) {
329 							T_LOG("Delivered SIGTERM to child %d pid %d",
330 							    k, pids[k]);
331 						} else if (errno != ESRCH) {
332 							SKT_LOG("kill(%d, SIGTERM): %s", pids[k], strerror(errno));
333 							test_exit(1);
334 						}
335 					}
336 				}
337 			}
338 		}
339 	}
340 
341 	if (skt->skt_fini) {
342 		skt->skt_fini();
343 	}
344 	curr_test = NULL;
345 
346 	if (childfail) {
347 		T_FAIL("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
348 		if ((skt->skt_required_features &
349 		    SK_FEATURE_NEXUS_KERNEL_PIPE) != 0 &&
350 		    geteuid() != 0) {
351 			T_LOG("%sPlease try running test as root%s",
352 			    BOLD, NORMAL);
353 		}
354 	} else {
355 		T_PASS("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
356 	}
357 
358 	posix_spawnattr_destroy(&attrs);
359 
360 	return 0;
361 }
362