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