1 /*
2 * Copyright (c) 2021, 2023 Apple Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <sys/types.h>
25 #include <sys/kern_control.h>
26 #include <sys/queue.h>
27 #include <sys/domain.h>
28 #include <sys/protosw.h>
29 #include <sys/syslog.h>
30 #include <sys/systm.h>
31 #include <sys/sysproto.h>
32 #include <sys/socketvar.h>
33 #include <IOKit/IOBSD.h>
34
35 #include <kern/sched_prim.h>
36 #include <kern/locks.h>
37 #include <kern/zalloc.h>
38 #include <kern/debug.h>
39 #include <net/necp.h>
40
41 #define _IP_VHL
42 #include <netinet/ip.h>
43 #include <netinet/in_pcb.h>
44 #include <string.h>
45 #include <libkern/libkern.h>
46 #include <net/sockaddr_utils.h>
47
48 extern int tcp_tcbhashsize;
49
50 int tracker_log_level = LOG_ERR;
51 static os_log_t tracker_db_log_handle = NULL;
52
53 /*
54 * Tracker Entry Garbage Collection:
55 */
56 static struct thread *g_tracker_gc_thread;
57 #define TRACKER_GC_RUN_INTERVAL_NSEC (10 * NSEC_PER_SEC) // GC wakes up periodically
58 #define TRACKER_GC_IDLE_TO (10) // age out entries when not used for a while
59
60 static int tracker_db_idle_timeout = TRACKER_GC_IDLE_TO;
61
62 /*
63 * Sysctls for debug logs control
64 */
65 SYSCTL_NODE(_net, OID_AUTO, tracker, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "tracker");
66
67 SYSCTL_INT(_net_tracker, OID_AUTO, log, CTLFLAG_RW | CTLFLAG_LOCKED,
68 &tracker_log_level, 0, "");
69
70 SYSCTL_INT(_net_tracker, OID_AUTO, idle_timeout, CTLFLAG_RW | CTLFLAG_LOCKED,
71 &tracker_db_idle_timeout, 0, "");
72
73 #define TRACKER_LOG(level, fmt, ...) \
74 do { \
75 if (tracker_log_level >= level && tracker_db_log_handle) { \
76 if (level == LOG_ERR) { \
77 os_log_error(tracker_db_log_handle, "TRACKER - %s:%d " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); \
78 } else { \
79 os_log(tracker_db_log_handle, "TRACKER - %s:%d " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); \
80 } \
81 } \
82 } while (0)
83
84 #define TRACKER_ENTRY_LOG(level, msg, entry, hash) \
85 do { \
86 if (tracker_log_level >= level) { \
87 tracker_entry_log(level, msg, entry, hash); \
88 } \
89 } while (0)
90
91 #define TRACKERHASHSIZE tcp_tcbhashsize
92
93 #define TRACKER_HASH_UUID_TO_BYTE(uuidptr) \
94 ( ((uint8_t *)uuidptr)[0] ^ ((uint8_t *)uuidptr)[1] ^ ((uint8_t *)uuidptr)[2] ^ ((uint8_t *)uuidptr)[3] ^ \
95 ((uint8_t *)uuidptr)[4] ^ ((uint8_t *)uuidptr)[5] ^ ((uint8_t *)uuidptr)[6] ^ ((uint8_t *)uuidptr)[7] ^ \
96 ((uint8_t *)uuidptr)[8] ^ ((uint8_t *)uuidptr)[9] ^ ((uint8_t *)uuidptr)[10] ^ ((uint8_t *)uuidptr)[11] ^ \
97 ((uint8_t *)uuidptr)[12] ^ ((uint8_t *)uuidptr)[13] ^ ((uint8_t *)uuidptr)[14] ^ ((uint8_t *)uuidptr)[15] )
98
99 #define TRACKER_HASH_WORD_TO_BYTE(wordptr) \
100 ( ((uint8_t *)wordptr)[0] ^ ((uint8_t *)wordptr)[1] ^ ((uint8_t *)wordptr)[2] ^ ((uint8_t *)wordptr)[3] )
101
102 #define TRACKER_HASH(uuidptr, wordptr0, wordptr1, wordptr2, wordptr3) \
103 ( TRACKER_HASH_WORD_TO_BYTE(wordptr0) ^ TRACKER_HASH_WORD_TO_BYTE(wordptr1) ^ TRACKER_HASH_WORD_TO_BYTE(wordptr2) ^ TRACKER_HASH_WORD_TO_BYTE(wordptr3) ^ \
104 TRACKER_HASH_UUID_TO_BYTE(uuidptr) )
105
106 #define TRACKER_SCRATCH_PAD_SIZE 200
107 #define TRACKER_DUMP_SCRATCH_PAD_SIZE 2048
108 #define TRACKER_TLV_HDR_LEN (sizeof(u_int8_t) + sizeof(u_int32_t))
109 #define TRACKER_BUFFER_ALLOC_MAX (1024 * 200)
110
111 static uint8_t scratch_pad_all[TRACKER_DUMP_SCRATCH_PAD_SIZE];
112 static uint8_t scratch_pad_entry[TRACKER_SCRATCH_PAD_SIZE];
113
114 #define TRACKER_HASH_ENTRY_HEADER_FIELDS \
115 LIST_ENTRY(tracker_hash_entry) entry_link; \
116 uuid_t app_uuid; \
117 sa_family_t address_family; \
118 union { \
119 struct in_addr_4in6 addr46; \
120 struct in6_addr addr6; \
121 } address; \
122 u_int64_t lastused;
123
124 typedef struct tracker_hash_entry {
125 TRACKER_HASH_ENTRY_HEADER_FIELDS
126 tracker_metadata_t metadata;
127 } tracker_hash_entry_t;
128
129 typedef struct tracker_hash_entry_short {
130 TRACKER_HASH_ENTRY_HEADER_FIELDS
131 tracker_metadata_short_t metadata;
132 } tracker_hash_entry_short_t;
133
134 LIST_HEAD(trackerhashhead, tracker_hash_entry);
135
136 struct tracker_db {
137 struct trackerhashhead * __indexable tracker_hashbase;
138 u_long tracker_hashmask;
139 uint32_t tracker_count;
140 uint32_t tracker_count_short;
141 uint32_t max_link_count;
142 };
143
144 static KALLOC_TYPE_DEFINE(tracker_hash_entry_zone,
145 struct tracker_hash_entry, NET_KT_DEFAULT);
146
147 static KALLOC_TYPE_DEFINE(tracker_hash_entry_short_zone,
148 struct tracker_hash_entry_short, NET_KT_DEFAULT);
149
150 static struct tracker_db g_tracker_db = { };
151
152 static LCK_GRP_DECLARE(g_tracker_lck_grp, "tracker");
153 static LCK_RW_DECLARE(g_tracker_lck_rw, &g_tracker_lck_grp);
154
155 #define TRACKER_LOCK_EXCLUSIVE lck_rw_lock_exclusive(&g_tracker_lck_rw);
156 #define TRACKER_UNLOCK_EXCLUSIVE lck_rw_unlock_exclusive(&g_tracker_lck_rw);
157 #define TRACKER_LOCK_SHARED lck_rw_lock_shared(&g_tracker_lck_rw);
158 #define TRACKER_UNLOCK_SHARED lck_rw_unlock_shared(&g_tracker_lck_rw);
159
160 static void tracker_gc_thread_func(void *v, wait_result_t w);
161 static void tracker_entry_expire(void *v, wait_result_t w);
162
163 #define ALLOC_ENTRY(flags, entry) \
164 if (flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) { \
165 tracker_hash_entry_short_t *short_entry = zalloc_flags(tracker_hash_entry_short_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL); \
166 if (short_entry == NULL) { \
167 TRACKER_LOG(LOG_ERR, "Failed to allocate tracker IP entry (Short)"); \
168 } else { \
169 entry = (tracker_hash_entry_t *)short_entry; \
170 } \
171 } else { \
172 entry = zalloc_flags(tracker_hash_entry_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL); \
173 if (entry == NULL) { \
174 TRACKER_LOG(LOG_ERR, "Failed to allocate tracker IP entry"); \
175 } \
176 }
177
178 #define FREE_ENTRY(entry) \
179 if (entry) { \
180 if (entry->metadata.flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) { \
181 zfree(tracker_hash_entry_short_zone, entry); \
182 } else { \
183 zfree(tracker_hash_entry_zone, entry); \
184 } \
185 }
186
187 #define SIZE_OF_ENTRY(entry) \
188 ((entry && entry->metadata.flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) ? \
189 sizeof(struct tracker_hash_entry_short) : entry ? sizeof(struct tracker_hash_entry) : 0)
190
191 #define STRLEN_SRC_DOMAIN strbuflen((char *)src_domain_buffer, src_domain_buffer_size)
192 #define STRLEN_SRC_DOMAIN_OWNER strbuflen((char *)src_domain_owner_buffer, src_domain_owner_buffer_size)
193 #define STRLEN_DST_DOMAIN strbuflen((char *)dst_domain_buffer, dst_domain_buffer_size)
194 #define STRLEN_DST_DOMAIN_OWNER strbuflen((char *)dst_domain_owner_buffer, dst_domain_owner_buffer_size)
195
196 #define GET_METADATA_BUFFERS_DST(metadata) \
197 size_t dst_domain_max = 0; \
198 size_t dst_domain_buffer_size = 0; \
199 uint8_t * __sized_by(dst_domain_buffer_size) dst_domain_buffer = NULL; \
200 size_t dst_domain_owner_buffer_size = 0; \
201 uint8_t * __sized_by(dst_domain_owner_buffer_size) dst_domain_owner_buffer = NULL; \
202 if (metadata != NULL) { \
203 if (metadata->flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) { \
204 tracker_metadata_short_t *short_metadata = (tracker_metadata_short_t *)metadata; \
205 dst_domain_max = TRACKER_DOMAIN_SHORT_MAX; \
206 dst_domain_buffer = (uint8_t *)(&short_metadata->domain); \
207 dst_domain_buffer_size = dst_domain_max + 1; \
208 dst_domain_owner_buffer = (uint8_t *)(&short_metadata->domain_owner); \
209 dst_domain_owner_buffer_size = dst_domain_max + 1; \
210 } else { \
211 dst_domain_max = TRACKER_DOMAIN_MAX; \
212 dst_domain_buffer = (uint8_t *)(&metadata->domain); \
213 dst_domain_buffer_size = dst_domain_max + 1; \
214 dst_domain_owner_buffer = (uint8_t *)(&metadata->domain_owner); \
215 dst_domain_owner_buffer_size = dst_domain_max + 1; \
216 } \
217 }
218
219 #define GET_METADATA_BUFFERS_SRC(metadata) \
220 size_t src_domain_max = 0; \
221 size_t src_domain_buffer_size = 0; \
222 uint8_t * __sized_by(src_domain_buffer_size) src_domain_buffer = NULL; \
223 size_t src_domain_owner_buffer_size = 0; \
224 uint8_t * __sized_by(src_domain_owner_buffer_size) src_domain_owner_buffer = NULL; \
225 if (metadata != NULL) { \
226 if (metadata->flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) { \
227 tracker_metadata_short_t *short_metadata = (tracker_metadata_short_t *)metadata; \
228 src_domain_max = TRACKER_DOMAIN_SHORT_MAX; \
229 src_domain_buffer = (uint8_t *)(&short_metadata->domain); \
230 src_domain_buffer_size = src_domain_max + 1; \
231 src_domain_owner_buffer = (uint8_t *)(&short_metadata->domain_owner); \
232 src_domain_owner_buffer_size = src_domain_max + 1; \
233 } else { \
234 src_domain_max = TRACKER_DOMAIN_MAX; \
235 src_domain_buffer = (uint8_t *)(&metadata->domain); \
236 src_domain_buffer_size = src_domain_max + 1; \
237 src_domain_owner_buffer = (uint8_t *)(&metadata->domain_owner); \
238 src_domain_owner_buffer_size = src_domain_max + 1; \
239 } \
240 }
241
242 static int
tracker_db_init(void)243 tracker_db_init(void)
244 {
245 tracker_db_log_handle = os_log_create("com.apple.xnu.kern.tracker_db", "tracker_db");
246
247 void * __single hash = hashinit(TRACKERHASHSIZE, M_TRACKER, &g_tracker_db.tracker_hashmask);
248 g_tracker_db.tracker_hashbase = __unsafe_forge_bidi_indexable(struct trackerhashhead *, hash, TRACKERHASHSIZE * sizeof(void *));
249
250 if (g_tracker_db.tracker_hashbase == NULL) {
251 TRACKER_LOG(LOG_ERR, "Failed to initialize");
252 return ENOMEM;
253 }
254 g_tracker_db.tracker_count = 0;
255
256 TRACKER_LOG(LOG_DEBUG, "Initialized: hashsize %d hashmask %lX", TRACKERHASHSIZE, g_tracker_db.tracker_hashmask);
257
258 // Spawn thread for gargage collection
259 if (kernel_thread_start(tracker_gc_thread_func, NULL,
260 &g_tracker_gc_thread) != KERN_SUCCESS) {
261 panic_plain("%s: Can't create Tracker GC thread", __func__);
262 /* NOTREACHED */
263 }
264 /* this must not fail */
265 VERIFY(g_tracker_gc_thread != NULL);
266
267 return 0;
268 }
269
270 static boolean_t
copy_metadata(tracker_metadata_t * dst_metadata,tracker_metadata_t * src_metadata)271 copy_metadata(tracker_metadata_t *dst_metadata, tracker_metadata_t *src_metadata)
272 {
273 bool is_short = false;
274
275 if (dst_metadata == NULL || src_metadata == NULL) {
276 return false;
277 }
278
279 GET_METADATA_BUFFERS_DST(dst_metadata)
280 GET_METADATA_BUFFERS_SRC(src_metadata)
281 if (dst_domain_max == 0 || src_domain_max == 0) {
282 TRACKER_LOG(LOG_ERR, "Failed to retrieve metadata domain buffers for copy");
283 return false;
284 }
285
286 size_t src_domain_len = STRLEN_SRC_DOMAIN;
287 size_t src_domain_owner_len = STRLEN_SRC_DOMAIN_OWNER;
288
289 if ((src_domain_len > dst_domain_max) || (src_domain_owner_len > dst_domain_max)) {
290 TRACKER_LOG(LOG_ERR, "Failed to copy metadata, dst buffer size too small");
291 return false;
292 }
293
294 if (src_domain_buffer[0]) {
295 size_t dst_domain_len = STRLEN_DST_DOMAIN;
296 if (dst_domain_len != src_domain_len ||
297 strbufcmp((char *)dst_domain_buffer, dst_domain_buffer_size, (char *)src_domain_buffer, src_domain_buffer_size)) {
298 if (src_domain_len <= dst_domain_max) {
299 bcopy(src_domain_buffer, dst_domain_buffer, src_domain_len);
300 dst_domain_buffer[src_domain_len] = 0;
301 }
302 }
303 } else {
304 dst_domain_buffer[0] = 0;
305 }
306
307 if (src_domain_owner_buffer[0]) {
308 size_t dst_domain_owner_len = STRLEN_DST_DOMAIN_OWNER;
309 if (dst_domain_owner_len != src_domain_owner_len ||
310 strbufcmp((char *)dst_domain_owner_buffer, dst_domain_owner_buffer_size, (char *)src_domain_owner_buffer, src_domain_owner_buffer_size)) {
311 if (src_domain_owner_len <= dst_domain_max) {
312 bcopy(src_domain_owner_buffer, dst_domain_owner_buffer, src_domain_owner_len);
313 dst_domain_owner_buffer[src_domain_owner_len] = 0;
314 }
315 }
316 } else {
317 dst_domain_owner_buffer[0] = 0;
318 }
319
320 is_short = (dst_metadata->flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT);
321 dst_metadata->flags = src_metadata->flags;
322 if (is_short) {
323 dst_metadata->flags |= SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT;
324 } else {
325 dst_metadata->flags &= ~SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT;
326 }
327
328 return true;
329 }
330
331 static int
fill_hash_entry(struct tracker_hash_entry * entry,uuid_t appuuid,struct sockaddr * address)332 fill_hash_entry(struct tracker_hash_entry *entry, uuid_t appuuid, struct sockaddr *address)
333 {
334 struct sockaddr_in *sin = NULL;
335 struct sockaddr_in6 *sin6 = NULL;
336
337 if (uuid_is_null(entry->app_uuid)) {
338 if (appuuid == NULL || uuid_is_null(appuuid)) {
339 return EINVAL;
340 }
341 uuid_copy(entry->app_uuid, appuuid);
342 }
343
344 if (address == NULL) {
345 TRACKER_LOG(LOG_ERR, "Missing remote address");
346 return EINVAL;
347 }
348
349 entry->lastused = net_uptime();
350
351 switch (address->sa_family) {
352 case AF_INET:
353 sin = SIN(address);
354 if (sin->sin_len < sizeof(*sin)) {
355 return EINVAL;
356 }
357 if (sin->sin_addr.s_addr) {
358 entry->address.addr46.ia46_addr4.s_addr = sin->sin_addr.s_addr;
359 }
360 entry->address_family = AF_INET;
361 return 0;
362 case AF_INET6:
363 sin6 = SIN6(address);
364 if (sin6->sin6_len < sizeof(*sin6)) {
365 return EINVAL;
366 }
367 if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
368 entry->address.addr6 = sin6->sin6_addr;
369 }
370 entry->address_family = AF_INET6;
371 return 0;
372 default:
373 TRACKER_LOG(LOG_ERR, "Invalid address family <%d>", address->sa_family);
374 return EINVAL;
375 }
376 }
377
378 static inline void
tracker_entry_log(int log_level,char * log_msg,struct tracker_hash_entry * entry,uint32_t hash)379 tracker_entry_log(int log_level, char *log_msg, struct tracker_hash_entry *entry, uint32_t hash)
380 {
381 char addr_buffer[MAX_IPv6_STR_LEN + 6];
382 const void *addr;
383
384 if (entry == NULL) {
385 return;
386 }
387
388 switch (entry->address_family) {
389 case AF_INET6:
390 addr = &entry->address.addr6;
391 inet_ntop(AF_INET6, addr, addr_buffer, sizeof(addr_buffer));
392 break;
393 case AF_INET:
394 addr = &entry->address.addr46.ia46_addr4.s_addr;
395 inet_ntop(AF_INET, addr, addr_buffer, sizeof(addr_buffer));
396 break;
397 default:
398 return;
399 }
400
401 GET_METADATA_BUFFERS_DST((&entry->metadata))
402
403 uint8_t *ptr = (uint8_t *)&entry->app_uuid;
404 TRACKER_LOG(log_level, "%s - %s <%s> len %d <%s> len %d <flags %X> %x%x%x%x-%x%x%x%x-%x%x%x%x-%x%x%x%x (hash 0x%X hashsize %d)", log_msg ? log_msg : "n/a",
405 addr_buffer, dst_domain_buffer, (int)STRLEN_DST_DOMAIN, dst_domain_owner_buffer, (int)STRLEN_DST_DOMAIN_OWNER,
406 entry->metadata.flags,
407 ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
408 ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15],
409 hash, TRACKERHASHSIZE);
410 }
411
412 static inline struct tracker_hash_entry *
tracker_search_and_insert(struct tracker_db * db,struct tracker_hash_entry * matchentry,boolean_t insert)413 tracker_search_and_insert(struct tracker_db *db, struct tracker_hash_entry *matchentry, boolean_t insert)
414 {
415 u_int32_t key0 = 0, key1 = 0, key2 = 0, key3 = 0;
416 struct trackerhashhead *trackerhash = NULL;
417 struct tracker_hash_entry * __single nextentry = NULL;
418 int hash_element = 0;
419 int count = 0;
420
421 if (db == NULL || matchentry == NULL) {
422 return NULL;
423 }
424
425 if (matchentry->address_family == AF_INET6) {
426 key0 = matchentry->address.addr6.s6_addr32[0];
427 key1 = matchentry->address.addr6.s6_addr32[1];
428 key2 = matchentry->address.addr6.s6_addr32[2];
429 key3 = matchentry->address.addr6.s6_addr32[3];
430 } else {
431 key0 = matchentry->address.addr46.ia46_addr4.s_addr;
432 }
433 hash_element = TRACKER_HASH(&matchentry->app_uuid, &key0, &key1, &key2, &key3) & db->tracker_hashmask;
434 trackerhash = &db->tracker_hashbase[hash_element];
435
436 LIST_FOREACH(nextentry, trackerhash, entry_link) {
437 count++;
438
439 if (uuid_compare(nextentry->app_uuid, matchentry->app_uuid) != 0) {
440 continue;
441 }
442
443 if ((nextentry->address_family == AF_INET && matchentry->address_family == AF_INET &&
444 nextentry->address.addr46.ia46_addr4.s_addr == matchentry->address.addr46.ia46_addr4.s_addr) ||
445 (nextentry->address_family == AF_INET6 && matchentry->address_family == AF_INET6 &&
446 IN6_ARE_ADDR_EQUAL(&nextentry->address.addr6, &matchentry->address.addr6))) {
447 TRACKER_ENTRY_LOG(LOG_DEBUG, "Matched entry", nextentry, hash_element);
448 if (db->max_link_count == 0 || db->max_link_count < count) {
449 db->max_link_count = count;
450 TRACKER_LOG(LOG_DEBUG, "Max link count %d (hash 0x%X)", db->max_link_count, hash_element);
451 }
452
453 // If this is for insert and we found an existing entry, update the metadata if different.
454 // Different domain aliases may resolve to the same IP, but we only keep one entry for the same
455 // IP address. Therefore, we update to the last metadata.
456 if (insert) {
457 if (copy_metadata(&nextentry->metadata, &matchentry->metadata) == true) {
458 TRACKER_ENTRY_LOG(LOG_DEBUG, "Updated entry", nextentry, hash_element);
459 return nextentry;
460 } else {
461 // Failed to update found entry, delete it from db and allow insertion of new entry.
462 TRACKER_ENTRY_LOG(LOG_ERR, "Failed to Update entry, deleting found entry", nextentry, hash_element);
463 g_tracker_db.tracker_count--;
464 if (nextentry->metadata.flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) {
465 g_tracker_db.tracker_count_short--;
466 }
467 LIST_REMOVE(nextentry, entry_link);
468 FREE_ENTRY(nextentry);
469 break;
470 }
471 } else {
472 return nextentry;
473 }
474 }
475 }
476 if (db->max_link_count == 0 || db->max_link_count < count) {
477 db->max_link_count = count;
478 TRACKER_LOG(LOG_DEBUG, "Max link count %d (hash 0x%X)", db->max_link_count, hash_element);
479 }
480
481 // Entry not found, insert it if requested.
482 if (insert) {
483 LIST_INSERT_HEAD(trackerhash, matchentry, entry_link);
484
485 // Wake gc thread if this is first flow added
486 if (db->tracker_count == 0) {
487 thread_wakeup((caddr_t)&db->tracker_count);
488 }
489
490 db->tracker_count++;
491 if (matchentry->metadata.flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) {
492 g_tracker_db.tracker_count_short++;
493 }
494 TRACKER_ENTRY_LOG(LOG_DEBUG, "Added entry", matchentry, hash_element);
495 TRACKER_LOG(LOG_DEBUG, "Total entries %d (hashmask 0x%lX)", db->tracker_count, db->tracker_hashmask);
496 }
497
498 return NULL;
499 }
500
501 static int
tracker_retrieve_attribute(u_int8_t * __sized_by (buffer_length)buffer,size_t buffer_length,u_int8_t type,u_int8_t * __sized_by (out_max_size)out_buffer,size_t out_size,size_t out_max_size)502 tracker_retrieve_attribute(u_int8_t * __sized_by(buffer_length)buffer, size_t buffer_length, u_int8_t type, u_int8_t * __sized_by(out_max_size)out_buffer, size_t out_size, size_t out_max_size)
503 {
504 int cursor = 0;
505 size_t value_size = 0;
506 u_int8_t *value = NULL;
507
508 cursor = necp_buffer_find_tlv(buffer, (u_int32_t)buffer_length, 0, type, NULL, 0);
509 if (cursor < 0) {
510 TRACKER_LOG(LOG_DEBUG, "No tracker attribute of type %d found in parameters", type);
511 return ENOENT;
512 }
513
514 value_size = necp_buffer_get_tlv_length(buffer, buffer_length, cursor);
515 if (out_size && value_size != out_size) {
516 TRACKER_LOG(LOG_ERR, "Wrong size for tracker attribute type %d size %zu <got size %zu>", type, out_size, value_size);
517 return EINVAL;
518 }
519 if (value_size > out_max_size) {
520 TRACKER_LOG(LOG_ERR, "Exceeded max size (%zu) - tracker attribute type %d size %zu", out_max_size, type, value_size);
521 return EINVAL;
522 }
523
524 value = necp_buffer_get_tlv_value(buffer, buffer_length, cursor, NULL);
525 if (value == NULL) {
526 TRACKER_LOG(LOG_ERR, "Failed to get value for tracker attribute type %d size %zu", type, value_size);
527 return EINVAL;
528 }
529
530 memcpy(out_buffer, value, value_size);
531 return 0;
532 }
533
534 static int
tracker_add(struct proc * p,struct tracker_action_args * uap,int * retval)535 tracker_add(struct proc *p, struct tracker_action_args *uap, int *retval)
536 {
537 uint8_t scratch_pad[TRACKER_SCRATCH_PAD_SIZE] = { };
538 struct sockaddr_in6 addrBuffer = { };
539 struct sockopt sopt = { };
540 struct tracker_hash_entry * __single entry = NULL;
541 struct tracker_db *db = NULL;
542 sa_family_t address_family = 0;
543 u_int address_size = 0;
544 u_int8_t * __indexable buffer = scratch_pad;
545 size_t buffer_size = 0;
546 int error = 0;
547 uint32_t flags = 0;
548
549 // Make sure parameter blob is valid
550 if (uap->buffer == 0 || uap->buffer_size == 0) {
551 *retval = EINVAL;
552 return EINVAL;
553 }
554
555 // If scratchpad not large enough, allocate memory
556 buffer_size = uap->buffer_size;
557 if (buffer_size > sizeof(scratch_pad)) {
558 if (buffer_size > TRACKER_BUFFER_ALLOC_MAX) {
559 TRACKER_LOG(LOG_ERR, "Failed to allocate buffer, size exceeded max allowed");
560 *retval = ENOMEM;
561 return ENOMEM;
562 }
563 buffer = (u_int8_t *)kalloc_data(buffer_size, Z_WAITOK | Z_ZERO);
564 if (buffer == NULL) {
565 *retval = ENOMEM;
566 return ENOMEM;
567 }
568 }
569 sopt.sopt_val = uap->buffer;
570 sopt.sopt_valsize = uap->buffer_size;
571 sopt.sopt_p = p;
572 error = sooptcopyin(&sopt, buffer, buffer_size, 0);
573 if (error) {
574 TRACKER_LOG(LOG_ERR, "Failed to copy parameters");
575 goto cleanup;
576 }
577
578 // Address Family (Required)
579 error = tracker_retrieve_attribute(buffer, buffer_size, SO_TRACKER_ATTRIBUTE_ADDRESS_FAMILY, (u_int8_t *)&address_family, sizeof(address_family), sizeof(address_family));
580 if (error) {
581 TRACKER_LOG(LOG_ERR, "Could not retrieve address family TLV from parameters");
582 goto cleanup;
583 }
584 if (address_family != AF_INET6 && address_family != AF_INET) {
585 error = EINVAL;
586 goto cleanup;
587 }
588 address_size = (address_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
589
590 // Address (Required)
591 error = tracker_retrieve_attribute(buffer, buffer_size, SO_TRACKER_ATTRIBUTE_ADDRESS, __SA_UTILS_CONV_TO_BYTES(&addrBuffer), address_size, address_size);
592 if (error) {
593 TRACKER_LOG(LOG_ERR, "Could not retrieve address TLV from parameters");
594 goto cleanup;
595 }
596 if (address_family != addrBuffer.sin6_family) {
597 TRACKER_LOG(LOG_ERR, "Address family parameter and address parameter family mismatch <%d != %d>",
598 address_family, addrBuffer.sin6_family);
599 error = EINVAL;
600 goto cleanup;
601 }
602
603 // Flags (Optional), so if not present, ignore and proceed.
604 error = tracker_retrieve_attribute(buffer, buffer_size, SO_TRACKER_ATTRIBUTE_FLAGS, (u_int8_t *)&flags, sizeof(flags), sizeof(flags));
605 if (error == EINVAL) {
606 TRACKER_LOG(LOG_INFO, "Could not retrieve flags TLV from parameters");
607 goto cleanup;
608 }
609
610 ALLOC_ENTRY(flags, entry)
611 if (entry == NULL) {
612 error = ENOMEM;
613 goto cleanup;
614 }
615 entry->metadata.flags = flags;
616 GET_METADATA_BUFFERS_DST((&entry->metadata))
617
618 // APP UUID (Required)
619 error = tracker_retrieve_attribute(buffer, buffer_size, SO_TRACKER_ATTRIBUTE_APP_UUID, (u_int8_t *)&entry->app_uuid, sizeof(uuid_t), sizeof(uuid_t));
620 if (error) {
621 TRACKER_LOG(LOG_ERR, "Could not retrieve APP UUID TLV from parameters");
622 error = EINVAL;
623 goto cleanup;
624 }
625
626 // Domain (Required)
627 error = tracker_retrieve_attribute(buffer, buffer_size, SO_TRACKER_ATTRIBUTE_DOMAIN, dst_domain_buffer, 0, dst_domain_max);
628 if (error) {
629 TRACKER_LOG(LOG_ERR, "Could not retrieve domain TLV from parameters");
630 error = EINVAL;
631 goto cleanup;
632 }
633
634 if (entry->metadata.flags & SO_TRACKER_ATTRIBUTE_FLAGS_TRACKER) {
635 // Domain Owner (Required only for tracker flow)
636 error = tracker_retrieve_attribute(buffer, buffer_size, SO_TRACKER_ATTRIBUTE_DOMAIN_OWNER, dst_domain_owner_buffer, 0, dst_domain_max);
637 if (error) {
638 TRACKER_LOG(LOG_ERR, "Could not retrieve domain owner TLV from parameters");
639 error = EINVAL;
640 goto cleanup;
641 }
642 }
643
644 uuid_t dummy = {};
645 if (fill_hash_entry(entry, dummy, SA(&addrBuffer)) != 0) {
646 error = EINVAL;
647 goto cleanup;
648 }
649
650 // If reach here, all required parameter are parsed, clear error.
651 error = 0;
652
653 TRACKER_LOCK_EXCLUSIVE
654
655 if (g_tracker_db.tracker_hashbase == NULL) {
656 if (tracker_db_init() != 0) {
657 error = ENOENT;
658 goto done;
659 }
660 }
661 db = &g_tracker_db;
662
663 // Insert if not already in hash.
664 if (tracker_search_and_insert(db, entry, true) != NULL) {
665 // A match is found, so new entry is not inserted. Free it.
666 FREE_ENTRY(entry);
667 }
668 entry = NULL;
669
670 done:
671 TRACKER_UNLOCK_EXCLUSIVE
672
673 cleanup:
674 if (buffer != scratch_pad) {
675 kfree_data(buffer, buffer_size);
676 }
677 if (error && entry) {
678 FREE_ENTRY(entry);
679 }
680
681 *retval = error;
682 return error;
683 }
684
685 static size_t
tracker_entry_dump_size(struct tracker_hash_entry * entry)686 tracker_entry_dump_size(struct tracker_hash_entry *entry)
687 {
688 size_t len = 0;
689 size_t str_len = 0;
690
691 if (entry == NULL) {
692 return 0;
693 }
694
695 len += TRACKER_TLV_HDR_LEN + sizeof(entry->address_family);
696
697 switch (entry->address_family) {
698 case AF_INET:
699 len += TRACKER_TLV_HDR_LEN + sizeof(entry->address.addr46.ia46_addr4.s_addr);
700 break;
701 case AF_INET6:
702 len += TRACKER_TLV_HDR_LEN + sizeof(entry->address.addr6);
703 break;
704 default:
705 TRACKER_LOG(LOG_ERR, "Could not calculate entry dump size - invalid addr family %d",
706 entry->address_family);
707 return 0;
708 }
709
710 len += TRACKER_TLV_HDR_LEN + sizeof(entry->app_uuid);
711
712 GET_METADATA_BUFFERS_DST((&entry->metadata))
713 if (dst_domain_max == 0) {
714 TRACKER_LOG(LOG_ERR, "Could not calculate entry dump size - 0 dst_domain_max");
715 return 0;
716 }
717
718 str_len = STRLEN_DST_DOMAIN;
719 if (str_len) {
720 len += TRACKER_TLV_HDR_LEN + str_len + 1;
721 }
722
723 str_len = STRLEN_DST_DOMAIN_OWNER;
724 if (str_len) {
725 len += TRACKER_TLV_HDR_LEN + str_len + 1;
726 }
727
728 if (entry->metadata.flags) {
729 len += TRACKER_TLV_HDR_LEN + sizeof(entry->metadata.flags);
730 }
731
732 return len;
733 }
734
735 static size_t
tracker_entry_dump(struct tracker_hash_entry * entry,uint8_t * __sized_by (buffer_size)buffer,size_t buffer_size)736 tracker_entry_dump(struct tracker_hash_entry *entry, uint8_t *__sized_by(buffer_size)buffer, size_t buffer_size)
737 {
738 u_int8_t *cursor = buffer;
739 size_t str_len = 0;
740
741 if (entry == NULL) {
742 return 0;
743 }
744 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_APP_UUID, (u_int32_t)sizeof(entry->app_uuid), &entry->app_uuid, buffer, (u_int32_t)buffer_size);
745 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_ADDRESS_FAMILY, (u_int32_t)sizeof(entry->address_family), &entry->address_family, buffer, (u_int32_t)buffer_size);
746
747 switch (entry->address_family) {
748 case AF_INET:
749 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_ADDRESS,
750 (u_int32_t)sizeof(entry->address.addr46.ia46_addr4.s_addr), &entry->address.addr46.ia46_addr4.s_addr, buffer, (u_int32_t)buffer_size);
751 break;
752 case AF_INET6:
753 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_ADDRESS, (u_int32_t)sizeof(entry->address.addr6), &entry->address.addr6, buffer, (u_int32_t)buffer_size);
754 break;
755 default:
756 TRACKER_LOG(LOG_ERR, "Could not dump entry - invalid addr family %d",
757 entry->address_family);
758 return 0;
759 }
760
761 if (entry->metadata.flags) {
762 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_FLAGS, (u_int32_t)sizeof(entry->metadata.flags), &entry->metadata.flags, buffer, (u_int32_t)buffer_size);
763 }
764
765 GET_METADATA_BUFFERS_DST((&entry->metadata))
766 if (dst_domain_max == 0) {
767 TRACKER_LOG(LOG_ERR, "Could not dump entry - 0 dst_domain_max");
768 return 0;
769 }
770
771 str_len = STRLEN_DST_DOMAIN;
772 TRACKER_LOG(LOG_DEBUG, "Dumping domain <%s> len <%zu>", dst_domain_buffer, str_len);
773 if (str_len) {
774 str_len++;
775 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_DOMAIN, (u_int32_t)str_len, dst_domain_buffer, buffer, (u_int32_t)buffer_size);
776 }
777
778 str_len = STRLEN_DST_DOMAIN_OWNER;
779 TRACKER_LOG(LOG_DEBUG, "Dumping domain owner <%s> len <%zu>", dst_domain_owner_buffer, str_len);
780 if (str_len) {
781 str_len++;
782 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_DOMAIN_OWNER, (u_int32_t)str_len, dst_domain_owner_buffer, buffer, (u_int32_t)buffer_size);
783 }
784
785 return cursor - buffer;
786 }
787
788 static int
tracker_dump(struct proc * p,struct tracker_action_args * uap,int * retval,bool by_app)789 tracker_dump(struct proc *p, struct tracker_action_args *uap, int *retval, bool by_app)
790 {
791 #pragma unused(p)
792 uint8_t app_uuid_tlv[TRACKER_TLV_HDR_LEN + sizeof(uuid_t)] = { };
793 struct sockopt sopt = { };
794 struct tracker_hash_entry *entry = NULL;
795 struct tracker_hash_entry *temp_entry = NULL;
796 struct trackerhashhead *hash = NULL;
797 uint8_t * __indexable buffer = scratch_pad_all;
798 size_t buffer_size = sizeof(scratch_pad_all);
799 uint8_t *data_start = NULL;
800 uint8_t *cursor = NULL;
801 size_t entry_tlv_size = 0;
802 size_t total_mem_size = 0;
803 size_t total_size_needed = 0;
804 size_t total_size = 0;
805 int error = 0;
806 uuid_t app_uuid;
807 bool has_app_uuid = false;
808
809 if (uap->buffer == 0 || uap->buffer_size == 0) {
810 TRACKER_LOG(LOG_ERR, "Could not dump entries, null output buffer");
811 *retval = EINVAL;
812 return EINVAL;
813 }
814
815 if (by_app) {
816 // Expect a UUID TLV
817 if (uap->buffer_size < sizeof(app_uuid_tlv)) {
818 return EINVAL;
819 }
820 sopt.sopt_val = uap->buffer;
821 sopt.sopt_valsize = sizeof(app_uuid_tlv);
822 sopt.sopt_p = p;
823 if (sooptcopyin(&sopt, app_uuid_tlv, sizeof(app_uuid_tlv), sizeof(app_uuid_tlv)) == 0) {
824 if (tracker_retrieve_attribute(app_uuid_tlv, sizeof(app_uuid_tlv), SO_TRACKER_ATTRIBUTE_APP_UUID, (u_int8_t *)&app_uuid, sizeof(app_uuid), sizeof(app_uuid)) == 0) {
825 has_app_uuid = true;
826 }
827 }
828 }
829
830 TRACKER_LOCK_EXCLUSIVE
831
832 if (g_tracker_db.tracker_hashbase == NULL || g_tracker_db.tracker_count == 0) {
833 error = ENOENT;
834 goto done;
835 }
836
837 for (int i = 0; i < TRACKERHASHSIZE; i++) {
838 hash = &g_tracker_db.tracker_hashbase[i];
839
840 LIST_FOREACH_SAFE(entry, hash, entry_link, temp_entry) {
841 if (has_app_uuid && uuid_compare(entry->app_uuid, app_uuid) != 0) {
842 continue;
843 }
844
845 entry_tlv_size = tracker_entry_dump_size(entry);
846 if (entry_tlv_size) {
847 entry_tlv_size += TRACKER_TLV_HDR_LEN;
848 if (os_add_overflow(total_size_needed, entry_tlv_size, &total_size_needed)) {
849 TRACKER_LOG(LOG_ERR, "Could not dump entries, failed to calculate total size needed");
850 error = EINVAL;
851 goto done;
852 }
853 }
854 }
855 }
856
857 // Add space for memory usage TLV
858 if (os_add_overflow(total_size_needed, TRACKER_TLV_HDR_LEN + sizeof(total_mem_size), &total_size_needed)) {
859 TRACKER_LOG(LOG_ERR, "Could not dump entries, failed to calculate total size needed for memory used)");
860 error = EINVAL;
861 goto done;
862 }
863
864 // pre-append 4-bytes size to start of buffer.
865 if (os_add_overflow(total_size_needed, sizeof(uint32_t), &total_size_needed)) {
866 TRACKER_LOG(LOG_ERR, "Could not dump entries, failed to add 4-bytes size to start of buffer");
867 error = EINVAL;
868 goto done;
869 }
870
871 if (total_size_needed > uap->buffer_size) {
872 TRACKER_LOG(LOG_ERR, "Could not dump entries, output buffer too small %lu (needed %lu)",
873 (unsigned long)uap->buffer_size, total_size_needed + sizeof(uint32_t));
874 error = EINVAL;
875 goto done;
876 }
877
878 // total tlv length + 4-bytes total size.
879 if (total_size_needed > sizeof(scratch_pad_all)) {
880 if (total_size_needed > TRACKER_BUFFER_ALLOC_MAX) {
881 TRACKER_LOG(LOG_ERR, "Failed to allocate buffer, size exceeded max allowed");
882 error = ENOMEM;
883 goto done;
884 }
885 buffer = (u_int8_t *)kalloc_data(total_size_needed, Z_ZERO);
886 if (buffer == NULL) {
887 TRACKER_LOG(LOG_ERR, "Could not dump entries, failed to allocate buffer");
888 error = ENOMEM;
889 goto done;
890 }
891 buffer_size = total_size_needed;
892 }
893
894 data_start = buffer + sizeof(uint32_t);
895 cursor = data_start;
896 for (int i = 0; i < TRACKERHASHSIZE; i++) {
897 hash = &g_tracker_db.tracker_hashbase[i];
898
899 LIST_FOREACH_SAFE(entry, hash, entry_link, temp_entry) {
900 if (has_app_uuid && uuid_compare(entry->app_uuid, app_uuid) != 0) {
901 continue;
902 }
903
904 entry_tlv_size = tracker_entry_dump(entry, scratch_pad_entry, sizeof(scratch_pad_entry));
905 if (entry_tlv_size <= 0) {
906 TRACKER_LOG(LOG_ERR, "Could not dump entry, exceeded entry tlv buffer size");
907 continue;
908 } else {
909 TRACKER_ENTRY_LOG(LOG_DEBUG, "Dumped entry", entry, 0);
910 }
911 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_DUMP_ENTRY, (u_int32_t)entry_tlv_size, scratch_pad_entry, data_start, (u_int32_t)(buffer_size - sizeof(uint32_t)));
912
913 if (os_add_overflow(total_mem_size, SIZE_OF_ENTRY(entry), &total_mem_size)) {
914 TRACKER_LOG(LOG_ERR, "Could not dump entries, failed to calculate total memory used");
915 error = EINVAL;
916 goto done;
917 }
918 TRACKER_LOG(LOG_DEBUG, "Total memory size %zu", total_mem_size);
919 }
920 }
921
922 cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_MEMORY_USED, sizeof(total_mem_size), &total_mem_size, data_start, (u_int32_t)(buffer_size - sizeof(uint32_t)));
923
924 // Fill in the total length at the start
925 total_size = cursor - data_start;
926 memcpy(buffer, (uint8_t *)&total_size, sizeof(uint32_t));
927
928 error = copyout(buffer, uap->buffer, total_size + sizeof(u_int32_t));
929 if (error) {
930 TRACKER_LOG(LOG_DEBUG, "Failed to copy out dump buffer (%lu bytes)", total_size + sizeof(u_int32_t));
931 }
932
933 done:
934 TRACKER_UNLOCK_EXCLUSIVE
935
936 if (buffer != scratch_pad_all) {
937 kfree_data(buffer, buffer_size);
938 }
939 *retval = error;
940 return error;
941 }
942
943 int
tracker_action(struct proc * p,struct tracker_action_args * uap,int * retval)944 tracker_action(struct proc *p, struct tracker_action_args *uap, int *retval)
945 {
946 const task_t __single task = proc_task(p);
947 if (task == NULL || !IOTaskHasEntitlement(task, "com.apple.private.ip-domain-table")) {
948 TRACKER_LOG(LOG_ERR, "Process (%d) does not hold the necessary entitlement", proc_pid(p));
949 *retval = EPERM;
950 return EPERM;
951 }
952
953 switch (uap->action) {
954 case SO_TRACKER_ACTION_ADD:
955 return tracker_add(p, uap, retval);
956 case SO_TRACKER_ACTION_DUMP_BY_APP:
957 return tracker_dump(p, uap, retval, true);
958 case SO_TRACKER_ACTION_DUMP_ALL:
959 return tracker_dump(p, uap, retval, false);
960 default:
961 break;
962 }
963 return 0;
964 }
965
966 int
tracker_lookup(uuid_t app_uuid,struct sockaddr * remote,tracker_metadata_t * metadata)967 tracker_lookup(uuid_t app_uuid, struct sockaddr *remote, tracker_metadata_t *metadata)
968 {
969 struct tracker_hash_entry matchentry = { };
970 struct tracker_hash_entry *foundentry = NULL;
971 struct tracker_db *db = NULL;
972 int error = 0;
973
974 if (remote == NULL || uuid_is_null(app_uuid)) {
975 TRACKER_LOG(LOG_DEBUG, "Failed lookup - remote %s null, app_uuid %s null",
976 remote ? "is not" : "is", !uuid_is_null(app_uuid) ? "is not" : "is");
977 return EINVAL;
978 }
979
980 TRACKER_LOCK_SHARED
981
982 if (g_tracker_db.tracker_hashbase == NULL || g_tracker_db.tracker_count == 0) {
983 error = ENOENT;
984 goto done;
985 }
986 db = &g_tracker_db;
987
988 if (fill_hash_entry(&matchentry, app_uuid, remote) != 0) {
989 error = EINVAL;
990 goto done;
991 }
992
993 TRACKER_ENTRY_LOG(LOG_DEBUG, "Lookup entry", &matchentry, 0);
994
995 foundentry = tracker_search_and_insert(db, &matchentry, false);
996 if (foundentry) {
997 if (metadata) {
998 if (copy_metadata(metadata, &foundentry->metadata) == false) {
999 TRACKER_ENTRY_LOG(LOG_ERR, "Failed to copy metadata", &matchentry, 0);
1000 error = ENOENT;
1001 }
1002 }
1003 foundentry->lastused = net_uptime();
1004 }
1005
1006 done:
1007 TRACKER_UNLOCK_SHARED
1008 return error;
1009 }
1010
1011 static void
tracker_gc_thread_sleep(bool forever)1012 tracker_gc_thread_sleep(bool forever)
1013 {
1014 if (forever) {
1015 (void) assert_wait((event_t) &g_tracker_db.tracker_count,
1016 THREAD_INTERRUPTIBLE);
1017 } else {
1018 uint64_t deadline = 0;
1019 nanoseconds_to_absolutetime(TRACKER_GC_RUN_INTERVAL_NSEC, &deadline);
1020 clock_absolutetime_interval_to_deadline(deadline, &deadline);
1021
1022 (void) assert_wait_deadline(&g_tracker_db.tracker_count,
1023 THREAD_INTERRUPTIBLE, deadline);
1024 }
1025 }
1026
1027 static void
tracker_gc_thread_func(void * v,wait_result_t w)1028 tracker_gc_thread_func(void *v, wait_result_t w)
1029 {
1030 #pragma unused(v, w)
1031
1032 ASSERT(g_tracker_gc_thread == current_thread());
1033 thread_set_thread_name(current_thread(), "TRACKER_GC");
1034
1035 // Kick off gc shortly
1036 tracker_gc_thread_sleep(false);
1037 thread_block_parameter((thread_continue_t) tracker_entry_expire, NULL);
1038 /* NOTREACHED */
1039 }
1040
1041 static bool
tracker_idle_timed_out(struct tracker_hash_entry * entry,u_int64_t timeout,u_int64_t current_time)1042 tracker_idle_timed_out(struct tracker_hash_entry *entry, u_int64_t timeout, u_int64_t current_time)
1043 {
1044 if (entry && (current_time - entry->lastused >= timeout)) {
1045 return true;
1046 }
1047 return false;
1048 }
1049
1050 static void
tracker_entry_expire(void * v,wait_result_t w)1051 tracker_entry_expire(void *v, wait_result_t w)
1052 {
1053 #pragma unused (v, w)
1054 struct tracker_hash_entry * __single entry = NULL;
1055 struct tracker_hash_entry *temp_entry = NULL;
1056 struct trackerhashhead *hash = NULL;
1057
1058 u_int64_t current_time = net_uptime();
1059 int deleted_count = 0;
1060 int remaining_count = 0;
1061
1062 for (int i = 0; i < TRACKERHASHSIZE; i++) {
1063 TRACKER_LOCK_EXCLUSIVE
1064
1065 if (g_tracker_db.tracker_hashbase == NULL || g_tracker_db.tracker_count == 0) {
1066 TRACKER_UNLOCK_EXCLUSIVE
1067 goto go_sleep;
1068 }
1069 hash = &g_tracker_db.tracker_hashbase[i];
1070
1071 LIST_FOREACH_SAFE(entry, hash, entry_link, temp_entry) {
1072 if (tracker_idle_timed_out(entry, tracker_db_idle_timeout, current_time)) {
1073 TRACKER_ENTRY_LOG(LOG_DEBUG, "Deleting entry - IDLE TO", entry, i);
1074 g_tracker_db.tracker_count--;
1075 if (entry->metadata.flags & SO_TRACKER_ATTRIBUTE_FLAGS_DOMAIN_SHORT) {
1076 g_tracker_db.tracker_count_short--;
1077 }
1078 LIST_REMOVE(entry, entry_link);
1079 FREE_ENTRY(entry);
1080 deleted_count++;
1081 }
1082 }
1083
1084 remaining_count = g_tracker_db.tracker_count;
1085 TRACKER_UNLOCK_EXCLUSIVE
1086 }
1087
1088 go_sleep:
1089
1090 TRACKER_LOG(LOG_DEBUG, "Garbage Collection done...(deleted %d - total count %d)", deleted_count, remaining_count);
1091
1092 // Sleep forever (until waken up) if no more UDP flow to clean
1093 TRACKER_LOCK_SHARED
1094 tracker_gc_thread_sleep(g_tracker_db.tracker_count == 0 ? true : false);
1095 TRACKER_UNLOCK_SHARED
1096 thread_block_parameter((thread_continue_t)tracker_entry_expire, NULL);
1097 /* NOTREACHED */
1098 }
1099