| 1 | /* |
| 2 | * Copyright (c) 2017-2018 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 <sys/types.h> |
| 30 | #include <sys/sysctl.h> |
| 31 | #include <sys/time.h> |
| 32 | #include <sys/mcache.h> |
| 33 | #include <sys/malloc.h> |
| 34 | #include <sys/kauth.h> |
| 35 | #include <sys/bitstring.h> |
| 36 | #include <sys/priv.h> |
| 37 | #include <sys/protosw.h> |
| 38 | #include <sys/socket.h> |
| 39 | |
| 40 | #include <kern/locks.h> |
| 41 | #include <kern/zalloc.h> |
| 42 | |
| 43 | #include <libkern/libkern.h> |
| 44 | |
| 45 | #include <net/kpi_interface.h> |
| 46 | #include <net/if_var.h> |
| 47 | #include <net/if_ports_used.h> |
| 48 | |
| 49 | #include <netinet/in_pcb.h> |
| 50 | |
| 51 | |
| 52 | #include <stdbool.h> |
| 53 | |
| 54 | #include <os/log.h> |
| 55 | |
| 56 | extern bool IOPMCopySleepWakeUUIDKey(char *buffer, size_t buf_len); |
| 57 | |
| 58 | SYSCTL_DECL(_net_link_generic_system); |
| 59 | |
| 60 | SYSCTL_NODE(_net_link_generic_system, OID_AUTO, port_used, |
| 61 | CTLFLAG_RW | CTLFLAG_LOCKED, 0, "if port used" ); |
| 62 | |
| 63 | static uuid_t current_wakeuuid; |
| 64 | SYSCTL_OPAQUE(_net_link_generic_system_port_used, OID_AUTO, current_wakeuuid, |
| 65 | CTLFLAG_RD|CTLFLAG_LOCKED, |
| 66 | current_wakeuuid, sizeof(uuid_t), "S,uuid_t" , "" ); |
| 67 | |
| 68 | static int sysctl_net_port_info_list SYSCTL_HANDLER_ARGS; |
| 69 | SYSCTL_PROC(_net_link_generic_system_port_used, OID_AUTO, list, |
| 70 | CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0, |
| 71 | sysctl_net_port_info_list, "S,xnpigen" , "" ); |
| 72 | |
| 73 | static int use_test_wakeuuid = 0; |
| 74 | static uuid_t test_wakeuuid; |
| 75 | |
| 76 | #if (DEVELOPMENT || DEBUG) |
| 77 | SYSCTL_INT(_net_link_generic_system_port_used, OID_AUTO, use_test_wakeuuid, |
| 78 | CTLFLAG_RW | CTLFLAG_LOCKED, |
| 79 | &use_test_wakeuuid, 0, "" ); |
| 80 | |
| 81 | int sysctl_new_test_wakeuuid SYSCTL_HANDLER_ARGS; |
| 82 | SYSCTL_PROC(_net_link_generic_system_port_used, OID_AUTO, new_test_wakeuuid, |
| 83 | CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0, |
| 84 | sysctl_new_test_wakeuuid, "S,uuid_t" , "" ); |
| 85 | |
| 86 | int sysctl_clear_test_wakeuuid SYSCTL_HANDLER_ARGS; |
| 87 | SYSCTL_PROC(_net_link_generic_system_port_used, OID_AUTO, clear_test_wakeuuid, |
| 88 | CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0, |
| 89 | sysctl_clear_test_wakeuuid, "S,uuid_t" , "" ); |
| 90 | |
| 91 | SYSCTL_OPAQUE(_net_link_generic_system_port_used, OID_AUTO, test_wakeuuid, |
| 92 | CTLFLAG_RD|CTLFLAG_LOCKED, |
| 93 | test_wakeuuid, sizeof(uuid_t), "S,uuid_t" , "" ); |
| 94 | #endif /* (DEVELOPMENT || DEBUG) */ |
| 95 | |
| 96 | static int sysctl_get_ports_used SYSCTL_HANDLER_ARGS; |
| 97 | SYSCTL_NODE(_net_link_generic_system, OID_AUTO, get_ports_used, |
| 98 | CTLFLAG_RD | CTLFLAG_LOCKED, |
| 99 | sysctl_get_ports_used, "" ); |
| 100 | |
| 101 | static uint32_t net_port_entry_count = 0; |
| 102 | SYSCTL_UINT(_net_link_generic_system_port_used, OID_AUTO, entry_count, |
| 103 | CTLFLAG_RW | CTLFLAG_LOCKED, |
| 104 | &net_port_entry_count, 0, "" ); |
| 105 | |
| 106 | static uint32_t net_port_entry_gen = 0; |
| 107 | SYSCTL_UINT(_net_link_generic_system_port_used, OID_AUTO, entry_gen, |
| 108 | CTLFLAG_RW | CTLFLAG_LOCKED, |
| 109 | &net_port_entry_gen, 0, "" ); |
| 110 | |
| 111 | static int if_ports_used_verbose = 0; |
| 112 | SYSCTL_INT(_net_link_generic_system_port_used, OID_AUTO, verbose, |
| 113 | CTLFLAG_RW | CTLFLAG_LOCKED, |
| 114 | &if_ports_used_verbose, 0, "" ); |
| 115 | |
| 116 | static unsigned long wakeuuid_not_set_count = 0; |
| 117 | SYSCTL_ULONG(_net_link_generic_system_port_used, OID_AUTO, |
| 118 | wakeuuid_not_set_count, CTLFLAG_RD | CTLFLAG_LOCKED, |
| 119 | &wakeuuid_not_set_count, 0); |
| 120 | |
| 121 | struct timeval wakeuuid_not_set_last_time; |
| 122 | int sysctl_wakeuuid_not_set_last_time SYSCTL_HANDLER_ARGS; |
| 123 | static SYSCTL_PROC(_net_link_generic_system_port_used, OID_AUTO, |
| 124 | wakeuuid_not_set_last_time, CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, |
| 125 | 0, 0, sysctl_wakeuuid_not_set_last_time, "S,timeval" , "" ); |
| 126 | |
| 127 | char wakeuuid_not_set_last_if [IFXNAMSIZ]; |
| 128 | int sysctl_wakeuuid_not_set_last_if SYSCTL_HANDLER_ARGS; |
| 129 | static SYSCTL_PROC(_net_link_generic_system_port_used, OID_AUTO, |
| 130 | wakeuuid_not_set_last_if, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_LOCKED, |
| 131 | 0, 0, sysctl_wakeuuid_not_set_last_if, "A" , "" ); |
| 132 | |
| 133 | |
| 134 | static int if_ports_used_inited = 0; |
| 135 | |
| 136 | decl_lck_mtx_data(static, net_port_entry_head_lock); |
| 137 | static lck_grp_t *net_port_entry_head_lock_group; |
| 138 | |
| 139 | struct net_port_entry { |
| 140 | SLIST_ENTRY(net_port_entry) npe_next; |
| 141 | struct net_port_info npe_npi; |
| 142 | }; |
| 143 | |
| 144 | static struct zone *net_port_entry_zone = NULL; |
| 145 | |
| 146 | #define NET_PORT_ENTRY_ZONE_MAX 128 |
| 147 | #define NET_PORT_ENTRY_ZONE_NAME "net_port_entry" |
| 148 | |
| 149 | static SLIST_HEAD(net_port_entry_list, net_port_entry) net_port_entry_list = |
| 150 | SLIST_HEAD_INITIALIZER(&net_port_entry_list); |
| 151 | |
| 152 | struct timeval wakeuiid_last_check; |
| 153 | |
| 154 | void |
| 155 | if_ports_used_init(void) |
| 156 | { |
| 157 | if (if_ports_used_inited == 0) { |
| 158 | lck_grp_attr_t *lck_grp_attributes = NULL; |
| 159 | lck_attr_t *lck_attributes = NULL; |
| 160 | |
| 161 | timerclear(&wakeuiid_last_check); |
| 162 | uuid_clear(current_wakeuuid); |
| 163 | uuid_clear(test_wakeuuid); |
| 164 | |
| 165 | lck_grp_attributes = lck_grp_attr_alloc_init(); |
| 166 | net_port_entry_head_lock_group = lck_grp_alloc_init( |
| 167 | "net port entry lock" , lck_grp_attributes); |
| 168 | |
| 169 | lck_attributes = lck_attr_alloc_init(); |
| 170 | if (lck_attributes == NULL) { |
| 171 | panic("%s: lck_attr_alloc_init() failed" , __func__); |
| 172 | } |
| 173 | lck_mtx_init(&net_port_entry_head_lock, |
| 174 | net_port_entry_head_lock_group, |
| 175 | lck_attributes); |
| 176 | |
| 177 | net_port_entry_count = 0; |
| 178 | net_port_entry_zone = zinit(sizeof(struct net_port_entry), |
| 179 | NET_PORT_ENTRY_ZONE_MAX * sizeof(struct net_port_entry), |
| 180 | 0, NET_PORT_ENTRY_ZONE_NAME); |
| 181 | if (net_port_entry_zone == NULL) { |
| 182 | panic("%s: zinit(%s) failed" , __func__, |
| 183 | NET_PORT_ENTRY_ZONE_NAME); |
| 184 | } |
| 185 | zone_change(net_port_entry_zone, Z_EXPAND, TRUE); |
| 186 | zone_change(net_port_entry_zone, Z_CALLERACCT, FALSE); |
| 187 | |
| 188 | if_ports_used_inited = 1; |
| 189 | |
| 190 | lck_attr_free(lck_attributes); |
| 191 | lck_grp_attr_free(lck_grp_attributes); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | static void |
| 196 | net_port_entry_list_clear(void) |
| 197 | { |
| 198 | struct net_port_entry *npe; |
| 199 | |
| 200 | LCK_MTX_ASSERT(&net_port_entry_head_lock, LCK_MTX_ASSERT_OWNED); |
| 201 | |
| 202 | while ((npe = SLIST_FIRST(&net_port_entry_list)) != NULL) { |
| 203 | SLIST_REMOVE_HEAD(&net_port_entry_list, npe_next); |
| 204 | |
| 205 | zfree(net_port_entry_zone, npe); |
| 206 | } |
| 207 | net_port_entry_count = 0; |
| 208 | net_port_entry_gen++; |
| 209 | } |
| 210 | |
| 211 | static bool |
| 212 | get_test_wake_uuid(uuid_t wakeuuid) |
| 213 | { |
| 214 | if (__improbable(use_test_wakeuuid)) { |
| 215 | if (!uuid_is_null(test_wakeuuid)) { |
| 216 | if (wakeuuid != NULL) { |
| 217 | uuid_copy(wakeuuid, test_wakeuuid); |
| 218 | } |
| 219 | return (true); |
| 220 | } else { |
| 221 | return (false); |
| 222 | } |
| 223 | } else { |
| 224 | return (false); |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | static bool |
| 229 | is_wakeuuid_set(void) |
| 230 | { |
| 231 | /* |
| 232 | * IOPMCopySleepWakeUUIDKey() tells if SleepWakeUUID is currently set |
| 233 | * That means we are currently in a sleep/wake cycle |
| 234 | */ |
| 235 | return (get_test_wake_uuid(NULL) || IOPMCopySleepWakeUUIDKey(NULL, 0)); |
| 236 | } |
| 237 | |
| 238 | void |
| 239 | if_ports_used_update_wakeuuid(struct ifnet *ifp) |
| 240 | { |
| 241 | uuid_t wakeuuid; |
| 242 | bool wakeuuid_is_set = false; |
| 243 | bool updated = false; |
| 244 | |
| 245 | if (__improbable(use_test_wakeuuid)) { |
| 246 | wakeuuid_is_set = get_test_wake_uuid(wakeuuid); |
| 247 | } else { |
| 248 | uuid_string_t wakeuuid_str; |
| 249 | |
| 250 | wakeuuid_is_set = IOPMCopySleepWakeUUIDKey(wakeuuid_str, |
| 251 | sizeof(wakeuuid_str)); |
| 252 | if (wakeuuid_is_set) { |
| 253 | uuid_parse(wakeuuid_str, wakeuuid); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | if (!wakeuuid_is_set) { |
| 258 | if (if_ports_used_verbose > 0) { |
| 259 | os_log_info(OS_LOG_DEFAULT, |
| 260 | "%s: SleepWakeUUID not set, " |
| 261 | "don't update the port list for %s\n" , |
| 262 | __func__, ifp != NULL ? if_name(ifp) : "" ); |
| 263 | } |
| 264 | wakeuuid_not_set_count += 1; |
| 265 | if (ifp != NULL) { |
| 266 | microtime(&wakeuuid_not_set_last_time); |
| 267 | strlcpy(wakeuuid_not_set_last_if, if_name(ifp), |
| 268 | sizeof(wakeuuid_not_set_last_if)); |
| 269 | } |
| 270 | return; |
| 271 | } |
| 272 | |
| 273 | lck_mtx_lock(&net_port_entry_head_lock); |
| 274 | if (uuid_compare(wakeuuid, current_wakeuuid) != 0) { |
| 275 | net_port_entry_list_clear(); |
| 276 | uuid_copy(current_wakeuuid, wakeuuid); |
| 277 | updated = true; |
| 278 | } |
| 279 | /* |
| 280 | * Record the time last checked |
| 281 | */ |
| 282 | microuptime(&wakeuiid_last_check); |
| 283 | lck_mtx_unlock(&net_port_entry_head_lock); |
| 284 | |
| 285 | if (updated && if_ports_used_verbose > 0) { |
| 286 | uuid_string_t uuid_str; |
| 287 | |
| 288 | uuid_unparse(current_wakeuuid, uuid_str); |
| 289 | log(LOG_ERR, "%s: current wakeuuid %s\n" , |
| 290 | __func__, |
| 291 | uuid_str); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | static bool |
| 296 | net_port_info_equal(const struct net_port_info *x, |
| 297 | const struct net_port_info *y) |
| 298 | { |
| 299 | ASSERT(x != NULL && y != NULL); |
| 300 | |
| 301 | if (x->npi_if_index == y->npi_if_index && |
| 302 | x->npi_local_port == y->npi_local_port && |
| 303 | x->npi_foreign_port == y->npi_foreign_port && |
| 304 | x->npi_owner_pid == y->npi_owner_pid && |
| 305 | x->npi_effective_pid == y->npi_effective_pid && |
| 306 | x->npi_flags == y->npi_flags && |
| 307 | memcmp(&x->npi_local_addr_, &y->npi_local_addr_, |
| 308 | sizeof(union in_addr_4_6)) == 0 && |
| 309 | memcmp(&x->npi_foreign_addr_, &y->npi_foreign_addr_, |
| 310 | sizeof(union in_addr_4_6)) == 0) { |
| 311 | return (true); |
| 312 | } |
| 313 | return (false); |
| 314 | } |
| 315 | |
| 316 | static bool |
| 317 | net_port_info_has_entry(const struct net_port_info *npi) |
| 318 | { |
| 319 | struct net_port_entry *npe; |
| 320 | |
| 321 | LCK_MTX_ASSERT(&net_port_entry_head_lock, LCK_MTX_ASSERT_OWNED); |
| 322 | |
| 323 | SLIST_FOREACH(npe, &net_port_entry_list, npe_next) { |
| 324 | if (net_port_info_equal(&npe->npe_npi, npi)) { |
| 325 | return (true); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | return (false); |
| 330 | } |
| 331 | |
| 332 | static bool |
| 333 | net_port_info_add_entry(const struct net_port_info *npi) |
| 334 | { |
| 335 | struct net_port_entry *npe = NULL; |
| 336 | uint32_t num = 0; |
| 337 | bool entry_added = false; |
| 338 | |
| 339 | ASSERT(npi != NULL); |
| 340 | |
| 341 | if (__improbable(is_wakeuuid_set() == false)) { |
| 342 | if (if_ports_used_verbose > 0) { |
| 343 | log(LOG_ERR, "%s: wakeuuid not set %u not adding " |
| 344 | "port: %u flags: 0x%xif: %u pid: %u epid %u\n" , |
| 345 | __func__, |
| 346 | ntohs(npi->npi_local_port), |
| 347 | npi->npi_flags, |
| 348 | npi->npi_if_index, |
| 349 | npi->npi_owner_pid, |
| 350 | npi->npi_effective_pid); |
| 351 | } |
| 352 | return (0); |
| 353 | } |
| 354 | |
| 355 | npe = zalloc(net_port_entry_zone); |
| 356 | if (__improbable(npe == NULL)) { |
| 357 | log(LOG_ERR, "%s: zalloc() failed for " |
| 358 | "port: %u flags: 0x%x if: %u pid: %u epid %u\n" , |
| 359 | __func__, |
| 360 | ntohs(npi->npi_local_port), |
| 361 | npi->npi_flags, |
| 362 | npi->npi_if_index, |
| 363 | npi->npi_owner_pid, |
| 364 | npi->npi_effective_pid); |
| 365 | return (0); |
| 366 | } |
| 367 | bzero(npe, sizeof(struct net_port_entry)); |
| 368 | |
| 369 | memcpy(&npe->npe_npi, npi, sizeof(npe->npe_npi)); |
| 370 | |
| 371 | lck_mtx_lock(&net_port_entry_head_lock); |
| 372 | |
| 373 | if (net_port_info_has_entry(npi) == false) { |
| 374 | SLIST_INSERT_HEAD(&net_port_entry_list, npe, npe_next); |
| 375 | num = net_port_entry_count++; |
| 376 | entry_added = true; |
| 377 | |
| 378 | if (if_ports_used_verbose > 0) { |
| 379 | log(LOG_ERR, "%s: num %u for " |
| 380 | "port: %u flags: 0x%x if: %u pid: %u epid %u\n" , |
| 381 | __func__, |
| 382 | num, |
| 383 | ntohs(npi->npi_local_port), |
| 384 | npi->npi_flags, |
| 385 | npi->npi_if_index, |
| 386 | npi->npi_owner_pid, |
| 387 | npi->npi_effective_pid); |
| 388 | } |
| 389 | } else { |
| 390 | if (if_ports_used_verbose > 0) { |
| 391 | log(LOG_ERR, "%s: entry already added " |
| 392 | "port: %u flags: 0x%x if: %u pid: %u epid %u\n" , |
| 393 | __func__, |
| 394 | ntohs(npi->npi_local_port), |
| 395 | npi->npi_flags, |
| 396 | npi->npi_if_index, |
| 397 | npi->npi_owner_pid, |
| 398 | npi->npi_effective_pid); |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | lck_mtx_unlock(&net_port_entry_head_lock); |
| 403 | |
| 404 | if (entry_added == false) { |
| 405 | zfree(net_port_entry_zone, npe); |
| 406 | npe = NULL; |
| 407 | } |
| 408 | return (entry_added); |
| 409 | } |
| 410 | |
| 411 | #if (DEVELOPMENT || DEBUG) |
| 412 | int |
| 413 | sysctl_new_test_wakeuuid SYSCTL_HANDLER_ARGS |
| 414 | { |
| 415 | #pragma unused(oidp, arg1, arg2) |
| 416 | int error = 0; |
| 417 | |
| 418 | if (kauth_cred_issuser(kauth_cred_get()) == 0) { |
| 419 | return (EPERM); |
| 420 | } |
| 421 | if (req->oldptr == USER_ADDR_NULL) { |
| 422 | req->oldidx = sizeof(uuid_t); |
| 423 | return (0); |
| 424 | } |
| 425 | if (req->newptr != USER_ADDR_NULL) { |
| 426 | uuid_generate(test_wakeuuid); |
| 427 | } |
| 428 | error = SYSCTL_OUT(req, test_wakeuuid, |
| 429 | MIN(sizeof(uuid_t), req->oldlen)); |
| 430 | |
| 431 | return (error); |
| 432 | } |
| 433 | |
| 434 | int |
| 435 | sysctl_clear_test_wakeuuid SYSCTL_HANDLER_ARGS |
| 436 | { |
| 437 | #pragma unused(oidp, arg1, arg2) |
| 438 | int error = 0; |
| 439 | |
| 440 | if (kauth_cred_issuser(kauth_cred_get()) == 0) { |
| 441 | return (EPERM); |
| 442 | } |
| 443 | if (req->oldptr == USER_ADDR_NULL) { |
| 444 | req->oldidx = sizeof(uuid_t); |
| 445 | return (0); |
| 446 | } |
| 447 | if (req->newptr != USER_ADDR_NULL) { |
| 448 | uuid_clear(test_wakeuuid); |
| 449 | } |
| 450 | error = SYSCTL_OUT(req, test_wakeuuid, |
| 451 | MIN(sizeof(uuid_t), req->oldlen)); |
| 452 | |
| 453 | return (error); |
| 454 | } |
| 455 | |
| 456 | #endif /* (DEVELOPMENT || DEBUG) */ |
| 457 | |
| 458 | int |
| 459 | sysctl_wakeuuid_not_set_last_time SYSCTL_HANDLER_ARGS |
| 460 | { |
| 461 | #pragma unused(oidp, arg1, arg2) |
| 462 | |
| 463 | if (proc_is64bit(req->p)) { |
| 464 | struct user64_timeval tv = {}; |
| 465 | |
| 466 | tv.tv_sec = wakeuuid_not_set_last_time.tv_sec; |
| 467 | tv.tv_usec = wakeuuid_not_set_last_time.tv_usec; |
| 468 | return SYSCTL_OUT(req, &tv, sizeof(tv)); |
| 469 | } else { |
| 470 | struct user32_timeval tv = {}; |
| 471 | |
| 472 | tv.tv_sec = wakeuuid_not_set_last_time.tv_sec; |
| 473 | tv.tv_usec = wakeuuid_not_set_last_time.tv_usec; |
| 474 | return SYSCTL_OUT(req, &tv, sizeof(tv)); |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | int |
| 479 | sysctl_wakeuuid_not_set_last_if SYSCTL_HANDLER_ARGS |
| 480 | { |
| 481 | #pragma unused(oidp, arg1, arg2) |
| 482 | |
| 483 | return SYSCTL_OUT(req, &wakeuuid_not_set_last_if, |
| 484 | strlen(wakeuuid_not_set_last_if) + 1); |
| 485 | } |
| 486 | |
| 487 | static int |
| 488 | sysctl_net_port_info_list SYSCTL_HANDLER_ARGS |
| 489 | { |
| 490 | #pragma unused(oidp, arg1, arg2) |
| 491 | int error = 0; |
| 492 | struct xnpigen xnpigen; |
| 493 | struct net_port_entry *npe; |
| 494 | |
| 495 | if ((error = priv_check_cred(kauth_cred_get(), |
| 496 | PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0)) != 0) { |
| 497 | return (EPERM); |
| 498 | } |
| 499 | lck_mtx_lock(&net_port_entry_head_lock); |
| 500 | |
| 501 | if (req->oldptr == USER_ADDR_NULL) { |
| 502 | /* Add a 25 % cushion */ |
| 503 | uint32_t cnt = net_port_entry_count; |
| 504 | cnt += cnt >> 4; |
| 505 | req->oldidx = sizeof(struct xnpigen) + |
| 506 | cnt * sizeof(struct net_port_info); |
| 507 | goto done; |
| 508 | } |
| 509 | |
| 510 | memset(&xnpigen, 0, sizeof(struct xnpigen)); |
| 511 | xnpigen.xng_len = sizeof(struct xnpigen); |
| 512 | xnpigen.xng_gen = net_port_entry_gen; |
| 513 | uuid_copy(xnpigen.xng_wakeuuid, current_wakeuuid); |
| 514 | xnpigen.xng_npi_count = net_port_entry_count; |
| 515 | xnpigen.xng_npi_size = sizeof(struct net_port_info); |
| 516 | error = SYSCTL_OUT(req, &xnpigen, sizeof (xnpigen)); |
| 517 | if (error != 0) { |
| 518 | printf("%s: SYSCTL_OUT(xnpigen) error %d\n" , |
| 519 | __func__, error); |
| 520 | goto done; |
| 521 | } |
| 522 | |
| 523 | SLIST_FOREACH(npe, &net_port_entry_list, npe_next) { |
| 524 | error = SYSCTL_OUT(req, &npe->npe_npi, |
| 525 | sizeof(struct net_port_info)); |
| 526 | if (error != 0) { |
| 527 | printf("%s: SYSCTL_OUT(npi) error %d\n" , |
| 528 | __func__, error); |
| 529 | goto done; |
| 530 | } |
| 531 | } |
| 532 | done: |
| 533 | lck_mtx_unlock(&net_port_entry_head_lock); |
| 534 | |
| 535 | return (error); |
| 536 | } |
| 537 | |
| 538 | /* |
| 539 | * Mirror the arguments of ifnet_get_local_ports_extended() |
| 540 | * ifindex |
| 541 | * protocol |
| 542 | * flags |
| 543 | */ |
| 544 | static int |
| 545 | sysctl_get_ports_used SYSCTL_HANDLER_ARGS |
| 546 | { |
| 547 | #pragma unused(oidp) |
| 548 | int *name = (int *)arg1; |
| 549 | int namelen = arg2; |
| 550 | int error = 0; |
| 551 | int idx; |
| 552 | protocol_family_t protocol; |
| 553 | u_int32_t flags; |
| 554 | ifnet_t ifp = NULL; |
| 555 | u_int8_t *bitfield = NULL; |
| 556 | |
| 557 | if (req->newptr != USER_ADDR_NULL) { |
| 558 | error = EPERM; |
| 559 | goto done; |
| 560 | } |
| 561 | /* |
| 562 | * 3 is the required number of parameters: ifindex, protocol and flags |
| 563 | */ |
| 564 | if (namelen != 3) { |
| 565 | error = ENOENT; |
| 566 | goto done; |
| 567 | } |
| 568 | |
| 569 | if (req->oldptr == USER_ADDR_NULL) { |
| 570 | req->oldidx = bitstr_size(IP_PORTRANGE_SIZE); |
| 571 | goto done; |
| 572 | } |
| 573 | if (req->oldlen < bitstr_size(IP_PORTRANGE_SIZE)) { |
| 574 | error = ENOMEM; |
| 575 | goto done; |
| 576 | } |
| 577 | |
| 578 | idx = name[0]; |
| 579 | protocol = name[1]; |
| 580 | flags = name[2]; |
| 581 | |
| 582 | ifnet_head_lock_shared(); |
| 583 | if (!IF_INDEX_IN_RANGE(idx)) { |
| 584 | ifnet_head_done(); |
| 585 | error = ENOENT; |
| 586 | goto done; |
| 587 | } |
| 588 | ifp = ifindex2ifnet[idx]; |
| 589 | ifnet_head_done(); |
| 590 | |
| 591 | bitfield = _MALLOC(bitstr_size(IP_PORTRANGE_SIZE), M_TEMP, |
| 592 | M_WAITOK | M_ZERO); |
| 593 | if (bitfield == NULL) { |
| 594 | error = ENOMEM; |
| 595 | goto done; |
| 596 | } |
| 597 | error = ifnet_get_local_ports_extended(ifp, protocol, flags, bitfield); |
| 598 | if (error != 0) { |
| 599 | printf("%s: ifnet_get_local_ports_extended() error %d\n" , |
| 600 | __func__, error); |
| 601 | goto done; |
| 602 | } |
| 603 | error = SYSCTL_OUT(req, bitfield, bitstr_size(IP_PORTRANGE_SIZE)); |
| 604 | done: |
| 605 | if (bitfield != NULL) |
| 606 | _FREE(bitfield, M_TEMP); |
| 607 | return (error); |
| 608 | } |
| 609 | |
| 610 | __private_extern__ void |
| 611 | if_ports_used_add_inpcb(const uint32_t ifindex, const struct inpcb *inp) |
| 612 | { |
| 613 | struct net_port_info npi; |
| 614 | struct socket *so = inp->inp_socket; |
| 615 | |
| 616 | bzero(&npi, sizeof(struct net_port_info)); |
| 617 | |
| 618 | npi.npi_if_index = ifindex; |
| 619 | |
| 620 | npi.npi_flags |= NPIF_SOCKET; |
| 621 | |
| 622 | npi.npi_timestamp.tv_sec = wakeuiid_last_check.tv_sec; |
| 623 | npi.npi_timestamp.tv_usec = wakeuiid_last_check.tv_usec; |
| 624 | |
| 625 | if (SOCK_PROTO(so) == IPPROTO_TCP) { |
| 626 | npi.npi_flags |= NPIF_TCP; |
| 627 | } else if (SOCK_PROTO(so) == IPPROTO_UDP) { |
| 628 | npi.npi_flags |= NPIF_UDP; |
| 629 | } else { |
| 630 | panic("%s: unexpected protocol %u for inp %p\n" , __func__, |
| 631 | SOCK_PROTO(inp->inp_socket), inp); |
| 632 | } |
| 633 | |
| 634 | uuid_copy(npi.npi_flow_uuid, inp->necp_client_uuid); |
| 635 | |
| 636 | npi.npi_local_port = inp->inp_lport; |
| 637 | npi.npi_foreign_port = inp->inp_fport; |
| 638 | |
| 639 | if (inp->inp_vflag & INP_IPV4) { |
| 640 | npi.npi_flags |= NPIF_IPV4; |
| 641 | npi.npi_local_addr_in = inp->inp_laddr; |
| 642 | npi.npi_foreign_addr_in = inp->inp_faddr; |
| 643 | } else { |
| 644 | npi.npi_flags |= NPIF_IPV6; |
| 645 | memcpy(&npi.npi_local_addr_in6, |
| 646 | &inp->in6p_laddr, sizeof (struct in6_addr)); |
| 647 | memcpy(&npi.npi_foreign_addr_in6, |
| 648 | &inp->in6p_faddr, sizeof (struct in6_addr)); |
| 649 | } |
| 650 | |
| 651 | npi.npi_owner_pid = so->last_pid; |
| 652 | |
| 653 | if (so->last_pid != 0) { |
| 654 | proc_name(so->last_pid, npi.npi_owner_pname, |
| 655 | sizeof(npi.npi_owner_pname)); |
| 656 | } |
| 657 | |
| 658 | if (so->so_flags & SOF_DELEGATED) { |
| 659 | npi.npi_flags |= NPIF_DELEGATED; |
| 660 | npi.npi_effective_pid = so->e_pid; |
| 661 | if (so->e_pid != 0) { |
| 662 | proc_name(so->e_pid, npi.npi_effective_pname, |
| 663 | sizeof(npi.npi_effective_pname)); |
| 664 | } |
| 665 | } else { |
| 666 | npi.npi_effective_pid = so->last_pid; |
| 667 | if (so->last_pid != 0) { |
| 668 | strlcpy(npi.npi_effective_pname, npi.npi_owner_pname, |
| 669 | sizeof(npi.npi_effective_pname)); |
| 670 | } |
| 671 | } |
| 672 | |
| 673 | (void) net_port_info_add_entry(&npi); |
| 674 | } |
| 675 | |
| 676 | |