1 /* 2 * Copyright (c) 2025 Apple Computer, 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 <darwintest.h> 30 #include <sys/sysctl.h> 31 #include <netinet/tcp_cache.h> 32 #include <arpa/inet.h> 33 #include <stdlib.h> 34 35 T_DECL(tcp_cache_list_sysctl, "Test retrieving TCP cache list via sysctl") 36 { 37 size_t size = 0; 38 int ret; 39 40 // First call to get the required buffer size 41 ret = sysctlbyname("net.inet.tcp.cache_list", NULL, &size, NULL, 0); 42 if (ret == -1) { 43 T_SKIP("sysctlbyname(\"net.inet.tcp.cache_list\") error: %d", errno); 44 } 45 46 T_LOG("TCP cache list size: %zu bytes", size); 47 48 if (size == 0) { 49 T_PASS("No TCP cache entries found"); 50 } 51 52 // Allocate buffer and retrieve the data 53 void *buffer = malloc(size); 54 T_QUIET; T_ASSERT_NOTNULL(buffer, "malloc buffer"); 55 56 ret = sysctlbyname("net.inet.tcp.cache_list", buffer, &size, NULL, 0); 57 T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname to get data"); 58 59 // Calculate number of entries 60 size_t num_entries = size / sizeof(struct tcp_cache_data); 61 T_LOG("Found %zu TCP cache entries", num_entries); 62 63 struct tcp_cache_data *entries = (struct tcp_cache_data *)buffer; 64 65 // Log all fields of each entry 66 for (size_t i = 0; i < num_entries; i++) { 67 struct tcp_cache_data *entry = &entries[i]; 68 69 T_LOG("Entry %zu:", i); 70 T_LOG(" tc_last_access: %u", entry->tc_last_access); 71 T_LOG(" tc_key.tck_family: %d", entry->tc_key.tck_family); 72 73 // Log source key info 74 T_LOG(" tc_key.tck_src.thk_family: %d", entry->tc_key.tck_src.thk_family); 75 if (entry->tc_key.tck_src.thk_family == AF_INET) { 76 T_LOG(" tc_key.tck_src IP: %s", inet_ntoa(entry->tc_key.tck_src.thk_ip.addr)); 77 } else if (entry->tc_key.tck_src.thk_family == AF_INET6) { 78 char addr_str[INET6_ADDRSTRLEN]; 79 inet_ntop(AF_INET6, &entry->tc_key.tck_src.thk_ip.addr6, addr_str, sizeof(addr_str)); 80 T_LOG(" tc_key.tck_src IPv6: %s", addr_str); 81 } 82 83 // Log destination address 84 if (entry->tc_key.tck_family == AF_INET) { 85 T_LOG(" tc_key.tck_dst IP: %s", inet_ntoa(entry->tc_key.tck_dst.addr)); 86 } else if (entry->tc_key.tck_family == AF_INET6) { 87 char addr_str[INET6_ADDRSTRLEN]; 88 inet_ntop(AF_INET6, &entry->tc_key.tck_dst.addr6, addr_str, sizeof(addr_str)); 89 T_LOG(" tc_key.tck_dst IPv6: %s", addr_str); 90 } 91 92 // Log TFO cookie info 93 T_LOG(" tc_tfo_cookie_len: %u", entry->tc_tfo_cookie_len); 94 if (entry->tc_tfo_cookie_len > 0) { 95 char cookie_hex[TFO_COOKIE_LEN_MAX * 2 + 1] = {0}; 96 for (int j = 0; j < entry->tc_tfo_cookie_len && j < TFO_COOKIE_LEN_MAX; j++) { 97 snprintf(cookie_hex + j * 2, 3, "%02x", entry->tc_tfo_cookie[j]); 98 } 99 T_LOG(" tc_tfo_cookie: %s", cookie_hex); 100 } 101 102 // Log MPTCP info 103 T_LOG(" tc_mptcp_version_confirmed: %u", entry->tc_mptcp_version_confirmed); 104 T_LOG(" tc_mptcp_version: %u", entry->tc_mptcp_version); 105 T_LOG(" tc_mptcp_next_version_try: %u", entry->tc_mptcp_next_version_try); 106 T_LOG(""); // Empty line between entries 107 } 108 109 free(buffer); 110 111 T_PASS("%s", __func__); 112 } 113 114 T_DECL(tcp_heuristics_list_sysctl, "Test retrieving TCP heuristics list via sysctl") 115 { 116 size_t size = 0; 117 int ret; 118 119 // First call to get the required buffer size 120 ret = sysctlbyname("net.inet.tcp.heuristics_list", NULL, &size, NULL, 0); 121 if (ret == -1) { 122 T_SKIP("sysctlbyname(\"net.inet.tcp.cache_list\") error: %d", errno); 123 } 124 125 T_LOG("TCP heuristics list size: %zu bytes", size); 126 127 if (size == 0) { 128 T_PASS("No TCP heuristics entries found"); 129 } 130 131 // Allocate buffer and retrieve the data 132 void *buffer = malloc(size); 133 T_QUIET; T_ASSERT_NOTNULL(buffer, "malloc buffer"); 134 135 ret = sysctlbyname("net.inet.tcp.heuristics_list", buffer, &size, NULL, 0); 136 T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname to get data"); 137 138 // Calculate number of entries 139 size_t num_entries = size / sizeof(struct tcp_heuristics_data); 140 T_LOG("Found %zu TCP heuristics entries", num_entries); 141 142 struct tcp_heuristics_data *entries = (struct tcp_heuristics_data *)buffer; 143 144 // Log all fields of each entry 145 for (size_t i = 0; i < num_entries; i++) { 146 struct tcp_heuristics_data *entry = &entries[i]; 147 148 T_LOG("Heuristics Entry %zu:", i); 149 T_LOG(" th_last_access: %u", entry->th_last_access); 150 T_LOG(" th_key.thk_family: %d", entry->th_key.thk_family); 151 152 // Log source key info 153 if (entry->th_key.thk_family == AF_INET) { 154 T_LOG(" th_key.thk_ip IP: %s", inet_ntoa(entry->th_key.thk_ip.addr)); 155 } else if (entry->th_key.thk_family == AF_INET6) { 156 char addr_str[INET6_ADDRSTRLEN]; 157 inet_ntop(AF_INET6, &entry->th_key.thk_ip.addr6, addr_str, sizeof(addr_str)); 158 T_LOG(" th_key.thk_ip IPv6: %s", addr_str); 159 } 160 161 // Log TFO heuristics 162 T_LOG(" th_tfo_data_loss: %u", entry->th_tfo_data_loss); 163 T_LOG(" th_tfo_req_loss: %u", entry->th_tfo_req_loss); 164 T_LOG(" th_tfo_data_rst: %u", entry->th_tfo_data_rst); 165 166 167 T_LOG(" th_tfo_req_rst: %u", entry->th_tfo_req_rst); 168 169 // Log MPTCP heuristics 170 T_LOG(" th_mptcp_loss: %u", entry->th_mptcp_loss); 171 T_LOG(" th_mptcp_success: %u", entry->th_mptcp_success); 172 173 // Log ECN heuristics 174 T_LOG(" th_ecn_droprst: %u", entry->th_ecn_droprst); 175 T_LOG(" th_ecn_synrst: %u", entry->th_ecn_synrst); 176 177 // Log timing information 178 T_LOG(" th_tfo_enabled_time: %u", entry->th_tfo_enabled_time); 179 T_LOG(" th_tfo_backoff_until: %u", entry->th_tfo_backoff_until); 180 T_LOG(" th_tfo_backoff: %u", entry->th_tfo_backoff); 181 T_LOG(" th_mptcp_backoff: %u", entry->th_mptcp_backoff); 182 T_LOG(" th_ecn_backoff: %u", entry->th_ecn_backoff); 183 184 // Log flags 185 T_LOG(" th_tfo_in_backoff: %u", entry->th_tfo_in_backoff); 186 T_LOG(" th_mptcp_in_backoff: %u", entry->th_mptcp_in_backoff); 187 T_LOG(" th_mptcp_heuristic_disabled: %u", entry->th_mptcp_heuristic_disabled); 188 T_LOG(""); // Empty line between entries 189 } 190 191 free(buffer); 192 193 T_PASS("%s", __func__); 194 } 195