1 /*
2 * Copyright (c) 2016-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 #include <assert.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdbool.h>
34 #include <pthread.h>
35 #include <unistd.h>
36 #include <uuid/uuid.h>
37 #include <sys/select.h>
38 #include <poll.h>
39 #include <time.h>
40 #include <sys/event.h>
41 #include <sys/sysctl.h>
42 #include <err.h>
43 #include <sysexits.h>
44 #include "skywalk_test_driver.h"
45 #include "skywalk_test_common.h"
46 #include "skywalk_test_utils.h"
47
48 struct context {
49 uuid_t nexus_uuid;
50 int argc;
51 char **argv;
52 int test_selector;
53 struct stage_ctx stage;
54 };
55
56 enum mangle_test_selector {
57 SKT_CHANGE_LEN,
58 SKT_BIG_LEN,
59 };
60
61 enum mangle_stage {
62 SKT_INIT=0,
63 SKT_TX_TURN,
64 SKT_RX_TURN,
65 };
66
67 struct internalize_ctx {
68 int utun_fd;
69 struct sktc_nexus_handles handles;
70 channel_port port;
71 struct sktu_flow *flow;
72 struct in_addr our_ip;
73 struct in_addr dst_ip;
74 };
75
76 static void
skt_mangle_init(void)77 skt_mangle_init(void)
78 {
79 uint32_t disable_panic_on_sync_err = 1;
80 struct sktc_nexus_attr attr = SKTC_NEXUS_ATTR_INIT();
81
82 sysctlbyname("kern.skywalk.disable_panic_on_sync_err",
83 NULL, 0, &disable_panic_on_sync_err,
84 sizeof(disable_panic_on_sync_err));
85
86 strncpy((char *)attr.name, "skywalk_test_mangle_upipe",
87 sizeof(nexus_name_t) - 1);
88 attr.type = NEXUS_TYPE_USER_PIPE;
89 attr.ntxrings = 1;
90 attr.nrxrings = 1;
91 attr.ntxslots = 64;
92 attr.nrxslots = 64;
93 attr.anonymous = 1;
94
95 sktc_setup_nexus(&attr);
96 }
97
98 static void
skt_mangle_fini(void)99 skt_mangle_fini(void)
100 {
101 uint32_t disable_panic_on_sync_err = 0;
102
103 sysctlbyname("kern.skywalk.disable_panic_on_sync_err",
104 NULL, 0, &disable_panic_on_sync_err,
105 sizeof(disable_panic_on_sync_err));
106
107 sktc_cleanup_nexus();
108 }
109
110 static void *
skt_mangle_verify_internalize_metadata(void * _ctx)111 skt_mangle_verify_internalize_metadata(void *_ctx)
112 {
113 struct internalize_ctx *ctx = _ctx;
114 uint16_t sport;
115 uint16_t dport;
116
117 sport = ntohs(ctx->flow->nfr.nfr_saddr.sin.sin_port);
118 dport = ntohs(ctx->flow->nfr.nfr_daddr.sin.sin_port);
119
120 my_payload tx_payload;
121 bzero(&tx_payload, sizeof(tx_payload));
122 tx_payload.packet_number = 0;
123 strlcpy(tx_payload.data, "udp_flow_send", sizeof(tx_payload.data));
124
125 struct sktu_frame *tx_frame;
126 sktu_create_udp_frames(&tx_frame, 1, IPVERSION,
127 &ctx->our_ip, &ctx->dst_ip, sport, dport, &tx_payload,
128 sizeof(tx_payload), 1500, CSUM_OFFLOAD);
129 uuid_copy(tx_frame->flow_uuid, ctx->flow->nfr.nfr_flow_uuid);
130
131 packet_t pkt = sktu_channel_port_frame_to_pkt(&ctx->port, tx_frame);
132
133 uint32_t frame_length = os_packet_get_data_length(pkt);
134 /* stuff_off to be greater than actual frame length */
135 os_packet_set_inet_checksum(pkt, PACKET_CSUM_PARTIAL, sizeof(struct ip),
136 frame_length + 1);
137
138 struct __user_quantum *uqum = SK_PTR_ADDR_UQUM(pkt);
139 /* pkt_length to be greater than stuff_off + sizeof(csum) */
140 uqum->qum_len = frame_length + 3;
141
142 sktu_channel_port_tx_burst_pkt(&ctx->port, &pkt, 1);
143 /*
144 * With the changes in rdar://problem/72632756, TX thread will crash
145 * after writing an invalid packet.
146 */
147
148 sktu_frame_free(tx_frame);
149 return 0;
150 }
151
152 static void *
skt_mangle_rx(void * ctx_)153 skt_mangle_rx(void *ctx_)
154 {
155 struct context *ctx = (struct context *)ctx_;
156 channel_t channel;
157 channel_ring_t rxring;
158 ring_dir_t ring_dir = CHANNEL_DIR_RX;
159 ring_id_t ring_id = CHANNEL_RING_ID_ANY;
160 uint32_t port = 0;
161 int kq_fd;
162 int error;
163
164 assert(ctx->stage.test_stage == SKT_INIT);
165
166 /* Initialize kqueue */
167 kq_fd = kqueue();
168 assert(kq_fd >= 0);
169
170 /* Initialize channel */
171 channel = sktu_channel_create_extended(
172 ctx->nexus_uuid,
173 port, ring_dir, ring_id,
174 NULL,
175 -1, -1, -1, -1, -1, -1, -1, 1, -1, -1);
176 assert(channel != NULL);
177
178 ring_id = os_channel_ring_id(channel, CHANNEL_FIRST_RX_RING);
179 rxring = os_channel_rx_ring(channel, ring_id);
180 assert(rxring);
181
182 /* Don't care about watermark, but set units to 'bytes' */
183 set_watermark(channel, false, CHANNEL_THRESHOLD_UNIT_BYTES, 1);
184
185 switch (ctx->test_selector) {
186 case SKT_CHANGE_LEN:
187 /* Let TX send one slot with 10 bytes */
188 test_stage_change(&ctx->stage, SKT_TX_TURN);
189 test_stage_wait(&ctx->stage, SKT_RX_TURN);
190
191 error = os_channel_sync(channel, CHANNEL_SYNC_RX);
192 SKTC_ASSERT_ERR(!error);
193
194 /* Receive that slot and mess with its length */
195 channel_slot_t slot;
196 struct slot_prop prop;
197 slot = os_channel_get_next_slot(rxring, NULL, &prop);
198
199 /*
200 * Try to trick the kernel into thinking there are 4 bytes
201 * less than there really are
202 */
203 assert(slot);
204 prop.sp_len = 6;
205 os_channel_set_slot_properties(rxring, slot, &prop);
206
207 error = os_channel_advance_slot(rxring, slot);
208 SKTC_ASSERT_ERR(!error);
209
210 error = os_channel_sync(channel, CHANNEL_SYNC_RX);
211 SKTC_ASSERT_ERR(!error);
212
213 test_stage_change(&ctx->stage, SKT_TX_TURN);
214
215 /*
216 * Get the kernel view of how many bytes are in the ring.
217 * should be the original count.
218 */
219 error = wait_on_fd(kq_fd, EVFILT_READ, channel, 0, TIMEOUT_FAIL);
220 SKTC_ASSERT_ERR(error == 10);
221 break;
222
223 case SKT_BIG_LEN:
224 /* Let TX try to send an unreasonably large slot */
225 test_stage_change(&ctx->stage, SKT_TX_TURN);
226
227 /*
228 * With the changes in rdar://problem/72632756,
229 * TX side will crash after writing an invalid
230 * length packet.
231 * Hence the below code is commented out.
232 */
233 #if 0
234 test_stage_wait(&ctx->stage, SKT_RX_TURN);
235
236 /* Guarantee the data in the ring is less than what was requested */
237 error = wait_on_fd(kq_fd, EVFILT_READ, channel, 0, TIMEOUT_FAIL);
238 SKTC_ASSERT_ERR(error == 214); /* 131 + 83 */
239 #endif
240
241 break;
242 }
243
244 return 0;
245 }
246
247 static void *
skt_mangle_tx(void * ctx_)248 skt_mangle_tx(void *ctx_)
249 {
250 struct context *ctx = (struct context *)ctx_;
251 channel_t channel;
252 channel_ring_t txring;
253 ring_dir_t ring_dir = CHANNEL_DIR_TX;
254 ring_id_t ring_id = CHANNEL_RING_ID_ANY;
255 uint32_t port = 1;
256 int error;
257 int kq_fd;
258
259 kq_fd = kqueue();
260 assert(kq_fd >= 0);
261
262 channel = sktu_channel_create_extended(
263 ctx->nexus_uuid,
264 port, ring_dir, ring_id,
265 NULL,
266 -1, -1, -1, -1, -1, -1, -1, 1, -1, -1);
267 assert(channel != NULL);
268
269 ring_id = os_channel_ring_id(channel, CHANNEL_FIRST_TX_RING);
270 txring = os_channel_tx_ring(channel, ring_id);
271 assert(txring);
272
273 switch (ctx->test_selector) {
274 case SKT_CHANGE_LEN:
275 /* Wait for RX to initialize */
276 test_stage_wait(&ctx->stage, SKT_TX_TURN);
277
278 /* Send 10 bytes */
279 send_bytes(txring, 10);
280 error = os_channel_sync(channel, CHANNEL_SYNC_TX);
281 SKTC_ASSERT_ERR(error == 0);
282 test_stage_change(&ctx->stage, SKT_RX_TURN);
283 test_stage_wait(&ctx->stage, SKT_TX_TURN);
284
285 send_bytes(txring, 10);
286 error = os_channel_sync(channel, CHANNEL_SYNC_TX);
287 SKTC_ASSERT_ERR(error == 0);
288 break;
289
290 case SKT_BIG_LEN: {
291 /* Wait for RX to initialize */
292 test_stage_wait(&ctx->stage, SKT_TX_TURN);
293
294 /*
295 * Write 3 slots; the one with an invalid length should
296 * be truncated by kernel (to 0 bytes), but not the
297 * other two.
298 */
299 slot_prop_t prop;
300 channel_slot_t slot = os_channel_get_next_slot(txring, NULL, &prop);
301 assert(slot);
302
303 /* Request the largest buffer the kernel structures will allow */
304 prop.sp_len = 65535;
305 os_channel_set_slot_properties(txring, slot, &prop);
306
307 slot = os_channel_get_next_slot(txring, slot, &prop);
308 assert(slot);
309 prop.sp_len = 131;
310 os_channel_set_slot_properties(txring, slot, &prop);
311
312 slot = os_channel_get_next_slot(txring, slot, &prop);
313 assert(slot);
314 prop.sp_len = 83;
315 os_channel_set_slot_properties(txring, slot, &prop);
316
317 error = os_channel_advance_slot(txring, slot);
318 SKTC_ASSERT_ERR(!error);
319
320 error = os_channel_sync(channel, CHANNEL_SYNC_TX);
321 /* Expect failure (and a process crash) here */
322
323 /*
324 * Sanity checks to localize failures, in case the crash
325 * doesn't succeed:
326 */
327 SKTC_ASSERT_ERR(error);
328 SKTC_ASSERT_ERR(errno == EFAULT);
329
330 //test_stage_change(&ctx->stage, SKT_RX_TURN);
331 break;
332 }
333 }
334
335 return 0;
336 }
337
338 static int
skt_mangle_main(int argc,char * argv[],int test_selector)339 skt_mangle_main(int argc, char *argv[], int test_selector)
340 {
341 pthread_t rx_thread, tx_thread;
342 struct context ctx;
343 int error;
344
345 test_stage_init(&ctx.stage, SKT_INIT);
346 ctx.argc = argc;
347 ctx.argv = argv;
348 ctx.test_selector = test_selector;
349
350 error = uuid_parse(argv[3], ctx.nexus_uuid);
351 SKTC_ASSERT_ERR(!error);
352
353 error = pthread_create(&rx_thread, NULL, &skt_mangle_rx, &ctx);
354 SKTC_ASSERT_ERR(!error);
355 error = pthread_create(&tx_thread, NULL, &skt_mangle_tx, &ctx);
356 SKTC_ASSERT_ERR(!error);
357
358 pthread_join(rx_thread, NULL);
359 pthread_join(tx_thread, NULL);
360
361 test_stage_destroy(&ctx.stage);
362
363 return 0;
364 }
365
366 static int
skt_change_len_main(int argc,char * argv[])367 skt_change_len_main(int argc, char *argv[])
368 {
369 return skt_mangle_main(argc, argv, SKT_CHANGE_LEN);
370 }
371
372 static int
skt_big_len_main(int argc,char * argv[])373 skt_big_len_main(int argc, char *argv[])
374 {
375 return skt_mangle_main(argc, argv, SKT_BIG_LEN);
376 }
377
378 static int
skt_mangle_verify_internalize_metadata_main(int argc,char * argv[])379 skt_mangle_verify_internalize_metadata_main(int argc, char *argv[])
380 {
381 #pragma unused (argc, argv)
382 char *utun_addr_str = "10.0.250.1";
383 char *peer_addr_str = "10.0.250.2";
384 char *broad_addr_str = "10.0.250.255";
385 char utun_ifname[IFNAMSIZ + 1];
386 struct internalize_ctx ctx;
387 struct in_addr broad_ip;
388 struct in_addr mask;
389 uint16_t sport = 0;
390 uint16_t dport = 4321;
391 int error;
392
393 inet_pton(AF_INET, utun_addr_str, &ctx.our_ip);
394 inet_pton(AF_INET, peer_addr_str, &ctx.dst_ip);
395 inet_pton(AF_INET, broad_addr_str, &broad_ip);
396 mask = sktc_make_in_addr(IN_CLASSC_NET);
397
398 ctx.utun_fd = sktu_create_interface(SKTU_IFT_UTUN,
399 SKTU_IFF_ENABLE_NETIF | SKTU_IFF_NO_ATTACH_FSW);
400 sktu_get_interface_name(SKTU_IFT_UTUN, ctx.utun_fd, utun_ifname);
401
402 if (sktc_ifnet_add_addr(utun_ifname, &ctx.our_ip, &mask, &broad_ip) !=
403 0) {
404 err(EX_OSERR, "Failed to add address for %s", utun_ifname);
405 }
406
407 if (sktc_ifnet_add_scoped_default_route(utun_ifname, ctx.our_ip) != 0) {
408 err(EX_OSERR, "Failed to add default route for %s\n",
409 utun_ifname);
410 }
411
412 bzero(&ctx.handles, sizeof(ctx.handles));
413 strlcpy(ctx.handles.netif_ifname, utun_ifname,
414 sizeof(ctx.handles.netif_ifname));
415 ctx.handles.netif_addr = ctx.our_ip;
416 ctx.handles.netif_mask = mask;
417 sktc_create_flowswitch_no_address(&ctx.handles, -1, -1, -1, -1, 0);
418
419 error =
420 os_nexus_controller_bind_provider_instance(ctx.handles.controller,
421 ctx.handles.fsw_nx_uuid, 3, getpid(), NULL, NULL, 0,
422 NEXUS_BIND_PID);
423 SKTC_ASSERT_ERR(error == 0);
424
425 sktu_channel_port_init(&ctx.port, ctx.handles.fsw_nx_uuid, 3, true,
426 false, false);
427 assert(ctx.port.chan != NULL);
428 assert(ctx.port.user_packet_pool);
429
430 ctx.flow = sktu_create_nexus_flow(&ctx.handles, AF_INET, &ctx.our_ip,
431 &ctx.dst_ip, IPPROTO_UDP, sport, dport);
432
433 pthread_t tx_thread;
434
435 error = pthread_create(&tx_thread, NULL,
436 &skt_mangle_verify_internalize_metadata, &ctx);
437 SKTC_ASSERT_ERR(!error);
438 pthread_join(tx_thread, NULL);
439
440 sktu_destroy_nexus_flow(ctx.flow);
441 sktc_cleanup_flowswitch(&ctx.handles);
442 close(ctx.utun_fd);
443 return 0;
444 }
445
446 struct skywalk_test skt_change_len = {
447 "change_len", "tests kernel resilience to modified slot lengths",
448 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE |
449 SK_FEATURE_DEV_OR_DEBUG,
450 skt_change_len_main, SKTC_GENERIC_UPIPE_ARGV,
451 skt_mangle_init, skt_mangle_fini,
452 };
453
454 struct skywalk_test skt_big_len = {
455 "big_len", "tests unrealistically large slot lengths",
456 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE |
457 SK_FEATURE_DEV_OR_DEBUG,
458 skt_big_len_main, SKTC_GENERIC_UPIPE_ARGV,
459 skt_mangle_init, skt_mangle_fini, (SIGABRT << 24), 0,
460 };
461
462 struct skywalk_test skt_internalizemetdata = {
463 .skt_testname = "internalizemetadata",
464 .skt_testdesc = "Internalize packet metadata verification",
465 .skt_required_features = SK_FEATURE_SKYWALK |
466 SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NEXUS_NETIF | SK_FEATURE_NETNS,
467 .skt_main = skt_mangle_verify_internalize_metadata_main,
468 .skt_expected_exception_code = (SIGABRT << 24),
469 .skt_expected_exception_code_ignore = 0,
470 };
471