xref: /xnu-12377.41.6/bsd/kern/socket_flows.c (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1 /*
2  * Copyright (c) 2021, 2023-2025, Apple Inc. All rights reserved.
3  * @APPLE_LICENSE_HEADER_START@
4  *
5  * This file contains Original Code and/or Modifications of Original Code
6  * as defined in and that are subject to the Apple Public Source License
7  * Version 2.0 (the 'License'). You may not use this file except in
8  * compliance with the License. Please obtain a copy of the License at
9  * http://www.opensource.apple.com/apsl/ and read it before using this
10  * file.
11  *
12  * The Original Code and all software distributed under the License are
13  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
17  * Please see the License for the specific language governing rights and
18  * limitations under the License.
19  *
20  * @APPLE_LICENSE_HEADER_END@
21  */
22 
23 /*
24  * LOCKING STRATEGY
25  *
26  * The struct socket's so_flow_db field (struct soflow_db and its hash entries
27  * struct soflow_hash_entry) is protected by the socket lock. This covers all the
28  * socket paths that calls soflow_get_flow() as well as the garbage collection.
29  * For the socket detach path, soflow_detach() cannot assume the socket lock is
30  * held. Thus, reference counts are added to both struct soflow_db and struct
31  * soflow_hash_entry to avoid access after freed issues.
32  *
33  * The global list, soflow_entry_head, keeps track of all struct soflow_hash_entry
34  * entries which is used by garbage collection when detecting idle entries.  This list
35  * is protected by the global lock soflow_lck_rw.
36  *
37  */
38 
39 #include <sys/types.h>
40 #include <sys/kern_control.h>
41 #include <sys/queue.h>
42 #include <sys/domain.h>
43 #include <sys/protosw.h>
44 #include <sys/syslog.h>
45 #include <sys/systm.h>
46 #include <sys/sysproto.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
49 
50 #include <kern/sched_prim.h>
51 #include <kern/locks.h>
52 #include <kern/zalloc.h>
53 #include <kern/debug.h>
54 #include <net/ntstat.h>
55 #include <netinet6/in6_var.h>
56 
57 #define _IP_VHL
58 #include <netinet/ip.h>
59 #include <netinet/in_pcb.h>
60 #include <netinet/udp.h>
61 #include <netinet/udp_var.h>
62 
63 #include <string.h>
64 #include <libkern/libkern.h>
65 #include <kern/socket_flows.h>
66 #include <net/sockaddr_utils.h>
67 
68 extern struct inpcbinfo ripcbinfo;
69 
70 /*
71  * Per-Socket Flow Management
72  */
73 
74 static int soflow_log_level = LOG_ERR;
75 static int soflow_log_port = 0;
76 static int soflow_log_pid = 0;
77 static int soflow_log_proto = 0;
78 static int soflow_nstat_disable = 0;
79 static int soflow_disable = 0;
80 static long soflow_attached_count = 0;
81 static long soflow_attached_high_water_mark = 0;
82 static os_log_t soflow_log_handle = NULL;
83 
84 /*
85  * Sysctls for debug logs control
86  */
87 SYSCTL_NODE(_net, OID_AUTO, soflow, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "soflow");
88 
89 SYSCTL_INT(_net_soflow, OID_AUTO, log_level, CTLFLAG_RW | CTLFLAG_LOCKED,
90     &soflow_log_level, 0, "");
91 
92 SYSCTL_INT(_net_soflow, OID_AUTO, log_port, CTLFLAG_RW | CTLFLAG_LOCKED,
93     &soflow_log_port, 0, "");
94 
95 SYSCTL_INT(_net_soflow, OID_AUTO, log_pid, CTLFLAG_RW | CTLFLAG_LOCKED,
96     &soflow_log_pid, 0, "");
97 
98 SYSCTL_INT(_net_soflow, OID_AUTO, log_proto, CTLFLAG_RW | CTLFLAG_LOCKED,
99     &soflow_log_proto, 0, "");
100 
101 SYSCTL_INT(_net_soflow, OID_AUTO, nstat_disable, CTLFLAG_RW | CTLFLAG_LOCKED,
102     &soflow_nstat_disable, 0, "");
103 
104 SYSCTL_INT(_net_soflow, OID_AUTO, disable, CTLFLAG_RW | CTLFLAG_LOCKED,
105     &soflow_disable, 0, "");
106 
107 SYSCTL_LONG(_net_soflow, OID_AUTO, count, CTLFLAG_LOCKED | CTLFLAG_RD, &soflow_attached_count, "");
108 SYSCTL_LONG(_net_soflow, OID_AUTO, high_water_mark, CTLFLAG_LOCKED | CTLFLAG_RD, &soflow_attached_high_water_mark, "");
109 
110 #define SOFLOW_LOG(level, so, debug, fmt, ...)                                                                      \
111 do {                                                                                                                \
112     if (soflow_log_level >= level && debug && soflow_log_handle) {                                                  \
113 	if (level == LOG_ERR) {                                                                                         \
114 	    os_log_error(soflow_log_handle, "SOFLOW - %s:%d <pid %d so %llx> " fmt "\n", __FUNCTION__, __LINE__,        \
115 	             so ? SOFLOW_SOCKET_PID(so) : 0, so ? (uint64_t)VM_KERNEL_ADDRPERM(so) : 0, ##__VA_ARGS__);         \
116 	} else {                                                                                                        \
117 	    os_log(soflow_log_handle, "SOFLOW - %s:%d <pid %d so %llx> " fmt "\n", __FUNCTION__, __LINE__,              \
118 	       so ? SOFLOW_SOCKET_PID(so) : 0, so ? (uint64_t)VM_KERNEL_ADDRPERM(so) : 0, ##__VA_ARGS__);               \
119 	}                                                                                                               \
120     }                                                                                                               \
121 } while (0)
122 
123 #define SOFLOW_ENTRY_LOG(level, so, entry, debug, msg)                                                              \
124 do {                                                                                                                \
125     if (soflow_log_level >= level && entry && debug) {                                                              \
126     soflow_entry_log(level, so, entry, msg);                                                                        \
127     }                                                                                                               \
128 } while (0)
129 
130 #define SOFLOW_HASH(laddr, faddr, lport, fport) ((faddr) ^ ((laddr) >> 16) ^ (fport) ^ (lport))
131 
132 #define SOFLOW_IS_UDP(so) (so && SOCK_CHECK_TYPE(so, SOCK_DGRAM) && SOCK_CHECK_PROTO(so, IPPROTO_UDP))
133 #define SOFLOW_GET_SO_PROTO(so) (so ? SOCK_PROTO(so) : IPPROTO_MAX)
134 
135 #define SOFLOW_SOCKET_PID(so) ((so->so_flags & SOF_DELEGATED) ? so->e_pid : so->last_pid)
136 
137 #define SOFLOW_ENABLE_DEBUG(so, entry) \
138     ((soflow_log_port == 0 || !entry || soflow_log_port == ntohs(entry->soflow_lport) || soflow_log_port == ntohs(entry->soflow_fport)) && \
139      (soflow_log_pid == 0 || !so || soflow_log_pid == SOFLOW_SOCKET_PID(so)) && \
140      (soflow_log_proto == 0 || !so || soflow_log_proto == SOFLOW_GET_SO_PROTO(so)))
141 
142 os_refgrp_decl(static, soflow_refgrp, "soflow_ref_group", NULL);
143 
144 #define SOFLOW_ENTRY_FREE(entry) \
145     if (entry && (os_ref_release(&entry->soflow_ref_count) == 0)) { \
146 	soflow_entry_free(entry); \
147     }
148 
149 #define SOFLOW_DB_FREE(db) \
150     if (db && (os_ref_release(&db->soflow_db_ref_count) == 0)) { \
151     soflow_db_free(db); \
152     }
153 
154 static int soflow_initialized = 0;
155 
156 TAILQ_HEAD(soflow_entry_head, soflow_hash_entry) soflow_entry_head;
157 static LCK_GRP_DECLARE(soflow_lck_grp, "Socket Flow");
158 static LCK_RW_DECLARE(soflow_lck_rw, &soflow_lck_grp);
159 
160 #define SOFLOW_LOCK_EXCLUSIVE lck_rw_lock_exclusive(&soflow_lck_rw)
161 #define SOFLOW_UNLOCK_EXCLUSIVE lck_rw_unlock_exclusive(&soflow_lck_rw)
162 #define SOFLOW_LOCK_SHARED lck_rw_lock_shared(&soflow_lck_rw)
163 #define SOFLOW_UNLOCK_SHARED lck_rw_unlock_shared(&soflow_lck_rw)
164 
165 /*
166  * Flow Garbage Collection:
167  */
168 static struct thread *soflow_gc_thread;
169 static soflow_feat_gc_needed_func soflow_feat_gc_needed_func_ptr = NULL;
170 static soflow_feat_gc_perform_func soflow_feat_gc_perform_func_ptr = NULL;
171 
172 #define SOFLOW_GC_IDLE_TO            30  // Flow Idle Timeout in seconds
173 #define SOFLOW_GC_MAX_COUNT          100 // Max sockets to be handled per run
174 #define SOFLOW_GC_RUN_INTERVAL_NSEC  (10 * NSEC_PER_SEC)  // GC wakes up every 10 seconds
175 
176 /*
177  * Feature Context Handling:
178  */
179 static soflow_feat_detach_entry_func soflow_feat_detach_entry_func_ptr = NULL;
180 static soflow_feat_detach_db_func soflow_feat_detach_db_func_ptr = NULL;
181 
182 static void soflow_gc_thread_func(void *v, wait_result_t w);
183 static void soflow_gc_expire(void *v, wait_result_t w);
184 static boolean_t soflow_entry_local_address_needs_update(struct soflow_hash_entry *);
185 static boolean_t soflow_entry_local_port_needs_update(struct socket *, struct soflow_hash_entry *);
186 
187 static void
soflow_init(void)188 soflow_init(void)
189 {
190 	if (soflow_initialized) {
191 		return;
192 	}
193 	soflow_initialized = 1;
194 
195 	if (soflow_log_handle == NULL) {
196 		soflow_log_handle = os_log_create("com.apple.xnu.net.soflow", "soflow");
197 	}
198 
199 	TAILQ_INIT(&soflow_entry_head);
200 
201 	// Spawn thread for gargage collection
202 	if (kernel_thread_start(soflow_gc_thread_func, NULL,
203 	    &soflow_gc_thread) != KERN_SUCCESS) {
204 		panic_plain("%s: Can't create SOFLOW GC thread", __func__);
205 		/* NOTREACHED */
206 	}
207 	/* this must not fail */
208 	VERIFY(soflow_gc_thread != NULL);
209 }
210 
211 static void
soflow_entry_log(int level,struct socket * so,struct soflow_hash_entry * entry,const char * msg)212 soflow_entry_log(int level, struct socket *so, struct soflow_hash_entry *entry, const char* msg)
213 {
214 #pragma unused(level, msg)
215 	char local[MAX_IPv6_STR_LEN + 6] = { 0 };
216 	char remote[MAX_IPv6_STR_LEN + 6] = { 0 };
217 	const void  *addr;
218 
219 	// No sock or not UDP, no-op
220 	if (entry == NULL) {
221 		return;
222 	}
223 
224 	switch (entry->soflow_family) {
225 	case AF_INET6:
226 		addr = &entry->soflow_laddr.addr6;
227 		inet_ntop(AF_INET6, addr, local, sizeof(local));
228 		addr = &entry->soflow_faddr.addr6;
229 		inet_ntop(AF_INET6, addr, remote, sizeof(local));
230 		break;
231 	case AF_INET:
232 		addr = &entry->soflow_laddr.addr46.ia46_addr4.s_addr;
233 		inet_ntop(AF_INET, addr, local, sizeof(local));
234 		addr = &entry->soflow_faddr.addr46.ia46_addr4.s_addr;
235 		inet_ntop(AF_INET, addr, remote, sizeof(local));
236 		break;
237 	default:
238 		return;
239 	}
240 
241 	SOFLOW_LOG(level, so, entry->soflow_debug, "<%s>: %s <%s(%d) entry %p, featureID %llu, filter_ctl 0x%x> outifp %d lport %d fport %d laddr %s faddr %s hash %X "
242 	    "<rx p %llu b %llu, tx p %llu b %llu>",
243 	    msg, entry->soflow_outgoing ? "OUT" : "IN ",
244 	    SOFLOW_IS_UDP(so) ? "UDP" : "proto", SOFLOW_GET_SO_PROTO(so),
245 	    entry, entry->soflow_feat_ctxt_id,
246 	    entry->soflow_filter_control_unit,
247 	    entry->soflow_outifindex,
248 	    ntohs(entry->soflow_lport), ntohs(entry->soflow_fport), local, remote,
249 	    entry->soflow_flowhash,
250 	    entry->soflow_rxpackets, entry->soflow_rxbytes, entry->soflow_txpackets, entry->soflow_txbytes);
251 }
252 
253 bool
soflow_fill_hash_entry_from_address(struct soflow_hash_entry * entry,bool isLocal,struct sockaddr * addr,bool islocalUpdate)254 soflow_fill_hash_entry_from_address(struct soflow_hash_entry *entry, bool isLocal, struct sockaddr *addr, bool islocalUpdate)
255 {
256 	struct sockaddr_in *sin = NULL;
257 	struct sockaddr_in6 *sin6 = NULL;
258 
259 	if (entry == NULL || addr == NULL) {
260 		return FALSE;
261 	}
262 
263 	switch (addr->sa_family) {
264 	case AF_INET:
265 		sin = satosin(addr);
266 		if (sin->sin_len < sizeof(*sin)) {
267 			return FALSE;
268 		}
269 		if (isLocal == TRUE) {
270 			if (sin->sin_port != 0) {
271 				entry->soflow_lport = sin->sin_port;
272 				if (islocalUpdate) {
273 					entry->soflow_lport_updated = TRUE;
274 				}
275 			}
276 			if (sin->sin_addr.s_addr != INADDR_ANY) {
277 				entry->soflow_laddr.addr46.ia46_addr4.s_addr = sin->sin_addr.s_addr;
278 				if (islocalUpdate) {
279 					entry->soflow_laddr_updated = TRUE;
280 				}
281 			}
282 		} else {
283 			if (sin->sin_port != 0) {
284 				entry->soflow_fport = sin->sin_port;
285 			}
286 			if (sin->sin_addr.s_addr != INADDR_ANY) {
287 				entry->soflow_faddr.addr46.ia46_addr4.s_addr = sin->sin_addr.s_addr;
288 			}
289 		}
290 		entry->soflow_family = AF_INET;
291 		return TRUE;
292 	case AF_INET6:
293 		sin6 = satosin6(addr);
294 		if (sin6->sin6_len < sizeof(*sin6)) {
295 			return FALSE;
296 		}
297 		if (isLocal == TRUE) {
298 			if (sin6->sin6_port != 0) {
299 				entry->soflow_lport = sin6->sin6_port;
300 				if (islocalUpdate) {
301 					entry->soflow_lport_updated = TRUE;
302 				}
303 			}
304 			if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
305 				entry->soflow_laddr.addr6 = sin6->sin6_addr;
306 				entry->soflow_laddr6_ifscope = sin6->sin6_scope_id;
307 				in6_verify_ifscope(&sin6->sin6_addr, sin6->sin6_scope_id);
308 				if (islocalUpdate) {
309 					entry->soflow_laddr_updated = TRUE;
310 				}
311 			}
312 		} else {
313 			if (sin6->sin6_port != 0) {
314 				entry->soflow_fport = sin6->sin6_port;
315 			}
316 			if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
317 				entry->soflow_faddr.addr6 = sin6->sin6_addr;
318 				entry->soflow_faddr6_ifscope = sin6->sin6_scope_id;
319 				in6_verify_ifscope(&sin6->sin6_addr, sin6->sin6_scope_id);
320 			}
321 		}
322 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
323 			entry->soflow_family = AF_INET;
324 		} else {
325 			entry->soflow_family = AF_INET6;
326 		}
327 		return TRUE;
328 	default:
329 		return FALSE;
330 	}
331 }
332 
333 bool
soflow_fill_hash_entry_from_inp(struct soflow_hash_entry * entry,bool isLocal,struct inpcb * inp,bool islocalUpdate)334 soflow_fill_hash_entry_from_inp(struct soflow_hash_entry *entry, bool isLocal, struct inpcb *inp, bool islocalUpdate)
335 {
336 	if (entry == NULL || inp == NULL) {
337 		return FALSE;
338 	}
339 
340 	if (inp->inp_vflag & INP_IPV6) {
341 		entry->soflow_family = AF_INET6;
342 		if (isLocal == TRUE) {
343 			if (inp->inp_lport) {
344 				entry->soflow_lport = inp->inp_lport;
345 				if (islocalUpdate) {
346 					entry->soflow_lport_updated = TRUE;
347 				}
348 			}
349 			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
350 				entry->soflow_laddr.addr6 = inp->in6p_laddr;
351 				entry->soflow_laddr6_ifscope = inp->inp_lifscope;
352 				in6_verify_ifscope(&entry->soflow_laddr.addr6, inp->inp_lifscope);
353 				if (islocalUpdate) {
354 					entry->soflow_laddr_updated = TRUE;
355 				}
356 				if (IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
357 					entry->soflow_family = AF_INET;
358 				}
359 			}
360 		} else {
361 			if (inp->inp_fport) {
362 				entry->soflow_fport = inp->inp_fport;
363 			}
364 			if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
365 				entry->soflow_faddr.addr6 = inp->in6p_faddr;
366 				entry->soflow_faddr6_ifscope = inp->inp_fifscope;
367 				in6_verify_ifscope(&entry->soflow_faddr.addr6, inp->inp_fifscope);
368 				if (IN6_IS_ADDR_V4MAPPED(&inp->in6p_faddr)) {
369 					entry->soflow_family = AF_INET;
370 				}
371 			}
372 		}
373 		return TRUE;
374 	} else if (inp->inp_vflag & INP_IPV4) {
375 		if (isLocal == TRUE) {
376 			if (inp->inp_lport) {
377 				entry->soflow_lport = inp->inp_lport;
378 				if (islocalUpdate) {
379 					entry->soflow_lport_updated = TRUE;
380 				}
381 			}
382 			if (inp->inp_laddr.s_addr) {
383 				entry->soflow_laddr.addr46.ia46_addr4.s_addr = inp->inp_laddr.s_addr;
384 				if (islocalUpdate) {
385 					entry->soflow_laddr_updated = TRUE;
386 				}
387 			}
388 		} else {
389 			if (inp->inp_fport) {
390 				entry->soflow_fport = inp->inp_fport;
391 			}
392 			if (inp->inp_faddr.s_addr) {
393 				entry->soflow_faddr.addr46.ia46_addr4.s_addr = inp->inp_faddr.s_addr;
394 			}
395 		}
396 		entry->soflow_family = AF_INET;
397 		return TRUE;
398 	}
399 	return FALSE;
400 }
401 
402 static errno_t
soflow_db_init(struct socket * so)403 soflow_db_init(struct socket *so)
404 {
405 	errno_t error = 0;
406 	struct soflow_db * __single db = NULL;
407 	struct soflow_hash_entry *hash_entry = NULL;
408 
409 	db = kalloc_type(struct soflow_db, Z_WAITOK | Z_ZERO | Z_NOFAIL);
410 	db->soflow_db_so = so;
411 	void * __single hash = hashinit(SOFLOW_HASH_SIZE, M_CFIL, &db->soflow_db_hashmask);
412 	db->soflow_db_hashbase = __unsafe_forge_bidi_indexable(struct soflow_hash_head *, hash, SOFLOW_HASH_SIZE * sizeof(void*));
413 	if (db->soflow_db_hashbase == NULL) {
414 		kfree_type(struct soflow_db, db);
415 		error = ENOMEM;
416 		goto done;
417 	}
418 	db->soflow_db_debug = SOFLOW_ENABLE_DEBUG(so, hash_entry);
419 	os_ref_init(&db->soflow_db_ref_count, &soflow_refgrp);
420 	so->so_flow_db = db;
421 done:
422 	return error;
423 }
424 
425 static void
soflow_entry_free(struct soflow_hash_entry * hash_entry)426 soflow_entry_free(struct soflow_hash_entry *hash_entry)
427 {
428 	struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
429 
430 	if (hash_entry == NULL) {
431 		return;
432 	}
433 
434 	SOFLOW_ENTRY_LOG(LOG_INFO, so, hash_entry, hash_entry->soflow_debug, "Free entry");
435 	kfree_type(struct soflow_hash_entry, hash_entry);
436 }
437 
438 static void
soflow_db_remove_entry(struct soflow_db * db,struct soflow_hash_entry * hash_entry)439 soflow_db_remove_entry(struct soflow_db *db, struct soflow_hash_entry *hash_entry)
440 {
441 	if (hash_entry == NULL) {
442 		return;
443 	}
444 	if (db == NULL || db->soflow_db_count == 0) {
445 		return;
446 	}
447 
448 #if defined(NSTAT_EXTENSION_FILTER_DOMAIN_INFO)
449 	if (hash_entry->soflow_nstat_context != NULL) {
450 		SOFLOW_LOG(LOG_INFO, db->soflow_db_so, hash_entry->soflow_debug, "<Close nstat> - context %lX", (unsigned long)hash_entry->soflow_nstat_context);
451 		nstat_provider_stats_close(hash_entry->soflow_nstat_context);
452 		hash_entry->soflow_nstat_context = NULL;
453 		SOFLOW_ENTRY_FREE(hash_entry);
454 	}
455 #endif
456 
457 	db->soflow_db_count--;
458 	if (db->soflow_db_only_entry == hash_entry) {
459 		db->soflow_db_only_entry = NULL;
460 	}
461 	LIST_REMOVE(hash_entry, soflow_entry_link);
462 
463 	// Feature context present, give feature a chance to detach and clean up
464 	if (hash_entry->soflow_feat_ctxt != NULL && soflow_feat_detach_entry_func_ptr != NULL) {
465 		soflow_feat_detach_entry_func_ptr(db->soflow_db_so, hash_entry);
466 		hash_entry->soflow_feat_ctxt = NULL;
467 		hash_entry->soflow_feat_ctxt_id = 0;
468 	}
469 
470 	hash_entry->soflow_db = NULL;
471 
472 	SOFLOW_LOCK_EXCLUSIVE;
473 	if (soflow_initialized) {
474 		TAILQ_REMOVE(&soflow_entry_head, hash_entry, soflow_entry_list_link);
475 		soflow_attached_count--;
476 	}
477 	SOFLOW_UNLOCK_EXCLUSIVE;
478 
479 	SOFLOW_ENTRY_FREE(hash_entry);
480 }
481 
482 static void
soflow_db_free(struct soflow_db * db)483 soflow_db_free(struct soflow_db *db)
484 {
485 	struct soflow_hash_entry *entry = NULL;
486 	struct soflow_hash_entry *temp_entry = NULL;
487 	struct soflow_hash_head *flowhash = NULL;
488 
489 	if (db == NULL) {
490 		return;
491 	}
492 
493 	SOFLOW_LOG(LOG_INFO, db->soflow_db_so, db->soflow_db_debug, "<db %p> freeing db (count == %d)", db, db->soflow_db_count);
494 
495 	for (int i = 0; i < SOFLOW_HASH_SIZE; i++) {
496 		flowhash = &db->soflow_db_hashbase[i];
497 		LIST_FOREACH_SAFE(entry, flowhash, soflow_entry_link, temp_entry) {
498 			SOFLOW_ENTRY_LOG(LOG_INFO, db->soflow_db_so, entry, entry->soflow_debug, "Remove entry");
499 			soflow_db_remove_entry(db, entry);
500 		}
501 	}
502 
503 	if (soflow_feat_detach_db_func_ptr != NULL) {
504 		soflow_feat_detach_db_func_ptr(db->soflow_db_so, db);
505 	}
506 
507 	// Make sure all entries are cleaned up!
508 	VERIFY(db->soflow_db_count == 0);
509 	hashdestroy(db->soflow_db_hashbase, M_CFIL, db->soflow_db_hashmask);
510 	kfree_type(struct soflow_db, db);
511 }
512 
513 void
soflow_detach(struct socket * so)514 soflow_detach(struct socket *so)
515 {
516 	if (so == NULL || so->so_flow_db == NULL) {
517 		return;
518 	}
519 	SOFLOW_DB_FREE(so->so_flow_db);
520 	so->so_flow_db = NULL;
521 }
522 
523 static boolean_t
soflow_match_entries_v4(struct soflow_hash_entry * entry1,struct soflow_hash_entry * entry2,boolean_t remoteOnly)524 soflow_match_entries_v4(struct soflow_hash_entry *entry1, struct soflow_hash_entry *entry2, boolean_t remoteOnly)
525 {
526 	if (entry1 == NULL || entry2 == NULL) {
527 		return false;
528 	}
529 
530 	// Ignore local match if remoteOnly or if local has been updated since entry added
531 	boolean_t lport_matched = (remoteOnly || entry1->soflow_lport_updated || entry1->soflow_lport == entry2->soflow_lport);
532 	boolean_t laddr_matched = (remoteOnly || entry1->soflow_laddr_updated ||
533 	    entry1->soflow_laddr.addr46.ia46_addr4.s_addr == entry2->soflow_laddr.addr46.ia46_addr4.s_addr);
534 
535 	// Entries match if local and remote ports and addresses all matched
536 	return lport_matched && entry1->soflow_fport == entry2->soflow_fport &&
537 	       laddr_matched && entry1->soflow_faddr.addr46.ia46_addr4.s_addr == entry2->soflow_faddr.addr46.ia46_addr4.s_addr;
538 }
539 
540 static boolean_t
soflow_match_entries_v6(struct soflow_hash_entry * entry1,struct soflow_hash_entry * entry2,boolean_t remoteOnly)541 soflow_match_entries_v6(struct soflow_hash_entry *entry1, struct soflow_hash_entry *entry2, boolean_t remoteOnly)
542 {
543 	if (entry1 == NULL || entry2 == NULL) {
544 		return false;
545 	}
546 
547 	// Ignore local match if remoteOnly or if local has been updated since entry added
548 	boolean_t lport_matched = (remoteOnly || entry1->soflow_lport_updated || entry1->soflow_lport == entry2->soflow_lport);
549 	boolean_t laddr_matched = (remoteOnly || entry1->soflow_laddr_updated ||
550 	    in6_are_addr_equal_scoped(&entry1->soflow_laddr.addr6, &entry2->soflow_laddr.addr6, entry1->soflow_laddr6_ifscope, entry2->soflow_laddr6_ifscope));
551 
552 	// Entries match if local and remote ports and addresses all matched
553 	return lport_matched && entry1->soflow_fport == entry2->soflow_fport &&
554 	       laddr_matched && in6_are_addr_equal_scoped(&entry1->soflow_faddr.addr6, &entry2->soflow_faddr.addr6, entry1->soflow_faddr6_ifscope, entry2->soflow_faddr6_ifscope);
555 }
556 
557 static struct soflow_hash_entry *
soflow_db_lookup_entry_internal(struct soflow_db * db,struct sockaddr * local,struct sockaddr * remote,boolean_t remoteOnly,boolean_t withLocalPort)558 soflow_db_lookup_entry_internal(struct soflow_db *db, struct sockaddr *local, struct sockaddr *remote, boolean_t remoteOnly, boolean_t withLocalPort)
559 {
560 	struct soflow_hash_entry matchentry = { };
561 	struct soflow_hash_entry *nextentry = NULL;
562 	struct inpcb *inp = sotoinpcb(db->soflow_db_so);
563 	u_int32_t hashkey_faddr = 0, hashkey_laddr = 0;
564 	u_int16_t hashkey_fport = 0, hashkey_lport = 0;
565 	int inp_hash_element = 0;
566 	struct soflow_hash_head *flowhash = NULL;
567 
568 	if (inp == NULL || db == NULL) {
569 		return NULL;
570 	}
571 
572 	if (local != NULL) {
573 		soflow_fill_hash_entry_from_address(&matchentry, TRUE, local, FALSE);
574 	} else {
575 		soflow_fill_hash_entry_from_inp(&matchentry, TRUE, inp, FALSE);
576 	}
577 	if (remote != NULL) {
578 		soflow_fill_hash_entry_from_address(&matchentry, FALSE, remote, FALSE);
579 	} else {
580 		soflow_fill_hash_entry_from_inp(&matchentry, FALSE, inp, FALSE);
581 	}
582 	matchentry.soflow_debug = SOFLOW_ENABLE_DEBUG(db->soflow_db_so, (&matchentry));
583 	SOFLOW_ENTRY_LOG(LOG_DEBUG, db->soflow_db_so, &matchentry, true, "Looking for entry");
584 
585 	if (matchentry.soflow_family == AF_INET6) {
586 		hashkey_faddr = matchentry.soflow_faddr.addr6.s6_addr32[3];
587 		hashkey_laddr = (remoteOnly == false) ? matchentry.soflow_laddr.addr6.s6_addr32[3] : 0;
588 	} else {
589 		hashkey_faddr = matchentry.soflow_faddr.addr46.ia46_addr4.s_addr;
590 		hashkey_laddr = (remoteOnly == false) ? matchentry.soflow_laddr.addr46.ia46_addr4.s_addr : 0;
591 	}
592 
593 	hashkey_fport = matchentry.soflow_fport;
594 	hashkey_lport = (remoteOnly == false || withLocalPort == true) ? matchentry.soflow_lport : 0;
595 
596 	inp_hash_element = SOFLOW_HASH(hashkey_laddr, hashkey_faddr, hashkey_lport, hashkey_fport);
597 	inp_hash_element &= db->soflow_db_hashmask;
598 	flowhash = &db->soflow_db_hashbase[inp_hash_element];
599 
600 	LIST_FOREACH(nextentry, flowhash, soflow_entry_link) {
601 		if (matchentry.soflow_family == AF_INET6) {
602 			if (soflow_match_entries_v6(nextentry, &matchentry, remoteOnly)) {
603 				SOFLOW_ENTRY_LOG(LOG_DEBUG, db->soflow_db_so, nextentry, nextentry->soflow_debug, "Found entry v6");
604 				break;
605 			}
606 		} else if (matchentry.soflow_family == AF_INET) {
607 			if (soflow_match_entries_v4(nextentry, &matchentry, remoteOnly)) {
608 				SOFLOW_ENTRY_LOG(LOG_DEBUG, db->soflow_db_so, nextentry, nextentry->soflow_debug, "Found entry v4");
609 				break;
610 			}
611 		}
612 	}
613 
614 	if (nextentry == NULL) {
615 		SOFLOW_ENTRY_LOG(LOG_DEBUG, db->soflow_db_so, &matchentry, matchentry.soflow_debug, "Entry not found");
616 	}
617 	return nextentry;
618 }
619 
620 static struct soflow_hash_entry *
soflow_db_lookup_entry(struct soflow_db * db,struct sockaddr * local,struct sockaddr * remote,boolean_t remoteOnly)621 soflow_db_lookup_entry(struct soflow_db *db, struct sockaddr *local, struct sockaddr *remote, boolean_t remoteOnly)
622 {
623 	struct soflow_hash_entry *entry = soflow_db_lookup_entry_internal(db, local, remote, remoteOnly, false);
624 	if (entry == NULL && remoteOnly == true) {
625 		entry = soflow_db_lookup_entry_internal(db, local, remote, remoteOnly, true);
626 	}
627 	return entry;
628 }
629 
630 static struct soflow_hash_entry *
soflow_db_lookup_by_feature_context_id(struct soflow_db * db,u_int64_t feature_context_id)631 soflow_db_lookup_by_feature_context_id(struct soflow_db *db, u_int64_t feature_context_id)
632 {
633 	struct soflow_hash_head *flowhash = NULL;
634 	u_int32_t inp_hash_element = (u_int32_t)(feature_context_id & 0x0ffffffff);
635 	struct soflow_hash_entry *nextentry;
636 
637 	inp_hash_element &= db->soflow_db_hashmask;
638 	flowhash = &db->soflow_db_hashbase[inp_hash_element];
639 
640 	LIST_FOREACH(nextentry, flowhash, soflow_entry_link) {
641 		SOFLOW_ENTRY_LOG(LOG_DEBUG, db->soflow_db_so, nextentry, nextentry->soflow_debug, "Looking at entry");
642 		if (nextentry->soflow_feat_ctxt != NULL &&
643 		    nextentry->soflow_feat_ctxt_id == feature_context_id) {
644 			SOFLOW_ENTRY_LOG(LOG_DEBUG, db->soflow_db_so, nextentry, nextentry->soflow_debug, "Found entry by feature context id");
645 			break;
646 		}
647 	}
648 
649 	if (nextentry == NULL) {
650 		SOFLOW_LOG(LOG_DEBUG, db->soflow_db_so, db->soflow_db_debug, "No entry found for featureID %llu <count %d hash %X %X>",
651 		    feature_context_id, db->soflow_db_count, inp_hash_element, (u_int32_t)(feature_context_id & 0x0ffffffff));
652 	}
653 	return nextentry;
654 }
655 
656 void *
soflow_db_get_feature_context(struct soflow_db * db,u_int64_t feature_context_id)657 soflow_db_get_feature_context(struct soflow_db *db, u_int64_t feature_context_id)
658 {
659 	struct soflow_hash_entry *hash_entry = NULL;
660 	void * __single context = NULL;
661 
662 	if (db == NULL || db->soflow_db_so == NULL || feature_context_id == 0) {
663 		return NULL;
664 	}
665 
666 	socket_lock_assert_owned(db->soflow_db_so);
667 
668 	// Take refcount of db before use.
669 	// Abort if db is already being freed.
670 	if (os_ref_retain_try(&db->soflow_db_ref_count) == false) {
671 		return NULL;
672 	}
673 
674 	// This is an optimization for datagram sockets with only one single flow.
675 	if (db->soflow_db_count == 1) {
676 		if (db->soflow_db_only_entry != NULL &&
677 		    db->soflow_db_only_entry->soflow_feat_ctxt != NULL && db->soflow_db_only_entry->soflow_feat_ctxt_id == feature_context_id) {
678 			SOFLOW_ENTRY_LOG(LOG_DEBUG, db->soflow_db_so, db->soflow_db_only_entry, db->soflow_db_only_entry->soflow_debug, "MATCHED only entry for featureID");
679 			context = db->soflow_db_only_entry->soflow_feat_ctxt;
680 		} else {
681 			SOFLOW_LOG(LOG_DEBUG, db->soflow_db_so, db->soflow_db_debug, "MISMATCHED only entry for featureID %llu (entry %p - cfil %p id %llu)",
682 			    feature_context_id,
683 			    db->soflow_db_only_entry,
684 			    db->soflow_db_only_entry ? db->soflow_db_only_entry->soflow_feat_ctxt : NULL,
685 			    db->soflow_db_only_entry ? db->soflow_db_only_entry->soflow_feat_ctxt_id : 0);
686 		}
687 	} else {
688 		hash_entry = soflow_db_lookup_by_feature_context_id(db, feature_context_id);
689 		context = hash_entry != NULL ? hash_entry->soflow_feat_ctxt : NULL;
690 	}
691 
692 	SOFLOW_DB_FREE(db);
693 	return context;
694 }
695 
696 u_int64_t
soflow_db_get_feature_context_id(struct soflow_db * db,struct sockaddr * local,struct sockaddr * remote)697 soflow_db_get_feature_context_id(struct soflow_db *db, struct sockaddr *local, struct sockaddr *remote)
698 {
699 	struct soflow_hash_entry *hash_entry = NULL;
700 	uint64_t context_id = 0;
701 
702 	if (db == NULL || db->soflow_db_so == NULL) {
703 		return 0;
704 	}
705 
706 	socket_lock_assert_owned(db->soflow_db_so);
707 
708 	// Take refcount of db before use.
709 	// Abort if db is already being freed.
710 	if (os_ref_retain_try(&db->soflow_db_ref_count) == false) {
711 		return 0;
712 	}
713 
714 	hash_entry = soflow_db_lookup_entry(db, local, remote, false);
715 	if (hash_entry == NULL) {
716 		// No match with both local and remote, try match with remote only
717 		hash_entry = soflow_db_lookup_entry(db, local, remote, true);
718 	}
719 	if (hash_entry != NULL && hash_entry->soflow_feat_ctxt != NULL) {
720 		context_id = hash_entry->soflow_feat_ctxt_id;
721 	}
722 
723 	SOFLOW_DB_FREE(db);
724 
725 	return context_id;
726 }
727 
728 static struct soflow_hash_entry *
soflow_db_add_entry(struct soflow_db * db,struct sockaddr * local,struct sockaddr * remote)729 soflow_db_add_entry(struct soflow_db *db, struct sockaddr *local, struct sockaddr *remote)
730 {
731 	struct soflow_hash_entry *entry = NULL;
732 	struct inpcb *inp = db ? sotoinpcb(db->soflow_db_so) : NULL;
733 	u_int32_t hashkey_faddr = 0, hashkey_laddr = 0;
734 	int inp_hash_element = 0;
735 	struct soflow_hash_head *flowhash = NULL;
736 
737 	if (db == NULL || inp == NULL) {
738 		goto done;
739 	}
740 
741 	entry = kalloc_type(struct soflow_hash_entry, Z_WAITOK | Z_ZERO | Z_NOFAIL);
742 	os_ref_init(&entry->soflow_ref_count, &soflow_refgrp);
743 
744 	if (local != NULL) {
745 		soflow_fill_hash_entry_from_address(entry, TRUE, local, FALSE);
746 	} else {
747 		soflow_fill_hash_entry_from_inp(entry, TRUE, inp, FALSE);
748 	}
749 	if (remote != NULL) {
750 		soflow_fill_hash_entry_from_address(entry, FALSE, remote, FALSE);
751 	} else {
752 		soflow_fill_hash_entry_from_inp(entry, FALSE, inp, FALSE);
753 	}
754 	entry->soflow_lastused = net_uptime();
755 	entry->soflow_db = db;
756 	entry->soflow_debug = SOFLOW_ENABLE_DEBUG(db->soflow_db_so, entry);
757 	microuptime(&entry->soflow_timestamp);
758 
759 	if (entry->soflow_family == AF_INET6) {
760 		hashkey_faddr = entry->soflow_faddr.addr6.s6_addr32[3];
761 		hashkey_laddr = entry->soflow_laddr.addr6.s6_addr32[3];
762 	} else {
763 		hashkey_faddr = entry->soflow_faddr.addr46.ia46_addr4.s_addr;
764 		hashkey_laddr = entry->soflow_laddr.addr46.ia46_addr4.s_addr;
765 	}
766 	entry->soflow_flowhash = SOFLOW_HASH(hashkey_laddr, hashkey_faddr,
767 	    entry->soflow_lport, entry->soflow_fport);
768 	inp_hash_element = entry->soflow_flowhash & db->soflow_db_hashmask;
769 
770 	socket_lock_assert_owned(db->soflow_db_so);
771 
772 	// Take refcount of db before use.
773 	// Abort if db is already being freed.
774 	if (os_ref_retain_try(&db->soflow_db_ref_count) == false) {
775 		return NULL;
776 	}
777 
778 	flowhash = &db->soflow_db_hashbase[inp_hash_element];
779 
780 	LIST_INSERT_HEAD(flowhash, entry, soflow_entry_link);
781 	db->soflow_db_count++;
782 	db->soflow_db_only_entry = entry;
783 	SOFLOW_LOG(LOG_INFO, db->soflow_db_so, db->soflow_db_debug, "total count %d", db->soflow_db_count);
784 
785 	SOFLOW_DB_FREE(db);
786 
787 done:
788 	return entry;
789 }
790 
791 static sa_family_t
soflow_udp_get_address_from_control(struct mbuf * control,uint8_t * __counted_by (* count)* address_ptr,int * count)792 soflow_udp_get_address_from_control(struct mbuf *control, uint8_t *__counted_by(*count) *address_ptr, int *count)
793 {
794 	struct cmsghdr *cm;
795 	struct in6_pktinfo *pi6;
796 	struct socket *so = NULL;
797 
798 	if (control == NULL || address_ptr == NULL) {
799 		return AF_UNSPEC;
800 	}
801 
802 	for (; control != NULL; control = control->m_next) {
803 		if (control->m_type != MT_CONTROL) {
804 			continue;
805 		}
806 
807 		for (cm = M_FIRST_CMSGHDR(control);
808 		    is_cmsg_valid(control, cm);
809 		    cm = M_NXT_CMSGHDR(control, cm)) {
810 			SOFLOW_LOG(LOG_DEBUG, so, true, "Check control type %d", cm->cmsg_type);
811 
812 			switch (cm->cmsg_type) {
813 			case IP_RECVDSTADDR:
814 				if (cm->cmsg_level == IPPROTO_IP &&
815 				    cm->cmsg_len == CMSG_LEN(sizeof(struct in_addr))) {
816 					*address_ptr = CMSG_DATA(cm);
817 					*count = sizeof(struct in_addr);
818 					return AF_INET;
819 				}
820 				break;
821 			case IPV6_PKTINFO:
822 			case IPV6_2292PKTINFO:
823 				if (cm->cmsg_level == IPPROTO_IPV6 &&
824 				    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
825 					pi6 = (struct in6_pktinfo *)(void *)CMSG_DATA(cm);
826 					*address_ptr = (uint8_t *)&pi6->ipi6_addr;
827 					*count = sizeof(struct in6_addr);
828 					return AF_INET6;
829 				}
830 				break;
831 			default:
832 				break;
833 			}
834 		}
835 	}
836 	return AF_UNSPEC;
837 }
838 
839 static boolean_t
soflow_entry_local_address_needs_update(struct soflow_hash_entry * entry)840 soflow_entry_local_address_needs_update(struct soflow_hash_entry *entry)
841 {
842 	if (entry->soflow_family == AF_INET6) {
843 		return IN6_IS_ADDR_UNSPECIFIED(&entry->soflow_laddr.addr6);
844 	} else if (entry->soflow_family == AF_INET) {
845 		return entry->soflow_laddr.addr46.ia46_addr4.s_addr == INADDR_ANY;
846 	}
847 	return false;
848 }
849 
850 static boolean_t
soflow_entry_local_port_needs_update(struct socket * so,struct soflow_hash_entry * entry)851 soflow_entry_local_port_needs_update(struct socket *so, struct soflow_hash_entry *entry)
852 {
853 	if (SOFLOW_IS_UDP(so)) {
854 		return entry->soflow_lport == 0;
855 	}
856 	return false;
857 }
858 
859 static void
soflow_entry_update_local(struct soflow_db * db,struct soflow_hash_entry * entry,struct sockaddr * local,struct mbuf * control,u_short rcv_ifindex)860 soflow_entry_update_local(struct soflow_db *db, struct soflow_hash_entry *entry, struct sockaddr *local, struct mbuf *control, u_short rcv_ifindex)
861 {
862 	struct inpcb *inp = sotoinpcb(db->soflow_db_so);
863 	union sockaddr_in_4_6 address_buf = { };
864 
865 	if (inp == NULL || entry == NULL) {
866 		return;
867 	}
868 
869 	if (entry->soflow_outifindex == 0 && (inp->inp_last_outifp != NULL || rcv_ifindex != 0)) {
870 		entry->soflow_outifindex = inp->inp_last_outifp ? inp->inp_last_outifp->if_index : rcv_ifindex;
871 		SOFLOW_ENTRY_LOG(LOG_INFO, db->soflow_db_so, entry, entry->soflow_debug, "Updated outifp");
872 	}
873 
874 	if (soflow_entry_local_address_needs_update(entry)) {
875 		// Flow does not have a local address yet.  Retrieve local address
876 		// from control mbufs if present.
877 		if (local == NULL && control != NULL) {
878 			int size = 0;
879 			uint8_t * __counted_by(size) addr_ptr = NULL;
880 			sa_family_t family = soflow_udp_get_address_from_control(control, &addr_ptr, &size);
881 
882 			if (family != AF_UNSPEC && size && addr_ptr) {
883 				switch (family) {
884 				case AF_INET:
885 					if (size == sizeof(struct in_addr)) {
886 						address_buf.sin.sin_port = 0;
887 						address_buf.sin.sin_family = AF_INET;
888 						address_buf.sin.sin_len = sizeof(struct sockaddr_in);
889 						(void) memcpy(&address_buf.sin.sin_addr, addr_ptr, sizeof(struct in_addr));
890 						local = sintosa(&address_buf.sin);
891 					}
892 					break;
893 				case AF_INET6:
894 					if (size == sizeof(struct in6_addr)) {
895 						address_buf.sin6.sin6_port = 0;
896 						address_buf.sin6.sin6_family = AF_INET6;
897 						address_buf.sin6.sin6_len = sizeof(struct sockaddr_in6);
898 						(void) memcpy(&address_buf.sin6.sin6_addr, addr_ptr, sizeof(struct in6_addr));
899 						local = sin6tosa(&address_buf.sin6);
900 					}
901 					break;
902 				default:
903 					break;
904 				}
905 			}
906 		}
907 		if (local != NULL) {
908 			soflow_fill_hash_entry_from_address(entry, TRUE, local, TRUE);
909 		} else {
910 			soflow_fill_hash_entry_from_inp(entry, TRUE, inp, TRUE);
911 		}
912 		if (entry->soflow_laddr_updated) {
913 			SOFLOW_ENTRY_LOG(LOG_INFO, db->soflow_db_so, entry, entry->soflow_debug, "Updated address");
914 		}
915 	}
916 
917 	if (soflow_entry_local_port_needs_update(db->soflow_db_so, entry)) {
918 		soflow_fill_hash_entry_from_inp(entry, TRUE, inp, TRUE);
919 		if (entry->soflow_lport_updated) {
920 			SOFLOW_ENTRY_LOG(LOG_INFO, db->soflow_db_so, entry, entry->soflow_debug, "Updated port");
921 		}
922 	}
923 
924 	return;
925 }
926 
927 #if defined(NSTAT_EXTENSION_FILTER_DOMAIN_INFO)
928 static u_int32_t
ifnet_to_flags(struct ifnet * ifp,struct socket * so)929 ifnet_to_flags(struct ifnet *ifp, struct socket *so)
930 {
931 	u_int32_t flags = 0;
932 
933 	if (ifp != NULL) {
934 		flags = nstat_ifnet_to_flags(ifp);
935 		if ((flags & NSTAT_IFNET_IS_WIFI) && ((flags & (NSTAT_IFNET_IS_AWDL | NSTAT_IFNET_IS_LLW)) == 0)) {
936 			flags |= NSTAT_IFNET_IS_WIFI_INFRA;
937 		}
938 	} else {
939 		flags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
940 	}
941 
942 	if (so != NULL && (so->so_flags1 & SOF1_CELLFALLBACK)) {
943 		flags |= NSTAT_IFNET_VIA_CELLFALLBACK;
944 	}
945 	return flags;
946 }
947 
948 static bool
soflow_nstat_provider_request_vals(nstat_provider_context ctx,u_int32_t * ifflagsp,nstat_counts * countsp,nstat_detailed_counts * detailsp,void * metadatap)949 soflow_nstat_provider_request_vals(nstat_provider_context ctx,
950     u_int32_t *ifflagsp,
951     nstat_counts *countsp,
952     nstat_detailed_counts *detailsp,
953     void *metadatap)
954 {
955 	struct soflow_hash_entry *hash_entry = (struct soflow_hash_entry *) ctx;
956 	struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
957 	struct inpcb *inp = so ? sotoinpcb(so) : NULL;
958 	char local[MAX_IPv6_STR_LEN + 6] = { 0 };
959 	char remote[MAX_IPv6_STR_LEN + 6] = { 0 };
960 	const void *addr = NULL;
961 
962 	if (hash_entry == NULL || so == NULL || inp == NULL) {
963 		return false;
964 	}
965 
966 	if (ifflagsp) {
967 		if (hash_entry->soflow_outifindex) {
968 			struct ifnet *ifp = ifindex2ifnet[hash_entry->soflow_outifindex];
969 			*ifflagsp = ifnet_to_flags(ifp, so);
970 		}
971 		if ((countsp == NULL) && (metadatap == NULL)) {
972 			SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug, "ifflagsp set to 0x%X", *ifflagsp);
973 			goto done;
974 		}
975 	}
976 
977 	if (countsp) {
978 		bzero(countsp, sizeof(*countsp));
979 		countsp->nstat_rxpackets = hash_entry->soflow_rxpackets;
980 		countsp->nstat_rxbytes = hash_entry->soflow_rxbytes;
981 		countsp->nstat_txpackets = hash_entry->soflow_txpackets;
982 		countsp->nstat_txbytes = hash_entry->soflow_txbytes;
983 
984 		SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug,
985 		    "Collected NSTAT counts: rxpackets %llu rxbytes %llu txpackets %llu txbytes %llu",
986 		    countsp->nstat_rxpackets, countsp->nstat_rxbytes, countsp->nstat_txpackets, countsp->nstat_txbytes);
987 	}
988 
989 	if (detailsp) {
990 		bzero(detailsp, sizeof(*detailsp));
991 		detailsp->nstat_media_stats.ms_total.ts_rxbytes = hash_entry->soflow_rxbytes;
992 		detailsp->nstat_media_stats.ms_total.ts_txbytes = hash_entry->soflow_txbytes;
993 		detailsp->nstat_media_stats.ms_total.ts_rxpackets = hash_entry->soflow_rxpackets;
994 		detailsp->nstat_media_stats.ms_total.ts_txpackets = hash_entry->soflow_txpackets;
995 
996 		SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug,
997 		    "Collected NSTAT detailed counts: rxpackets %llu rxbytes %llu txpackets %llu txbytes %llu",
998 		    detailsp->nstat_media_stats.ms_total.ts_rxpackets, detailsp->nstat_media_stats.ms_total.ts_rxbytes,
999 		    detailsp->nstat_media_stats.ms_total.ts_txpackets, detailsp->nstat_media_stats.ms_total.ts_txbytes);
1000 	}
1001 	if (metadatap) {
1002 		nstat_udp_descriptor *desc = (nstat_udp_descriptor *)metadatap;
1003 		bzero(desc, sizeof(*desc));
1004 
1005 		if (so->so_flags & SOF_DELEGATED) {
1006 			desc->eupid = so->e_upid;
1007 			desc->epid = so->e_pid;
1008 			uuid_copy(desc->euuid, so->e_uuid);
1009 		} else {
1010 			desc->eupid = so->last_upid;
1011 			desc->epid = so->last_pid;
1012 			uuid_copy(desc->euuid, so->last_uuid);
1013 		}
1014 
1015 		uuid_copy(desc->vuuid, so->so_vuuid);
1016 		uuid_copy(desc->fuuid, hash_entry->soflow_uuid);
1017 
1018 		if (hash_entry->soflow_family == AF_INET6) {
1019 			in6_ip6_to_sockaddr(&hash_entry->soflow_laddr.addr6, hash_entry->soflow_lport, hash_entry->soflow_laddr6_ifscope,
1020 			    &desc->local.v6, sizeof(desc->local.v6));
1021 			in6_ip6_to_sockaddr(&hash_entry->soflow_faddr.addr6, hash_entry->soflow_fport, hash_entry->soflow_faddr6_ifscope,
1022 			    &desc->remote.v6, sizeof(desc->remote.v6));
1023 		} else if (hash_entry->soflow_family == AF_INET) {
1024 			desc->local.v4.sin_family = AF_INET;
1025 			desc->local.v4.sin_len = sizeof(struct sockaddr_in);
1026 			desc->local.v4.sin_port = hash_entry->soflow_lport;
1027 			desc->local.v4.sin_addr = hash_entry->soflow_laddr.addr46.ia46_addr4;
1028 
1029 			desc->remote.v4.sin_family = AF_INET;
1030 			desc->remote.v4.sin_len = sizeof(struct sockaddr_in);
1031 			desc->remote.v4.sin_port = hash_entry->soflow_fport;
1032 			desc->remote.v4.sin_addr = hash_entry->soflow_faddr.addr46.ia46_addr4;
1033 		}
1034 
1035 		desc->ifindex = hash_entry->soflow_outifindex;
1036 		if (hash_entry->soflow_outifindex) {
1037 			struct ifnet *ifp = ifindex2ifnet[hash_entry->soflow_outifindex];
1038 			desc->ifnet_properties = (uint16_t)ifnet_to_flags(ifp, so);
1039 		}
1040 
1041 		desc->rcvbufsize = so->so_rcv.sb_hiwat;
1042 		desc->rcvbufused = so->so_rcv.sb_cc;
1043 		desc->traffic_class = so->so_traffic_class;
1044 		inp_get_activity_bitmap(inp, &desc->activity_bitmap);
1045 
1046 		if (hash_entry->soflow_debug) {
1047 			switch (hash_entry->soflow_family) {
1048 			case AF_INET6:
1049 				addr = &desc->local.v6;
1050 				inet_ntop(AF_INET6, addr, local, sizeof(local));
1051 				addr = &desc->remote.v6;
1052 				inet_ntop(AF_INET6, addr, remote, sizeof(local));
1053 				break;
1054 			case AF_INET:
1055 				addr = &desc->local.v4.sin_addr;
1056 				inet_ntop(AF_INET, addr, local, sizeof(local));
1057 				addr = &desc->remote.v4.sin_addr;
1058 				inet_ntop(AF_INET, addr, remote, sizeof(local));
1059 				break;
1060 			default:
1061 				break;
1062 			}
1063 
1064 			uint8_t *ptr = (uint8_t *)&desc->euuid;
1065 
1066 			SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug,
1067 			    "Collected NSTAT metadata: eupid %llu epid %d euuid %x%x%x%x-%x%x%x%x-%x%x%x%x-%x%x%x%x "
1068 			    "outifp %d properties 0x%X lport %d fport %d laddr %s faddr %s "
1069 			    "rcvbufsize %u rcvbufused %u traffic_class %u",
1070 			    desc->eupid, desc->epid,
1071 			    ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
1072 			    ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15],
1073 			    desc->ifindex, desc->ifnet_properties,
1074 			    ntohs(desc->local.v4.sin_port), ntohs(desc->remote.v4.sin_port), local, remote,
1075 			    desc->rcvbufsize, desc->rcvbufused, desc->traffic_class);
1076 		}
1077 	}
1078 done:
1079 	return true;
1080 }
1081 
1082 static size_t
soflow_nstat_provider_request_extensions(nstat_provider_context ctx,int requested_extension,void * buf,size_t buf_size)1083 soflow_nstat_provider_request_extensions(nstat_provider_context ctx,
1084     int requested_extension,
1085     void *buf,
1086     size_t buf_size)
1087 {
1088 	struct soflow_hash_entry *hash_entry = (struct soflow_hash_entry *) ctx;
1089 	struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
1090 	struct inpcb *inp = so ? sotoinpcb(so) : NULL;
1091 	struct nstat_domain_info *domain_info = NULL;
1092 	size_t size = 0;
1093 
1094 	if (hash_entry == NULL || so == NULL || inp == NULL) {
1095 		return 0;
1096 	}
1097 
1098 	if (buf == NULL) {
1099 		switch (requested_extension) {
1100 		case NSTAT_EXTENDED_UPDATE_TYPE_DOMAIN:
1101 			return sizeof(nstat_domain_info);
1102 		default:
1103 			return 0;
1104 		}
1105 	}
1106 
1107 	if (buf_size < sizeof(nstat_domain_info)) {
1108 		return 0;
1109 	}
1110 
1111 	switch (requested_extension) {
1112 	case NSTAT_EXTENDED_UPDATE_TYPE_DOMAIN:
1113 
1114 		domain_info = (struct nstat_domain_info *)buf;
1115 		necp_copy_inp_domain_info(inp, so, domain_info);
1116 
1117 		if (hash_entry->soflow_debug) {
1118 			SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug, "Collected NSTAT domain_info:pid %d domain <%s> owner <%s> "
1119 			    "ctxt <%s> bundle id <%s> is_tracker %d is_non_app_initiated %d is_silent %d",
1120 			    so->so_flags & SOF_DELEGATED ? so->e_pid : so->last_pid,
1121 			    domain_info->domain_name,
1122 			    domain_info->domain_owner,
1123 			    domain_info->domain_tracker_ctxt,
1124 			    domain_info->domain_attributed_bundle_id,
1125 			    domain_info->is_tracker,
1126 			    domain_info->is_non_app_initiated,
1127 			    domain_info->is_silent);
1128 		}
1129 		size = sizeof(nstat_domain_info);
1130 
1131 	default:
1132 		break;
1133 	}
1134 
1135 	return size;
1136 }
1137 #endif
1138 
1139 static void
soflow_update_flow_stats(struct soflow_hash_entry * hash_entry,size_t data_size,bool outgoing)1140 soflow_update_flow_stats(struct soflow_hash_entry *hash_entry, size_t data_size, bool outgoing)
1141 {
1142 	struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
1143 
1144 	if (hash_entry != NULL) {
1145 		if (outgoing) {
1146 			hash_entry->soflow_txbytes += data_size;
1147 			hash_entry->soflow_txpackets++;
1148 			SOFLOW_ENTRY_LOG(LOG_DEBUG, so, hash_entry, hash_entry->soflow_debug, "Stats update - Outgoing");
1149 		} else {
1150 			hash_entry->soflow_rxbytes += data_size;
1151 			hash_entry->soflow_rxpackets++;
1152 			SOFLOW_ENTRY_LOG(LOG_DEBUG, so, hash_entry, hash_entry->soflow_debug, "Stats update - Incoming");
1153 		}
1154 	}
1155 }
1156 
1157 struct soflow_hash_entry *
soflow_get_flow(struct socket * so,struct sockaddr * local,struct sockaddr * remote,struct mbuf * control,size_t data_size,soflow_direction_t direction,uint16_t rcv_ifindex)1158 soflow_get_flow(struct socket *so, struct sockaddr *local, struct sockaddr *remote, struct mbuf *control,
1159     size_t data_size, soflow_direction_t direction, uint16_t rcv_ifindex)
1160 {
1161 	struct soflow_hash_entry *hash_entry = NULL;
1162 	struct inpcb *inp = sotoinpcb(so);
1163 
1164 	// Check if feature is disabled
1165 	if (soflow_disable) {
1166 		return NULL;
1167 	}
1168 
1169 	socket_lock_assert_owned(so);
1170 
1171 	if (so->so_flow_db != NULL) {
1172 		// Take refcount of db before use.
1173 		// Abort if db is already being freed.
1174 		if (os_ref_retain_try(&so->so_flow_db->soflow_db_ref_count) == false) {
1175 			return NULL;
1176 		}
1177 
1178 		// DB already exists, check if this is existing flow
1179 		hash_entry = soflow_db_lookup_entry(so->so_flow_db, local, remote, false);
1180 		if (hash_entry == NULL) {
1181 			// No match with both local and remote, try match with remote only
1182 			hash_entry = soflow_db_lookup_entry(so->so_flow_db, local, remote, true);
1183 		}
1184 		if (hash_entry != NULL) {
1185 			// Take refcount of entry before use.
1186 			// Abort if entry is already being freed.
1187 			if (os_ref_retain_try(&hash_entry->soflow_ref_count) == false) {
1188 				SOFLOW_DB_FREE(so->so_flow_db);
1189 				return NULL;
1190 			}
1191 
1192 			// Try to update flow info from socket and/or control mbufs if necessary
1193 			if (hash_entry->soflow_outifindex == 0 ||
1194 			    soflow_entry_local_address_needs_update(hash_entry) || soflow_entry_local_port_needs_update(so, hash_entry)) {
1195 				soflow_entry_update_local(so->so_flow_db, hash_entry, local, control, rcv_ifindex);
1196 			}
1197 			hash_entry->soflow_lastused = net_uptime();
1198 			if (data_size > 0 && direction != SOFLOW_DIRECTION_UNKNOWN) {
1199 				soflow_update_flow_stats(hash_entry, data_size, direction == SOFLOW_DIRECTION_OUTBOUND);
1200 			}
1201 
1202 			SOFLOW_DB_FREE(so->so_flow_db);
1203 			return hash_entry;
1204 		}
1205 
1206 		SOFLOW_DB_FREE(so->so_flow_db);
1207 	}
1208 
1209 	// No flow was found. Only add a new flow if the direction is known.
1210 	if (direction == SOFLOW_DIRECTION_UNKNOWN) {
1211 		return NULL;
1212 	}
1213 
1214 	if (so->so_flow_db == NULL) {
1215 		// If new socket, allocate cfil db
1216 		if (soflow_db_init(so) != 0) {
1217 			return NULL;
1218 		}
1219 	}
1220 
1221 	hash_entry = soflow_db_add_entry(so->so_flow_db, local, remote);
1222 	if (hash_entry == NULL) {
1223 		SOFLOW_LOG(LOG_ERR, so, true, "Failed to add entry");
1224 		return NULL;
1225 	}
1226 
1227 	// Take refcount of entry before use.
1228 	// Abort if entry is already being freed.
1229 	if (os_ref_retain_try(&hash_entry->soflow_ref_count) == false) {
1230 		return NULL;
1231 	}
1232 
1233 	if (inp && (inp->inp_last_outifp != NULL || rcv_ifindex != 0)) {
1234 		hash_entry->soflow_outifindex = inp->inp_last_outifp ? inp->inp_last_outifp->if_index : rcv_ifindex;
1235 	}
1236 
1237 	// Check if we can update the new flow's local address from control mbufs
1238 	if (control != NULL) {
1239 		soflow_entry_update_local(so->so_flow_db, hash_entry, local, control, rcv_ifindex);
1240 	}
1241 	hash_entry->soflow_outgoing = (direction == SOFLOW_DIRECTION_OUTBOUND);
1242 	if (data_size > 0) {
1243 		soflow_update_flow_stats(hash_entry, data_size, direction == SOFLOW_DIRECTION_OUTBOUND);
1244 	}
1245 
1246 	// Only report flow to NSTAT if unconnected UDP
1247 	if (!soflow_nstat_disable && SOFLOW_IS_UDP(so) && !(so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING))) {
1248 #if defined(NSTAT_EXTENSION_FILTER_DOMAIN_INFO)
1249 		// Take refcount of entry before handing it to nstat. Abort if fail.
1250 		if (os_ref_retain_try(&hash_entry->soflow_ref_count) == false) {
1251 			return NULL;
1252 		}
1253 		uuid_generate_random(hash_entry->soflow_uuid);
1254 		hash_entry->soflow_nstat_context = nstat_provider_stats_open((nstat_provider_context) hash_entry,
1255 		    NSTAT_PROVIDER_UDP_SUBFLOW, 0,
1256 		    soflow_nstat_provider_request_vals,
1257 		    soflow_nstat_provider_request_extensions);
1258 		SOFLOW_LOG(LOG_INFO, so, hash_entry->soflow_debug, "<Open nstat> - context %lX", (unsigned long)hash_entry->soflow_nstat_context);
1259 #endif
1260 	}
1261 
1262 	SOFLOW_LOCK_EXCLUSIVE;
1263 	if (soflow_initialized == 0) {
1264 		soflow_init();
1265 	}
1266 	TAILQ_INSERT_TAIL(&soflow_entry_head, hash_entry, soflow_entry_list_link);
1267 	if (soflow_attached_count == 0) {
1268 		thread_wakeup((caddr_t)&soflow_attached_count);
1269 	}
1270 	soflow_attached_count++;
1271 	if (soflow_attached_high_water_mark < soflow_attached_count) {
1272 		soflow_attached_high_water_mark = soflow_attached_count;
1273 	}
1274 	SOFLOW_UNLOCK_EXCLUSIVE;
1275 
1276 	SOFLOW_ENTRY_LOG(LOG_INFO, so, hash_entry, hash_entry->soflow_debug, "Added entry");
1277 	return hash_entry;
1278 }
1279 
1280 void
soflow_free_flow(struct soflow_hash_entry * entry)1281 soflow_free_flow(struct soflow_hash_entry *entry)
1282 {
1283 	SOFLOW_ENTRY_FREE(entry);
1284 }
1285 
1286 static bool
soflow_socket_safe_lock(struct inpcb * inp,struct inpcbinfo * pcbinfo)1287 soflow_socket_safe_lock(struct inpcb *inp, struct inpcbinfo *pcbinfo)
1288 {
1289 	struct socket *so = NULL;
1290 
1291 	VERIFY(pcbinfo != NULL);
1292 
1293 	if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) != WNT_STOPUSING) {
1294 		// Safeguarded the inp state, unlock pcbinfo before locking socket.
1295 		lck_rw_done(&pcbinfo->ipi_lock);
1296 
1297 		so = inp->inp_socket;
1298 		socket_lock(so, 1);
1299 		if (in_pcb_checkstate(inp, WNT_RELEASE, 1) != WNT_STOPUSING) {
1300 			return true;
1301 		}
1302 	} else {
1303 		// Failed to safeguarded the inp state, unlock pcbinfo and abort.
1304 		lck_rw_done(&pcbinfo->ipi_lock);
1305 	}
1306 
1307 	if (so) {
1308 		socket_unlock(so, 1);
1309 	}
1310 	return false;
1311 }
1312 
1313 static struct socket *
soflow_validate_dgram_socket(struct socket * so)1314 soflow_validate_dgram_socket(struct socket *so)
1315 {
1316 	struct inpcb *inp = NULL;
1317 	struct inpcbinfo *pcbinfo = NULL;
1318 	struct socket *locked = NULL;
1319 
1320 	pcbinfo = &udbinfo;
1321 	lck_rw_lock_shared(&pcbinfo->ipi_lock);
1322 	LIST_FOREACH(inp, pcbinfo->ipi_listhead, inp_list) {
1323 		if (inp->inp_state != INPCB_STATE_DEAD && inp->inp_socket == so) {
1324 			if (soflow_socket_safe_lock(inp, pcbinfo)) {
1325 				locked = inp->inp_socket;
1326 			}
1327 			/* pcbinfo is already unlocked, we are done. */
1328 			goto done;
1329 		}
1330 	}
1331 	lck_rw_done(&pcbinfo->ipi_lock);
1332 	if (locked != NULL) {
1333 		goto done;
1334 	}
1335 
1336 	pcbinfo = &ripcbinfo;
1337 	lck_rw_lock_shared(&pcbinfo->ipi_lock);
1338 	LIST_FOREACH(inp, pcbinfo->ipi_listhead, inp_list) {
1339 		if (inp->inp_state != INPCB_STATE_DEAD && inp->inp_socket == so) {
1340 			if (soflow_socket_safe_lock(inp, pcbinfo)) {
1341 				locked = inp->inp_socket;
1342 			}
1343 			/* pcbinfo is already unlocked, we are done. */
1344 			goto done;
1345 		}
1346 	}
1347 	lck_rw_done(&pcbinfo->ipi_lock);
1348 
1349 done:
1350 	return locked;
1351 }
1352 
1353 static void
soflow_gc_thread_sleep(bool forever)1354 soflow_gc_thread_sleep(bool forever)
1355 {
1356 	if (forever) {
1357 		(void) assert_wait((event_t) &soflow_attached_count,
1358 		    THREAD_INTERRUPTIBLE);
1359 	} else {
1360 		uint64_t deadline = 0;
1361 		nanoseconds_to_absolutetime(SOFLOW_GC_RUN_INTERVAL_NSEC, &deadline);
1362 		clock_absolutetime_interval_to_deadline(deadline, &deadline);
1363 
1364 		(void) assert_wait_deadline(&soflow_attached_count,
1365 		    THREAD_INTERRUPTIBLE, deadline);
1366 	}
1367 }
1368 
1369 static void
soflow_gc_thread_func(void * v,wait_result_t w)1370 soflow_gc_thread_func(void *v, wait_result_t w)
1371 {
1372 #pragma unused(v, w)
1373 
1374 	ASSERT(soflow_gc_thread == current_thread());
1375 	thread_set_thread_name(current_thread(), "SOFLOW_GC");
1376 
1377 	// Kick off gc shortly
1378 	soflow_gc_thread_sleep(false);
1379 	thread_block_parameter((thread_continue_t) soflow_gc_expire, NULL);
1380 	/* NOTREACHED */
1381 }
1382 
1383 static bool
soflow_gc_idle_timed_out(struct soflow_hash_entry * hash_entry,int timeout,u_int64_t current_time)1384 soflow_gc_idle_timed_out(struct soflow_hash_entry *hash_entry, int timeout, u_int64_t current_time)
1385 {
1386 	struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
1387 
1388 	if (hash_entry && (current_time - hash_entry->soflow_lastused >= (u_int64_t)timeout)) {
1389 		SOFLOW_ENTRY_LOG(LOG_INFO, so, hash_entry, hash_entry->soflow_debug, "GC Idle Timeout detected");
1390 		return true;
1391 	}
1392 	return false;
1393 }
1394 
1395 static int
soflow_gc_cleanup(struct socket * so)1396 soflow_gc_cleanup(struct socket *so)
1397 {
1398 	struct soflow_hash_entry *entry = NULL;
1399 	struct soflow_hash_entry *temp_entry = NULL;
1400 	struct soflow_hash_head *flowhash = NULL;
1401 	struct soflow_db *db = NULL;
1402 	int cleaned = 0;
1403 
1404 	if (so == NULL || so->so_flow_db == NULL) {
1405 		return 0;
1406 	}
1407 	db = so->so_flow_db;
1408 
1409 	// Do not collect garbage for databases that have only one flow.
1410 	if (db->soflow_db_count == 1 && db->soflow_db_only_entry != NULL) {
1411 		return 0;
1412 	}
1413 
1414 	socket_lock_assert_owned(so);
1415 
1416 	// Take refcount of db before use.
1417 	// Abort if db is already being freed.
1418 	if (os_ref_retain_try(&db->soflow_db_ref_count) == false) {
1419 		return 0;
1420 	}
1421 
1422 	for (int i = 0; i < SOFLOW_HASH_SIZE; i++) {
1423 		flowhash = &db->soflow_db_hashbase[i];
1424 		LIST_FOREACH_SAFE(entry, flowhash, soflow_entry_link, temp_entry) {
1425 			if (entry->soflow_gc || entry->soflow_feat_gc) {
1426 				if (entry->soflow_feat_ctxt != NULL && soflow_feat_gc_perform_func_ptr != NULL) {
1427 					soflow_feat_gc_perform_func_ptr(so, entry);
1428 					entry->soflow_feat_ctxt = NULL;
1429 					entry->soflow_feat_ctxt_id = 0;
1430 				}
1431 				entry->soflow_feat_gc = 0;
1432 
1433 				if (entry->soflow_gc) {
1434 					SOFLOW_ENTRY_LOG(LOG_INFO, so, entry, entry->soflow_debug, "GC cleanup entry");
1435 					entry->soflow_gc = 0;
1436 					soflow_db_remove_entry(db, entry);
1437 					cleaned++;
1438 				}
1439 			}
1440 		}
1441 	}
1442 
1443 	SOFLOW_DB_FREE(db);
1444 	return cleaned;
1445 }
1446 
1447 static void
soflow_gc_expire(void * v,wait_result_t w)1448 soflow_gc_expire(void *v, wait_result_t w)
1449 {
1450 #pragma unused(v, w)
1451 
1452 	static struct socket *socket_array[SOFLOW_GC_MAX_COUNT];
1453 	struct soflow_hash_entry *hash_entry = NULL;
1454 	struct socket *so = NULL;
1455 	u_int64_t current_time = net_uptime();
1456 	uint32_t socket_count = 0;
1457 	uint32_t cleaned_count = 0;
1458 	bool recorded = false;
1459 
1460 	// Collect a list of socket with expired flows
1461 
1462 	SOFLOW_LOCK_SHARED;
1463 
1464 	if (soflow_attached_count == 0) {
1465 		SOFLOW_UNLOCK_SHARED;
1466 		goto go_sleep;
1467 	}
1468 
1469 	// Go thorough all flows in the flow list and record any socket with expired flows.
1470 	TAILQ_FOREACH(hash_entry, &soflow_entry_head, soflow_entry_list_link) {
1471 		if (socket_count >= SOFLOW_GC_MAX_COUNT) {
1472 			break;
1473 		}
1474 		so = hash_entry->soflow_db ? hash_entry->soflow_db->soflow_db_so : NULL;
1475 
1476 		// Check if we need to perform cleanup due to idle time or feature specified rules
1477 		hash_entry->soflow_gc = soflow_gc_idle_timed_out(hash_entry, SOFLOW_GC_IDLE_TO, current_time);
1478 		hash_entry->soflow_feat_gc = (soflow_feat_gc_needed_func_ptr != NULL && soflow_feat_gc_needed_func_ptr(so, hash_entry, current_time));
1479 
1480 		if (hash_entry->soflow_gc || hash_entry->soflow_feat_gc) {
1481 			if (so != NULL) {
1482 				recorded = false;
1483 				for (int i = 0; i < socket_count; i++) {
1484 					if (socket_array[socket_count] == so) {
1485 						recorded = true;
1486 						break;
1487 					}
1488 				}
1489 				if (recorded == false) {
1490 					socket_array[socket_count] = so;
1491 					socket_count++;
1492 				}
1493 			}
1494 		}
1495 	}
1496 	SOFLOW_UNLOCK_SHARED;
1497 
1498 	if (socket_count == 0) {
1499 		goto go_sleep;
1500 	}
1501 
1502 	for (uint32_t i = 0; i < socket_count; i++) {
1503 		// Validate socket and lock it
1504 		so = soflow_validate_dgram_socket(socket_array[i]);
1505 		if (so == NULL) {
1506 			continue;
1507 		}
1508 		cleaned_count += soflow_gc_cleanup(so);
1509 		socket_unlock(so, 1);
1510 	}
1511 
1512 	so = NULL;
1513 	SOFLOW_LOG(LOG_INFO, so, true, "<GC cleaned %d flows>", cleaned_count);
1514 
1515 go_sleep:
1516 
1517 	// Sleep forever (until waken up) if no more UDP flow to clean
1518 	SOFLOW_LOCK_SHARED;
1519 	soflow_gc_thread_sleep(soflow_attached_count == 0 ? true : false);
1520 	SOFLOW_UNLOCK_SHARED;
1521 	thread_block_parameter((thread_continue_t)soflow_gc_expire, NULL);
1522 	/* NOTREACHED */
1523 }
1524 
1525 void
soflow_feat_set_functions(soflow_feat_gc_needed_func gc_needed_fn,soflow_feat_gc_perform_func gc_perform_fn,soflow_feat_detach_entry_func feat_detach_entry_fn,soflow_feat_detach_db_func feat_detach_db_fn)1526 soflow_feat_set_functions(soflow_feat_gc_needed_func gc_needed_fn,
1527     soflow_feat_gc_perform_func gc_perform_fn,
1528     soflow_feat_detach_entry_func feat_detach_entry_fn,
1529     soflow_feat_detach_db_func feat_detach_db_fn)
1530 {
1531 	soflow_feat_gc_needed_func_ptr = gc_needed_fn;
1532 	soflow_feat_gc_perform_func_ptr = gc_perform_fn;
1533 	soflow_feat_detach_entry_func_ptr = feat_detach_entry_fn;
1534 	soflow_feat_detach_db_func_ptr = feat_detach_db_fn;
1535 }
1536 
1537 bool
soflow_db_apply(struct soflow_db * db,soflow_entry_apply_func entry_apply_fn,void * context)1538 soflow_db_apply(struct soflow_db *db, soflow_entry_apply_func entry_apply_fn, void *context)
1539 {
1540 	struct soflow_hash_entry *entry = NULL;
1541 	struct soflow_hash_entry *temp_entry = NULL;
1542 	struct soflow_hash_head *flowhash = NULL;
1543 
1544 	if (db == NULL || db->soflow_db_so == NULL || entry_apply_fn == NULL) {
1545 		return false;
1546 	}
1547 
1548 	socket_lock_assert_owned(db->soflow_db_so);
1549 
1550 	// Take refcount of db before use.
1551 	// Abort if db is already being freed.
1552 	if (os_ref_retain_try(&db->soflow_db_ref_count) == false) {
1553 		return false;
1554 	}
1555 
1556 	for (int i = 0; i < SOFLOW_HASH_SIZE; i++) {
1557 		flowhash = &db->soflow_db_hashbase[i];
1558 		LIST_FOREACH_SAFE(entry, flowhash, soflow_entry_link, temp_entry) {
1559 			if (entry_apply_fn(db->soflow_db_so, entry, context) == false) {
1560 				goto done;
1561 			}
1562 		}
1563 	}
1564 
1565 done:
1566 	SOFLOW_DB_FREE(db);
1567 	return true;
1568 }
1569