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