xref: /xnu-10002.81.5/bsd/kern/tracker.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
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 
47 extern int tcp_tcbhashsize;
48 
49 int tracker_log_level = LOG_ERR;
50 static os_log_t tracker_db_log_handle = NULL;
51 
52 /*
53  * Tracker Entry Garbage Collection:
54  */
55 static 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 
59 static int tracker_db_idle_timeout = TRACKER_GC_IDLE_TO;
60 
61 /*
62  * Sysctls for debug logs control
63  */
64 SYSCTL_NODE(_net, OID_AUTO, tracker, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "tracker");
65 
66 SYSCTL_INT(_net_tracker, OID_AUTO, log, CTLFLAG_RW | CTLFLAG_LOCKED,
67     &tracker_log_level, 0, "");
68 
69 SYSCTL_INT(_net_tracker, OID_AUTO, idle_timeout, CTLFLAG_RW | CTLFLAG_LOCKED,
70     &tracker_db_idle_timeout, 0, "");
71 
72 #define TRACKER_LOG(level, fmt, ...)                                                                                    \
73 do {                                                                                                                    \
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)                                                                      \
84 do {                                                                                                                    \
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 
110 static uint8_t scratch_pad_all[TRACKER_DUMP_SCRATCH_PAD_SIZE];
111 static 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 
123 typedef struct tracker_hash_entry {
124 	TRACKER_HASH_ENTRY_HEADER_FIELDS
125 	tracker_metadata_t                  metadata;
126 } tracker_hash_entry_t;
127 
128 typedef struct tracker_hash_entry_short {
129 	TRACKER_HASH_ENTRY_HEADER_FIELDS
130 	tracker_metadata_short_t            metadata;
131 } tracker_hash_entry_short_t;
132 
133 LIST_HEAD(trackerhashhead, tracker_hash_entry);
134 
135 struct 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 
143 static KALLOC_TYPE_DEFINE(tracker_hash_entry_zone,
144     struct tracker_hash_entry, NET_KT_DEFAULT);
145 
146 static KALLOC_TYPE_DEFINE(tracker_hash_entry_short_zone,
147     struct tracker_hash_entry_short, NET_KT_DEFAULT);
148 
149 static struct tracker_db g_tracker_db = { };
150 
151 static LCK_GRP_DECLARE(g_tracker_lck_grp, "tracker");
152 static 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 
159 static void tracker_gc_thread_func(void *v, wait_result_t w);
160 static 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) {                                \
213 tracker_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 
224 static int
tracker_db_init(void)225 tracker_db_init(void)
226 {
227 	tracker_db_log_handle = os_log_create("com.apple.xnu.kern.tracker_db", "tracker_db");
228 
229 	g_tracker_db.tracker_hashbase = hashinit(TRACKERHASHSIZE, M_TRACKER, &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(tracker_gc_thread_func, NULL,
240 	    &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 
250 static boolean_t
copy_metadata(tracker_metadata_t * dst_metadata,tracker_metadata_t * src_metadata)251 copy_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((const char *)src_domain_buffer);
267 	size_t src_domain_owner_len = strlen((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((const char *)dst_domain_buffer);
276 		if (dst_domain_len != src_domain_len || strncmp((const char *)dst_domain_buffer, (const char *)src_domain_buffer, src_domain_len)) {
277 			if (src_domain_len <= dst_domain_max) {
278 				bcopy(src_domain_buffer, dst_domain_buffer, 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((const char *)dst_domain_owner_buffer);
288 		if (dst_domain_owner_len != src_domain_owner_len || strncmp((const char *)dst_domain_owner_buffer, (const char *)src_domain_owner_buffer, src_domain_owner_len)) {
289 			if (src_domain_owner_len <= dst_domain_max) {
290 				bcopy(src_domain_owner_buffer, dst_domain_owner_buffer, 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 
309 static int
fill_hash_entry(struct tracker_hash_entry * entry,uuid_t appuuid,struct sockaddr * address)310 fill_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(entry->app_uuid)) {
316 		if (appuuid == NULL || uuid_is_null(appuuid)) {
317 			return EINVAL;
318 		}
319 		uuid_copy(entry->app_uuid, 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 
356 static inline void
tracker_entry_log(int log_level,char * log_msg,struct tracker_hash_entry * entry,uint32_t hash)357 tracker_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 
390 static inline struct tracker_hash_entry *
tracker_search_and_insert(struct tracker_db * db,struct tracker_hash_entry * matchentry,boolean_t insert)391 tracker_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(nextentry->app_uuid, 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(&nextentry->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 
479 static int
tracker_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)480 tracker_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, (u_int32_t)buffer_length, 0, type, NULL, 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, 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, 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(out_buffer, value, value_size);
509 	return 0;
510 }
511 
512 static int
tracker_add(struct proc * p,struct tracker_action_args * uap,int * retval)513 tracker_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, buffer, buffer_size, 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_size, SO_TRACKER_ATTRIBUTE_ADDRESS_FAMILY, (u_int8_t *)&address_family, sizeof(address_family), 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_size, SO_TRACKER_ATTRIBUTE_ADDRESS, (u_int8_t *)&addrBuffer, address_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_size, SO_TRACKER_ATTRIBUTE_FLAGS, (u_int8_t *)&flags, sizeof(flags), 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_size, SO_TRACKER_ATTRIBUTE_APP_UUID, (u_int8_t *)&entry->app_uuid, sizeof(uuid_t), 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_size, SO_TRACKER_ATTRIBUTE_DOMAIN, dst_domain_buffer, 0, 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_size, SO_TRACKER_ATTRIBUTE_DOMAIN_OWNER, dst_domain_owner_buffer, 0, 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, (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, 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 
647 done:
648 	TRACKER_UNLOCK_EXCLUSIVE
649 
650 cleanup:
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 
662 static size_t
tracker_entry_dump_size(struct tracker_hash_entry * entry)663 tracker_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((const char *)dst_domain_buffer);
696 	if (str_len) {
697 		len += TRACKER_TLV_HDR_LEN + str_len + 1;
698 	}
699 
700 	str_len = strlen((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 
712 static size_t
tracker_entry_dump(struct tracker_hash_entry * entry,uint8_t * buffer,size_t buffer_size)713 tracker_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, SO_TRACKER_ATTRIBUTE_APP_UUID, (u_int32_t)sizeof(entry->app_uuid), &entry->app_uuid, buffer, (u_int32_t)buffer_size);
722 	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);
723 
724 	switch (entry->address_family) {
725 	case AF_INET:
726 		cursor = necp_buffer_write_tlv(cursor, SO_TRACKER_ATTRIBUTE_ADDRESS,
727 		    (u_int32_t)sizeof(entry->address.addr46.ia46_addr4.s_addr), &entry->address.addr46.ia46_addr4.s_addr, buffer, (u_int32_t)buffer_size);
728 		break;
729 	case AF_INET6:
730 		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);
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, SO_TRACKER_ATTRIBUTE_FLAGS, (u_int32_t)sizeof(entry->metadata.flags), &entry->metadata.flags, buffer, (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((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, SO_TRACKER_ATTRIBUTE_DOMAIN, (u_int32_t)str_len, dst_domain_buffer, buffer, (u_int32_t)buffer_size);
753 	}
754 
755 	str_len = strlen((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, SO_TRACKER_ATTRIBUTE_DOMAIN_OWNER, (u_int32_t)str_len, dst_domain_owner_buffer, buffer, (u_int32_t)buffer_size);
760 	}
761 
762 	return cursor - buffer;
763 }
764 
765 static int
tracker_dump(struct proc * p,struct tracker_action_args * uap,int * retval,bool by_app)766 tracker_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, app_uuid_tlv, sizeof(app_uuid_tlv), sizeof(app_uuid_tlv)) == 0) {
801 			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) {
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(entry->app_uuid, 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(entry->app_uuid, app_uuid) != 0) {
878 				continue;
879 			}
880 
881 			entry_tlv_size = tracker_entry_dump(entry, scratch_pad_entry, 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, SO_TRACKER_ATTRIBUTE_DUMP_ENTRY, (u_int32_t)entry_tlv_size, scratch_pad_entry, data_start, (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, SO_TRACKER_ATTRIBUTE_MEMORY_USED, sizeof(total_mem_size), &total_mem_size, data_start, (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(buffer, (uint8_t *)&total_size, 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 
910 done:
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 
920 int
tracker_action(struct proc * p,struct tracker_action_args * uap,int * retval)921 tracker_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, "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 
943 int
tracker_lookup(uuid_t app_uuid,struct sockaddr * remote,tracker_metadata_t * metadata)944 tracker_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(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(&matchentry, app_uuid, 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, false);
973 	if (foundentry) {
974 		if (metadata) {
975 			if (copy_metadata(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 
983 done:
984 	TRACKER_UNLOCK_SHARED
985 	return error;
986 }
987 
988 static void
tracker_gc_thread_sleep(bool forever)989 tracker_gc_thread_sleep(bool forever)
990 {
991 	if (forever) {
992 		(void) assert_wait((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, &deadline);
997 		clock_absolutetime_interval_to_deadline(deadline, &deadline);
998 
999 		(void) assert_wait_deadline(&g_tracker_db.tracker_count,
1000 		    THREAD_INTERRUPTIBLE, deadline);
1001 	}
1002 }
1003 
1004 static void
tracker_gc_thread_func(void * v,wait_result_t w)1005 tracker_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(current_thread(), "TRACKER_GC");
1011 
1012 	// Kick off gc shortly
1013 	tracker_gc_thread_sleep(false);
1014 	thread_block_parameter((thread_continue_t) tracker_entry_expire, NULL);
1015 	/* NOTREACHED */
1016 }
1017 
1018 static bool
tracker_idle_timed_out(struct tracker_hash_entry * entry,u_int64_t timeout,u_int64_t current_time)1019 tracker_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 
1027 static void
tracker_entry_expire(void * v,wait_result_t w)1028 tracker_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, 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 
1065 go_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(g_tracker_db.tracker_count == 0 ? true : false);
1072 	TRACKER_UNLOCK_SHARED
1073 	thread_block_parameter((thread_continue_t)tracker_entry_expire, NULL);
1074 	/* NOTREACHED */
1075 }
1076