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