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