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 | |