]>
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> | |
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)) { | |
d9a64523 | 464 | struct user64_timeval tv = {}; |
a39ff7e2 A |
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 { | |
d9a64523 | 470 | struct user32_timeval tv = {}; |
a39ff7e2 A |
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 |