xref: /xnu-12377.1.9/tests/skywalk/skywalk_mptest_driver.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
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 <os/log.h>
49 #include <darwintest.h>
50 #include "skywalk_test_driver.h"
51 #include "skywalk_test_common.h"
52 #include "skywalk_test_utils.h"
53 
54 static uint64_t skywalk_features;
55 extern char **environ;
56 bool skywalk_in_driver;
57 static struct timeval inittime;
58 static struct skywalk_mptest *curr_test;
59 
60 #define test_exit(ecode)                                                                           \
61 	{                                                                                          \
62 	        T_LOG("%s:%d := Test exiting with error code %d\n", __func__, __LINE__, (ecode)); \
63 	        T_END;                                                                            \
64 	}
65 
66 /*
67  * Print extra stats (e.g. AQM, ARP) that doesn't have to be done by the child.
68  */
69 static void
print_extra_stats(struct skywalk_mptest * test)70 print_extra_stats(struct skywalk_mptest *test)
71 {
72 	struct protox *tp;
73 
74 	skt_aqstatpr("feth0");  /* netstat -qq -I feth0 */
75 	skt_aqstatpr("feth1");  /* netstat -qq -I feth1 */
76 	if ((!strncmp(test->skt_testname, "xferrdudpping",
77 	    strlen("xferrdudpping")))) {
78 		skt_aqstatpr("rd0");  /* netstat -qq -I rd0 */
79 	}
80 
81 	/* netstat -sp arp */
82 	tp = &protox[0];
83 	skt_printproto(tp, tp->pr_name);
84 }
85 
86 void
skywalk_mptest_driver_SIGINT_handler(int sig)87 skywalk_mptest_driver_SIGINT_handler(int sig)
88 {
89 	signal(sig, SIG_IGN);
90 
91 	if (curr_test != NULL) {
92 		if (curr_test->skt_fini != NULL) {
93 			curr_test->skt_fini();
94 		}
95 	}
96 
97 	exit(0);
98 }
99 
100 static void
print_fsw_stats(void)101 print_fsw_stats(void)
102 {
103 	struct sk_stats_flow_switch *sfsw;
104 	struct sk_stats_flow_switch *entry;
105 	size_t len;
106 	int ret;
107 
108 	ret = sktu_get_nexus_flowswitch_stats(&sfsw, &len);
109 	assert(ret == 0);
110 
111 	os_log(OS_LOG_DEFAULT, "Flowswitch stats\n");
112 	for (entry = sfsw; (void *)entry < (void *)sfsw + len; entry++) {
113 		uuid_string_t   uuid_str;
114 		uuid_unparse_upper(entry->sfs_nx_uuid, uuid_str);
115 		os_log(OS_LOG_DEFAULT, "%s: %s\n", entry->sfs_if_name, uuid_str);
116 		__fsw_stats_print(&entry->sfs_fsws);
117 	}
118 }
119 
120 void
skywalk_mptest_driver_SIGABRT_handler(int s)121 skywalk_mptest_driver_SIGABRT_handler(int s)
122 {
123 	print_fsw_stats();
124 }
125 
126 void
skywalk_mptest_driver_init(void)127 skywalk_mptest_driver_init(void)
128 {
129 	size_t len;
130 
131 	assert(!skywalk_in_driver); // only call init once
132 
133 	skywalk_in_driver = true;
134 
135 	/* Query the kernel for available skywalk features */
136 	len = sizeof(skywalk_features);
137 	sysctlbyname("kern.skywalk.features", &skywalk_features, &len, NULL, 0);
138 
139 	gettimeofday(&inittime, NULL);
140 
141 	curr_test = NULL;
142 
143 	signal(SIGINT, skywalk_mptest_driver_SIGINT_handler);
144 	signal(SIGABRT, skywalk_mptest_driver_SIGABRT_handler);
145 }
146 
147 
148 static struct skywalk_mptest_check *sk_checks[] = {
149 	&skt_filternative_check,
150 	&skt_filtercompat_check,
151 };
152 
153 bool
skywalk_mptest_supported(const char * name)154 skywalk_mptest_supported(const char *name)
155 {
156 	int i;
157 
158 	for (i = 0; i < sizeof(sk_checks) / sizeof(sk_checks[0]); i++) {
159 		if (strcmp(name, sk_checks[i]->skt_testname) == 0) {
160 			return sk_checks[i]->skt_supported();
161 		}
162 	}
163 	return TRUE;
164 }
165 
166 int
skywalk_mptest_driver_run(struct skywalk_mptest * skt,bool ignoreskip)167 skywalk_mptest_driver_run(struct skywalk_mptest *skt, bool ignoreskip)
168 {
169 	posix_spawnattr_t attrs;
170 	int error;
171 	int childfail;
172 	bool childarg;
173 
174 	skywalk_mptest_driver_init();
175 
176 	/* Initialize posix_spawn attributes */
177 	posix_spawnattr_init(&attrs);
178 
179 	if ((error = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) {
180 		T_LOG("posix_spawnattr_setflags: %s", strerror(error));
181 		test_exit(1);
182 	}
183 
184 	/* Run Tests */
185 	T_LOG("Test \"%s\":\t%s", skt->skt_testname, skt->skt_testdesc);
186 
187 	if ((skt->skt_required_features & skywalk_features) !=
188 	    skt->skt_required_features) {
189 		T_LOG("Required features: 0x%llx, actual features 0x%llx",
190 		    skt->skt_required_features, skywalk_features);
191 		if (!ignoreskip) {
192 			T_LOG("Test Result: SKIPPED\n-------------------");
193 			return 0;
194 		} else {
195 			T_LOG("Proceeding with skipped test");
196 		}
197 	}
198 	if (!skywalk_mptest_supported(skt->skt_testname)) {
199 		T_LOG("Test is not supported on this device");
200 		if (!ignoreskip) {
201 			T_LOG("Test Result: SKIPPED\n-------------------");
202 			return 0;
203 		} else {
204 			T_LOG("Proceeding with skipped test");
205 		}
206 	}
207 	if (skt->skt_init) {
208 		skt->skt_init();
209 	}
210 	curr_test = skt;
211 
212 	pid_t pids[skt->skt_nchildren];
213 	int fds[skt->skt_nchildren];
214 
215 	/* If the child arg isn't set, then set it */
216 	childarg = !skt->skt_argv[3];
217 
218 	T_LOG("Spawning %d children", skt->skt_nchildren);
219 
220 	for (int j = 0; j < skt->skt_nchildren; j++) {
221 		int pfd[2];
222 		char argbuf[11];
223 
224 		if (childarg) {
225 			snprintf(argbuf, sizeof(argbuf), "%d", j);
226 			skt->skt_argv[3] = "--child";
227 			skt->skt_argv[4] = argbuf;
228 		}
229 
230 		//T_LOG("Spawning:");
231 		//for (int k = 0; skt->skt_argv[k]; k++) {
232 		//	T_LOG(" %s", skt->skt_argv[k]);
233 		//}
234 		//T_LOG("\n");
235 
236 		if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pfd) == -1) {
237 			SKT_LOG("socketpair: %s", strerror(errno));
238 			test_exit(1);
239 		}
240 
241 		/* Fork and exec child */
242 		if ((pids[j] = fork()) == -1) {
243 			SKT_LOG("fork: %s", strerror(errno));
244 			test_exit(1);
245 		}
246 
247 		if (pids[j] == 0) {
248 			/* XXX If a parent process test init expects to share an fd with a
249 			 * child process, care must be taken to make sure this doesn't
250 			 * accidentally close it.  So far, no test does this.  Another
251 			 * option would be to pass an fd number to the child's argv instead
252 			 * of assuming the hard coded fd number.
253 			 */
254 			error = close(pfd[1]);
255 			assert(!error);
256 			dup2(pfd[0], MPTEST_SEQ_FILENO);
257 			if (pfd[0] != MPTEST_SEQ_FILENO) {
258 				error = close(pfd[0]);
259 				assert(!error);
260 			}
261 
262 			int argc = 0;  /* XXX */
263 			int ret = skt->skt_main(argc, skt->skt_argv);
264 			exit(ret);
265 			/* Not reached */
266 			abort();
267 		}
268 
269 		error = close(pfd[0]);
270 		assert(!error);
271 		int fdflags = fcntl(pfd[1], F_GETFD);
272 		if (fdflags == -1) {
273 			SKT_LOG("fcntl(F_GETFD): %s", strerror(errno));
274 			test_exit(1);
275 		}
276 		error = fcntl(pfd[1], F_SETFD, fdflags | FD_CLOEXEC);
277 		assert(!error);
278 		fds[j] = pfd[1];
279 	}
280 
281 	/* Wait for input from children */
282 	for (int j = 0; j < skt->skt_nchildren; j++) {
283 		ssize_t ret;
284 		char buf[1];
285 		if ((ret = read(fds[j], buf, sizeof(buf))) == -1) {
286 			SKT_LOG("read: %s", strerror(errno));
287 			test_exit(1);
288 		}
289 		assert(ret == 1);
290 	}
291 
292 	// sleep for debug introspection
293 	//T_LOG("sleep 100\n");
294 	//sleep(100);
295 
296 	/* Send output to children */
297 	for (int j = 0; j < skt->skt_nchildren; j++) {
298 		ssize_t ret;
299 		char buf[1] = { 0 };
300 		if ((ret = write(fds[j], buf, sizeof(buf))) == -1) {
301 			SKT_LOG("write: %s", strerror(errno));
302 			test_exit(1);
303 		}
304 		assert(ret == 1);
305 
306 		error = close(fds[j]);
307 		assert(!error);
308 	}
309 
310 	childfail = 0;
311 	for (int j = 0; j < skt->skt_nchildren; j++) {
312 		int child;
313 		pid_t childpid;
314 		int child_status;
315 
316 		/* Wait for children and check results */
317 		if ((childpid = wait(&child_status)) == -1) {
318 			SKT_LOG("wait: %s", strerror(errno));
319 			test_exit(1);
320 		}
321 
322 		/* Which child was this? */
323 		for (child = 0; child < skt->skt_nchildren; child++) {
324 			if (childpid == pids[child]) {
325 				pids[child] = 0;
326 				break;
327 			}
328 		}
329 		if (child == skt->skt_nchildren) {
330 			T_LOG("Received unexpected child status from pid %d\n", childpid);
331 			test_exit(1);
332 		}
333 
334 		if (WIFEXITED(child_status)) {
335 			if (WEXITSTATUS(child_status)) {
336 				T_LOG("Child %d (pid %d) exited with status %d",
337 				    child, childpid, WEXITSTATUS(child_status));
338 			}
339 		}
340 		if (WIFSIGNALED(child_status)) {
341 			T_LOG("Child %d (pid %d) signaled with signal %d coredump %d",
342 			    child, childpid, WTERMSIG(child_status), WCOREDUMP(child_status));
343 		}
344 
345 		if (!WIFEXITED(child_status) || WEXITSTATUS(child_status)) {
346 			childfail++;
347 			/* Wait for a second and then send sigterm to remaining children
348 			 * in case they may be stuck
349 			 */
350 			if (childfail == 1) {
351 				print_extra_stats(skt);
352 				sleep(1);
353 				for (int k = 0; k < skt->skt_nchildren; k++) {
354 					if (pids[k]) {
355 						error = kill(pids[k], SIGTERM);
356 						if (error == 0) {
357 							T_LOG("Delivered SIGTERM to child %d pid %d",
358 							    k, pids[k]);
359 						} else if (errno != ESRCH) {
360 							SKT_LOG("kill(%d, SIGTERM): %s", pids[k], strerror(errno));
361 							test_exit(1);
362 						}
363 					}
364 				}
365 			}
366 		}
367 	}
368 
369 	if (skt->skt_fini) {
370 		skt->skt_fini();
371 	}
372 	curr_test = NULL;
373 
374 	if (childfail) {
375 		T_FAIL("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
376 		if ((skt->skt_required_features &
377 		    SK_FEATURE_NEXUS_KERNEL_PIPE) != 0 &&
378 		    geteuid() != 0) {
379 			T_LOG("%sPlease try running test as root%s",
380 			    BOLD, NORMAL);
381 		}
382 	} else {
383 		T_PASS("Test %s: %s", skt->skt_testname, skt->skt_testdesc);
384 	}
385 
386 	posix_spawnattr_destroy(&attrs);
387 
388 	return 0;
389 }
390