This is xnu-11215.1.10. See this file in:
/*
* Copyright (c) 2017-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 "skywalk_test_common.h"
#include "skywalk_test_driver.h"
#include "skywalk_test_utils.h"
#include <TargetConditionals.h>
#include <arpa/inet.h>
#include <assert.h>
#include <err.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <sys/event.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <unistd.h>
#define NX_PORT 3
static int utun_fd;
static char utun_ifname[IFNAMSIZ + 1];
static struct sktc_nexus_handles handles;
static channel_port port;
static struct in_addr our_ip, dst_ip, broad_ip, zero_ip;
static struct in_addr mask;
uint16_t sport = 0;
uint16_t dport = 4321;
static char *utun_addr_str = "10.0.250.1";
static char *peer_addr_str = "10.0.250.2";
static char *broad_addr_str = "10.0.250.255";
static char *utun_addr_str_v6 = "2607:1111::1111";
static char *peer_addr_str_v6 = "2607:2222::2222";
static char *zero_addr_str_v6 = "::";
static int addr_prefix_length = 128; // 128 for POINTOPOINT UTUN
struct in6_addr our_ip_v6, dst_ip_v6, zero_ip_v6;
typedef int (^test_block_t)();
static int
test_block_with_leeway(int sec_leeway, test_block_t test_block)
{
int ret;
struct timeval start, now;
gettimeofday(&start, NULL);
do {
ret = test_block();
if (ret == 0) {
break;
}
gettimeofday(&now, NULL);
} while (now.tv_sec - start.tv_sec < sec_leeway);
return ret;
}
static void
test_tcp_flow_send()
{
struct sktu_flow *flow;
my_payload tx_payload;
struct sktu_frame *tx_frame;
flow = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, &our_ip, &dst_ip, IPPROTO_TCP, 0, dport);
sport = ntohs(flow->nfr.nfr_saddr.sin.sin_port);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 0;
strlcpy(tx_payload.data, "tcp_flow_send", sizeof(tx_payload.data));
flow->create_output_frames(flow, &tx_frame, 1, &tx_payload, sizeof(tx_payload), NO_CSUM_OFFLOAD);
sktu_dump_buffer(stderr, NULL, &tx_frame->bytes[0], tx_frame->len);
sktu_channel_port_tx_burst(&port, &tx_frame, 1);
test_block_with_leeway(10, ^(void) {
struct sktu_frame *rx_frame;
int ret = sktu_utun_fd_rx_burst(utun_fd, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
my_payload rx_payload;
uint32_t rx_payload_len = 0;
ret = sktu_parse_tcp4_frame(rx_frame, &rx_payload, &rx_payload_len);
if (ret != 0) {
return ret;
}
assert(rx_payload.packet_number == 0);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
sktu_frame_free(rx_frame);
return 0;
});
sktu_frame_free(tx_frame);
sktu_destroy_nexus_flow(flow);
}
static void
test_tcp_flow_receive()
{
struct sktu_flow *flow;
struct sktu_frame *tx_frame, *rx_frame;
my_payload tx_payload;
T_LOG("%s\n", __func__);
flow = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, &our_ip, &dst_ip, IPPROTO_TCP, 0, dport);
sport = ntohs(flow->nfr.nfr_saddr.sin.sin_port);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 0;
strlcpy(tx_payload.data, "tcp_flow_receive", sizeof(tx_payload.data));
flow->create_input_frames(flow, &tx_frame, 1, &tx_payload, sizeof(tx_payload));
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
int ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
assert(uuid_compare(rx_frame->flow_uuid, flow->nfr.nfr_flow_uuid) == 0);
my_payload rx_payload;
uint32_t rx_payload_len = 0;
ret = sktu_parse_tcp4_frame(rx_frame, &rx_payload, &rx_payload_len);
assert(ret == 0);
assert(rx_payload.packet_number == 0);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
sktu_frame_free(rx_frame);
sktu_frame_free(tx_frame);
sktu_destroy_nexus_flow(flow);
}
static void
test_tcp_flow_listen(bool any_src_ip)
{
struct sktu_flow *listener, *connection;
my_payload rx_payload, tx_payload;
struct sktu_frame *rx_frame, *tx_frame;
T_LOG("%s\n", __func__);
listener = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, any_src_ip ? &zero_ip : &our_ip, &zero_ip, IPPROTO_TCP, 0, 0);
assert(listener);
sport = listener->sport;
dport = 1234;
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = __LINE__;
strlcpy(tx_payload.data, "tcp_flow_connect", sizeof(tx_payload.data));
sktu_create_tcp_frames(&tx_frame, 1, IPVERSION, &dst_ip, &our_ip, dport, sport, &tx_payload, sizeof(tx_payload), 1500, NO_CSUM_OFFLOAD);
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
int ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
assert(uuid_compare(rx_frame->flow_uuid, listener->nfr.nfr_flow_uuid) == 0);
uint32_t rx_payload_len = 0;
ret = sktu_parse_tcp4_frame(rx_frame, &rx_payload, &rx_payload_len);
assert(ret == 0);
assert(rx_payload.packet_number == tx_payload.packet_number);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
// connect a new flow based on the listener flow
connection = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, &our_ip, &dst_ip, IPPROTO_TCP, sport, dport);
assert(connection);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 1;
strlcpy(tx_payload.data, "tcp_flow_conected", sizeof(tx_payload.data));
connection->create_input_frames(connection, &tx_frame, 1, &tx_payload, sizeof(tx_payload));
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
assert(uuid_compare(rx_frame->flow_uuid, connection->nfr.nfr_flow_uuid) == 0);
ret = sktu_parse_tcp4_frame(rx_frame, &rx_payload, &rx_payload_len);
assert(ret == 0);
assert(rx_payload.packet_number == 1);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
sktu_frame_free(rx_frame);
sktu_frame_free(tx_frame);
sktu_destroy_nexus_flow(connection);
sktu_destroy_nexus_flow(listener);
}
static void
test_tcp_flow_listen_v6()
{
struct sktu_flow *listener, *connection;
my_payload rx_payload, tx_payload;
struct sktu_frame *rx_frame, *tx_frame;
T_LOG("%s\n", __func__);
listener = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET6, &zero_ip_v6, &zero_ip_v6, IPPROTO_TCP, 0, 0);
assert(listener);
sport = listener->sport;
dport = 1234;
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 0;
strlcpy(tx_payload.data, "tcp_flow_connect", sizeof(tx_payload.data));
sktu_create_tcp_frames(&tx_frame, 1, IPVERSION, &dst_ip, &our_ip, dport, sport, &tx_payload, sizeof(tx_payload), 1500, NO_CSUM_OFFLOAD);
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
int ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
assert(uuid_compare(rx_frame->flow_uuid, listener->nfr.nfr_flow_uuid) == 0);
uint32_t rx_payload_len = 0;
ret = sktu_parse_tcp4_frame(rx_frame, &rx_payload, &rx_payload_len);
assert(ret == 0);
assert(rx_payload.packet_number == 0);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
// connect a new flow based on the listener flow
connection = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, &our_ip, &dst_ip, IPPROTO_TCP, sport, dport);
assert(connection);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 1;
strlcpy(tx_payload.data, "tcp_flow_conected", sizeof(tx_payload.data));
connection->create_input_frames(connection, &tx_frame, 1, &tx_payload, sizeof(tx_payload));
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
assert(uuid_compare(rx_frame->flow_uuid, connection->nfr.nfr_flow_uuid) == 0);
ret = sktu_parse_tcp4_frame(rx_frame, &rx_payload, &rx_payload_len);
assert(ret == 0);
assert(rx_payload.packet_number == 1);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
sktu_frame_free(rx_frame);
sktu_frame_free(tx_frame);
sktu_destroy_nexus_flow(connection);
sktu_destroy_nexus_flow(listener);
}
static void
test_ip_flow_send()
{
struct sktu_flow *ip_flow;
my_payload tx_payload;
struct sktu_frame *rx_frame, *tx_frame;
T_LOG("%s\n", __func__);
ip_flow = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, &our_ip, &dst_ip, IPPROTO_IPEIP, 0, 0);
assert(ip_flow);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 0;
strlcpy(tx_payload.data, "ip_send", sizeof(tx_payload.data));
ip_flow->create_output_frames(ip_flow, &tx_frame, 1, &tx_payload, sizeof(tx_payload), false);
sktu_channel_port_tx_burst(&port, &tx_frame, 1);
int ret = sktu_utun_fd_rx_burst(utun_fd, &rx_frame, 1);
assert(ret == 1);
sktu_frame_free(rx_frame);
sktu_frame_free(tx_frame);
sktu_destroy_nexus_flow(ip_flow);
}
static void
test_ip_flow_receive()
{
struct sktu_flow *ip_flow;
my_payload tx_payload;
struct sktu_frame *rx_frame, *tx_frame;
T_LOG("%s\n", __func__);
ip_flow = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, &our_ip, &dst_ip, IPPROTO_IPEIP, 0, 0);
assert(ip_flow);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 0;
strlcpy(tx_payload.data, "ip_receive", sizeof(tx_payload.data));
ip_flow->create_input_frames(ip_flow, &tx_frame, 1, &tx_payload, sizeof(tx_payload));
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
int ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_frame_free(rx_frame);
sktu_frame_free(tx_frame);
sktu_destroy_nexus_flow(ip_flow);
}
static void
test_ip_flow_listen(bool any_src_ip)
{
struct sktu_flow *ip_listener, *ip_connection;
my_payload rx_payload, tx_payload;
struct sktu_frame *rx_frame, *tx_frame;
T_LOG("%s\n", __func__);
ip_listener = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, any_src_ip ? &zero_ip : &our_ip, &zero_ip, IPPROTO_IPEIP, 0, 0);
assert(ip_listener);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 0;
strlcpy(tx_payload.data, "ip_flow_connect", sizeof(tx_payload.data));
ip_listener->create_input_frames(ip_listener, &tx_frame, 1, &tx_payload, sizeof(tx_payload));
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
int ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
assert(uuid_compare(rx_frame->flow_uuid, ip_listener->nfr.nfr_flow_uuid) == 0);
uint32_t rx_payload_len = 0;
ret = sktu_parse_ipv4_frame(rx_frame, &rx_payload, &rx_payload_len);
assert(ret == 0);
assert(rx_payload.packet_number == 0);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
// connect a new flow based on the listener flow
ip_connection = sktu_create_nexus_flow_with_nx_port(&handles, NX_PORT, AF_INET, &our_ip, &dst_ip, IPPROTO_IPEIP, 0, 0);
assert(ip_connection);
bzero(&tx_payload, sizeof(tx_payload));
tx_payload.packet_number = 1;
strlcpy(tx_payload.data, "ip_flow_conected", sizeof(tx_payload.data));
ip_connection->create_input_frames(ip_connection, &tx_frame, 1, &tx_payload, sizeof(tx_payload));
sktu_utun_fd_tx_burst(utun_fd, &tx_frame, 1);
ret = sktu_channel_port_rx_burst(&port, &rx_frame, 1);
assert(ret == 1);
sktu_dump_buffer(stderr, "Received", &rx_frame->bytes, rx_frame->len);
assert(uuid_compare(rx_frame->flow_uuid, ip_connection->nfr.nfr_flow_uuid) == 0);
ret = sktu_parse_ipv4_frame(rx_frame, &rx_payload, &rx_payload_len);
assert(ret == 0);
assert(rx_payload.packet_number == 1);
assert(strcmp(rx_payload.data, tx_payload.data) == 0);
sktu_frame_free(rx_frame);
sktu_frame_free(tx_frame);
sktu_destroy_nexus_flow(ip_connection);
sktu_destroy_nexus_flow(ip_listener);
}
static void
dev_init()
{
int error;
inet_pton(AF_INET, utun_addr_str, &our_ip);
inet_pton(AF_INET, peer_addr_str, &dst_ip);
inet_pton(AF_INET, broad_addr_str, &broad_ip);
zero_ip = (struct in_addr){.s_addr = htonl(INADDR_ANY)};
mask = sktc_make_in_addr(IN_CLASSC_NET);
sktc_config_fsw_rx_agg_tcp(0);
utun_fd = sktu_create_interface(SKTU_IFT_UTUN,
SKTU_IFF_ENABLE_NETIF | SKTU_IFF_NO_ATTACH_FSW);
sktu_get_interface_name(SKTU_IFT_UTUN, utun_fd, utun_ifname);
if (sktc_ifnet_add_addr(utun_ifname, &our_ip, &mask, &broad_ip) != 0) {
err(EX_OSERR, "Failed to add address for %s", utun_ifname);
}
inet_pton(AF_INET6, utun_addr_str_v6, &our_ip_v6);
inet_pton(AF_INET6, peer_addr_str_v6, &dst_ip_v6);
inet_pton(AF_INET6, zero_addr_str_v6, &zero_ip_v6);
error = sktc_ifnet_add_addr6(utun_ifname, &dst_ip_v6, &dst_ip_v6,
addr_prefix_length, 0);
if (sktc_ifnet_add_scoped_default_route(utun_ifname, our_ip) != 0) {
err(EX_OSERR, "Failed to add default route for %s\n", utun_ifname);
}
bzero(&handles, sizeof(handles));
strlcpy(handles.netif_ifname, utun_ifname, sizeof(handles.netif_ifname));
handles.netif_addr = our_ip;
handles.netif_mask = mask;
sktc_create_flowswitch_no_address(&handles, -1, -1, -1, -1, 0);
error = os_nexus_controller_bind_provider_instance(handles.controller,
handles.fsw_nx_uuid, NX_PORT, getpid(), NULL, NULL, 0,
NEXUS_BIND_PID);
SKTC_ASSERT_ERR(error == 0);
sktu_channel_port_init(&port, handles.fsw_nx_uuid, NX_PORT, true, false,
false);
assert(port.chan != NULL);
}
void
dev_fini(void)
{
sktc_cleanup_flowswitch(&handles);
close(utun_fd);
sktc_restore_fsw_rx_agg_tcp();
}
int
skt_flowlookup_main(int argc, char *argv[])
{
atexit(dev_fini);
dev_init();
test_tcp_flow_send();
test_tcp_flow_receive();
test_tcp_flow_listen(true);
test_tcp_flow_listen(false);
test_tcp_flow_listen_v6();
test_ip_flow_send();
test_ip_flow_receive();
test_ip_flow_listen(true);
test_ip_flow_listen(false);
return 0;
}
struct skywalk_test skt_flowlookup = {
.skt_testname = "flowlookup",
.skt_testdesc = "test flow lookup by send/receive of packets",
.skt_required_features = SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF | SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
.skt_main = skt_flowlookup_main,
};