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,void * metadatap)949 soflow_nstat_provider_request_vals(nstat_provider_context ctx,
950 u_int32_t *ifflagsp,
951 nstat_counts *countsp,
952 void *metadatap)
953 {
954 struct soflow_hash_entry *hash_entry = (struct soflow_hash_entry *) ctx;
955 struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
956 struct inpcb *inp = so ? sotoinpcb(so) : NULL;
957 char local[MAX_IPv6_STR_LEN + 6] = { 0 };
958 char remote[MAX_IPv6_STR_LEN + 6] = { 0 };
959 const void *addr = NULL;
960
961 if (hash_entry == NULL || so == NULL || inp == NULL) {
962 return false;
963 }
964
965 if (ifflagsp) {
966 if (hash_entry->soflow_outifindex) {
967 struct ifnet *ifp = ifindex2ifnet[hash_entry->soflow_outifindex];
968 *ifflagsp = ifnet_to_flags(ifp, so);
969 }
970 if ((countsp == NULL) && (metadatap == NULL)) {
971 SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug, "ifflagsp set to 0x%X", *ifflagsp);
972 goto done;
973 }
974 }
975
976 if (countsp) {
977 bzero(countsp, sizeof(*countsp));
978 countsp->nstat_rxpackets = hash_entry->soflow_rxpackets;
979 countsp->nstat_rxbytes = hash_entry->soflow_rxbytes;
980 countsp->nstat_txpackets = hash_entry->soflow_txpackets;
981 countsp->nstat_txbytes = hash_entry->soflow_txbytes;
982
983 SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug,
984 "Collected NSTAT counts: rxpackets %llu rxbytes %llu txpackets %llu txbytes %llu",
985 countsp->nstat_rxpackets, countsp->nstat_rxbytes, countsp->nstat_txpackets, countsp->nstat_txbytes);
986 }
987
988 if (metadatap) {
989 nstat_udp_descriptor *desc = (nstat_udp_descriptor *)metadatap;
990 bzero(desc, sizeof(*desc));
991
992 if (so->so_flags & SOF_DELEGATED) {
993 desc->eupid = so->e_upid;
994 desc->epid = so->e_pid;
995 uuid_copy(desc->euuid, so->e_uuid);
996 } else {
997 desc->eupid = so->last_upid;
998 desc->epid = so->last_pid;
999 uuid_copy(desc->euuid, so->last_uuid);
1000 }
1001
1002 uuid_copy(desc->vuuid, so->so_vuuid);
1003 uuid_copy(desc->fuuid, hash_entry->soflow_uuid);
1004
1005 if (hash_entry->soflow_family == AF_INET6) {
1006 in6_ip6_to_sockaddr(&hash_entry->soflow_laddr.addr6, hash_entry->soflow_lport, hash_entry->soflow_laddr6_ifscope,
1007 &desc->local.v6, sizeof(desc->local.v6));
1008 in6_ip6_to_sockaddr(&hash_entry->soflow_faddr.addr6, hash_entry->soflow_fport, hash_entry->soflow_faddr6_ifscope,
1009 &desc->remote.v6, sizeof(desc->remote.v6));
1010 } else if (hash_entry->soflow_family == AF_INET) {
1011 desc->local.v4.sin_family = AF_INET;
1012 desc->local.v4.sin_len = sizeof(struct sockaddr_in);
1013 desc->local.v4.sin_port = hash_entry->soflow_lport;
1014 desc->local.v4.sin_addr = hash_entry->soflow_laddr.addr46.ia46_addr4;
1015
1016 desc->remote.v4.sin_family = AF_INET;
1017 desc->remote.v4.sin_len = sizeof(struct sockaddr_in);
1018 desc->remote.v4.sin_port = hash_entry->soflow_fport;
1019 desc->remote.v4.sin_addr = hash_entry->soflow_faddr.addr46.ia46_addr4;
1020 }
1021
1022 desc->ifindex = hash_entry->soflow_outifindex;
1023 if (hash_entry->soflow_outifindex) {
1024 struct ifnet *ifp = ifindex2ifnet[hash_entry->soflow_outifindex];
1025 desc->ifnet_properties = (uint16_t)ifnet_to_flags(ifp, so);
1026 }
1027
1028 desc->rcvbufsize = so->so_rcv.sb_hiwat;
1029 desc->rcvbufused = so->so_rcv.sb_cc;
1030 desc->traffic_class = so->so_traffic_class;
1031 inp_get_activity_bitmap(inp, &desc->activity_bitmap);
1032
1033 if (hash_entry->soflow_debug) {
1034 switch (hash_entry->soflow_family) {
1035 case AF_INET6:
1036 addr = &desc->local.v6;
1037 inet_ntop(AF_INET6, addr, local, sizeof(local));
1038 addr = &desc->remote.v6;
1039 inet_ntop(AF_INET6, addr, remote, sizeof(local));
1040 break;
1041 case AF_INET:
1042 addr = &desc->local.v4.sin_addr;
1043 inet_ntop(AF_INET, addr, local, sizeof(local));
1044 addr = &desc->remote.v4.sin_addr;
1045 inet_ntop(AF_INET, addr, remote, sizeof(local));
1046 break;
1047 default:
1048 break;
1049 }
1050
1051 uint8_t *ptr = (uint8_t *)&desc->euuid;
1052
1053 SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug,
1054 "Collected NSTAT metadata: eupid %llu epid %d euuid %x%x%x%x-%x%x%x%x-%x%x%x%x-%x%x%x%x "
1055 "outifp %d properties 0x%X lport %d fport %d laddr %s faddr %s "
1056 "rcvbufsize %u rcvbufused %u traffic_class %u",
1057 desc->eupid, desc->epid,
1058 ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
1059 ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15],
1060 desc->ifindex, desc->ifnet_properties,
1061 ntohs(desc->local.v4.sin_port), ntohs(desc->remote.v4.sin_port), local, remote,
1062 desc->rcvbufsize, desc->rcvbufused, desc->traffic_class);
1063 }
1064 }
1065 done:
1066 return true;
1067 }
1068
1069 static size_t
soflow_nstat_provider_request_extensions(nstat_provider_context ctx,int requested_extension,void * buf,size_t buf_size)1070 soflow_nstat_provider_request_extensions(nstat_provider_context ctx,
1071 int requested_extension,
1072 void *buf,
1073 size_t buf_size)
1074 {
1075 struct soflow_hash_entry *hash_entry = (struct soflow_hash_entry *) ctx;
1076 struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
1077 struct inpcb *inp = so ? sotoinpcb(so) : NULL;
1078 struct nstat_domain_info *domain_info = NULL;
1079 size_t size = 0;
1080
1081 if (hash_entry == NULL || so == NULL || inp == NULL) {
1082 return 0;
1083 }
1084
1085 if (buf == NULL) {
1086 switch (requested_extension) {
1087 case NSTAT_EXTENDED_UPDATE_TYPE_DOMAIN:
1088 return sizeof(nstat_domain_info);
1089 default:
1090 return 0;
1091 }
1092 }
1093
1094 if (buf_size < sizeof(nstat_domain_info)) {
1095 return 0;
1096 }
1097
1098 switch (requested_extension) {
1099 case NSTAT_EXTENDED_UPDATE_TYPE_DOMAIN:
1100
1101 domain_info = (struct nstat_domain_info *)buf;
1102 necp_copy_inp_domain_info(inp, so, domain_info);
1103
1104 if (hash_entry->soflow_debug) {
1105 SOFLOW_LOG(LOG_DEBUG, so, hash_entry->soflow_debug, "Collected NSTAT domain_info:pid %d domain <%s> owner <%s> "
1106 "ctxt <%s> bundle id <%s> is_tracker %d is_non_app_initiated %d is_silent %d",
1107 so->so_flags & SOF_DELEGATED ? so->e_pid : so->last_pid,
1108 domain_info->domain_name,
1109 domain_info->domain_owner,
1110 domain_info->domain_tracker_ctxt,
1111 domain_info->domain_attributed_bundle_id,
1112 domain_info->is_tracker,
1113 domain_info->is_non_app_initiated,
1114 domain_info->is_silent);
1115 }
1116 size = sizeof(nstat_domain_info);
1117
1118 default:
1119 break;
1120 }
1121
1122 return size;
1123 }
1124 #endif
1125
1126 static void
soflow_update_flow_stats(struct soflow_hash_entry * hash_entry,size_t data_size,bool outgoing)1127 soflow_update_flow_stats(struct soflow_hash_entry *hash_entry, size_t data_size, bool outgoing)
1128 {
1129 struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
1130
1131 if (hash_entry != NULL) {
1132 if (outgoing) {
1133 hash_entry->soflow_txbytes += data_size;
1134 hash_entry->soflow_txpackets++;
1135 SOFLOW_ENTRY_LOG(LOG_DEBUG, so, hash_entry, hash_entry->soflow_debug, "Stats update - Outgoing");
1136 } else {
1137 hash_entry->soflow_rxbytes += data_size;
1138 hash_entry->soflow_rxpackets++;
1139 SOFLOW_ENTRY_LOG(LOG_DEBUG, so, hash_entry, hash_entry->soflow_debug, "Stats update - Incoming");
1140 }
1141 }
1142 }
1143
1144 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)1145 soflow_get_flow(struct socket *so, struct sockaddr *local, struct sockaddr *remote, struct mbuf *control,
1146 size_t data_size, soflow_direction_t direction, uint16_t rcv_ifindex)
1147 {
1148 struct soflow_hash_entry *hash_entry = NULL;
1149 struct inpcb *inp = sotoinpcb(so);
1150
1151 // Check if feature is disabled
1152 if (soflow_disable) {
1153 return NULL;
1154 }
1155
1156 socket_lock_assert_owned(so);
1157
1158 if (so->so_flow_db != NULL) {
1159 // Take refcount of db before use.
1160 // Abort if db is already being freed.
1161 if (os_ref_retain_try(&so->so_flow_db->soflow_db_ref_count) == false) {
1162 return NULL;
1163 }
1164
1165 // DB already exists, check if this is existing flow
1166 hash_entry = soflow_db_lookup_entry(so->so_flow_db, local, remote, false);
1167 if (hash_entry == NULL) {
1168 // No match with both local and remote, try match with remote only
1169 hash_entry = soflow_db_lookup_entry(so->so_flow_db, local, remote, true);
1170 }
1171 if (hash_entry != NULL) {
1172 // Take refcount of entry before use.
1173 // Abort if entry is already being freed.
1174 if (os_ref_retain_try(&hash_entry->soflow_ref_count) == false) {
1175 SOFLOW_DB_FREE(so->so_flow_db);
1176 return NULL;
1177 }
1178
1179 // Try to update flow info from socket and/or control mbufs if necessary
1180 if (hash_entry->soflow_outifindex == 0 ||
1181 soflow_entry_local_address_needs_update(hash_entry) || soflow_entry_local_port_needs_update(so, hash_entry)) {
1182 soflow_entry_update_local(so->so_flow_db, hash_entry, local, control, rcv_ifindex);
1183 }
1184 hash_entry->soflow_lastused = net_uptime();
1185 if (data_size > 0 && direction != SOFLOW_DIRECTION_UNKNOWN) {
1186 soflow_update_flow_stats(hash_entry, data_size, direction == SOFLOW_DIRECTION_OUTBOUND);
1187 }
1188
1189 SOFLOW_DB_FREE(so->so_flow_db);
1190 return hash_entry;
1191 }
1192
1193 SOFLOW_DB_FREE(so->so_flow_db);
1194 }
1195
1196 // No flow was found. Only add a new flow if the direction is known.
1197 if (direction == SOFLOW_DIRECTION_UNKNOWN) {
1198 return NULL;
1199 }
1200
1201 if (so->so_flow_db == NULL) {
1202 // If new socket, allocate cfil db
1203 if (soflow_db_init(so) != 0) {
1204 return NULL;
1205 }
1206 }
1207
1208 hash_entry = soflow_db_add_entry(so->so_flow_db, local, remote);
1209 if (hash_entry == NULL) {
1210 SOFLOW_LOG(LOG_ERR, so, true, "Failed to add entry");
1211 return NULL;
1212 }
1213
1214 // Take refcount of entry before use.
1215 // Abort if entry is already being freed.
1216 if (os_ref_retain_try(&hash_entry->soflow_ref_count) == false) {
1217 return NULL;
1218 }
1219
1220 if (inp && (inp->inp_last_outifp != NULL || rcv_ifindex != 0)) {
1221 hash_entry->soflow_outifindex = inp->inp_last_outifp ? inp->inp_last_outifp->if_index : rcv_ifindex;
1222 }
1223
1224 // Check if we can update the new flow's local address from control mbufs
1225 if (control != NULL) {
1226 soflow_entry_update_local(so->so_flow_db, hash_entry, local, control, rcv_ifindex);
1227 }
1228 hash_entry->soflow_outgoing = (direction == SOFLOW_DIRECTION_OUTBOUND);
1229 if (data_size > 0) {
1230 soflow_update_flow_stats(hash_entry, data_size, direction == SOFLOW_DIRECTION_OUTBOUND);
1231 }
1232
1233 // Only report flow to NSTAT if unconnected UDP
1234 if (!soflow_nstat_disable && SOFLOW_IS_UDP(so) && !(so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING))) {
1235 #if defined(NSTAT_EXTENSION_FILTER_DOMAIN_INFO)
1236 // Take refcount of entry before handing it to nstat. Abort if fail.
1237 if (os_ref_retain_try(&hash_entry->soflow_ref_count) == false) {
1238 return NULL;
1239 }
1240 uuid_generate_random(hash_entry->soflow_uuid);
1241 hash_entry->soflow_nstat_context = nstat_provider_stats_open((nstat_provider_context) hash_entry,
1242 NSTAT_PROVIDER_UDP_SUBFLOW, 0,
1243 soflow_nstat_provider_request_vals,
1244 soflow_nstat_provider_request_extensions);
1245 SOFLOW_LOG(LOG_INFO, so, hash_entry->soflow_debug, "<Open nstat> - context %lX", (unsigned long)hash_entry->soflow_nstat_context);
1246 #endif
1247 }
1248
1249 SOFLOW_LOCK_EXCLUSIVE;
1250 if (soflow_initialized == 0) {
1251 soflow_init();
1252 }
1253 TAILQ_INSERT_TAIL(&soflow_entry_head, hash_entry, soflow_entry_list_link);
1254 if (soflow_attached_count == 0) {
1255 thread_wakeup((caddr_t)&soflow_attached_count);
1256 }
1257 soflow_attached_count++;
1258 if (soflow_attached_high_water_mark < soflow_attached_count) {
1259 soflow_attached_high_water_mark = soflow_attached_count;
1260 }
1261 SOFLOW_UNLOCK_EXCLUSIVE;
1262
1263 SOFLOW_ENTRY_LOG(LOG_INFO, so, hash_entry, hash_entry->soflow_debug, "Added entry");
1264 return hash_entry;
1265 }
1266
1267 void
soflow_free_flow(struct soflow_hash_entry * entry)1268 soflow_free_flow(struct soflow_hash_entry *entry)
1269 {
1270 SOFLOW_ENTRY_FREE(entry);
1271 }
1272
1273 static bool
soflow_socket_safe_lock(struct inpcb * inp,struct inpcbinfo * pcbinfo)1274 soflow_socket_safe_lock(struct inpcb *inp, struct inpcbinfo *pcbinfo)
1275 {
1276 struct socket *so = NULL;
1277
1278 VERIFY(pcbinfo != NULL);
1279
1280 if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) != WNT_STOPUSING) {
1281 // Safeguarded the inp state, unlock pcbinfo before locking socket.
1282 lck_rw_done(&pcbinfo->ipi_lock);
1283
1284 so = inp->inp_socket;
1285 socket_lock(so, 1);
1286 if (in_pcb_checkstate(inp, WNT_RELEASE, 1) != WNT_STOPUSING) {
1287 return true;
1288 }
1289 } else {
1290 // Failed to safeguarded the inp state, unlock pcbinfo and abort.
1291 lck_rw_done(&pcbinfo->ipi_lock);
1292 }
1293
1294 if (so) {
1295 socket_unlock(so, 1);
1296 }
1297 return false;
1298 }
1299
1300 static struct socket *
soflow_validate_dgram_socket(struct socket * so)1301 soflow_validate_dgram_socket(struct socket *so)
1302 {
1303 struct inpcb *inp = NULL;
1304 struct inpcbinfo *pcbinfo = NULL;
1305 struct socket *locked = NULL;
1306
1307 pcbinfo = &udbinfo;
1308 lck_rw_lock_shared(&pcbinfo->ipi_lock);
1309 LIST_FOREACH(inp, pcbinfo->ipi_listhead, inp_list) {
1310 if (inp->inp_state != INPCB_STATE_DEAD && inp->inp_socket == so) {
1311 if (soflow_socket_safe_lock(inp, pcbinfo)) {
1312 locked = inp->inp_socket;
1313 }
1314 /* pcbinfo is already unlocked, we are done. */
1315 goto done;
1316 }
1317 }
1318 lck_rw_done(&pcbinfo->ipi_lock);
1319 if (locked != NULL) {
1320 goto done;
1321 }
1322
1323 pcbinfo = &ripcbinfo;
1324 lck_rw_lock_shared(&pcbinfo->ipi_lock);
1325 LIST_FOREACH(inp, pcbinfo->ipi_listhead, inp_list) {
1326 if (inp->inp_state != INPCB_STATE_DEAD && inp->inp_socket == so) {
1327 if (soflow_socket_safe_lock(inp, pcbinfo)) {
1328 locked = inp->inp_socket;
1329 }
1330 /* pcbinfo is already unlocked, we are done. */
1331 goto done;
1332 }
1333 }
1334 lck_rw_done(&pcbinfo->ipi_lock);
1335
1336 done:
1337 return locked;
1338 }
1339
1340 static void
soflow_gc_thread_sleep(bool forever)1341 soflow_gc_thread_sleep(bool forever)
1342 {
1343 if (forever) {
1344 (void) assert_wait((event_t) &soflow_attached_count,
1345 THREAD_INTERRUPTIBLE);
1346 } else {
1347 uint64_t deadline = 0;
1348 nanoseconds_to_absolutetime(SOFLOW_GC_RUN_INTERVAL_NSEC, &deadline);
1349 clock_absolutetime_interval_to_deadline(deadline, &deadline);
1350
1351 (void) assert_wait_deadline(&soflow_attached_count,
1352 THREAD_INTERRUPTIBLE, deadline);
1353 }
1354 }
1355
1356 static void
soflow_gc_thread_func(void * v,wait_result_t w)1357 soflow_gc_thread_func(void *v, wait_result_t w)
1358 {
1359 #pragma unused(v, w)
1360
1361 ASSERT(soflow_gc_thread == current_thread());
1362 thread_set_thread_name(current_thread(), "SOFLOW_GC");
1363
1364 // Kick off gc shortly
1365 soflow_gc_thread_sleep(false);
1366 thread_block_parameter((thread_continue_t) soflow_gc_expire, NULL);
1367 /* NOTREACHED */
1368 }
1369
1370 static bool
soflow_gc_idle_timed_out(struct soflow_hash_entry * hash_entry,int timeout,u_int64_t current_time)1371 soflow_gc_idle_timed_out(struct soflow_hash_entry *hash_entry, int timeout, u_int64_t current_time)
1372 {
1373 struct socket *so = (hash_entry && hash_entry->soflow_db) ? hash_entry->soflow_db->soflow_db_so : NULL;
1374
1375 if (hash_entry && (current_time - hash_entry->soflow_lastused >= (u_int64_t)timeout)) {
1376 SOFLOW_ENTRY_LOG(LOG_INFO, so, hash_entry, hash_entry->soflow_debug, "GC Idle Timeout detected");
1377 return true;
1378 }
1379 return false;
1380 }
1381
1382 static int
soflow_gc_cleanup(struct socket * so)1383 soflow_gc_cleanup(struct socket *so)
1384 {
1385 struct soflow_hash_entry *entry = NULL;
1386 struct soflow_hash_entry *temp_entry = NULL;
1387 struct soflow_hash_head *flowhash = NULL;
1388 struct soflow_db *db = NULL;
1389 int cleaned = 0;
1390
1391 if (so == NULL || so->so_flow_db == NULL) {
1392 return 0;
1393 }
1394 db = so->so_flow_db;
1395
1396 // Do not collect garbage for databases that have only one flow.
1397 if (db->soflow_db_count == 1 && db->soflow_db_only_entry != NULL) {
1398 return 0;
1399 }
1400
1401 socket_lock_assert_owned(so);
1402
1403 // Take refcount of db before use.
1404 // Abort if db is already being freed.
1405 if (os_ref_retain_try(&db->soflow_db_ref_count) == false) {
1406 return 0;
1407 }
1408
1409 for (int i = 0; i < SOFLOW_HASH_SIZE; i++) {
1410 flowhash = &db->soflow_db_hashbase[i];
1411 LIST_FOREACH_SAFE(entry, flowhash, soflow_entry_link, temp_entry) {
1412 if (entry->soflow_gc || entry->soflow_feat_gc) {
1413 if (entry->soflow_feat_ctxt != NULL && soflow_feat_gc_perform_func_ptr != NULL) {
1414 soflow_feat_gc_perform_func_ptr(so, entry);
1415 entry->soflow_feat_ctxt = NULL;
1416 entry->soflow_feat_ctxt_id = 0;
1417 }
1418 entry->soflow_feat_gc = 0;
1419
1420 if (entry->soflow_gc) {
1421 SOFLOW_ENTRY_LOG(LOG_INFO, so, entry, entry->soflow_debug, "GC cleanup entry");
1422 entry->soflow_gc = 0;
1423 soflow_db_remove_entry(db, entry);
1424 cleaned++;
1425 }
1426 }
1427 }
1428 }
1429
1430 SOFLOW_DB_FREE(db);
1431 return cleaned;
1432 }
1433
1434 static void
soflow_gc_expire(void * v,wait_result_t w)1435 soflow_gc_expire(void *v, wait_result_t w)
1436 {
1437 #pragma unused(v, w)
1438
1439 static struct socket *socket_array[SOFLOW_GC_MAX_COUNT];
1440 struct soflow_hash_entry *hash_entry = NULL;
1441 struct socket *so = NULL;
1442 u_int64_t current_time = net_uptime();
1443 uint32_t socket_count = 0;
1444 uint32_t cleaned_count = 0;
1445 bool recorded = false;
1446
1447 // Collect a list of socket with expired flows
1448
1449 SOFLOW_LOCK_SHARED;
1450
1451 if (soflow_attached_count == 0) {
1452 SOFLOW_UNLOCK_SHARED;
1453 goto go_sleep;
1454 }
1455
1456 // Go thorough all flows in the flow list and record any socket with expired flows.
1457 TAILQ_FOREACH(hash_entry, &soflow_entry_head, soflow_entry_list_link) {
1458 if (socket_count >= SOFLOW_GC_MAX_COUNT) {
1459 break;
1460 }
1461 so = hash_entry->soflow_db ? hash_entry->soflow_db->soflow_db_so : NULL;
1462
1463 // Check if we need to perform cleanup due to idle time or feature specified rules
1464 hash_entry->soflow_gc = soflow_gc_idle_timed_out(hash_entry, SOFLOW_GC_IDLE_TO, current_time);
1465 hash_entry->soflow_feat_gc = (soflow_feat_gc_needed_func_ptr != NULL && soflow_feat_gc_needed_func_ptr(so, hash_entry, current_time));
1466
1467 if (hash_entry->soflow_gc || hash_entry->soflow_feat_gc) {
1468 if (so != NULL) {
1469 recorded = false;
1470 for (int i = 0; i < socket_count; i++) {
1471 if (socket_array[socket_count] == so) {
1472 recorded = true;
1473 break;
1474 }
1475 }
1476 if (recorded == false) {
1477 socket_array[socket_count] = so;
1478 socket_count++;
1479 }
1480 }
1481 }
1482 }
1483 SOFLOW_UNLOCK_SHARED;
1484
1485 if (socket_count == 0) {
1486 goto go_sleep;
1487 }
1488
1489 for (uint32_t i = 0; i < socket_count; i++) {
1490 // Validate socket and lock it
1491 so = soflow_validate_dgram_socket(socket_array[i]);
1492 if (so == NULL) {
1493 continue;
1494 }
1495 cleaned_count += soflow_gc_cleanup(so);
1496 socket_unlock(so, 1);
1497 }
1498
1499 so = NULL;
1500 SOFLOW_LOG(LOG_INFO, so, true, "<GC cleaned %d flows>", cleaned_count);
1501
1502 go_sleep:
1503
1504 // Sleep forever (until waken up) if no more UDP flow to clean
1505 SOFLOW_LOCK_SHARED;
1506 soflow_gc_thread_sleep(soflow_attached_count == 0 ? true : false);
1507 SOFLOW_UNLOCK_SHARED;
1508 thread_block_parameter((thread_continue_t)soflow_gc_expire, NULL);
1509 /* NOTREACHED */
1510 }
1511
1512 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)1513 soflow_feat_set_functions(soflow_feat_gc_needed_func gc_needed_fn,
1514 soflow_feat_gc_perform_func gc_perform_fn,
1515 soflow_feat_detach_entry_func feat_detach_entry_fn,
1516 soflow_feat_detach_db_func feat_detach_db_fn)
1517 {
1518 soflow_feat_gc_needed_func_ptr = gc_needed_fn;
1519 soflow_feat_gc_perform_func_ptr = gc_perform_fn;
1520 soflow_feat_detach_entry_func_ptr = feat_detach_entry_fn;
1521 soflow_feat_detach_db_func_ptr = feat_detach_db_fn;
1522 }
1523
1524 bool
soflow_db_apply(struct soflow_db * db,soflow_entry_apply_func entry_apply_fn,void * context)1525 soflow_db_apply(struct soflow_db *db, soflow_entry_apply_func entry_apply_fn, void *context)
1526 {
1527 struct soflow_hash_entry *entry = NULL;
1528 struct soflow_hash_entry *temp_entry = NULL;
1529 struct soflow_hash_head *flowhash = NULL;
1530
1531 if (db == NULL || db->soflow_db_so == NULL || entry_apply_fn == NULL) {
1532 return false;
1533 }
1534
1535 socket_lock_assert_owned(db->soflow_db_so);
1536
1537 // Take refcount of db before use.
1538 // Abort if db is already being freed.
1539 if (os_ref_retain_try(&db->soflow_db_ref_count) == false) {
1540 return false;
1541 }
1542
1543 for (int i = 0; i < SOFLOW_HASH_SIZE; i++) {
1544 flowhash = &db->soflow_db_hashbase[i];
1545 LIST_FOREACH_SAFE(entry, flowhash, soflow_entry_link, temp_entry) {
1546 if (entry_apply_fn(db->soflow_db_so, entry, context) == false) {
1547 goto done;
1548 }
1549 }
1550 }
1551
1552 done:
1553 SOFLOW_DB_FREE(db);
1554 return true;
1555 }
1556