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