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