This is xnu-11215.1.10. See this file in:
/*
* Copyright (c) 2016-2024 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <sys/select.h>
#include <poll.h>
#include <time.h>
#include <sys/event.h>
#include <sys/sysctl.h>
#include <err.h>
#include <sysexits.h>
#include "skywalk_test_driver.h"
#include "skywalk_test_common.h"
#include "skywalk_test_utils.h"
struct context {
uuid_t nexus_uuid;
int argc;
char **argv;
int test_selector;
struct stage_ctx stage;
};
enum mangle_test_selector {
SKT_CHANGE_LEN,
SKT_BIG_LEN,
};
enum mangle_stage {
SKT_INIT=0,
SKT_TX_TURN,
SKT_RX_TURN,
};
struct internalize_ctx {
int utun_fd;
struct sktc_nexus_handles handles;
channel_port port;
struct sktu_flow *flow;
struct in_addr our_ip;
struct in_addr dst_ip;
};
static void
skt_mangle_init(void)
{
uint32_t disable_panic_on_sync_err = 1;
struct sktc_nexus_attr attr = SKTC_NEXUS_ATTR_INIT();
sysctlbyname("kern.skywalk.disable_panic_on_sync_err",
NULL, 0, &disable_panic_on_sync_err,
sizeof(disable_panic_on_sync_err));
strncpy((char *)attr.name, "skywalk_test_mangle_upipe",
sizeof(nexus_name_t) - 1);
attr.type = NEXUS_TYPE_USER_PIPE;
attr.ntxrings = 1;
attr.nrxrings = 1;
attr.ntxslots = 64;
attr.nrxslots = 64;
attr.anonymous = 1;
sktc_setup_nexus(&attr);
}
static void
skt_mangle_fini(void)
{
uint32_t disable_panic_on_sync_err = 0;
sysctlbyname("kern.skywalk.disable_panic_on_sync_err",
NULL, 0, &disable_panic_on_sync_err,
sizeof(disable_panic_on_sync_err));
sktc_cleanup_nexus();
}
static void *
skt_mangle_verify_internalize_metadata(void *_ctx)
{
struct internalize_ctx *ctx = _ctx;
uint16_t sport;
uint16_t dport;
sport = ntohs(ctx->flow->nfr.nfr_saddr.sin.sin_port);
dport = ntohs(ctx->flow->nfr.nfr_daddr.sin.sin_port);
my_payload tx_payload;
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 0;
strlcpy(tx_payload.data, "udp_flow_send", sizeof(tx_payload.data));
struct sktu_frame *tx_frame;
sktu_create_udp_frames(&tx_frame, 1, IPVERSION,
&ctx->our_ip, &ctx->dst_ip, sport, dport, &tx_payload,
sizeof(tx_payload), 1500, CSUM_OFFLOAD);
uuid_copy(tx_frame->flow_uuid, ctx->flow->nfr.nfr_flow_uuid);
packet_t pkt = sktu_channel_port_frame_to_pkt(&ctx->port, tx_frame);
uint32_t frame_length = os_packet_get_data_length(pkt);
/* stuff_off to be greater than actual frame length */
os_packet_set_inet_checksum(pkt, PACKET_CSUM_PARTIAL, sizeof(struct ip),
frame_length + 1);
struct __user_quantum *uqum = SK_PTR_ADDR_UQUM(pkt);
/* pkt_length to be greater than stuff_off + sizeof(csum) */
uqum->qum_len = frame_length + 3;
sktu_channel_port_tx_burst_pkt(&ctx->port, &pkt, 1);
/*
* With the changes in rdar://problem/72632756, TX thread will crash
* after writing an invalid packet.
*/
sktu_frame_free(tx_frame);
return 0;
}
static void *
skt_mangle_rx(void *ctx_)
{
struct context *ctx = (struct context *)ctx_;
channel_t channel;
channel_ring_t rxring;
ring_dir_t ring_dir = CHANNEL_DIR_RX;
ring_id_t ring_id = CHANNEL_RING_ID_ANY;
uint32_t port = 0;
int kq_fd;
int error;
assert(ctx->stage.test_stage == SKT_INIT);
/* Initialize kqueue */
kq_fd = kqueue();
assert(kq_fd >= 0);
/* Initialize channel */
channel = sktu_channel_create_extended(
ctx->nexus_uuid,
port, ring_dir, ring_id,
NULL,
-1, -1, -1, -1, -1, -1, -1, 1, -1, -1);
assert(channel != NULL);
ring_id = os_channel_ring_id(channel, CHANNEL_FIRST_RX_RING);
rxring = os_channel_rx_ring(channel, ring_id);
assert(rxring);
/* Don't care about watermark, but set units to 'bytes' */
set_watermark(channel, false, CHANNEL_THRESHOLD_UNIT_BYTES, 1);
switch (ctx->test_selector) {
case SKT_CHANGE_LEN:
/* Let TX send one slot with 10 bytes */
test_stage_change(&ctx->stage, SKT_TX_TURN);
test_stage_wait(&ctx->stage, SKT_RX_TURN);
error = os_channel_sync(channel, CHANNEL_SYNC_RX);
SKTC_ASSERT_ERR(!error);
/* Receive that slot and mess with its length */
channel_slot_t slot;
struct slot_prop prop;
slot = os_channel_get_next_slot(rxring, NULL, &prop);
/*
* Try to trick the kernel into thinking there are 4 bytes
* less than there really are
*/
assert(slot);
prop.sp_len = 6;
os_channel_set_slot_properties(rxring, slot, &prop);
error = os_channel_advance_slot(rxring, slot);
SKTC_ASSERT_ERR(!error);
error = os_channel_sync(channel, CHANNEL_SYNC_RX);
SKTC_ASSERT_ERR(!error);
test_stage_change(&ctx->stage, SKT_TX_TURN);
/*
* Get the kernel view of how many bytes are in the ring.
* should be the original count.
*/
error = wait_on_fd(kq_fd, EVFILT_READ, channel, 0, TIMEOUT_FAIL);
SKTC_ASSERT_ERR(error == 10);
break;
case SKT_BIG_LEN:
/* Let TX try to send an unreasonably large slot */
test_stage_change(&ctx->stage, SKT_TX_TURN);
/*
* With the changes in rdar://problem/72632756,
* TX side will crash after writing an invalid
* length packet.
* Hence the below code is commented out.
*/
#if 0
test_stage_wait(&ctx->stage, SKT_RX_TURN);
/* Guarantee the data in the ring is less than what was requested */
error = wait_on_fd(kq_fd, EVFILT_READ, channel, 0, TIMEOUT_FAIL);
SKTC_ASSERT_ERR(error == 214); /* 131 + 83 */
#endif
break;
}
return 0;
}
static void *
skt_mangle_tx(void *ctx_)
{
struct context *ctx = (struct context *)ctx_;
channel_t channel;
channel_ring_t txring;
ring_dir_t ring_dir = CHANNEL_DIR_TX;
ring_id_t ring_id = CHANNEL_RING_ID_ANY;
uint32_t port = 1;
int error;
int kq_fd;
kq_fd = kqueue();
assert(kq_fd >= 0);
channel = sktu_channel_create_extended(
ctx->nexus_uuid,
port, ring_dir, ring_id,
NULL,
-1, -1, -1, -1, -1, -1, -1, 1, -1, -1);
assert(channel != NULL);
ring_id = os_channel_ring_id(channel, CHANNEL_FIRST_TX_RING);
txring = os_channel_tx_ring(channel, ring_id);
assert(txring);
switch (ctx->test_selector) {
case SKT_CHANGE_LEN:
/* Wait for RX to initialize */
test_stage_wait(&ctx->stage, SKT_TX_TURN);
/* Send 10 bytes */
send_bytes(txring, 10);
error = os_channel_sync(channel, CHANNEL_SYNC_TX);
SKTC_ASSERT_ERR(error == 0);
test_stage_change(&ctx->stage, SKT_RX_TURN);
test_stage_wait(&ctx->stage, SKT_TX_TURN);
send_bytes(txring, 10);
error = os_channel_sync(channel, CHANNEL_SYNC_TX);
SKTC_ASSERT_ERR(error == 0);
break;
case SKT_BIG_LEN: {
/* Wait for RX to initialize */
test_stage_wait(&ctx->stage, SKT_TX_TURN);
/*
* Write 3 slots; the one with an invalid length should
* be truncated by kernel (to 0 bytes), but not the
* other two.
*/
slot_prop_t prop;
channel_slot_t slot = os_channel_get_next_slot(txring, NULL, &prop);
assert(slot);
/* Request the largest buffer the kernel structures will allow */
prop.sp_len = 65535;
os_channel_set_slot_properties(txring, slot, &prop);
slot = os_channel_get_next_slot(txring, slot, &prop);
assert(slot);
prop.sp_len = 131;
os_channel_set_slot_properties(txring, slot, &prop);
slot = os_channel_get_next_slot(txring, slot, &prop);
assert(slot);
prop.sp_len = 83;
os_channel_set_slot_properties(txring, slot, &prop);
error = os_channel_advance_slot(txring, slot);
SKTC_ASSERT_ERR(!error);
error = os_channel_sync(channel, CHANNEL_SYNC_TX);
/* Expect failure (and a process crash) here */
/*
* Sanity checks to localize failures, in case the crash
* doesn't succeed:
*/
SKTC_ASSERT_ERR(error);
SKTC_ASSERT_ERR(errno == EFAULT);
//test_stage_change(&ctx->stage, SKT_RX_TURN);
break;
}
}
return 0;
}
static int
skt_mangle_main(int argc, char *argv[], int test_selector)
{
pthread_t rx_thread, tx_thread;
struct context ctx;
int error;
test_stage_init(&ctx.stage, SKT_INIT);
ctx.argc = argc;
ctx.argv = argv;
ctx.test_selector = test_selector;
error = uuid_parse(argv[3], ctx.nexus_uuid);
SKTC_ASSERT_ERR(!error);
error = pthread_create(&rx_thread, NULL, &skt_mangle_rx, &ctx);
SKTC_ASSERT_ERR(!error);
error = pthread_create(&tx_thread, NULL, &skt_mangle_tx, &ctx);
SKTC_ASSERT_ERR(!error);
pthread_join(rx_thread, NULL);
pthread_join(tx_thread, NULL);
test_stage_destroy(&ctx.stage);
return 0;
}
static int
skt_change_len_main(int argc, char *argv[])
{
return skt_mangle_main(argc, argv, SKT_CHANGE_LEN);
}
static int
skt_big_len_main(int argc, char *argv[])
{
return skt_mangle_main(argc, argv, SKT_BIG_LEN);
}
static int
skt_mangle_verify_internalize_metadata_main(int argc, char *argv[])
{
#pragma unused (argc, argv)
char *utun_addr_str = "10.0.250.1";
char *peer_addr_str = "10.0.250.2";
char *broad_addr_str = "10.0.250.255";
char utun_ifname[IFNAMSIZ + 1];
struct internalize_ctx ctx;
struct in_addr broad_ip;
struct in_addr mask;
uint16_t sport = 0;
uint16_t dport = 4321;
int error;
inet_pton(AF_INET, utun_addr_str, &ctx.our_ip);
inet_pton(AF_INET, peer_addr_str, &ctx.dst_ip);
inet_pton(AF_INET, broad_addr_str, &broad_ip);
mask = sktc_make_in_addr(IN_CLASSC_NET);
ctx.utun_fd = sktu_create_interface(SKTU_IFT_UTUN,
SKTU_IFF_ENABLE_NETIF | SKTU_IFF_NO_ATTACH_FSW);
sktu_get_interface_name(SKTU_IFT_UTUN, ctx.utun_fd, utun_ifname);
if (sktc_ifnet_add_addr(utun_ifname, &ctx.our_ip, &mask, &broad_ip) !=
0) {
err(EX_OSERR, "Failed to add address for %s", utun_ifname);
}
if (sktc_ifnet_add_scoped_default_route(utun_ifname, ctx.our_ip) != 0) {
err(EX_OSERR, "Failed to add default route for %s\n",
utun_ifname);
}
bzero(&ctx.handles, sizeof(ctx.handles));
strlcpy(ctx.handles.netif_ifname, utun_ifname,
sizeof(ctx.handles.netif_ifname));
ctx.handles.netif_addr = ctx.our_ip;
ctx.handles.netif_mask = mask;
sktc_create_flowswitch_no_address(&ctx.handles, -1, -1, -1, -1, 0);
error =
os_nexus_controller_bind_provider_instance(ctx.handles.controller,
ctx.handles.fsw_nx_uuid, 3, getpid(), NULL, NULL, 0,
NEXUS_BIND_PID);
SKTC_ASSERT_ERR(error == 0);
sktu_channel_port_init(&ctx.port, ctx.handles.fsw_nx_uuid, 3, true,
false, false);
assert(ctx.port.chan != NULL);
assert(ctx.port.user_packet_pool);
ctx.flow = sktu_create_nexus_flow(&ctx.handles, AF_INET, &ctx.our_ip,
&ctx.dst_ip, IPPROTO_UDP, sport, dport);
pthread_t tx_thread;
error = pthread_create(&tx_thread, NULL,
&skt_mangle_verify_internalize_metadata, &ctx);
SKTC_ASSERT_ERR(!error);
pthread_join(tx_thread, NULL);
sktu_destroy_nexus_flow(ctx.flow);
sktc_cleanup_flowswitch(&ctx.handles);
close(ctx.utun_fd);
return 0;
}
struct skywalk_test skt_change_len = {
"change_len", "tests kernel resilience to modified slot lengths",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE |
SK_FEATURE_DEV_OR_DEBUG,
skt_change_len_main, SKTC_GENERIC_UPIPE_ARGV,
skt_mangle_init, skt_mangle_fini,
};
struct skywalk_test skt_big_len = {
"big_len", "tests unrealistically large slot lengths",
SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE |
SK_FEATURE_DEV_OR_DEBUG,
skt_big_len_main, SKTC_GENERIC_UPIPE_ARGV,
skt_mangle_init, skt_mangle_fini, (SIGABRT << 24), 0,
};
struct skywalk_test skt_internalizemetdata = {
.skt_testname = "internalizemetadata",
.skt_testdesc = "Internalize packet metadata verification",
.skt_required_features = SK_FEATURE_SKYWALK |
SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NEXUS_NETIF | SK_FEATURE_NETNS,
.skt_main = skt_mangle_verify_internalize_metadata_main,
.skt_expected_exception_code = (SIGABRT << 24),
.skt_expected_exception_code_ignore = 0,
};