2 * Copyright (c) 2017-2018 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/types.h>
30 #include <sys/sysctl.h>
32 #include <sys/mcache.h>
33 #include <sys/malloc.h>
34 #include <sys/kauth.h>
35 #include <sys/bitstring.h>
37 #include <sys/socket.h>
39 #include <kern/locks.h>
40 #include <kern/zalloc.h>
42 #include <libkern/libkern.h>
43 #include <mach/branch_predicates.h>
45 #include <net/kpi_interface.h>
46 #include <net/if_var.h>
47 #include <net/if_ports_used.h>
49 #include <netinet/in_pcb.h>
56 extern bool IOPMCopySleepWakeUUIDKey(char *buffer
, size_t buf_len
);
58 SYSCTL_DECL(_net_link_generic_system
);
60 SYSCTL_NODE(_net_link_generic_system
, OID_AUTO
, port_used
,
61 CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, "if port used");
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", "");
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", "");
73 static int use_test_wakeuuid
= 0;
74 static uuid_t test_wakeuuid
;
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, "");
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", "");
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", "");
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) */
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
, "");
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, "");
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, "");
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, "");
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);
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", "");
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", "");
134 static int if_ports_used_inited
= 0;
136 decl_lck_mtx_data(static, net_port_entry_head_lock
);
137 static lck_grp_t
*net_port_entry_head_lock_group
;
139 struct net_port_entry
{
140 SLIST_ENTRY(net_port_entry
) npe_next
;
141 struct net_port_info npe_npi
;
144 static struct zone
*net_port_entry_zone
= NULL
;
146 #define NET_PORT_ENTRY_ZONE_MAX 128
147 #define NET_PORT_ENTRY_ZONE_NAME "net_port_entry"
149 static SLIST_HEAD(net_port_entry_list
, net_port_entry
) net_port_entry_list
=
150 SLIST_HEAD_INITIALIZER(&net_port_entry_list
);
152 struct timeval wakeuiid_last_check
;
155 if_ports_used_init(void)
157 if (if_ports_used_inited
== 0) {
158 lck_grp_attr_t
*lck_grp_attributes
= NULL
;
159 lck_attr_t
*lck_attributes
= NULL
;
161 timerclear(&wakeuiid_last_check
);
162 uuid_clear(current_wakeuuid
);
163 uuid_clear(test_wakeuuid
);
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
);
169 lck_attributes
= lck_attr_alloc_init();
170 if (lck_attributes
== NULL
) {
171 panic("%s: lck_attr_alloc_init() failed", __func__
);
173 lck_mtx_init(&net_port_entry_head_lock
,
174 net_port_entry_head_lock_group
,
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
);
185 zone_change(net_port_entry_zone
, Z_EXPAND
, TRUE
);
186 zone_change(net_port_entry_zone
, Z_CALLERACCT
, FALSE
);
188 if_ports_used_inited
= 1;
190 lck_attr_free(lck_attributes
);
191 lck_grp_attr_free(lck_grp_attributes
);
196 net_port_entry_list_clear(void)
198 struct net_port_entry
*npe
;
200 LCK_MTX_ASSERT(&net_port_entry_head_lock
, LCK_MTX_ASSERT_OWNED
);
202 while ((npe
= SLIST_FIRST(&net_port_entry_list
)) != NULL
) {
203 SLIST_REMOVE_HEAD(&net_port_entry_list
, npe_next
);
205 zfree(net_port_entry_zone
, npe
);
207 net_port_entry_count
= 0;
208 net_port_entry_gen
++;
212 get_test_wake_uuid(uuid_t wakeuuid
)
214 if (__improbable(use_test_wakeuuid
)) {
215 if (!uuid_is_null(test_wakeuuid
)) {
216 if (wakeuuid
!= NULL
) {
217 uuid_copy(wakeuuid
, test_wakeuuid
);
229 is_wakeuuid_set(void)
232 * IOPMCopySleepWakeUUIDKey() tells if SleepWakeUUID is currently set
233 * That means we are currently in a sleep/wake cycle
235 return (get_test_wake_uuid(NULL
) || IOPMCopySleepWakeUUIDKey(NULL
, 0));
239 if_ports_used_update_wakeuuid(struct ifnet
*ifp
)
242 bool wakeuuid_is_set
= false;
243 bool updated
= false;
245 if (__improbable(use_test_wakeuuid
)) {
246 wakeuuid_is_set
= get_test_wake_uuid(wakeuuid
);
248 uuid_string_t wakeuuid_str
;
250 wakeuuid_is_set
= IOPMCopySleepWakeUUIDKey(wakeuuid_str
,
251 sizeof(wakeuuid_str
));
252 if (wakeuuid_is_set
) {
253 uuid_parse(wakeuuid_str
, wakeuuid
);
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
) : "");
264 wakeuuid_not_set_count
+= 1;
266 microtime(&wakeuuid_not_set_last_time
);
267 strlcpy(wakeuuid_not_set_last_if
, if_name(ifp
),
268 sizeof(wakeuuid_not_set_last_if
));
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
);
280 * Record the time last checked
282 microuptime(&wakeuiid_last_check
);
283 lck_mtx_unlock(&net_port_entry_head_lock
);
285 if (updated
&& if_ports_used_verbose
> 0) {
286 uuid_string_t uuid_str
;
288 uuid_unparse(current_wakeuuid
, uuid_str
);
289 log(LOG_ERR
, "%s: current wakeuuid %s\n",
296 net_port_info_equal(const struct net_port_info
*x
,
297 const struct net_port_info
*y
)
299 ASSERT(x
!= NULL
&& y
!= NULL
);
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) {
317 net_port_info_has_entry(const struct net_port_info
*npi
)
319 struct net_port_entry
*npe
;
321 LCK_MTX_ASSERT(&net_port_entry_head_lock
, LCK_MTX_ASSERT_OWNED
);
323 SLIST_FOREACH(npe
, &net_port_entry_list
, npe_next
) {
324 if (net_port_info_equal(&npe
->npe_npi
, npi
)) {
333 net_port_info_add_entry(const struct net_port_info
*npi
)
335 struct net_port_entry
*npe
= NULL
;
337 bool entry_added
= false;
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",
346 ntohs(npi
->npi_local_port
),
350 npi
->npi_effective_pid
);
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",
360 ntohs(npi
->npi_local_port
),
364 npi
->npi_effective_pid
);
367 bzero(npe
, sizeof(struct net_port_entry
));
369 memcpy(&npe
->npe_npi
, npi
, sizeof(npe
->npe_npi
));
371 lck_mtx_lock(&net_port_entry_head_lock
);
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
++;
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",
383 ntohs(npi
->npi_local_port
),
387 npi
->npi_effective_pid
);
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",
394 ntohs(npi
->npi_local_port
),
398 npi
->npi_effective_pid
);
402 lck_mtx_unlock(&net_port_entry_head_lock
);
404 if (entry_added
== false) {
405 zfree(net_port_entry_zone
, npe
);
408 return (entry_added
);
411 #if (DEVELOPMENT || DEBUG)
413 sysctl_new_test_wakeuuid SYSCTL_HANDLER_ARGS
415 #pragma unused(oidp, arg1, arg2)
418 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
421 if (req
->oldptr
== USER_ADDR_NULL
) {
422 req
->oldidx
= sizeof(uuid_t
);
425 if (req
->newptr
!= USER_ADDR_NULL
) {
426 uuid_generate(test_wakeuuid
);
428 error
= SYSCTL_OUT(req
, test_wakeuuid
,
429 MIN(sizeof(uuid_t
), req
->oldlen
));
435 sysctl_clear_test_wakeuuid SYSCTL_HANDLER_ARGS
437 #pragma unused(oidp, arg1, arg2)
440 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
443 if (req
->oldptr
== USER_ADDR_NULL
) {
444 req
->oldidx
= sizeof(uuid_t
);
447 if (req
->newptr
!= USER_ADDR_NULL
) {
448 uuid_clear(test_wakeuuid
);
450 error
= SYSCTL_OUT(req
, test_wakeuuid
,
451 MIN(sizeof(uuid_t
), req
->oldlen
));
456 #endif /* (DEVELOPMENT || DEBUG) */
459 sysctl_wakeuuid_not_set_last_time SYSCTL_HANDLER_ARGS
461 #pragma unused(oidp, arg1, arg2)
463 if (proc_is64bit(req
->p
)) {
464 struct user64_timeval tv
;
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
));
470 struct user32_timeval tv
;
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
));
479 sysctl_wakeuuid_not_set_last_if SYSCTL_HANDLER_ARGS
481 #pragma unused(oidp, arg1, arg2)
483 return SYSCTL_OUT(req
, &wakeuuid_not_set_last_if
,
484 strlen(wakeuuid_not_set_last_if
) + 1);
488 sysctl_net_port_info_list SYSCTL_HANDLER_ARGS
490 #pragma unused(oidp, arg1, arg2)
492 struct xnpigen xnpigen
;
493 struct net_port_entry
*npe
;
495 if ((error
= priv_check_cred(kauth_cred_get(),
496 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS
, 0)) != 0) {
499 lck_mtx_lock(&net_port_entry_head_lock
);
501 if (req
->oldptr
== USER_ADDR_NULL
) {
502 /* Add a 25 % cushion */
503 uint32_t cnt
= net_port_entry_count
;
505 req
->oldidx
= sizeof(struct xnpigen
) +
506 cnt
* sizeof(struct net_port_info
);
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
));
518 printf("%s: SYSCTL_OUT(xnpigen) error %d\n",
523 SLIST_FOREACH(npe
, &net_port_entry_list
, npe_next
) {
524 error
= SYSCTL_OUT(req
, &npe
->npe_npi
,
525 sizeof(struct net_port_info
));
527 printf("%s: SYSCTL_OUT(npi) error %d\n",
533 lck_mtx_unlock(&net_port_entry_head_lock
);
539 * Mirror the arguments of ifnet_get_local_ports_extended()
545 sysctl_get_ports_used SYSCTL_HANDLER_ARGS
548 int *name
= (int *)arg1
;
552 protocol_family_t protocol
;
555 u_int8_t
*bitfield
= NULL
;
557 if (req
->newptr
!= USER_ADDR_NULL
) {
562 * 3 is the required number of parameters: ifindex, protocol and flags
569 if (req
->oldptr
== USER_ADDR_NULL
) {
570 req
->oldidx
= bitstr_size(IP_PORTRANGE_SIZE
);
573 if (req
->oldlen
< bitstr_size(IP_PORTRANGE_SIZE
)) {
582 ifnet_head_lock_shared();
583 if (!IF_INDEX_IN_RANGE(idx
)) {
588 ifp
= ifindex2ifnet
[idx
];
591 bitfield
= _MALLOC(bitstr_size(IP_PORTRANGE_SIZE
), M_TEMP
,
593 if (bitfield
== NULL
) {
597 error
= ifnet_get_local_ports_extended(ifp
, protocol
, flags
, bitfield
);
599 printf("%s: ifnet_get_local_ports_extended() error %d\n",
603 error
= SYSCTL_OUT(req
, bitfield
, bitstr_size(IP_PORTRANGE_SIZE
));
605 if (bitfield
!= NULL
)
606 _FREE(bitfield
, M_TEMP
);
610 __private_extern__
void
611 if_ports_used_add_inpcb(const uint32_t ifindex
, const struct inpcb
*inp
)
613 struct net_port_info npi
;
614 struct socket
*so
= inp
->inp_socket
;
616 bzero(&npi
, sizeof(struct net_port_info
));
618 npi
.npi_if_index
= ifindex
;
620 npi
.npi_flags
|= NPIF_SOCKET
;
622 npi
.npi_timestamp
.tv_sec
= wakeuiid_last_check
.tv_sec
;
623 npi
.npi_timestamp
.tv_usec
= wakeuiid_last_check
.tv_usec
;
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
;
630 panic("%s: unexpected protocol %u for inp %p\n", __func__
,
631 SOCK_PROTO(inp
->inp_socket
), inp
);
634 uuid_copy(npi
.npi_flow_uuid
, inp
->necp_client_uuid
);
636 npi
.npi_local_port
= inp
->inp_lport
;
637 npi
.npi_foreign_port
= inp
->inp_fport
;
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
;
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
));
651 npi
.npi_owner_pid
= so
->last_pid
;
653 if (so
->last_pid
!= 0) {
654 proc_name(so
->last_pid
, npi
.npi_owner_pname
,
655 sizeof(npi
.npi_owner_pname
));
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
));
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
));
673 (void) net_port_info_add_entry(&npi
);