xref: /xnu-11215/bsd/net/ntstat.c (revision 4f1223e8)
1 /*
2  * Copyright (c) 2010-2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/kpi_mbuf.h>
32 #include <sys/socket.h>
33 #include <sys/kern_control.h>
34 #include <sys/mcache.h>
35 #include <sys/socketvar.h>
36 #include <sys/sysctl.h>
37 #include <sys/queue.h>
38 #include <sys/priv.h>
39 #include <sys/protosw.h>
40 #include <sys/persona.h>
41 #include <sys/random.h>
42 #include <kern/clock.h>
43 #include <kern/debug.h>
44 
45 #include <libkern/libkern.h>
46 #include <libkern/OSAtomic.h>
47 #include <libkern/locks.h>
48 
49 #include <net/if.h>
50 #include <net/if_var.h>
51 #include <net/if_types.h>
52 #include <net/route.h>
53 #include <net/route_private.h>
54 #include <net/dlil.h>
55 
56 // These includes appear in ntstat.h but we include them here first so they won't trigger
57 // any clang diagnostic errors.
58 #include <netinet/in.h>
59 #include <netinet/in_stat.h>
60 #include <netinet/tcp.h>
61 
62 #pragma clang diagnostic push
63 #pragma clang diagnostic error "-Wpadded"
64 #pragma clang diagnostic error "-Wpacked"
65 // This header defines structures shared with user space, so we need to ensure there is
66 // no compiler inserted padding in case the user space process isn't using the same
67 // architecture as the kernel (example: i386 process with x86_64 kernel).
68 #include <net/ntstat.h>
69 #pragma clang diagnostic pop
70 
71 #include <netinet/ip_var.h>
72 #include <netinet/in_pcb.h>
73 #include <netinet/in_var.h>
74 #include <netinet/tcp_var.h>
75 #include <netinet/tcp_fsm.h>
76 #include <netinet/tcp_cc.h>
77 #include <netinet/udp.h>
78 #include <netinet/udp_var.h>
79 #include <netinet6/in6_pcb.h>
80 #include <netinet6/in6_var.h>
81 
82 #include <net/sockaddr_utils.h>
83 
84 __private_extern__ int  nstat_collect = 1;
85 
86 #define NSTAT_TRACE_ENABLED                 0
87 #define NSTAT_FUZZ_TIMING                   0
88 
89 
90 #if (DEBUG || DEVELOPMENT)
91 SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
92     &nstat_collect, 0, "Collect detailed statistics");
93 #endif /* (DEBUG || DEVELOPMENT) */
94 
95 #if !XNU_TARGET_OS_OSX
96 static int nstat_privcheck = 1;
97 #else /* XNU_TARGET_OS_OSX */
98 static int nstat_privcheck = 0;
99 #endif /* XNU_TARGET_OS_OSX */
100 SYSCTL_INT(_net, OID_AUTO, statistics_privcheck, CTLFLAG_RW | CTLFLAG_LOCKED,
101     &nstat_privcheck, 0, "Entitlement check");
102 
103 SYSCTL_NODE(_net, OID_AUTO, stats,
104     CTLFLAG_RW | CTLFLAG_LOCKED, 0, "network statistics");
105 
106 static int nstat_debug = 0;
107 SYSCTL_INT(_net_stats, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
108     &nstat_debug, 0, "");
109 
110 static int nstat_debug_pid = 0; // Only log socket level debug for specified pid
111 SYSCTL_INT(_net_stats, OID_AUTO, debug_pid, CTLFLAG_RW | CTLFLAG_LOCKED,
112     &nstat_debug_pid, 0, "");
113 
114 static int nstat_sendspace = 2048;
115 SYSCTL_INT(_net_stats, OID_AUTO, sendspace, CTLFLAG_RW | CTLFLAG_LOCKED,
116     &nstat_sendspace, 0, "");
117 
118 static int nstat_recvspace = 8192;
119 SYSCTL_INT(_net_stats, OID_AUTO, recvspace, CTLFLAG_RW | CTLFLAG_LOCKED,
120     &nstat_recvspace, 0, "");
121 
122 static struct nstat_stats nstat_stats;
123 SYSCTL_STRUCT(_net_stats, OID_AUTO, stats, CTLFLAG_RD | CTLFLAG_LOCKED,
124     &nstat_stats, nstat_stats, "");
125 
126 static u_int32_t nstat_lim_interval = 30 * 60; /* Report interval, seconds */
127 static u_int32_t nstat_lim_min_tx_pkts = 100;
128 static u_int32_t nstat_lim_min_rx_pkts = 100;
129 #if (DEBUG || DEVELOPMENT)
130 SYSCTL_INT(_net_stats, OID_AUTO, lim_report_interval,
131     CTLFLAG_RW | CTLFLAG_LOCKED, &nstat_lim_interval, 0,
132     "Low internet stat report interval");
133 
134 SYSCTL_INT(_net_stats, OID_AUTO, lim_min_tx_pkts,
135     CTLFLAG_RW | CTLFLAG_LOCKED, &nstat_lim_min_tx_pkts, 0,
136     "Low Internet, min transmit packets threshold");
137 
138 SYSCTL_INT(_net_stats, OID_AUTO, lim_min_rx_pkts,
139     CTLFLAG_RW | CTLFLAG_LOCKED, &nstat_lim_min_rx_pkts, 0,
140     "Low Internet, min receive packets threshold");
141 #endif /* DEBUG || DEVELOPMENT */
142 
143 static int ntstat_progress_indicators(struct sysctl_req *);
144 static struct net_api_stats net_api_stats_before;
145 static u_int64_t net_api_stats_last_report_time;
146 #define NET_API_STATS_REPORT_INTERVAL (12 * 60 * 60) /* 12 hours, in seconds */
147 static u_int32_t net_api_stats_report_interval = NET_API_STATS_REPORT_INTERVAL;
148 
149 #if (DEBUG || DEVELOPMENT)
150 SYSCTL_UINT(_net_stats, OID_AUTO, api_report_interval,
151     CTLFLAG_RW | CTLFLAG_LOCKED, &net_api_stats_report_interval, 0, "");
152 #endif /* DEBUG || DEVELOPMENT */
153 
154 static os_log_t nstat_log_handle = NULL;
155 #define _NSTAT_LOG(handle, type, fmt, ...) do {                           \
156 	os_log_with_type(handle, type, "%s - " fmt, __func__, ##__VA_ARGS__); \
157 } while (0)
158 
159 #define NSTAT_LOG(fmt, ...)         _NSTAT_LOG(nstat_log_handle, OS_LOG_TYPE_DEFAULT, fmt, ##__VA_ARGS__)
160 #define NSTAT_LOG_DEBUG(fmt, ...)   _NSTAT_LOG(nstat_log_handle, OS_LOG_TYPE_DEBUG,   fmt, ##__VA_ARGS__)
161 #define NSTAT_LOG_INFO(fmt, ...)    _NSTAT_LOG(nstat_log_handle, OS_LOG_TYPE_INFO,    fmt, ##__VA_ARGS__)
162 #define NSTAT_LOG_ERROR(fmt, ...)   _NSTAT_LOG(nstat_log_handle, OS_LOG_TYPE_ERROR,   fmt, ##__VA_ARGS__)
163 
164 #define NSTAT_DEBUG_SOCKET_PID_MATCHED(so) \
165     (so && (nstat_debug_pid == (so->so_flags & SOF_DELEGATED ? so->e_pid : so->last_pid)))
166 
167 #define NSTAT_DEBUG_SOCKET_ON(so) \
168     ((nstat_debug && (!nstat_debug_pid || NSTAT_DEBUG_SOCKET_PID_MATCHED(so))) ? nstat_debug : 0)
169 
170 #define NSTAT_DEBUG_SOCKET_LOG(so, fmt, ...)        \
171     if (NSTAT_DEBUG_SOCKET_ON(so)) {                \
172 	    NSTAT_LOG_DEBUG("NSTAT_DEBUG_SOCKET <pid %d>: " fmt, (so->so_flags & SOF_DELEGATED ? so->e_pid : so->last_pid), ##__VA_ARGS__); \
173     }
174 
175 enum{
176 	NSTAT_FLAG_CLEANUP              = (1 << 0),
177 	NSTAT_FLAG_REQCOUNTS            = (1 << 1),
178 	NSTAT_FLAG_SUPPORTS_UPDATES     = (1 << 2),
179 	NSTAT_FLAG_SYSINFO_SUBSCRIBED   = (1 << 3),
180 };
181 
182 static int
183 progress_indicators SYSCTL_HANDLER_ARGS
184 {
185 	return ntstat_progress_indicators(req);
186 }
187 
188 SYSCTL_PROC(_net_stats, OID_AUTO, progress,
189     CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_LOCKED | CTLFLAG_ANYBODY, 0, 0,
190     progress_indicators, "S", "Various items that indicate the current state of progress on the link");
191 
192 #if !XNU_TARGET_OS_OSX
193 #define QUERY_CONTINUATION_SRC_COUNT 50
194 #else /* XNU_TARGET_OS_OSX */
195 #define QUERY_CONTINUATION_SRC_COUNT 100
196 #endif /* XNU_TARGET_OS_OSX */
197 #define QUERY_CONTINUATION_MIN_SRC_COUNT 5
198 
199 #ifndef ROUNDUP64
200 #define ROUNDUP64(x) P2ROUNDUP((x), sizeof (u_int64_t))
201 #endif
202 
203 #ifndef ADVANCE64
204 #define ADVANCE64(p, n) (void*)((char *)(p) + ROUNDUP64(n))
205 #endif
206 
207 typedef TAILQ_HEAD(, nstat_src)     tailq_head_nstat_src;
208 typedef TAILQ_ENTRY(nstat_src)      tailq_entry_nstat_src;
209 
210 typedef TAILQ_HEAD(, nstat_tu_shadow)   tailq_head_tu_shadow;
211 typedef TAILQ_ENTRY(nstat_tu_shadow)    tailq_entry_tu_shadow;
212 
213 typedef TAILQ_HEAD(, nstat_generic_shadow) tailq_head_generic_shadow;
214 typedef TAILQ_ENTRY(nstat_generic_shadow)  tailq_entry_generic_shadow;
215 
216 typedef TAILQ_HEAD(, nstat_procdetails) tailq_head_procdetails;
217 typedef TAILQ_ENTRY(nstat_procdetails)  tailq_entry_procdetails;
218 
219 
220 typedef void *nstat_provider_cookie_t;
221 
222 struct nstat_procdetails {
223 	tailq_entry_procdetails         pdet_link;
224 	int                             pdet_pid;
225 	u_int64_t                       pdet_upid;
226 	char                            pdet_procname[64];
227 	uuid_t                          pdet_uuid;
228 	u_int32_t                       pdet_refcnt;
229 	u_int32_t                       pdet_magic;
230 };
231 
232 typedef struct nstat_provider_filter {
233 	u_int64_t                       npf_flags;
234 	u_int64_t                       npf_events;
235 	u_int64_t                       npf_extensions;
236 	pid_t                           npf_pid;
237 	uuid_t                          npf_uuid;
238 } nstat_provider_filter;
239 
240 
241 struct nstat_global_counts {
242 	uint64_t nstat_global_client_current;   // current number of clients overall
243 	uint64_t nstat_global_client_max;       // max number of clients overall
244 	uint64_t nstat_global_client_allocs;    // total number of clients allocated
245 
246 	uint64_t nstat_global_src_current;      // current number of srcs overall
247 	uint64_t nstat_global_src_max;          // max number of srcs overall
248 	uint64_t nstat_global_src_allocs;       // total number of sources allocated
249 	uint64_t nstat_global_src_idlecheck_gone;// total number of sources discovered "gone" in idle check
250 
251 	uint64_t nstat_global_tucookie_current; // current number of tucookies overall
252 	uint64_t nstat_global_tucookie_max;     // max number of tucookies overall
253 	uint64_t nstat_global_tucookie_allocs;  // total number of tucookies allocated
254 	// Details for tucookie lifecycle
255 	uint64_t nstat_global_tucookie_skip_dead; // When adding a watcher, pcb with "dead" state skipped over
256 	uint64_t nstat_global_tucookie_skip_stopusing; // When adding a watcher, pcb marked as stop using
257 	uint64_t nstat_global_tucookie_alloc_fail;     // Allocation failure for a tucookie
258 
259 	uint64_t nstat_global_tu_shad_current;  // current number of nstat_tu_shadow objects overall
260 	uint64_t nstat_global_tu_shad_max;      // max number of tu_shadows overall
261 	uint64_t nstat_global_tu_shad_allocs;   // total number of tu_shadows allocated
262 
263 	uint64_t nstat_global_gshad_current;    // current number of generic shadow objects overall
264 	uint64_t nstat_global_gshad_max;        // max number of srcs overall
265 	uint64_t nstat_global_gshad_allocs;     // total number of sources allocated
266 
267 	uint64_t nstat_global_procdetails_current;// current number of procdetails objects overall
268 	uint64_t nstat_global_procdetails_max;   // max number of procdetails overall
269 	uint64_t nstat_global_procdetails_allocs;// total number of procdetails allocated
270 };
271 
272 struct nstat_metrics {
273 	uint32_t nstat_src_current;             // current number of srcs for client
274 	uint32_t nstat_src_max;                 // max number of srcs for client
275 	uint32_t nstat_first_uint32_count;      // Subsequent fields must be uint32_t values that, if kept per-client,
276 	                                        // should simply added to the global counts when the client exit
277 
278 	// Tracking client requests
279 	uint32_t nstat_query_request_all;       // Client requests for all counts
280 	uint32_t nstat_query_request_one;       // Client request for counts on a single source
281 	uint32_t nstat_query_description_all;   // Client requests for all descriptors
282 	uint32_t nstat_query_description_one;   // Client requests for descriptor on a single source
283 	uint32_t nstat_query_update_all;        // Client requests for all updates
284 	uint32_t nstat_query_update_one;        // Client requests for update on a single source
285 	uint32_t nstat_remove_src_found;        // Client request to remove a source which is still in existence
286 	uint32_t nstat_remove_src_missed;       // Client request to remove a source which is no longer there
287 
288 	// Details for nstat_query_request all/one
289 	uint32_t nstat_query_request_nobuf;     // No buffers for message send
290 	uint32_t nstat_query_request_upgrade;   // Successful lock upgrade to handle "gone" source
291 	uint32_t nstat_query_request_noupgrade; // Unsuccessful lock upgrade to handle "gone" source
292 	uint32_t nstat_query_request_nodesc;    // Can't send a descriptor for "gone" source
293 	uint32_t nstat_query_request_yield;     // Client yields lock due to possibly higher priority processing
294 	uint32_t nstat_query_request_limit;     // Client requests for all counts
295 
296 	// Details for nstat_query_description all/one
297 	uint32_t nstat_query_description_nobuf; // No buffers for message send
298 	uint32_t nstat_query_description_yield; // Client yields lock due to possibly higher priority processing
299 	uint32_t nstat_query_description_limit; // Client requests for all counts
300 
301 	// Details for nstat_query_update all/one
302 	uint32_t nstat_query_update_nobuf;      // No buffers for message send
303 	uint32_t nstat_query_update_upgrade;    // Successful lock upgrade to handle "gone" source
304 	uint32_t nstat_query_update_noupgrade;  // Unsuccessful lock upgrade to handle "gone" source
305 	uint32_t nstat_query_update_nodesc;     // Can't send a descriptor for "gone" source
306 	uint32_t nstat_query_update_yield;      // Client yields lock due to possibly higher priority processing
307 	uint32_t nstat_query_update_limit;      // Client requests for all counts
308 
309 	// Details for adding a source
310 	uint32_t nstat_src_add_success;         // successful src_add
311 	uint32_t nstat_src_add_no_buf;          // fail to get buffer for initial src-added
312 	uint32_t nstat_src_add_no_src_mem;      // fail to get memory for nstat_src structure
313 	uint32_t nstat_src_add_send_err;        // fail to send initial src-added
314 	uint32_t nstat_src_add_while_cleanup;   // fail to add because client is in clean up state
315 
316 	uint32_t nstat_src_gone_idlecheck;      // src gone noted during periodic idle check
317 
318 	uint32_t nstat_last_uint32_count;       // Must be the last uint32_t count in the structure
319 	uint32_t nstat_stats_pad;
320 };
321 
322 #define NUM_NSTAT_METRICS_UINT32_COUNTS ((__builtin_offsetof(struct nstat_metrics, nstat_last_uint32_count) - \
323 	                                  __builtin_offsetof(struct nstat_metrics, nstat_first_uint32_count)) / sizeof(uint32_t))
324 
325 
326 typedef struct nstat_trace_entry {
327 	u_int32_t                       nte_seqno;
328 	u_int32_t                       nte_event;
329 	u_int64_t                       nte_qualifier;
330 } nstat_trace_entry;
331 
332 #define NSTAT_TRACE_ENTRIES_PER_CLIENT  (16 * 1024)
333 
334 typedef struct nstat_cyclic_trace {
335 	uint32_t            ntt_next_trace_id;
336 	int32_t             ntt_pad;
337 	nstat_trace_entry   ntt_entry[NSTAT_TRACE_ENTRIES_PER_CLIENT];
338 } nstat_cyclic_trace;
339 
340 
341 typedef struct nstat_client {
342 	struct nstat_client      *ntc_next;
343 	/* A bitmask to indicate whether a provider ever done NSTAT_MSG_TYPE_ADD_ALL_SRCS */
344 	u_int32_t               ntc_watching;
345 	/* A bitmask to indicate whether a provider ever done NSTAT_MSG_TYPE_ADD_SRC */
346 	u_int32_t               ntc_added_src;
347 	decl_lck_mtx_data(, ntc_user_mtx);      // Mutual exclusion for user level requests on this ntc_client
348 	kern_ctl_ref            ntc_kctl;
349 	u_int32_t               ntc_unit;
350 	nstat_src_ref_t         ntc_next_srcref;
351 	tailq_head_nstat_src    ntc_src_queue;
352 	mbuf_t                  ntc_accumulated;
353 	u_int32_t               ntc_flags;
354 	nstat_provider_filter   ntc_provider_filters[NSTAT_PROVIDER_COUNT];
355 	/* state maintained for partial query requests */
356 	u_int64_t               ntc_context;
357 	u_int64_t               ntc_seq;
358 	/* For ease of debugging with lldb macros */
359 	struct nstat_procdetails *ntc_procdetails;
360 	struct nstat_metrics    ntc_metrics;
361 #if NSTAT_TRACE_ENABLED
362 	nstat_cyclic_trace      *ntc_trace;
363 #endif
364 } nstat_client;
365 
366 typedef struct nstat_provider {
367 	struct nstat_provider   *next;
368 	nstat_provider_id_t     nstat_provider_id;
369 	size_t                  nstat_descriptor_length;
370 	errno_t                 (*nstat_lookup)(const void *__sized_by (length)data, u_int32_t length, nstat_provider_cookie_t *out_cookie);
371 	int                     (*nstat_gone)(nstat_provider_cookie_t cookie);
372 	errno_t                 (*nstat_counts)(nstat_provider_cookie_t cookie, struct nstat_counts *out_counts, int *out_gone);
373 	errno_t                 (*nstat_watcher_add)(nstat_client *client, nstat_msg_add_all_srcs *req);
374 	void                    (*nstat_watcher_remove)(nstat_client *client);
375 	errno_t                 (*nstat_copy_descriptor)(nstat_provider_cookie_t cookie, void *__sized_by (len)data, size_t len);
376 	void                    (*nstat_release)(nstat_provider_cookie_t cookie, boolean_t locked);
377 	bool                    (*nstat_reporting_allowed)(nstat_provider_cookie_t cookie, nstat_provider_filter *filter, u_int64_t suppression_flags);
378 	bool                    (*nstat_cookie_equal)(nstat_provider_cookie_t cookie1, nstat_provider_cookie_t cookie2);
379 	size_t                  (*nstat_copy_extension)(nstat_provider_cookie_t cookie, u_int32_t extension_id, void *buf, size_t len);
380 } nstat_provider;
381 
382 typedef struct nstat_src {
383 	tailq_entry_nstat_src   nts_client_link;    // All sources for the nstat_client, for iterating over.
384 	nstat_client            *nts_client;        // The nstat_client that this is a source for
385 	nstat_src_ref_t         nts_srcref;         // The reference quoted in any messages
386 	nstat_provider          *nts_provider;      // The "provider" for the source, e.g. for kernel TCP sockets
387 	nstat_provider_cookie_t nts_cookie;         // Provider-specific futher information,
388 	uint32_t                nts_filter;
389 	bool                    nts_reported;       // At least one update/counts/desc message has been sent
390 	uint64_t                nts_seq;            // Keeping track when getting partial poll results
391 } nstat_src;
392 
393 // The merge structures are intended to give a global picture of what may be asked for by the current set of clients
394 // This is to avoid taking locks to check them all individually
395 typedef struct nstat_merged_provider_filter {
396 	u_int64_t               mf_events;      // So far we only merge the events portion of any filters
397 } nstat_merged_provider_filter;
398 
399 typedef struct nstat_merged_provider_filters {
400 	nstat_merged_provider_filter    mpf_filters[NSTAT_PROVIDER_COUNT];
401 } nstat_merged_provider_filters;
402 
403 static errno_t      nstat_client_send_counts(nstat_client *client, nstat_src *src, unsigned long long context, u_int16_t hdr_flags, int *gone);
404 static int          nstat_client_send_description(nstat_client *client, nstat_src *src, u_int64_t context, u_int16_t hdr_flags);
405 static int          nstat_client_send_update(nstat_client *client, nstat_src *src, u_int64_t context, u_int64_t event, u_int16_t hdr_flags, int *gone);
406 static errno_t      nstat_client_send_removed(nstat_client *client, nstat_src *src, u_int16_t hdr_flags);
407 static errno_t      nstat_client_send_goodbye(nstat_client  *client, nstat_src *src);
408 static void         nstat_client_cleanup_source(nstat_client *client, nstat_src *src, boolean_t);
409 static bool         nstat_client_reporting_allowed(nstat_client *client, nstat_src *src, u_int64_t suppression_flags);
410 static boolean_t    nstat_client_begin_query(nstat_client *client, const nstat_msg_hdr *hdrp);
411 static u_int16_t    nstat_client_end_query(nstat_client *client, nstat_src *last_src, boolean_t partial);
412 static void         nstat_ifnet_report_lim_stats(void);
413 static void         nstat_net_api_report_stats(void);
414 static errno_t      nstat_set_provider_filter( nstat_client  *client, nstat_msg_add_all_srcs *req);
415 static errno_t      nstat_client_send_event(nstat_client *client, nstat_src *src, u_int64_t event);
416 
417 static u_int32_t    nstat_udp_watchers = 0;
418 static u_int32_t    nstat_tcp_watchers = 0;
419 static nstat_merged_provider_filters merged_filters = {};
420 
421 static nstat_client *nstat_clients = NULL;
422 static uint64_t     nstat_idle_time = 0;
423 
424 #if NSTAT_FUZZ_TIMING
425 static uint32_t nstat_random_delay_insert_modulo = 5000;
426 static uint32_t nstat_max_nsec_delay = (NSEC_PER_SEC / 1000);
427 #endif // NSTAT_FUZZ_TIMING
428 
429 static struct nstat_metrics nstat_metrics;
430 static struct nstat_global_counts nstat_global_counts;
431 
432 // For lldb macro usage
433 static __unused const size_t nstat_trace_entries_per_client = NSTAT_TRACE_ENTRIES_PER_CLIENT;
434 
435 #if NSTAT_TRACE_ENABLED
436 static nstat_cyclic_trace      nstat_global_trace;
437 #endif
438 
439 #if NSTAT_TRACE_ENABLED
440 #define NSTAT_TRACE(field, client, qual) \
441 { \
442     nstat_trace(offsetof(struct nstat_metrics, field), client, (uint64_t)qual); \
443 }
444 #else // NSTAT_TRACE_ENABLED
445 #define NSTAT_TRACE(field, client, qual)
446 #endif
447 
448 #if NSTAT_FUZZ_TIMING
449 #define NSTAT_RANDOM_DELAY(client) \
450 { \
451     nstat_random_delay(client); \
452 }
453 #else
454 #define NSTAT_RANDOM_DELAY(client)
455 #endif
456 
457 #define NSTAT_NOTE_QUAL(field, client, qual) \
458 {   \
459     NSTAT_TRACE(field, client, qual); \
460     NSTAT_RANDOM_DELAY(client); \
461     client->ntc_metrics.field++; \
462 }
463 
464 #define NSTAT_NOTE_SRC(field, client, src) \
465 {   \
466     NSTAT_TRACE(field, client, (src != NULL)? src->nts_srcref: 0); \
467     NSTAT_RANDOM_DELAY(client); \
468     client->ntc_metrics.field++; \
469 }
470 
471 #define NSTAT_GLOBAL_COUNT_INCREMENT_WITH_MAX(_currentfield, _maxfield) \
472 {   \
473 	uint64_t _prev_count = os_atomic_inc_orig(&nstat_global_counts._currentfield, relaxed); \
474 	if (_prev_count >= os_atomic_load(&nstat_global_counts._maxfield, relaxed)) {  \
475 	        os_atomic_store(&nstat_global_counts._maxfield, _prev_count + 1, relaxed); \
476 	}   \
477 }
478 
479 #define NSTAT_GLOBAL_COUNT_INCREMENT(_field) \
480 {   \
481 	os_atomic_inc_orig(&nstat_global_counts._field, relaxed); \
482 }
483 
484 #define NSTAT_GLOBAL_COUNT_DECREMENT(_field) \
485 {   \
486 	os_atomic_dec_orig(&nstat_global_counts._field, relaxed); \
487 }
488 
489 static errno_t      nstat_client_send_counts(nstat_client *client, nstat_src *src, unsigned long long context, u_int16_t hdr_flags, int *gone);
490 static int          nstat_client_send_description(nstat_client *client, nstat_src *src, u_int64_t context, u_int16_t hdr_flags);
491 static int          nstat_client_send_update(nstat_client *client, nstat_src *src, u_int64_t context, u_int64_t event, u_int16_t hdr_flags, int *gone);
492 static errno_t      nstat_client_send_removed(nstat_client *client, nstat_src *src, u_int16_t hdr_flags);
493 static errno_t      nstat_client_send_goodbye(nstat_client  *client, nstat_src *src);
494 static void         nstat_client_cleanup_source(nstat_client *client, nstat_src *src, boolean_t);
495 static bool         nstat_client_reporting_allowed(nstat_client *client, nstat_src *src, u_int64_t suppression_flags);
496 static boolean_t    nstat_client_begin_query(nstat_client *client, const nstat_msg_hdr *hdrp);
497 static u_int16_t    nstat_client_end_query(nstat_client *client, nstat_src *last_src, boolean_t partial);
498 static void         nstat_ifnet_report_lim_stats(void);
499 static void         nstat_net_api_report_stats(void);
500 static errno_t      nstat_set_provider_filter( nstat_client  *client, nstat_msg_add_all_srcs *req);
501 static errno_t      nstat_client_send_event(nstat_client *client, nstat_src *src, u_int64_t event);
502 static void         nstat_client_register(void);
503 
504 /*
505  * The lock order is as follows:
506  *
507  *     nstat_rwlock
508  *
509  * Or:
510  *
511  * client->ntc_user_mtx
512  *     nstat_rwlock
513  *
514  *
515  * The nstat_rwlock may be held in exclusive (writer) or shared (reader) mode.
516  *
517  * In general, events from the kernel, such as flow creation and deletion,
518  * require that the lock be held in exclusive mode while "nstat_src" structures
519  * are created or destroyed and appropriate linkages made or broken.
520  *
521  * In contrast, things that are driven from ntstat user level clients,
522  * most obviously polls, typically only require shared mode locking.
523  * There are some rare exceptions where a need to manipulate linkages
524  * that go across multiple clients will require an upgrade to an exlusive lock.
525  *
526  * There is one other lock to consider. Calls from NetworkStatistics clients will first
527  * obtain the per-client ntc_user_mtx.  This ensures that only one such client thread
528  * can be in progress at a time, making it easier to reason about correctness.
529  */
530 static LCK_ATTR_DECLARE(nstat_lck_attr, 0, 0);
531 static LCK_GRP_DECLARE(nstat_lck_grp, "network statistics kctl");
532 static LCK_RW_DECLARE_ATTR(nstat_rwlock, &nstat_lck_grp, &nstat_lck_attr);
533 
534 #define NSTAT_LOCK_EXCLUSIVE() lck_rw_lock_exclusive(&nstat_rwlock)
535 #define NSTAT_LOCK_SHARED() lck_rw_lock_shared(&nstat_rwlock)
536 #define NSTAT_LOCK_SHARED_TO_EXCLUSIVE() lck_rw_lock_shared_to_exclusive(&nstat_rwlock)
537 #define NSTAT_LOCK_EXCLUSIVE_TO_SHARED() lck_rw_lock_exclusive_to_shared(&nstat_rwlock)
538 #define NSTAT_UNLOCK() lck_rw_done(&nstat_rwlock)
539 #define NSTAT_UNLOCK_EXCLUSIVE() lck_rw_unlock_exclusive(&nstat_rwlock)
540 #define NSTAT_UNLOCK_SHARED() lck_rw_unlock_shared(&nstat_rwlock)
541 #define NSTAT_LOCK_WOULD_YIELD() lck_rw_lock_would_yield_shared(&nstat_rwlock)
542 #define NSTAT_LOCK_YIELD() lck_rw_lock_yield_shared(&nstat_rwlock, FALSE)
543 #define NSTAT_ASSERT_LOCKED_EXCLUSIVE() LCK_RW_ASSERT(&nstat_rwlock, LCK_RW_ASSERT_EXCLUSIVE)
544 #define NSTAT_ASSERT_LOCKED_SHARED() LCK_RW_ASSERT(&nstat_rwlock, LCK_RW_ASSERT_SHARED)
545 #define NSTAT_ASSERT_LOCKED() LCK_RW_ASSERT(&nstat_rwlock, LCK_RW_ASSERT_HELD)
546 #define NSTAT_ASSERT_UNLOCKED() LCK_RW_ASSERT(&nstat_rwlock, LCK_RW_ASSERT_NOTHELD)
547 
548 typedef enum {
549 	NSTAT_LOCK_NOTHELD  = 0,
550 	NSTAT_LOCK_HELD     = 1,
551 } nstat_lock_status;
552 
553 /* some extern definitions */
554 extern void tcp_report_stats(void);
555 
556 #if NSTAT_TRACE_ENABLED
557 static void
nstat_trace(int32_t trace_id,nstat_client * client,uint64_t qual)558 nstat_trace(int32_t trace_id, nstat_client *client, uint64_t qual)
559 {
560 	nstat_cyclic_trace *trace;
561 	nstat_trace_entry *entry;
562 	uint32_t seqno;
563 	if (client == NULL) {
564 		trace = &nstat_global_trace;
565 	} else {
566 		trace = client->ntc_trace;
567 	}
568 	if (trace != NULL) {
569 		seqno = OSIncrementAtomic(&trace->ntt_next_trace_id);
570 		if (seqno > (NSTAT_TRACE_ENTRIES_PER_CLIENT - 2)) {
571 			seqno = NSTAT_TRACE_ENTRIES_PER_CLIENT - 2;
572 		}
573 		int32_t index = seqno % NSTAT_TRACE_ENTRIES_PER_CLIENT;
574 		entry = &trace->ntt_entry[index];
575 		entry->nte_seqno = seqno;
576 		entry->nte_event = trace_id;
577 		entry->nte_qualifier = qual;
578 	}
579 }
580 #endif
581 
582 #if NSTAT_FUZZ_TIMING
583 static void
nstat_random_delay(nstat_client * client)584 nstat_random_delay(nstat_client *client)
585 {
586 	if (nstat_random_delay_insert_modulo) {
587 		uint64_t random;
588 		read_random(&random, sizeof(random));
589 		if ((random % nstat_random_delay_insert_modulo) == 0) {
590 #if NSTAT_TRACE_ENABLED
591 			nstat_trace(1, client, 0);
592 #endif
593 			read_random(&random, sizeof(random));
594 			uint64_t nsec_delay = (random % nstat_max_nsec_delay);
595 			delay_for_interval(nsec_delay, 1);
596 #if NSTAT_TRACE_ENABLED
597 			nstat_trace(2, client, 0);
598 #endif
599 		}
600 	}
601 }
602 #endif
603 
604 static void
nstat_accumulate_client_metrics(nstat_client * client)605 nstat_accumulate_client_metrics(nstat_client *client)
606 {
607 	if (nstat_metrics.nstat_src_max < client->ntc_metrics.nstat_src_max) {
608 		nstat_metrics.nstat_src_max = client->ntc_metrics.nstat_src_max;
609 	}
610 	// Most of the counts happen to be consecutive uint32_t values that can be picked up via pointer iteration rather than name
611 	uint32_t *srcptr = __unsafe_forge_bidi_indexable(uint32_t *,
612 	    (uint32_t *)(void *)&client->ntc_metrics.nstat_first_uint32_count,
613 	    (NUM_NSTAT_METRICS_UINT32_COUNTS * sizeof(uint32_t)));
614 	uint32_t *destptr = __unsafe_forge_bidi_indexable(uint32_t *,
615 	    (uint32_t *)(void *)&nstat_metrics.nstat_first_uint32_count,
616 	    (NUM_NSTAT_METRICS_UINT32_COUNTS * sizeof(uint32_t)));
617 
618 	for (int i = 0; i < NUM_NSTAT_METRICS_UINT32_COUNTS; i++) {
619 		destptr[i] += srcptr[i];
620 	}
621 }
622 
623 static void
nstat_copy_sa_out(const struct sockaddr * src,struct sockaddr * dst,int maxlen)624 nstat_copy_sa_out(
625 	const struct sockaddr   *src,
626 	struct sockaddr                 *dst,
627 	int                                             maxlen)
628 {
629 	if (src->sa_len > maxlen) {
630 		return;
631 	}
632 
633 	SOCKADDR_COPY(src, dst, src->sa_len);
634 	if (src->sa_family == AF_INET6 &&
635 	    src->sa_len >= sizeof(struct sockaddr_in6)) {
636 		struct sockaddr_in6     *sin6 = SIN6(dst);
637 		if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
638 			sin6->sin6_scope_id = (SIN6(src))->sin6_scope_id;
639 			if (in6_embedded_scope) {
640 				in6_verify_ifscope(&sin6->sin6_addr, sin6->sin6_scope_id);
641 				sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
642 				sin6->sin6_addr.s6_addr16[1] = 0;
643 			}
644 		}
645 	}
646 }
647 
648 static void
nstat_ip_to_sockaddr(const struct in_addr * ip,u_int16_t port,struct sockaddr_in * sin,u_int32_t maxlen)649 nstat_ip_to_sockaddr(
650 	const struct in_addr    *ip,
651 	u_int16_t               port,
652 	struct sockaddr_in      *sin,
653 	u_int32_t               maxlen)
654 {
655 	if (maxlen < sizeof(struct sockaddr_in)) {
656 		return;
657 	}
658 
659 	sin->sin_family = AF_INET;
660 	sin->sin_len = sizeof(*sin);
661 	sin->sin_port = port;
662 	sin->sin_addr = *ip;
663 }
664 
665 u_int32_t
nstat_ifnet_to_flags(struct ifnet * ifp)666 nstat_ifnet_to_flags(
667 	struct ifnet *ifp)
668 {
669 	u_int32_t flags = 0;
670 	u_int32_t functional_type = if_functional_type(ifp, FALSE);
671 	u_int32_t peer_egress_functional_type = IFRTYPE_FUNCTIONAL_UNKNOWN;
672 
673 	// If known VPN, check the delegate interface to see if it has peer
674 	// egress interface type set to cell.
675 	peer_egress_functional_type = if_peer_egress_functional_type(ifp, !(IFNET_IS_VPN(ifp)));
676 
677 	/* Panic if someone adds a functional type without updating ntstat. */
678 	VERIFY(0 <= functional_type && functional_type <= IFRTYPE_FUNCTIONAL_LAST);
679 
680 	switch (functional_type) {
681 	case IFRTYPE_FUNCTIONAL_UNKNOWN:
682 		flags |= NSTAT_IFNET_IS_UNKNOWN_TYPE;
683 		break;
684 	case IFRTYPE_FUNCTIONAL_LOOPBACK:
685 		flags |= NSTAT_IFNET_IS_LOOPBACK;
686 		break;
687 	case IFRTYPE_FUNCTIONAL_WIRED:
688 	case IFRTYPE_FUNCTIONAL_INTCOPROC:
689 	case IFRTYPE_FUNCTIONAL_MANAGEMENT:
690 		flags |= NSTAT_IFNET_IS_WIRED;
691 		break;
692 	case IFRTYPE_FUNCTIONAL_WIFI_INFRA:
693 		flags |= NSTAT_IFNET_IS_WIFI | NSTAT_IFNET_IS_WIFI_INFRA;
694 		break;
695 	case IFRTYPE_FUNCTIONAL_WIFI_AWDL:
696 		flags |= NSTAT_IFNET_IS_WIFI | NSTAT_IFNET_IS_AWDL;
697 		break;
698 	case IFRTYPE_FUNCTIONAL_CELLULAR:
699 		flags |= NSTAT_IFNET_IS_CELLULAR;
700 		break;
701 	case IFRTYPE_FUNCTIONAL_COMPANIONLINK:
702 		flags |= NSTAT_IFNET_IS_COMPANIONLINK;
703 		if (IFNET_IS_COMPANION_LINK_BLUETOOTH(ifp)) {
704 			flags |= NSTAT_IFNET_IS_COMPANIONLINK_BT;
705 		}
706 		break;
707 	}
708 
709 	switch (peer_egress_functional_type) {
710 	case IFRTYPE_FUNCTIONAL_CELLULAR:
711 		flags |= NSTAT_IFNET_PEEREGRESSINTERFACE_IS_CELLULAR;
712 		break;
713 	}
714 
715 	if (IFNET_IS_EXPENSIVE(ifp)) {
716 		flags |= NSTAT_IFNET_IS_EXPENSIVE;
717 	}
718 	if (IFNET_IS_CONSTRAINED(ifp)) {
719 		flags |= NSTAT_IFNET_IS_CONSTRAINED;
720 	}
721 	if (ifnet_is_low_latency(ifp)) {
722 		flags |= NSTAT_IFNET_IS_WIFI | NSTAT_IFNET_IS_LLW;
723 	}
724 	if (IFNET_IS_VPN(ifp)) {
725 		flags |= NSTAT_IFNET_IS_VPN;
726 	}
727 
728 	return flags;
729 }
730 
731 static void
nstat_update_local_flag_from_inpcb_route(const struct inpcb * inp,u_int32_t * flags)732 nstat_update_local_flag_from_inpcb_route(const struct inpcb *inp,
733     u_int32_t *flags)
734 {
735 	if (inp != NULL &&
736 	    ((inp->inp_route.ro_rt != NULL &&
737 	    IS_LOCALNET_ROUTE(inp->inp_route.ro_rt)) ||
738 	    (inp->inp_flags2 & INP2_LAST_ROUTE_LOCAL))) {
739 		*flags |= NSTAT_IFNET_IS_LOCAL;
740 	} else {
741 		*flags |= NSTAT_IFNET_IS_NON_LOCAL;
742 	}
743 }
744 
745 static u_int32_t
nstat_inpcb_to_flags(const struct inpcb * inp)746 nstat_inpcb_to_flags(
747 	const struct inpcb *inp)
748 {
749 	u_int32_t flags = 0;
750 
751 	if (inp != NULL) {
752 		if (inp->inp_last_outifp != NULL) {
753 			struct ifnet *ifp = inp->inp_last_outifp;
754 			flags = nstat_ifnet_to_flags(ifp);
755 
756 			struct tcpcb  *tp = intotcpcb(inp);
757 			if (tp) {
758 				if (tp->t_flags & TF_LOCAL) {
759 					flags |= NSTAT_IFNET_IS_LOCAL;
760 				} else {
761 					flags |= NSTAT_IFNET_IS_NON_LOCAL;
762 				}
763 			} else {
764 				nstat_update_local_flag_from_inpcb_route(inp, &flags);
765 			}
766 		} else {
767 			flags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
768 			nstat_update_local_flag_from_inpcb_route(inp, &flags);
769 		}
770 		if (inp->inp_socket != NULL &&
771 		    (inp->inp_socket->so_flags1 & SOF1_CELLFALLBACK)) {
772 			flags |= NSTAT_IFNET_VIA_CELLFALLBACK;
773 		}
774 	}
775 	return flags;
776 }
777 
778 static void
merge_current_event_filters(void)779 merge_current_event_filters(void)
780 {
781 	// The nstat_rwlock is assumed locked
782 	NSTAT_ASSERT_LOCKED();
783 
784 	nstat_merged_provider_filters new_merge = {};
785 	nstat_provider_type_t provider;
786 	nstat_client *client;
787 
788 	for (client = nstat_clients; client; client = client->ntc_next) {
789 		for (provider = NSTAT_PROVIDER_NONE; provider <= NSTAT_PROVIDER_LAST; provider++) {
790 			new_merge.mpf_filters[provider].mf_events |= client->ntc_provider_filters[provider].npf_events;
791 		}
792 	}
793 	for (provider = NSTAT_PROVIDER_NONE; provider <= NSTAT_PROVIDER_LAST; provider++) {
794 		// This should do atomic updates of the 64 bit words, where memcpy would be undefined
795 		merged_filters.mpf_filters[provider].mf_events = new_merge.mpf_filters[provider].mf_events;
796 	}
797 }
798 
799 
800 #pragma mark -- Network Statistic Providers --
801 
802 static errno_t nstat_client_source_add(u_int64_t context, nstat_client *client, nstat_provider *provider,
803     nstat_provider_cookie_t cookie, nstat_lock_status lock_status);
804 struct nstat_provider   *nstat_providers = NULL;
805 
806 static struct nstat_provider*
nstat_find_provider_by_id(nstat_provider_id_t id)807 nstat_find_provider_by_id(
808 	nstat_provider_id_t     id)
809 {
810 	struct nstat_provider   *provider;
811 
812 	for (provider = nstat_providers; provider != NULL; provider = provider->next) {
813 		if (provider->nstat_provider_id == id) {
814 			break;
815 		}
816 	}
817 
818 	return provider;
819 }
820 
821 static errno_t
nstat_lookup_entry(nstat_provider_id_t id,const void * __sized_by (length)data,u_int32_t length,nstat_provider ** out_provider,nstat_provider_cookie_t * out_cookie)822 nstat_lookup_entry(
823 	nstat_provider_id_t     id,
824 	const void              *__sized_by(length)data,
825 	u_int32_t               length,
826 	nstat_provider          **out_provider,
827 	nstat_provider_cookie_t *out_cookie)
828 {
829 	*out_provider = nstat_find_provider_by_id(id);
830 	if (*out_provider == NULL) {
831 		return ENOENT;
832 	}
833 
834 	return (*out_provider)->nstat_lookup(data, length, out_cookie);
835 }
836 
837 static void
nstat_client_sanitize_cookie(nstat_client * client,nstat_provider_id_t id,nstat_provider_cookie_t cookie)838 nstat_client_sanitize_cookie(
839 	nstat_client            *client,
840 	nstat_provider_id_t     id,
841 	nstat_provider_cookie_t cookie)
842 {
843 	nstat_src *src = NULL;
844 
845 	// Scan the source list to find any duplicate entry and remove it.
846 	NSTAT_LOCK_EXCLUSIVE();
847 	TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
848 	{
849 		nstat_provider *sp = src->nts_provider;
850 		if (sp->nstat_provider_id == id &&
851 		    sp->nstat_cookie_equal != NULL &&
852 		    sp->nstat_cookie_equal(src->nts_cookie, cookie)) {
853 			break;
854 		}
855 	}
856 	if (src) {
857 		nstat_client_send_goodbye(client, src);
858 		TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
859 	}
860 	NSTAT_UNLOCK_EXCLUSIVE();
861 
862 	if (src) {
863 		nstat_client_cleanup_source(NULL, src, TRUE);
864 	}
865 }
866 
867 static void nstat_init_route_provider(void);
868 static void nstat_init_tcp_provider(void);
869 static void nstat_init_udp_provider(void);
870 #if SKYWALK
871 static void nstat_init_userland_tcp_provider(void);
872 static void nstat_init_userland_udp_provider(void);
873 static void nstat_init_userland_quic_provider(void);
874 #endif /* SKYWALK */
875 static void nstat_init_userland_conn_provider(void);
876 static void nstat_init_udp_subflow_provider(void);
877 static void nstat_init_ifnet_provider(void);
878 
879 __private_extern__ void
nstat_init(void)880 nstat_init(void)
881 {
882 	nstat_log_handle = os_log_create("com.apple.xnu.net", "ntstat");
883 	nstat_init_route_provider();
884 	nstat_init_tcp_provider();
885 	nstat_init_udp_provider();
886 #if SKYWALK
887 	nstat_init_userland_tcp_provider();
888 	nstat_init_userland_udp_provider();
889 	nstat_init_userland_quic_provider();
890 #endif /* SKYWALK */
891 	nstat_init_userland_conn_provider();
892 	nstat_init_udp_subflow_provider();
893 	nstat_init_ifnet_provider();
894 	nstat_client_register();
895 }
896 
897 #pragma mark -- Utilities --
898 
899 #define NSTAT_PROCDETAILS_MAGIC     0xfeedc001
900 #define NSTAT_PROCDETAILS_UNMAGIC   0xdeadc001
901 
902 static tailq_head_procdetails nstat_procdetails_head = TAILQ_HEAD_INITIALIZER(nstat_procdetails_head);
903 
904 static struct nstat_procdetails *
nstat_retain_curprocdetails(void)905 nstat_retain_curprocdetails(void)
906 {
907 	struct nstat_procdetails *procdetails = NULL;
908 	uint64_t upid = proc_uniqueid(current_proc());
909 
910 	NSTAT_LOCK_SHARED();
911 	TAILQ_FOREACH(procdetails, &nstat_procdetails_head, pdet_link) {
912 		assert(procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
913 
914 		if (procdetails->pdet_upid == upid) {
915 			OSIncrementAtomic(&procdetails->pdet_refcnt);
916 			break;
917 		}
918 	}
919 	NSTAT_UNLOCK_SHARED();
920 
921 	if (!procdetails) {
922 		// No need for paranoia on locking, it would be OK if there are duplicate structs on the list
923 		procdetails = kalloc_type(struct nstat_procdetails,
924 		    Z_WAITOK | Z_NOFAIL);
925 		procdetails->pdet_pid = proc_selfpid();
926 		procdetails->pdet_upid = upid;
927 		proc_selfname(procdetails->pdet_procname, sizeof(procdetails->pdet_procname));
928 		proc_getexecutableuuid(current_proc(), procdetails->pdet_uuid, sizeof(uuid_t));
929 		procdetails->pdet_refcnt = 1;
930 		procdetails->pdet_magic = NSTAT_PROCDETAILS_MAGIC;
931 		NSTAT_LOCK_EXCLUSIVE();
932 		TAILQ_INSERT_HEAD(&nstat_procdetails_head, procdetails, pdet_link);
933 		NSTAT_UNLOCK_EXCLUSIVE();
934 		NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_procdetails_allocs);
935 		NSTAT_GLOBAL_COUNT_INCREMENT_WITH_MAX(nstat_global_procdetails_current, nstat_global_procdetails_max);
936 	}
937 
938 	return procdetails;
939 }
940 
941 static void
nstat_release_procdetails(struct nstat_procdetails * procdetails)942 nstat_release_procdetails(struct nstat_procdetails *procdetails)
943 {
944 	assert(procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
945 	// These are harvested later to amortize costs
946 	OSDecrementAtomic(&procdetails->pdet_refcnt);
947 }
948 
949 static void
nstat_prune_procdetails(void)950 nstat_prune_procdetails(void)
951 {
952 	struct nstat_procdetails *procdetails;
953 	struct nstat_procdetails *tmpdetails;
954 	tailq_head_procdetails dead_list;
955 
956 	TAILQ_INIT(&dead_list);
957 	NSTAT_LOCK_EXCLUSIVE();
958 
959 	TAILQ_FOREACH_SAFE(procdetails, &nstat_procdetails_head, pdet_link, tmpdetails)
960 	{
961 		assert(procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
962 		if (procdetails->pdet_refcnt == 0) {
963 			// Pull it off the list
964 			TAILQ_REMOVE(&nstat_procdetails_head, procdetails, pdet_link);
965 			TAILQ_INSERT_TAIL(&dead_list, procdetails, pdet_link);
966 		}
967 	}
968 	NSTAT_UNLOCK_EXCLUSIVE();
969 
970 	while ((procdetails = TAILQ_FIRST(&dead_list))) {
971 		TAILQ_REMOVE(&dead_list, procdetails, pdet_link);
972 		procdetails->pdet_magic = NSTAT_PROCDETAILS_UNMAGIC;
973 		NSTAT_GLOBAL_COUNT_DECREMENT(nstat_global_procdetails_current);
974 		kfree_type(struct nstat_procdetails, procdetails);
975 	}
976 }
977 
978 #pragma mark -- Route Provider --
979 
980 static nstat_provider   nstat_route_provider;
981 
982 static errno_t
nstat_route_lookup(const void * data,u_int32_t length,nstat_provider_cookie_t * out_cookie)983 nstat_route_lookup(
984 	const void      *data,
985 	u_int32_t       length,
986 	nstat_provider_cookie_t *out_cookie)
987 {
988 	struct sockaddr                 *dst = NULL;
989 	struct sockaddr                 *mask = NULL;
990 	const nstat_route_add_param     *param = (const nstat_route_add_param*)data;
991 	*out_cookie = NULL;
992 
993 	if (length < sizeof(*param)) {
994 		return EINVAL;
995 	}
996 
997 	if (param->dst.v4.sin_family == 0 ||
998 	    param->dst.v4.sin_family > AF_MAX ||
999 	    (param->mask.v4.sin_family != 0 && param->mask.v4.sin_family != param->dst.v4.sin_family)) {
1000 		return EINVAL;
1001 	}
1002 
1003 	if (param->dst.v4.sin_len > sizeof(param->dst) ||
1004 	    (param->mask.v4.sin_family && param->mask.v4.sin_len > sizeof(param->mask.v4.sin_len))) {
1005 		return EINVAL;
1006 	}
1007 	if ((param->dst.v4.sin_family == AF_INET &&
1008 	    param->dst.v4.sin_len < sizeof(struct sockaddr_in)) ||
1009 	    (param->dst.v6.sin6_family == AF_INET6 &&
1010 	    param->dst.v6.sin6_len < sizeof(struct sockaddr_in6))) {
1011 		return EINVAL;
1012 	}
1013 
1014 	dst = __DECONST_SA(&param->dst.v4);
1015 	mask = param->mask.v4.sin_family ? __DECONST_SA(&param->mask.v4) : NULL;
1016 
1017 	struct radix_node_head  *rnh = rt_tables[dst->sa_family];
1018 	if (rnh == NULL) {
1019 		return EAFNOSUPPORT;
1020 	}
1021 
1022 	lck_mtx_lock(rnh_lock);
1023 	struct rtentry *rt = rt_lookup(TRUE, dst, mask, rnh, param->ifindex);
1024 	lck_mtx_unlock(rnh_lock);
1025 
1026 	if (rt) {
1027 		*out_cookie = (nstat_provider_cookie_t)rt;
1028 	}
1029 
1030 	return rt ? 0 : ENOENT;
1031 }
1032 
1033 static int
nstat_route_gone(nstat_provider_cookie_t cookie)1034 nstat_route_gone(
1035 	nstat_provider_cookie_t cookie)
1036 {
1037 	struct rtentry          *rt = (struct rtentry*)cookie;
1038 	return ((rt->rt_flags & RTF_UP) == 0) ? 1 : 0;
1039 }
1040 
1041 static errno_t
nstat_route_counts(nstat_provider_cookie_t cookie,struct nstat_counts * out_counts,int * out_gone)1042 nstat_route_counts(
1043 	nstat_provider_cookie_t cookie,
1044 	struct nstat_counts     *out_counts,
1045 	int                     *out_gone)
1046 {
1047 	struct rtentry          *rt = (struct rtentry*)cookie;
1048 	struct nstat_counts     *rt_stats = rt->rt_stats;
1049 
1050 	if (out_gone) {
1051 		*out_gone = 0;
1052 	}
1053 
1054 	if (out_gone && (rt->rt_flags & RTF_UP) == 0) {
1055 		*out_gone = 1;
1056 	}
1057 
1058 	if (rt_stats) {
1059 		out_counts->nstat_rxpackets = os_atomic_load(&rt_stats->nstat_rxpackets, relaxed);
1060 		out_counts->nstat_rxbytes = os_atomic_load(&rt_stats->nstat_rxbytes, relaxed);
1061 		out_counts->nstat_txpackets = os_atomic_load(&rt_stats->nstat_txpackets, relaxed);
1062 		out_counts->nstat_txbytes = os_atomic_load(&rt_stats->nstat_txbytes, relaxed);
1063 		out_counts->nstat_rxduplicatebytes = rt_stats->nstat_rxduplicatebytes;
1064 		out_counts->nstat_rxoutoforderbytes = rt_stats->nstat_rxoutoforderbytes;
1065 		out_counts->nstat_txretransmit = rt_stats->nstat_txretransmit;
1066 		out_counts->nstat_connectattempts = rt_stats->nstat_connectattempts;
1067 		out_counts->nstat_connectsuccesses = rt_stats->nstat_connectsuccesses;
1068 		out_counts->nstat_min_rtt = rt_stats->nstat_min_rtt;
1069 		out_counts->nstat_avg_rtt = rt_stats->nstat_avg_rtt;
1070 		out_counts->nstat_var_rtt = rt_stats->nstat_var_rtt;
1071 		out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
1072 	} else {
1073 		bzero(out_counts, sizeof(*out_counts));
1074 	}
1075 
1076 	return 0;
1077 }
1078 
1079 static void
nstat_route_release(nstat_provider_cookie_t cookie,__unused int locked)1080 nstat_route_release(
1081 	nstat_provider_cookie_t cookie,
1082 	__unused int locked)
1083 {
1084 	rtfree((struct rtentry*)cookie);
1085 }
1086 
1087 static u_int32_t    nstat_route_watchers = 0;
1088 
1089 static int
nstat_route_walktree_add(struct radix_node * rn,void * context)1090 nstat_route_walktree_add(
1091 	struct radix_node       *rn,
1092 	void                            *context)
1093 {
1094 	errno_t result = 0;
1095 	struct rtentry *rt = (struct rtentry *)rn;
1096 	nstat_client     *client  = (nstat_client*)context;
1097 
1098 	LCK_MTX_ASSERT(rnh_lock, LCK_MTX_ASSERT_OWNED);
1099 
1100 	/* RTF_UP can't change while rnh_lock is held */
1101 	if ((rt->rt_flags & RTF_UP) != 0) {
1102 		/* Clear RTPRF_OURS if the route is still usable */
1103 		RT_LOCK(rt);
1104 		if (rt_validate(rt)) {
1105 			RT_ADDREF_LOCKED(rt);
1106 			RT_UNLOCK(rt);
1107 		} else {
1108 			RT_UNLOCK(rt);
1109 			rt = NULL;
1110 		}
1111 
1112 		/* Otherwise if RTF_CONDEMNED, treat it as if it were down */
1113 		if (rt == NULL) {
1114 			return 0;
1115 		}
1116 
1117 		result = nstat_client_source_add(0, client, &nstat_route_provider, rt, NSTAT_LOCK_NOTHELD);
1118 		if (result != 0) {
1119 			rtfree_locked(rt);
1120 		}
1121 	}
1122 
1123 	return result;
1124 }
1125 
1126 static errno_t
nstat_route_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req)1127 nstat_route_add_watcher(
1128 	nstat_client *client,
1129 	nstat_msg_add_all_srcs *req)
1130 {
1131 	int i;
1132 	errno_t result = 0;
1133 
1134 	lck_mtx_lock(rnh_lock);
1135 
1136 	result = nstat_set_provider_filter(client, req);
1137 	if (result == 0) {
1138 		OSIncrementAtomic(&nstat_route_watchers);
1139 
1140 		for (i = 1; i < AF_MAX; i++) {
1141 			struct radix_node_head *rnh;
1142 			rnh = rt_tables[i];
1143 			if (!rnh) {
1144 				continue;
1145 			}
1146 
1147 			result = rnh->rnh_walktree(rnh, nstat_route_walktree_add, client);
1148 			if (result != 0) {
1149 				// This is probably resource exhaustion.
1150 				// There currently isn't a good way to recover from this.
1151 				// Least bad seems to be to give up on the add-all but leave
1152 				// the watcher in place.
1153 				break;
1154 			}
1155 		}
1156 	}
1157 	lck_mtx_unlock(rnh_lock);
1158 
1159 	return result;
1160 }
1161 
1162 __private_extern__ void
nstat_route_new_entry(struct rtentry * rt)1163 nstat_route_new_entry(
1164 	struct rtentry  *rt)
1165 {
1166 	if (nstat_route_watchers == 0) {
1167 		return;
1168 	}
1169 
1170 	NSTAT_LOCK_EXCLUSIVE();
1171 	if ((rt->rt_flags & RTF_UP) != 0) {
1172 		nstat_client *client;
1173 		for (client = nstat_clients; client; client = client->ntc_next) {
1174 			if ((client->ntc_watching & (1 << NSTAT_PROVIDER_ROUTE)) != 0) {
1175 				// this client is watching routes
1176 				// acquire a reference for the route
1177 				RT_ADDREF(rt);
1178 
1179 				// add the source, if that fails, release the reference
1180 				if (nstat_client_source_add(0, client, &nstat_route_provider, rt, NSTAT_LOCK_HELD) != 0) {
1181 					RT_REMREF(rt);
1182 				}
1183 			}
1184 		}
1185 	}
1186 	NSTAT_UNLOCK_EXCLUSIVE();
1187 }
1188 
1189 static void
nstat_route_remove_watcher(__unused nstat_client * client)1190 nstat_route_remove_watcher(
1191 	__unused nstat_client    *client)
1192 {
1193 	OSDecrementAtomic(&nstat_route_watchers);
1194 }
1195 
1196 static errno_t
nstat_route_copy_descriptor(nstat_provider_cookie_t cookie,void * __sized_by (len)data,size_t len)1197 nstat_route_copy_descriptor(
1198 	nstat_provider_cookie_t cookie,
1199 	void                    *__sized_by(len)data,
1200 	size_t                  len)
1201 {
1202 	nstat_route_descriptor  *desc = (nstat_route_descriptor*)data;
1203 	if (len < sizeof(*desc)) {
1204 		return EINVAL;
1205 	}
1206 	bzero(desc, sizeof(*desc));
1207 
1208 	struct rtentry  *rt = (struct rtentry*)cookie;
1209 	desc->id = (uint64_t)VM_KERNEL_ADDRHASH(rt);
1210 	desc->parent_id = (uint64_t)VM_KERNEL_ADDRHASH(rt->rt_parent);
1211 	desc->gateway_id = (uint64_t)VM_KERNEL_ADDRHASH(rt->rt_gwroute);
1212 
1213 
1214 	// key/dest
1215 	struct sockaddr *sa;
1216 	if ((sa = rt_key(rt))) {
1217 		nstat_copy_sa_out(sa, &desc->dst.sa, sizeof(desc->dst));
1218 	}
1219 
1220 	// mask
1221 	if ((sa = rt_mask(rt))) {
1222 		nstat_copy_sa_out(sa, &desc->mask.sa, sizeof(desc->mask));
1223 	}
1224 
1225 	// gateway
1226 	if ((sa = rt->rt_gateway)) {
1227 		nstat_copy_sa_out(sa, &desc->gateway.sa, sizeof(desc->gateway));
1228 	}
1229 
1230 	if (rt->rt_ifp) {
1231 		desc->ifindex = rt->rt_ifp->if_index;
1232 	}
1233 
1234 	desc->flags = rt->rt_flags;
1235 
1236 	return 0;
1237 }
1238 
1239 static bool
nstat_route_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,__unused u_int64_t suppression_flags)1240 nstat_route_reporting_allowed(
1241 	nstat_provider_cookie_t cookie,
1242 	nstat_provider_filter *filter,
1243 	__unused u_int64_t suppression_flags)
1244 {
1245 	bool retval = true;
1246 
1247 	if ((filter->npf_flags & NSTAT_FILTER_IFNET_FLAGS) != 0) {
1248 		struct rtentry  *rt = (struct rtentry*)cookie;
1249 		struct ifnet *ifp = rt->rt_ifp;
1250 
1251 		if (ifp) {
1252 			uint32_t interface_properties = nstat_ifnet_to_flags(ifp);
1253 
1254 			if ((filter->npf_flags & interface_properties) == 0) {
1255 				retval = false;
1256 			}
1257 		}
1258 	}
1259 	return retval;
1260 }
1261 
1262 static bool
nstat_route_cookie_equal(nstat_provider_cookie_t cookie1,nstat_provider_cookie_t cookie2)1263 nstat_route_cookie_equal(
1264 	nstat_provider_cookie_t cookie1,
1265 	nstat_provider_cookie_t cookie2)
1266 {
1267 	struct rtentry *rt1 = (struct rtentry *)cookie1;
1268 	struct rtentry *rt2 = (struct rtentry *)cookie2;
1269 
1270 	return (rt1 == rt2) ? true : false;
1271 }
1272 
1273 static void
nstat_init_route_provider(void)1274 nstat_init_route_provider(void)
1275 {
1276 	bzero(&nstat_route_provider, sizeof(nstat_route_provider));
1277 	nstat_route_provider.nstat_descriptor_length = sizeof(nstat_route_descriptor);
1278 	nstat_route_provider.nstat_provider_id = NSTAT_PROVIDER_ROUTE;
1279 	nstat_route_provider.nstat_lookup = nstat_route_lookup;
1280 	nstat_route_provider.nstat_gone = nstat_route_gone;
1281 	nstat_route_provider.nstat_counts = nstat_route_counts;
1282 	nstat_route_provider.nstat_release = nstat_route_release;
1283 	nstat_route_provider.nstat_watcher_add = nstat_route_add_watcher;
1284 	nstat_route_provider.nstat_watcher_remove = nstat_route_remove_watcher;
1285 	nstat_route_provider.nstat_copy_descriptor = nstat_route_copy_descriptor;
1286 	nstat_route_provider.nstat_reporting_allowed = nstat_route_reporting_allowed;
1287 	nstat_route_provider.nstat_cookie_equal = nstat_route_cookie_equal;
1288 	nstat_route_provider.next = nstat_providers;
1289 	nstat_providers = &nstat_route_provider;
1290 }
1291 
1292 #pragma mark -- Route Collection --
1293 
1294 __private_extern__ struct nstat_counts*
nstat_route_attach(struct rtentry * rte)1295 nstat_route_attach(
1296 	struct rtentry  *rte)
1297 {
1298 	struct nstat_counts * __single  result = rte->rt_stats;
1299 	if (result) {
1300 		return result;
1301 	}
1302 
1303 	result = kalloc_type(struct nstat_counts, Z_WAITOK | Z_ZERO);
1304 	if (!result) {
1305 		return result;
1306 	}
1307 
1308 	if (!OSCompareAndSwapPtr(NULL, result, &rte->rt_stats)) {
1309 		kfree_type(struct nstat_counts, result);
1310 		result = rte->rt_stats;
1311 	}
1312 
1313 	return result;
1314 }
1315 
1316 __private_extern__ void
nstat_route_detach(struct rtentry * rte)1317 nstat_route_detach(
1318 	struct rtentry  *rte)
1319 {
1320 	if (rte->rt_stats) {
1321 		kfree_type(struct nstat_counts, rte->rt_stats);
1322 		rte->rt_stats = NULL;
1323 	}
1324 }
1325 
1326 __private_extern__ void
nstat_route_connect_attempt(struct rtentry * rte)1327 nstat_route_connect_attempt(
1328 	struct rtentry  *rte)
1329 {
1330 	while (rte) {
1331 		struct nstat_counts*    stats = nstat_route_attach(rte);
1332 		if (stats) {
1333 			OSIncrementAtomic(&stats->nstat_connectattempts);
1334 		}
1335 
1336 		rte = rte->rt_parent;
1337 	}
1338 }
1339 
1340 __private_extern__ void
nstat_route_connect_success(struct rtentry * rte)1341 nstat_route_connect_success(
1342 	struct rtentry  *rte)
1343 {
1344 	// This route
1345 	while (rte) {
1346 		struct nstat_counts*    stats = nstat_route_attach(rte);
1347 		if (stats) {
1348 			OSIncrementAtomic(&stats->nstat_connectsuccesses);
1349 		}
1350 
1351 		rte = rte->rt_parent;
1352 	}
1353 }
1354 
1355 __private_extern__ void
nstat_route_tx(struct rtentry * rte,u_int32_t packets,u_int32_t bytes,u_int32_t flags)1356 nstat_route_tx(
1357 	struct rtentry  *rte,
1358 	u_int32_t       packets,
1359 	u_int32_t       bytes,
1360 	u_int32_t       flags)
1361 {
1362 	while (rte) {
1363 		struct nstat_counts*    stats = nstat_route_attach(rte);
1364 		if (stats) {
1365 			if ((flags & NSTAT_TX_FLAG_RETRANSMIT) != 0) {
1366 				OSAddAtomic(bytes, &stats->nstat_txretransmit);
1367 			} else {
1368 				OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_txpackets);
1369 				OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_txbytes);
1370 			}
1371 		}
1372 
1373 		rte = rte->rt_parent;
1374 	}
1375 }
1376 
1377 __private_extern__ void
nstat_route_rx(struct rtentry * rte,u_int32_t packets,u_int32_t bytes,u_int32_t flags)1378 nstat_route_rx(
1379 	struct rtentry  *rte,
1380 	u_int32_t       packets,
1381 	u_int32_t       bytes,
1382 	u_int32_t       flags)
1383 {
1384 	while (rte) {
1385 		struct nstat_counts*    stats = nstat_route_attach(rte);
1386 		if (stats) {
1387 			if (flags == 0) {
1388 				OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_rxpackets);
1389 				OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_rxbytes);
1390 			} else {
1391 				if (flags & NSTAT_RX_FLAG_OUT_OF_ORDER) {
1392 					OSAddAtomic(bytes, &stats->nstat_rxoutoforderbytes);
1393 				}
1394 				if (flags & NSTAT_RX_FLAG_DUPLICATE) {
1395 					OSAddAtomic(bytes, &stats->nstat_rxduplicatebytes);
1396 				}
1397 			}
1398 		}
1399 
1400 		rte = rte->rt_parent;
1401 	}
1402 }
1403 
1404 /* atomically average current value at _val_addr with _new_val and store  */
1405 #define NSTAT_EWMA_ATOMIC(_val_addr, _new_val, _decay) do {                                     \
1406 	volatile uint32_t _old_val;                                                                                             \
1407 	volatile uint32_t _avg;                                                                                                 \
1408 	do {                                                                                                                                    \
1409 	        _old_val = *_val_addr;                                                                                          \
1410 	        if (_old_val == 0)                                                                                                      \
1411 	        {                                                                                                                                       \
1412 	                _avg = _new_val;                                                                                                \
1413 	        }                                                                                                                                       \
1414 	        else                                                                                                                            \
1415 	        {                                                                                                                                       \
1416 	                _avg = _old_val - (_old_val >> _decay) + (_new_val >> _decay);  \
1417 	        }                                                                                                                                       \
1418 	        if (_old_val == _avg) break;                                                                            \
1419 	} while (!OSCompareAndSwap(_old_val, _avg, _val_addr));                                 \
1420 } while (0);
1421 
1422 /* atomically compute minimum of current value at _val_addr with _new_val and store  */
1423 #define NSTAT_MIN_ATOMIC(_val_addr, _new_val) do {                              \
1424 	volatile uint32_t _old_val;                                                                     \
1425 	do {                                                                                                            \
1426 	        _old_val = *_val_addr;                                                                  \
1427 	        if (_old_val != 0 && _old_val < _new_val)                               \
1428 	        {                                                                                                               \
1429 	                break;                                                                                          \
1430 	        }                                                                                                               \
1431 	} while (!OSCompareAndSwap(_old_val, _new_val, _val_addr));     \
1432 } while (0);
1433 
1434 __private_extern__ void
nstat_route_rtt(struct rtentry * rte,u_int32_t rtt,u_int32_t rtt_var)1435 nstat_route_rtt(
1436 	struct rtentry  *rte,
1437 	u_int32_t               rtt,
1438 	u_int32_t               rtt_var)
1439 {
1440 	const uint32_t decay = 3;
1441 
1442 	while (rte) {
1443 		struct nstat_counts*    stats = nstat_route_attach(rte);
1444 		if (stats) {
1445 			NSTAT_EWMA_ATOMIC(&stats->nstat_avg_rtt, rtt, decay);
1446 			NSTAT_MIN_ATOMIC(&stats->nstat_min_rtt, rtt);
1447 			NSTAT_EWMA_ATOMIC(&stats->nstat_var_rtt, rtt_var, decay);
1448 		}
1449 		rte = rte->rt_parent;
1450 	}
1451 }
1452 
1453 __private_extern__ void
nstat_route_update(struct rtentry * rte,uint32_t connect_attempts,uint32_t connect_successes,uint32_t rx_packets,uint32_t rx_bytes,uint32_t rx_duplicatebytes,uint32_t rx_outoforderbytes,uint32_t tx_packets,uint32_t tx_bytes,uint32_t tx_retransmit,uint32_t rtt,uint32_t rtt_var)1454 nstat_route_update(
1455 	struct rtentry  *rte,
1456 	uint32_t        connect_attempts,
1457 	uint32_t        connect_successes,
1458 	uint32_t        rx_packets,
1459 	uint32_t        rx_bytes,
1460 	uint32_t        rx_duplicatebytes,
1461 	uint32_t        rx_outoforderbytes,
1462 	uint32_t        tx_packets,
1463 	uint32_t        tx_bytes,
1464 	uint32_t        tx_retransmit,
1465 	uint32_t        rtt,
1466 	uint32_t        rtt_var)
1467 {
1468 	const uint32_t decay = 3;
1469 
1470 	while (rte) {
1471 		struct nstat_counts*    stats = nstat_route_attach(rte);
1472 		if (stats) {
1473 			OSAddAtomic(connect_attempts, &stats->nstat_connectattempts);
1474 			OSAddAtomic(connect_successes, &stats->nstat_connectsuccesses);
1475 			OSAddAtomic64((SInt64)tx_packets, (SInt64*)&stats->nstat_txpackets);
1476 			OSAddAtomic64((SInt64)tx_bytes, (SInt64*)&stats->nstat_txbytes);
1477 			OSAddAtomic(tx_retransmit, &stats->nstat_txretransmit);
1478 			OSAddAtomic64((SInt64)rx_packets, (SInt64*)&stats->nstat_rxpackets);
1479 			OSAddAtomic64((SInt64)rx_bytes, (SInt64*)&stats->nstat_rxbytes);
1480 			OSAddAtomic(rx_outoforderbytes, &stats->nstat_rxoutoforderbytes);
1481 			OSAddAtomic(rx_duplicatebytes, &stats->nstat_rxduplicatebytes);
1482 
1483 			if (rtt != 0) {
1484 				NSTAT_EWMA_ATOMIC(&stats->nstat_avg_rtt, rtt, decay);
1485 				NSTAT_MIN_ATOMIC(&stats->nstat_min_rtt, rtt);
1486 				NSTAT_EWMA_ATOMIC(&stats->nstat_var_rtt, rtt_var, decay);
1487 			}
1488 		}
1489 		rte = rte->rt_parent;
1490 	}
1491 }
1492 
1493 #pragma mark -- TCP Kernel Provider --
1494 
1495 #define PNAME_MAX_LENGTH    ((2 * MAXCOMLEN) + 1)
1496 
1497 /*
1498  * Due to the way the kernel deallocates a process (the process structure
1499  * might be gone by the time we get the PCB detach notification),
1500  * we need to cache the process name. Without this, proc_best_name_for_pid()
1501  * would return null and the process name would never be sent to userland.
1502  *
1503  * For UDP sockets, we also store the cached the connection tuples along with
1504  * the interface index. This is necessary because when UDP sockets are
1505  * disconnected, the connection tuples are forever lost from the inpcb, thus
1506  * we need to keep track of the last call to connect() in ntstat.
1507  */
1508 struct nstat_tucookie {
1509 	struct inpcb    *inp;
1510 	char            pname[PNAME_MAX_LENGTH];
1511 	bool            cached;
1512 	union{
1513 		struct sockaddr_in      v4;
1514 		struct sockaddr_in6     v6;
1515 	} local;
1516 	union{
1517 		struct sockaddr_in      v4;
1518 		struct sockaddr_in6     v6;
1519 	} remote;
1520 	unsigned int    if_index;
1521 	uint32_t        ifnet_properties;
1522 };
1523 
1524 static struct nstat_tucookie *
nstat_tucookie_alloc_ref_internal(struct inpcb * inp,bool locked)1525 nstat_tucookie_alloc_ref_internal(
1526 	struct inpcb *inp,
1527 	bool          locked)
1528 {
1529 	struct nstat_tucookie *cookie;
1530 
1531 	if (inp->inp_state == INPCB_STATE_DEAD) {
1532 		NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_tucookie_skip_dead);
1533 		return NULL;
1534 	}
1535 
1536 	cookie = kalloc_type(struct nstat_tucookie, Z_WAITOK | Z_ZERO);
1537 
1538 	if (cookie == NULL) {
1539 		NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_tucookie_alloc_fail);
1540 		return NULL;
1541 	}
1542 	NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_tucookie_allocs);
1543 	NSTAT_GLOBAL_COUNT_INCREMENT_WITH_MAX(nstat_global_tucookie_current, nstat_global_tucookie_max);
1544 
1545 	if (in_pcb_checkstate(inp, WNT_ACQUIRE, locked) == WNT_STOPUSING) {
1546 		NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_tucookie_skip_stopusing);
1547 		NSTAT_GLOBAL_COUNT_DECREMENT(nstat_global_tucookie_current);
1548 		kfree_type(struct nstat_tucookie, cookie);
1549 		return NULL;
1550 	}
1551 	cookie->inp = inp;
1552 	proc_best_name_for_pid(inp->inp_socket->last_pid, cookie->pname, sizeof(cookie->pname));
1553 	/*
1554 	 * We only increment the reference count for UDP sockets because we
1555 	 * only cache UDP socket tuples.
1556 	 */
1557 	if (SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP) {
1558 		OSIncrementAtomic(&inp->inp_nstat_refcnt);
1559 	}
1560 
1561 	return cookie;
1562 }
1563 
1564 static struct nstat_tucookie *
nstat_tucookie_alloc_ref(struct inpcb * inp)1565 nstat_tucookie_alloc_ref(
1566 	struct inpcb *inp)
1567 {
1568 	return nstat_tucookie_alloc_ref_internal(inp, false);
1569 }
1570 
1571 static struct nstat_tucookie *
nstat_tucookie_alloc_ref_locked(struct inpcb * inp)1572 nstat_tucookie_alloc_ref_locked(
1573 	struct inpcb *inp)
1574 {
1575 	return nstat_tucookie_alloc_ref_internal(inp, true);
1576 }
1577 
1578 static void
nstat_tucookie_release_internal(struct nstat_tucookie * cookie,int inplock)1579 nstat_tucookie_release_internal(
1580 	struct nstat_tucookie *cookie,
1581 	int                   inplock)
1582 {
1583 	if (SOCK_PROTO(cookie->inp->inp_socket) == IPPROTO_UDP) {
1584 		OSDecrementAtomic(&cookie->inp->inp_nstat_refcnt);
1585 	}
1586 	in_pcb_checkstate(cookie->inp, WNT_RELEASE, inplock);
1587 	NSTAT_GLOBAL_COUNT_DECREMENT(nstat_global_tucookie_current);
1588 	kfree_type(struct nstat_tucookie, cookie);
1589 }
1590 
1591 static void
nstat_tucookie_release(struct nstat_tucookie * cookie)1592 nstat_tucookie_release(
1593 	struct nstat_tucookie *cookie)
1594 {
1595 	nstat_tucookie_release_internal(cookie, false);
1596 }
1597 
1598 static void
nstat_tucookie_release_locked(struct nstat_tucookie * cookie)1599 nstat_tucookie_release_locked(
1600 	struct nstat_tucookie *cookie)
1601 {
1602 	nstat_tucookie_release_internal(cookie, true);
1603 }
1604 
1605 
1606 static size_t
nstat_inp_domain_info(struct inpcb * inp,nstat_domain_info * domain_info,size_t len)1607 nstat_inp_domain_info(struct inpcb *inp, nstat_domain_info *domain_info, size_t len)
1608 {
1609 	// Note, the caller has guaranteed that the buffer has been zeroed, there is no need to clear it again
1610 	struct socket         *so = inp->inp_socket;
1611 
1612 	if (so == NULL) {
1613 		return 0;
1614 	}
1615 
1616 	NSTAT_DEBUG_SOCKET_LOG(so, "NSTAT: Collecting stats");
1617 
1618 	if (domain_info == NULL) {
1619 		return sizeof(nstat_domain_info);
1620 	}
1621 
1622 	if (len < sizeof(nstat_domain_info)) {
1623 		return 0;
1624 	}
1625 
1626 	necp_copy_inp_domain_info(inp, so, domain_info);
1627 
1628 	NSTAT_DEBUG_SOCKET_LOG(so, "NSTAT: <pid %d> Collected stats - domain <%s> owner <%s> ctxt <%s> bundle id <%s> "
1629 	    "is_tracker %d is_non_app_initiated %d is_silent %d",
1630 	    so->so_flags & SOF_DELEGATED ? so->e_pid : so->last_pid,
1631 	    domain_info->domain_name,
1632 	    domain_info->domain_owner,
1633 	    domain_info->domain_tracker_ctxt,
1634 	    domain_info->domain_attributed_bundle_id,
1635 	    domain_info->is_tracker,
1636 	    domain_info->is_non_app_initiated,
1637 	    domain_info->is_silent);
1638 
1639 	return sizeof(nstat_domain_info);
1640 }
1641 
1642 static size_t
nstat_inp_bluetooth_counts(struct inpcb * inp,nstat_interface_counts * buf,size_t len)1643 nstat_inp_bluetooth_counts(struct inpcb *inp, nstat_interface_counts *buf, size_t len)
1644 {
1645 	// Note, the caller has guaranteed that the buffer has been zeroed, there is no need to clear it again
1646 	struct socket *so = inp->inp_socket;
1647 
1648 	if (so == NULL) {
1649 		return 0;
1650 	}
1651 
1652 	if (buf == NULL) {
1653 		uint64_t rxbytes = 0;
1654 		uint64_t txbytes = 0;
1655 		rxbytes = os_atomic_load(&inp->inp_btstat->rxbytes, relaxed);
1656 		txbytes = os_atomic_load(&inp->inp_btstat->txbytes, relaxed);
1657 
1658 		if ((rxbytes == 0) && (txbytes == 0)) {
1659 			// It's more efficient to skip sending counts if they're only going to be zero
1660 			return 0;
1661 		}
1662 		return sizeof(nstat_interface_counts);
1663 	}
1664 
1665 	if (len < sizeof(nstat_interface_counts)) {
1666 		return 0;
1667 	}
1668 
1669 	// if the pcb is in the dead state, we should stop using it
1670 	if (!(intotcpcb(inp)) || inp->inp_state == INPCB_STATE_DEAD) {
1671 		return 0;
1672 	}
1673 	buf->nstat_rxpackets = os_atomic_load(&inp->inp_btstat->rxpackets, relaxed);
1674 	buf->nstat_rxbytes = os_atomic_load(&inp->inp_btstat->rxbytes, relaxed);
1675 	buf->nstat_txpackets = os_atomic_load(&inp->inp_btstat->txpackets, relaxed);
1676 	buf->nstat_txbytes = os_atomic_load(&inp->inp_btstat->txbytes, relaxed);
1677 	return sizeof(nstat_interface_counts);
1678 }
1679 
1680 static nstat_provider   nstat_tcp_provider;
1681 
1682 static errno_t
nstat_tcp_lookup(__unused const void * data,__unused u_int32_t length,__unused nstat_provider_cookie_t * out_cookie)1683 nstat_tcp_lookup(
1684 	__unused const void              *data,
1685 	__unused u_int32_t               length,
1686 	__unused nstat_provider_cookie_t *out_cookie)
1687 {
1688 	// Looking up a specific connection is not supported.
1689 	return ENOTSUP;
1690 }
1691 
1692 static int
nstat_tcp_gone(nstat_provider_cookie_t cookie)1693 nstat_tcp_gone(
1694 	nstat_provider_cookie_t cookie)
1695 {
1696 	struct nstat_tucookie *tucookie =
1697 	    (struct nstat_tucookie *)cookie;
1698 	struct inpcb *inp;
1699 	struct tcpcb *tp;
1700 
1701 	return (!(inp = tucookie->inp) ||
1702 	       !(tp = intotcpcb(inp)) ||
1703 	       inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
1704 }
1705 
1706 static errno_t
nstat_tcp_counts(nstat_provider_cookie_t cookie,struct nstat_counts * out_counts,int * out_gone)1707 nstat_tcp_counts(
1708 	nstat_provider_cookie_t cookie,
1709 	struct nstat_counts     *out_counts,
1710 	int                     *out_gone)
1711 {
1712 	struct nstat_tucookie *tucookie =
1713 	    (struct nstat_tucookie *)cookie;
1714 	struct inpcb *inp;
1715 
1716 	bzero(out_counts, sizeof(*out_counts));
1717 
1718 	if (out_gone) {
1719 		*out_gone = 0;
1720 	}
1721 
1722 	// if the pcb is in the dead state, we should stop using it
1723 	if (nstat_tcp_gone(cookie)) {
1724 		if (out_gone) {
1725 			*out_gone = 1;
1726 		}
1727 		if (!(inp = tucookie->inp) || !intotcpcb(inp)) {
1728 			return EINVAL;
1729 		}
1730 	}
1731 	inp = tucookie->inp;
1732 	struct tcpcb *tp = intotcpcb(inp);
1733 
1734 	out_counts->nstat_rxpackets = os_atomic_load(&inp->inp_stat->rxpackets, relaxed);
1735 	out_counts->nstat_rxbytes = os_atomic_load(&inp->inp_stat->rxbytes, relaxed);
1736 	out_counts->nstat_txpackets = os_atomic_load(&inp->inp_stat->txpackets, relaxed);
1737 	out_counts->nstat_txbytes = os_atomic_load(&inp->inp_stat->txbytes, relaxed);
1738 	out_counts->nstat_rxduplicatebytes = tp->t_stat.rxduplicatebytes;
1739 	out_counts->nstat_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes;
1740 	out_counts->nstat_txretransmit = tp->t_stat.txretransmitbytes;
1741 	out_counts->nstat_connectattempts = tp->t_state >= TCPS_SYN_SENT ? 1 : 0;
1742 	out_counts->nstat_connectsuccesses = tp->t_state >= TCPS_ESTABLISHED ? 1 : 0;
1743 	out_counts->nstat_avg_rtt = tp->t_srtt;
1744 	out_counts->nstat_min_rtt = tp->t_rttbest;
1745 	out_counts->nstat_var_rtt = tp->t_rttvar;
1746 	if (out_counts->nstat_avg_rtt < out_counts->nstat_min_rtt) {
1747 		out_counts->nstat_min_rtt = out_counts->nstat_avg_rtt;
1748 	}
1749 	out_counts->nstat_cell_rxbytes = os_atomic_load(&inp->inp_cstat->rxbytes, relaxed);
1750 	out_counts->nstat_cell_txbytes = os_atomic_load(&inp->inp_cstat->txbytes, relaxed);
1751 	out_counts->nstat_wifi_rxbytes = os_atomic_load(&inp->inp_wstat->rxbytes, relaxed);
1752 	out_counts->nstat_wifi_txbytes = os_atomic_load(&inp->inp_wstat->txbytes, relaxed);
1753 	out_counts->nstat_wired_rxbytes = os_atomic_load(&inp->inp_Wstat->rxbytes, relaxed);
1754 	out_counts->nstat_wired_txbytes = os_atomic_load(&inp->inp_Wstat->txbytes, relaxed);
1755 
1756 	return 0;
1757 }
1758 
1759 static void
nstat_tcp_release(nstat_provider_cookie_t cookie,int locked)1760 nstat_tcp_release(
1761 	nstat_provider_cookie_t cookie,
1762 	int locked)
1763 {
1764 	struct nstat_tucookie *tucookie =
1765 	    (struct nstat_tucookie *)cookie;
1766 
1767 	nstat_tucookie_release_internal(tucookie, locked);
1768 }
1769 
1770 static errno_t
nstat_tcp_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req)1771 nstat_tcp_add_watcher(
1772 	nstat_client            *client,
1773 	nstat_msg_add_all_srcs  *req)
1774 {
1775 	// There is a tricky issue around getting all TCP sockets added once
1776 	// and only once.  nstat_tcp_new_pcb() is called prior to the new item
1777 	// being placed on any lists where it might be found.
1778 	// By locking the tcbinfo.ipi_lock prior to marking the client as a watcher,
1779 	// it should be impossible for a new socket to be added twice.
1780 	// On the other hand, there is still a timing issue where a new socket
1781 	// results in a call to nstat_tcp_new_pcb() before this watcher
1782 	// is instantiated and yet the socket doesn't make it into ipi_listhead
1783 	// prior to the scan.  <rdar://problem/30361716>
1784 
1785 	errno_t result;
1786 
1787 	lck_rw_lock_shared(&tcbinfo.ipi_lock);
1788 	result = nstat_set_provider_filter(client, req);
1789 	if (result == 0) {
1790 		OSIncrementAtomic(&nstat_tcp_watchers);
1791 
1792 		// Add all current tcp inpcbs. Ignore those in timewait
1793 		struct inpcb *inp;
1794 		struct nstat_tucookie *cookie;
1795 		LIST_FOREACH(inp, tcbinfo.ipi_listhead, inp_list)
1796 		{
1797 			cookie = nstat_tucookie_alloc_ref(inp);
1798 			if (cookie == NULL) {
1799 				continue;
1800 			}
1801 			if (nstat_client_source_add(0, client, &nstat_tcp_provider,
1802 			    cookie, NSTAT_LOCK_NOTHELD) != 0) {
1803 				nstat_tucookie_release(cookie);
1804 				break;
1805 			}
1806 		}
1807 	}
1808 
1809 	lck_rw_done(&tcbinfo.ipi_lock);
1810 
1811 	return result;
1812 }
1813 
1814 static void
nstat_tcp_remove_watcher(__unused nstat_client * client)1815 nstat_tcp_remove_watcher(
1816 	__unused nstat_client    *client)
1817 {
1818 	OSDecrementAtomic(&nstat_tcp_watchers);
1819 }
1820 
1821 __private_extern__ void
nstat_tcp_new_pcb(struct inpcb * inp)1822 nstat_tcp_new_pcb(
1823 	struct inpcb    *inp)
1824 {
1825 	struct nstat_tucookie *cookie;
1826 
1827 	inp->inp_start_timestamp = mach_continuous_time();
1828 
1829 	if (nstat_tcp_watchers == 0) {
1830 		return;
1831 	}
1832 
1833 	socket_lock(inp->inp_socket, 0);
1834 	NSTAT_LOCK_EXCLUSIVE();
1835 	nstat_client     *client;
1836 	for (client = nstat_clients; client; client = client->ntc_next) {
1837 		if ((client->ntc_watching & (1 << NSTAT_PROVIDER_TCP_KERNEL)) != 0) {
1838 			// this client is watching tcp
1839 			// acquire a reference for it
1840 			cookie = nstat_tucookie_alloc_ref_locked(inp);
1841 			if (cookie == NULL) {
1842 				continue;
1843 			}
1844 			// add the source, if that fails, release the reference
1845 			if (nstat_client_source_add(0, client,
1846 			    &nstat_tcp_provider, cookie, NSTAT_LOCK_HELD) != 0) {
1847 				nstat_tucookie_release_locked(cookie);
1848 			}
1849 		}
1850 	}
1851 	NSTAT_UNLOCK_EXCLUSIVE();
1852 	socket_unlock(inp->inp_socket, 0);
1853 }
1854 
1855 __private_extern__ void
nstat_pcb_detach(struct inpcb * inp)1856 nstat_pcb_detach(struct inpcb *inp)
1857 {
1858 	nstat_client *client;
1859 	nstat_src *src;
1860 	tailq_head_nstat_src dead_list;
1861 	struct nstat_tucookie *tucookie;
1862 	errno_t result;
1863 
1864 	if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0)) {
1865 		return;
1866 	}
1867 
1868 	TAILQ_INIT(&dead_list);
1869 	NSTAT_LOCK_EXCLUSIVE();
1870 	for (client = nstat_clients; client; client = client->ntc_next) {
1871 		TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
1872 		{
1873 			nstat_provider_id_t provider_id = src->nts_provider->nstat_provider_id;
1874 			if (provider_id == NSTAT_PROVIDER_TCP_KERNEL || provider_id == NSTAT_PROVIDER_UDP_KERNEL) {
1875 				tucookie = (struct nstat_tucookie *)src->nts_cookie;
1876 				if (tucookie->inp == inp) {
1877 					break;
1878 				}
1879 			}
1880 		}
1881 
1882 		if (src) {
1883 			result = nstat_client_send_goodbye(client, src);
1884 
1885 			TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
1886 			TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
1887 		}
1888 	}
1889 	NSTAT_UNLOCK_EXCLUSIVE();
1890 
1891 	while ((src = TAILQ_FIRST(&dead_list))) {
1892 		TAILQ_REMOVE(&dead_list, src, nts_client_link);
1893 		nstat_client_cleanup_source(NULL, src, TRUE);
1894 	}
1895 }
1896 
1897 __private_extern__ void
nstat_pcb_event(struct inpcb * inp,u_int64_t event)1898 nstat_pcb_event(struct inpcb *inp, u_int64_t event)
1899 {
1900 	nstat_client *client;
1901 	nstat_src *src;
1902 	struct nstat_tucookie *tucookie;
1903 	errno_t result;
1904 	nstat_provider_id_t provider_id;
1905 
1906 	if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0)) {
1907 		return;
1908 	}
1909 	if (((merged_filters.mpf_filters[NSTAT_PROVIDER_TCP_KERNEL].mf_events & event) == 0) &&
1910 	    ((merged_filters.mpf_filters[NSTAT_PROVIDER_UDP_KERNEL].mf_events & event) == 0)) {
1911 		// There are clients for TCP and UDP, but none are interested in the event
1912 		// This check saves taking the mutex and scanning the list
1913 		return;
1914 	}
1915 	NSTAT_LOCK_EXCLUSIVE();
1916 	for (client = nstat_clients; client; client = client->ntc_next) {
1917 		if (((client->ntc_provider_filters[NSTAT_PROVIDER_TCP_KERNEL].npf_events & event) == 0) &&
1918 		    ((client->ntc_provider_filters[NSTAT_PROVIDER_UDP_KERNEL].npf_events & event) == 0)) {
1919 			continue;
1920 		}
1921 		TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
1922 		{
1923 			provider_id = src->nts_provider->nstat_provider_id;
1924 			if (provider_id == NSTAT_PROVIDER_TCP_KERNEL || provider_id == NSTAT_PROVIDER_UDP_KERNEL) {
1925 				tucookie = (struct nstat_tucookie *)src->nts_cookie;
1926 				if (tucookie->inp == inp) {
1927 					break;
1928 				}
1929 			}
1930 		}
1931 
1932 		if (src && ((client->ntc_provider_filters[provider_id].npf_events & event) != 0)) {
1933 			result = nstat_client_send_event(client, src, event);
1934 		}
1935 	}
1936 	NSTAT_UNLOCK_EXCLUSIVE();
1937 }
1938 
1939 
1940 __private_extern__ void
nstat_pcb_cache(struct inpcb * inp)1941 nstat_pcb_cache(struct inpcb *inp)
1942 {
1943 	nstat_client *client;
1944 	nstat_src *src;
1945 	struct nstat_tucookie *tucookie;
1946 
1947 	if (inp == NULL || nstat_udp_watchers == 0 ||
1948 	    inp->inp_nstat_refcnt == 0) {
1949 		return;
1950 	}
1951 	VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
1952 	NSTAT_LOCK_EXCLUSIVE();
1953 	for (client = nstat_clients; client; client = client->ntc_next) {
1954 		TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
1955 		{
1956 			tucookie = (struct nstat_tucookie *)src->nts_cookie;
1957 			if (tucookie->inp == inp) {
1958 				if (inp->inp_vflag & INP_IPV6) {
1959 					in6_ip6_to_sockaddr(&inp->in6p_laddr,
1960 					    inp->inp_lport,
1961 					    inp->inp_lifscope,
1962 					    &tucookie->local.v6,
1963 					    sizeof(tucookie->local));
1964 					in6_ip6_to_sockaddr(&inp->in6p_faddr,
1965 					    inp->inp_fport,
1966 					    inp->inp_fifscope,
1967 					    &tucookie->remote.v6,
1968 					    sizeof(tucookie->remote));
1969 				} else if (inp->inp_vflag & INP_IPV4) {
1970 					nstat_ip_to_sockaddr(&inp->inp_laddr,
1971 					    inp->inp_lport,
1972 					    &tucookie->local.v4,
1973 					    sizeof(tucookie->local));
1974 					nstat_ip_to_sockaddr(&inp->inp_faddr,
1975 					    inp->inp_fport,
1976 					    &tucookie->remote.v4,
1977 					    sizeof(tucookie->remote));
1978 				}
1979 				if (inp->inp_last_outifp) {
1980 					tucookie->if_index =
1981 					    inp->inp_last_outifp->if_index;
1982 				}
1983 
1984 				tucookie->ifnet_properties = nstat_inpcb_to_flags(inp);
1985 				tucookie->cached = true;
1986 				break;
1987 			}
1988 		}
1989 	}
1990 	NSTAT_UNLOCK_EXCLUSIVE();
1991 }
1992 
1993 __private_extern__ void
nstat_pcb_invalidate_cache(struct inpcb * inp)1994 nstat_pcb_invalidate_cache(struct inpcb *inp)
1995 {
1996 	nstat_client *client;
1997 	nstat_src *src;
1998 	struct nstat_tucookie *tucookie;
1999 
2000 	if (inp == NULL || nstat_udp_watchers == 0 ||
2001 	    inp->inp_nstat_refcnt == 0) {
2002 		return;
2003 	}
2004 	VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
2005 	NSTAT_LOCK_EXCLUSIVE();
2006 	for (client = nstat_clients; client; client = client->ntc_next) {
2007 		TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
2008 		{
2009 			tucookie = (struct nstat_tucookie *)src->nts_cookie;
2010 			if (tucookie->inp == inp) {
2011 				tucookie->cached = false;
2012 				break;
2013 			}
2014 		}
2015 	}
2016 	NSTAT_UNLOCK_EXCLUSIVE();
2017 }
2018 
2019 static errno_t
nstat_tcp_copy_descriptor(nstat_provider_cookie_t cookie,void * __sized_by (len)data,size_t len)2020 nstat_tcp_copy_descriptor(
2021 	nstat_provider_cookie_t cookie,
2022 	void                    *__sized_by(len)data,
2023 	size_t                  len)
2024 {
2025 	if (len < sizeof(nstat_tcp_descriptor)) {
2026 		return EINVAL;
2027 	}
2028 
2029 	if (nstat_tcp_gone(cookie)) {
2030 		return EINVAL;
2031 	}
2032 
2033 	nstat_tcp_descriptor    *desc = (nstat_tcp_descriptor*)data;
2034 	struct nstat_tucookie *tucookie =
2035 	    (struct nstat_tucookie *)cookie;
2036 	struct inpcb            *inp = tucookie->inp;
2037 	struct tcpcb            *tp = intotcpcb(inp);
2038 	bzero(desc, sizeof(*desc));
2039 
2040 	if (inp->inp_vflag & INP_IPV6) {
2041 		in6_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport, inp->inp_lifscope,
2042 		    &desc->local.v6, sizeof(desc->local));
2043 		in6_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport, inp->inp_fifscope,
2044 		    &desc->remote.v6, sizeof(desc->remote));
2045 	} else if (inp->inp_vflag & INP_IPV4) {
2046 		nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
2047 		    &desc->local.v4, sizeof(desc->local));
2048 		nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
2049 		    &desc->remote.v4, sizeof(desc->remote));
2050 	}
2051 
2052 	desc->state = intotcpcb(inp)->t_state;
2053 	desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
2054 	    inp->inp_last_outifp->if_index;
2055 
2056 	// danger - not locked, values could be bogus
2057 	desc->txunacked = tp->snd_max - tp->snd_una;
2058 	desc->txwindow = tp->snd_wnd;
2059 	desc->txcwindow = tp->snd_cwnd;
2060 	desc->ifnet_properties = nstat_inpcb_to_flags(inp);
2061 
2062 	if (CC_ALGO(tp)->name != NULL) {
2063 		strbufcpy(desc->cc_algo, CC_ALGO(tp)->name);
2064 	}
2065 
2066 	struct socket *so = inp->inp_socket;
2067 	if (so) {
2068 		// TBD - take the socket lock around these to make sure
2069 		// they're in sync?
2070 		desc->upid = so->last_upid;
2071 		desc->pid = so->last_pid;
2072 		desc->traffic_class = so->so_traffic_class;
2073 		if ((so->so_flags1 & SOF1_TRAFFIC_MGT_SO_BACKGROUND)) {
2074 			desc->traffic_mgt_flags |= TRAFFIC_MGT_SO_BACKGROUND;
2075 		}
2076 		if ((so->so_flags1 & SOF1_TRAFFIC_MGT_TCP_RECVBG)) {
2077 			desc->traffic_mgt_flags |= TRAFFIC_MGT_TCP_RECVBG;
2078 		}
2079 		if (so->so_flags1 & SOF1_INBOUND) {
2080 			desc->ifnet_properties |= NSTAT_SOURCE_IS_INBOUND;
2081 		} else if (desc->state == TCPS_LISTEN) {
2082 			desc->ifnet_properties |= NSTAT_SOURCE_IS_LISTENER;
2083 			tucookie->ifnet_properties = NSTAT_SOURCE_IS_LISTENER;
2084 		} else if (desc->state != TCPS_CLOSED) {
2085 			desc->ifnet_properties |= NSTAT_SOURCE_IS_OUTBOUND;
2086 			tucookie->ifnet_properties = NSTAT_SOURCE_IS_OUTBOUND;
2087 		} else {
2088 			desc->ifnet_properties |= tucookie->ifnet_properties;
2089 		}
2090 		proc_best_name_for_pid(desc->pid, desc->pname, sizeof(desc->pname));
2091 		if (desc->pname[0] == 0) {
2092 			strbufcpy(desc->pname, tucookie->pname);
2093 		} else {
2094 			desc->pname[sizeof(desc->pname) - 1] = 0;
2095 			strbufcpy(tucookie->pname, desc->pname);
2096 		}
2097 		memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
2098 		memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
2099 		if (so->so_flags & SOF_DELEGATED) {
2100 			desc->eupid = so->e_upid;
2101 			desc->epid = so->e_pid;
2102 			memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
2103 		} else if (!uuid_is_null(so->so_ruuid)) {
2104 			memcpy(desc->euuid, so->so_ruuid, sizeof(so->so_ruuid));
2105 		} else {
2106 			desc->eupid = desc->upid;
2107 			desc->epid = desc->pid;
2108 			memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
2109 		}
2110 		uuid_copy(desc->fuuid, inp->necp_client_uuid);
2111 		desc->persona_id = so->so_persona_id;
2112 		desc->uid = kauth_cred_getuid(so->so_cred);
2113 		desc->sndbufsize = so->so_snd.sb_hiwat;
2114 		desc->sndbufused = so->so_snd.sb_cc;
2115 		desc->rcvbufsize = so->so_rcv.sb_hiwat;
2116 		desc->rcvbufused = so->so_rcv.sb_cc;
2117 		desc->fallback_mode = so->so_fallback_mode;
2118 
2119 		if (nstat_debug) {
2120 			uuid_string_t euuid_str = { 0 };
2121 			uuid_unparse(desc->euuid, euuid_str);
2122 			NSTAT_DEBUG_SOCKET_LOG(so, "NSTAT: TCP - pid %d uid %d euuid %s persona id %d", desc->pid, desc->uid, euuid_str, desc->persona_id);
2123 		}
2124 	}
2125 
2126 	tcp_get_connectivity_status(tp, &desc->connstatus);
2127 	inp_get_activity_bitmap(inp, &desc->activity_bitmap);
2128 	desc->start_timestamp = inp->inp_start_timestamp;
2129 	desc->timestamp = mach_continuous_time();
2130 	return 0;
2131 }
2132 
2133 static bool
nstat_tcpudp_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,bool is_UDP)2134 nstat_tcpudp_reporting_allowed(nstat_provider_cookie_t cookie, nstat_provider_filter *filter, bool is_UDP)
2135 {
2136 	bool retval = true;
2137 
2138 	if ((filter->npf_flags & (NSTAT_FILTER_IFNET_FLAGS | NSTAT_FILTER_SPECIFIC_USER)) != 0) {
2139 		struct nstat_tucookie *tucookie = (struct nstat_tucookie *)cookie;
2140 		struct inpcb *inp = tucookie->inp;
2141 
2142 		/* Only apply interface filter if at least one is allowed. */
2143 		if ((filter->npf_flags & NSTAT_FILTER_IFNET_FLAGS) != 0) {
2144 			uint32_t interface_properties = nstat_inpcb_to_flags(inp);
2145 
2146 			if ((filter->npf_flags & interface_properties) == 0) {
2147 				// For UDP, we could have an undefined interface and yet transfers may have occurred.
2148 				// We allow reporting if there have been transfers of the requested kind.
2149 				// This is imperfect as we cannot account for the expensive attribute over wifi.
2150 				// We also assume that cellular is expensive and we have no way to select for AWDL
2151 				if (is_UDP) {
2152 					do{
2153 						if ((filter->npf_flags & (NSTAT_FILTER_ACCEPT_CELLULAR | NSTAT_FILTER_ACCEPT_EXPENSIVE)) &&
2154 						    (inp->inp_cstat->rxbytes || inp->inp_cstat->txbytes)) {
2155 							break;
2156 						}
2157 						if ((filter->npf_flags & NSTAT_FILTER_ACCEPT_WIFI) &&
2158 						    (inp->inp_wstat->rxbytes || inp->inp_wstat->txbytes)) {
2159 							break;
2160 						}
2161 						if ((filter->npf_flags & NSTAT_FILTER_ACCEPT_WIRED) &&
2162 						    (inp->inp_Wstat->rxbytes || inp->inp_Wstat->txbytes)) {
2163 							break;
2164 						}
2165 						if ((filter->npf_flags & NSTAT_FILTER_ACCEPT_COMPANIONLINK_BT) &&
2166 						    (inp->inp_btstat->rxbytes || inp->inp_btstat->txbytes)) {
2167 							break;
2168 						}
2169 						return false;
2170 					} while (0);
2171 				} else {
2172 					return false;
2173 				}
2174 			}
2175 		}
2176 
2177 		if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER) != 0) && (retval)) {
2178 			struct socket *so = inp->inp_socket;
2179 			retval = false;
2180 
2181 			if (so) {
2182 				if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_PID) != 0) &&
2183 				    (filter->npf_pid == so->last_pid)) {
2184 					retval = true;
2185 				} else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_EPID) != 0) &&
2186 				    (filter->npf_pid == (so->so_flags & SOF_DELEGATED)? so->e_upid : so->last_pid)) {
2187 					retval = true;
2188 				} else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_UUID) != 0) &&
2189 				    (memcmp(filter->npf_uuid, so->last_uuid, sizeof(so->last_uuid)) == 0)) {
2190 					retval = true;
2191 				} else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_EUUID) != 0) &&
2192 				    (memcmp(filter->npf_uuid, (so->so_flags & SOF_DELEGATED)? so->e_uuid : so->last_uuid,
2193 				    sizeof(so->last_uuid)) == 0)) {
2194 					retval = true;
2195 				}
2196 			}
2197 		}
2198 	}
2199 	return retval;
2200 }
2201 
2202 static bool
nstat_tcp_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,__unused u_int64_t suppression_flags)2203 nstat_tcp_reporting_allowed(
2204 	nstat_provider_cookie_t cookie,
2205 	nstat_provider_filter *filter,
2206 	__unused u_int64_t suppression_flags)
2207 {
2208 	return nstat_tcpudp_reporting_allowed(cookie, filter, FALSE);
2209 }
2210 
2211 static size_t
nstat_tcp_extensions(nstat_provider_cookie_t cookie,u_int32_t extension_id,void * buf,size_t len)2212 nstat_tcp_extensions(nstat_provider_cookie_t cookie, u_int32_t extension_id, void *buf, size_t len)
2213 {
2214 	struct nstat_tucookie *tucookie =  (struct nstat_tucookie *)cookie;
2215 	struct inpcb          *inp = tucookie->inp;
2216 
2217 	if (nstat_tcp_gone(cookie)) {
2218 		return 0;
2219 	}
2220 
2221 	switch (extension_id) {
2222 	case NSTAT_EXTENDED_UPDATE_TYPE_DOMAIN:
2223 		return nstat_inp_domain_info(inp, (nstat_domain_info *)buf, len);
2224 
2225 	case NSTAT_EXTENDED_UPDATE_TYPE_BLUETOOTH_COUNTS:
2226 		return nstat_inp_bluetooth_counts(inp, (nstat_interface_counts *)buf, len);
2227 
2228 	case NSTAT_EXTENDED_UPDATE_TYPE_NECP_TLV:
2229 	default:
2230 		break;
2231 	}
2232 	return 0;
2233 }
2234 
2235 static void
nstat_init_tcp_provider(void)2236 nstat_init_tcp_provider(void)
2237 {
2238 	bzero(&nstat_tcp_provider, sizeof(nstat_tcp_provider));
2239 	nstat_tcp_provider.nstat_descriptor_length = sizeof(nstat_tcp_descriptor);
2240 	nstat_tcp_provider.nstat_provider_id = NSTAT_PROVIDER_TCP_KERNEL;
2241 	nstat_tcp_provider.nstat_lookup = nstat_tcp_lookup;
2242 	nstat_tcp_provider.nstat_gone = nstat_tcp_gone;
2243 	nstat_tcp_provider.nstat_counts = nstat_tcp_counts;
2244 	nstat_tcp_provider.nstat_release = nstat_tcp_release;
2245 	nstat_tcp_provider.nstat_watcher_add = nstat_tcp_add_watcher;
2246 	nstat_tcp_provider.nstat_watcher_remove = nstat_tcp_remove_watcher;
2247 	nstat_tcp_provider.nstat_copy_descriptor = nstat_tcp_copy_descriptor;
2248 	nstat_tcp_provider.nstat_reporting_allowed = nstat_tcp_reporting_allowed;
2249 	nstat_tcp_provider.nstat_copy_extension = nstat_tcp_extensions;
2250 	nstat_tcp_provider.next = nstat_providers;
2251 	nstat_providers = &nstat_tcp_provider;
2252 }
2253 
2254 #pragma mark -- UDP Provider --
2255 
2256 static nstat_provider   nstat_udp_provider;
2257 
2258 static errno_t
nstat_udp_lookup(__unused const void * data,__unused u_int32_t length,__unused nstat_provider_cookie_t * out_cookie)2259 nstat_udp_lookup(
2260 	__unused const void              *data,
2261 	__unused u_int32_t               length,
2262 	__unused nstat_provider_cookie_t *out_cookie)
2263 {
2264 	// Looking up a specific connection is not supported.
2265 	return ENOTSUP;
2266 }
2267 
2268 static int
nstat_udp_gone(nstat_provider_cookie_t cookie)2269 nstat_udp_gone(
2270 	nstat_provider_cookie_t cookie)
2271 {
2272 	struct nstat_tucookie *tucookie =
2273 	    (struct nstat_tucookie *)cookie;
2274 	struct inpcb *inp;
2275 
2276 	return (!(inp = tucookie->inp) ||
2277 	       inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
2278 }
2279 
2280 static errno_t
nstat_udp_counts(nstat_provider_cookie_t cookie,struct nstat_counts * out_counts,int * out_gone)2281 nstat_udp_counts(
2282 	nstat_provider_cookie_t cookie,
2283 	struct nstat_counts     *out_counts,
2284 	int                     *out_gone)
2285 {
2286 	struct nstat_tucookie *tucookie =
2287 	    (struct nstat_tucookie *)cookie;
2288 
2289 	if (out_gone) {
2290 		*out_gone = 0;
2291 	}
2292 
2293 	// if the pcb is in the dead state, we should stop using it
2294 	if (nstat_udp_gone(cookie)) {
2295 		if (out_gone) {
2296 			*out_gone = 1;
2297 		}
2298 		if (!tucookie->inp) {
2299 			return EINVAL;
2300 		}
2301 	}
2302 	struct inpcb *inp = tucookie->inp;
2303 
2304 	out_counts->nstat_rxpackets = os_atomic_load(&inp->inp_stat->rxpackets, relaxed);
2305 	out_counts->nstat_rxbytes = os_atomic_load(&inp->inp_stat->rxbytes, relaxed);
2306 	out_counts->nstat_txpackets = os_atomic_load(&inp->inp_stat->txpackets, relaxed);
2307 	out_counts->nstat_txbytes = os_atomic_load(&inp->inp_stat->txbytes, relaxed);
2308 	out_counts->nstat_cell_rxbytes = os_atomic_load(&inp->inp_cstat->rxbytes, relaxed);
2309 	out_counts->nstat_cell_txbytes = os_atomic_load(&inp->inp_cstat->txbytes, relaxed);
2310 	out_counts->nstat_wifi_rxbytes = os_atomic_load(&inp->inp_wstat->rxbytes, relaxed);
2311 	out_counts->nstat_wifi_txbytes = os_atomic_load(&inp->inp_wstat->txbytes, relaxed);
2312 	out_counts->nstat_wired_rxbytes = os_atomic_load(&inp->inp_Wstat->rxbytes, relaxed);
2313 	out_counts->nstat_wired_txbytes = os_atomic_load(&inp->inp_Wstat->txbytes, relaxed);
2314 
2315 	return 0;
2316 }
2317 
2318 static void
nstat_udp_release(nstat_provider_cookie_t cookie,int locked)2319 nstat_udp_release(
2320 	nstat_provider_cookie_t cookie,
2321 	int locked)
2322 {
2323 	struct nstat_tucookie *tucookie =
2324 	    (struct nstat_tucookie *)cookie;
2325 
2326 	nstat_tucookie_release_internal(tucookie, locked);
2327 }
2328 
2329 static errno_t
nstat_udp_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req)2330 nstat_udp_add_watcher(
2331 	nstat_client            *client,
2332 	nstat_msg_add_all_srcs  *req)
2333 {
2334 	// There is a tricky issue around getting all UDP sockets added once
2335 	// and only once.  nstat_udp_new_pcb() is called prior to the new item
2336 	// being placed on any lists where it might be found.
2337 	// By locking the udpinfo.ipi_lock prior to marking the client as a watcher,
2338 	// it should be impossible for a new socket to be added twice.
2339 	// On the other hand, there is still a timing issue where a new socket
2340 	// results in a call to nstat_udp_new_pcb() before this watcher
2341 	// is instantiated and yet the socket doesn't make it into ipi_listhead
2342 	// prior to the scan. <rdar://problem/30361716>
2343 
2344 	errno_t result;
2345 
2346 	lck_rw_lock_shared(&udbinfo.ipi_lock);
2347 	result = nstat_set_provider_filter(client, req);
2348 
2349 	if (result == 0) {
2350 		struct inpcb *inp;
2351 		struct nstat_tucookie *cookie;
2352 
2353 		OSIncrementAtomic(&nstat_udp_watchers);
2354 
2355 		// Add all current UDP inpcbs.
2356 		LIST_FOREACH(inp, udbinfo.ipi_listhead, inp_list)
2357 		{
2358 			cookie = nstat_tucookie_alloc_ref(inp);
2359 			if (cookie == NULL) {
2360 				continue;
2361 			}
2362 			if (nstat_client_source_add(0, client, &nstat_udp_provider,
2363 			    cookie, NSTAT_LOCK_NOTHELD) != 0) {
2364 				nstat_tucookie_release(cookie);
2365 				break;
2366 			}
2367 		}
2368 	}
2369 
2370 	lck_rw_done(&udbinfo.ipi_lock);
2371 
2372 	return result;
2373 }
2374 
2375 static void
nstat_udp_remove_watcher(__unused nstat_client * client)2376 nstat_udp_remove_watcher(
2377 	__unused nstat_client    *client)
2378 {
2379 	OSDecrementAtomic(&nstat_udp_watchers);
2380 }
2381 
2382 __private_extern__ void
nstat_udp_new_pcb(struct inpcb * inp)2383 nstat_udp_new_pcb(
2384 	struct inpcb    *inp)
2385 {
2386 	struct nstat_tucookie *cookie;
2387 
2388 	inp->inp_start_timestamp = mach_continuous_time();
2389 
2390 	if (nstat_udp_watchers == 0) {
2391 		return;
2392 	}
2393 
2394 	socket_lock(inp->inp_socket, 0);
2395 	NSTAT_LOCK_EXCLUSIVE();
2396 	nstat_client     *client;
2397 	for (client = nstat_clients; client; client = client->ntc_next) {
2398 		if ((client->ntc_watching & (1 << NSTAT_PROVIDER_UDP_KERNEL)) != 0) {
2399 			// this client is watching udp
2400 			// acquire a reference for it
2401 			cookie = nstat_tucookie_alloc_ref_locked(inp);
2402 			if (cookie == NULL) {
2403 				continue;
2404 			}
2405 			// add the source, if that fails, release the reference
2406 			if (nstat_client_source_add(0, client,
2407 			    &nstat_udp_provider, cookie, NSTAT_LOCK_HELD) != 0) {
2408 				nstat_tucookie_release_locked(cookie);
2409 			}
2410 		}
2411 	}
2412 	NSTAT_UNLOCK_EXCLUSIVE();
2413 	socket_unlock(inp->inp_socket, 0);
2414 }
2415 
2416 static errno_t
nstat_udp_copy_descriptor(nstat_provider_cookie_t cookie,void * __sized_by (len)data,size_t len)2417 nstat_udp_copy_descriptor(
2418 	nstat_provider_cookie_t cookie,
2419 	void                    *__sized_by(len)data,
2420 	size_t                  len)
2421 {
2422 	if (len < sizeof(nstat_udp_descriptor)) {
2423 		return EINVAL;
2424 	}
2425 
2426 	if (nstat_udp_gone(cookie)) {
2427 		return EINVAL;
2428 	}
2429 
2430 	struct nstat_tucookie   *tucookie =
2431 	    (struct nstat_tucookie *)cookie;
2432 	nstat_udp_descriptor    *desc = (nstat_udp_descriptor*)data;
2433 	struct inpcb            *inp = tucookie->inp;
2434 
2435 	bzero(desc, sizeof(*desc));
2436 
2437 	if (tucookie->cached == false) {
2438 		if (inp->inp_vflag & INP_IPV6) {
2439 			in6_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport, inp->inp_lifscope,
2440 			    &desc->local.v6, sizeof(desc->local.v6));
2441 			in6_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport, inp->inp_fifscope,
2442 			    &desc->remote.v6, sizeof(desc->remote.v6));
2443 		} else if (inp->inp_vflag & INP_IPV4) {
2444 			nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
2445 			    &desc->local.v4, sizeof(desc->local.v4));
2446 			nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
2447 			    &desc->remote.v4, sizeof(desc->remote.v4));
2448 		}
2449 		desc->ifnet_properties = nstat_inpcb_to_flags(inp);
2450 	} else {
2451 		if (inp->inp_vflag & INP_IPV6) {
2452 			memcpy(&desc->local.v6, &tucookie->local.v6,
2453 			    sizeof(desc->local.v6));
2454 			memcpy(&desc->remote.v6, &tucookie->remote.v6,
2455 			    sizeof(desc->remote.v6));
2456 		} else if (inp->inp_vflag & INP_IPV4) {
2457 			memcpy(&desc->local.v4, &tucookie->local.v4,
2458 			    sizeof(desc->local.v4));
2459 			memcpy(&desc->remote.v4, &tucookie->remote.v4,
2460 			    sizeof(desc->remote.v4));
2461 		}
2462 		desc->ifnet_properties = tucookie->ifnet_properties;
2463 	}
2464 
2465 	if (inp->inp_last_outifp) {
2466 		desc->ifindex = inp->inp_last_outifp->if_index;
2467 	} else {
2468 		desc->ifindex = tucookie->if_index;
2469 	}
2470 
2471 	struct socket *so = inp->inp_socket;
2472 	if (so) {
2473 		// TBD - take the socket lock around these to make sure
2474 		// they're in sync?
2475 		desc->upid = so->last_upid;
2476 		desc->pid = so->last_pid;
2477 		proc_best_name_for_pid(desc->pid, desc->pname, sizeof(desc->pname));
2478 		if (desc->pname[0] == 0) {
2479 			strbufcpy(desc->pname, tucookie->pname);
2480 		} else {
2481 			desc->pname[sizeof(desc->pname) - 1] = 0;
2482 			strbufcpy(tucookie->pname, desc->pname);
2483 		}
2484 		memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
2485 		memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
2486 		if (so->so_flags & SOF_DELEGATED) {
2487 			desc->eupid = so->e_upid;
2488 			desc->epid = so->e_pid;
2489 			memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
2490 		} else if (!uuid_is_null(so->so_ruuid)) {
2491 			memcpy(desc->euuid, so->so_ruuid, sizeof(so->so_ruuid));
2492 		} else {
2493 			desc->eupid = desc->upid;
2494 			desc->epid = desc->pid;
2495 			memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
2496 		}
2497 		uuid_copy(desc->fuuid, inp->necp_client_uuid);
2498 		desc->persona_id = so->so_persona_id;
2499 		desc->uid = kauth_cred_getuid(so->so_cred);
2500 		desc->rcvbufsize = so->so_rcv.sb_hiwat;
2501 		desc->rcvbufused = so->so_rcv.sb_cc;
2502 		desc->traffic_class = so->so_traffic_class;
2503 		desc->fallback_mode = so->so_fallback_mode;
2504 		inp_get_activity_bitmap(inp, &desc->activity_bitmap);
2505 		desc->start_timestamp = inp->inp_start_timestamp;
2506 		desc->timestamp = mach_continuous_time();
2507 
2508 		if (nstat_debug) {
2509 			uuid_string_t euuid_str = { 0 };
2510 			uuid_unparse(desc->euuid, euuid_str);
2511 			NSTAT_DEBUG_SOCKET_LOG(so, "NSTAT: UDP - pid %d uid %d euuid %s persona id %d", desc->pid, desc->uid, euuid_str, desc->persona_id);
2512 		}
2513 	}
2514 
2515 	return 0;
2516 }
2517 
2518 static bool
nstat_udp_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,__unused u_int64_t suppression_flags)2519 nstat_udp_reporting_allowed(
2520 	nstat_provider_cookie_t cookie,
2521 	nstat_provider_filter *filter,
2522 	__unused u_int64_t suppression_flags)
2523 {
2524 	return nstat_tcpudp_reporting_allowed(cookie, filter, TRUE);
2525 }
2526 
2527 
2528 static size_t
nstat_udp_extensions(nstat_provider_cookie_t cookie,u_int32_t extension_id,void * buf,size_t len)2529 nstat_udp_extensions(nstat_provider_cookie_t cookie, u_int32_t extension_id, void *buf, size_t len)
2530 {
2531 	struct nstat_tucookie *tucookie =  (struct nstat_tucookie *)cookie;
2532 	struct inpcb          *inp = tucookie->inp;
2533 	if (nstat_udp_gone(cookie)) {
2534 		return 0;
2535 	}
2536 
2537 	switch (extension_id) {
2538 	case NSTAT_EXTENDED_UPDATE_TYPE_DOMAIN:
2539 		return nstat_inp_domain_info(inp, (nstat_domain_info *)buf, len);
2540 
2541 	case NSTAT_EXTENDED_UPDATE_TYPE_BLUETOOTH_COUNTS:
2542 		return nstat_inp_bluetooth_counts(inp, (nstat_interface_counts *)buf, len);
2543 
2544 	default:
2545 		break;
2546 	}
2547 	return 0;
2548 }
2549 
2550 
2551 static void
nstat_init_udp_provider(void)2552 nstat_init_udp_provider(void)
2553 {
2554 	bzero(&nstat_udp_provider, sizeof(nstat_udp_provider));
2555 	nstat_udp_provider.nstat_provider_id = NSTAT_PROVIDER_UDP_KERNEL;
2556 	nstat_udp_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor);
2557 	nstat_udp_provider.nstat_lookup = nstat_udp_lookup;
2558 	nstat_udp_provider.nstat_gone = nstat_udp_gone;
2559 	nstat_udp_provider.nstat_counts = nstat_udp_counts;
2560 	nstat_udp_provider.nstat_watcher_add = nstat_udp_add_watcher;
2561 	nstat_udp_provider.nstat_watcher_remove = nstat_udp_remove_watcher;
2562 	nstat_udp_provider.nstat_copy_descriptor = nstat_udp_copy_descriptor;
2563 	nstat_udp_provider.nstat_release = nstat_udp_release;
2564 	nstat_udp_provider.nstat_reporting_allowed = nstat_udp_reporting_allowed;
2565 	nstat_udp_provider.nstat_copy_extension = nstat_udp_extensions;
2566 	nstat_udp_provider.next = nstat_providers;
2567 	nstat_providers = &nstat_udp_provider;
2568 }
2569 
2570 #if SKYWALK
2571 
2572 #pragma mark -- TCP/UDP/QUIC Userland
2573 
2574 // Almost all of this infrastucture is common to both TCP and UDP
2575 
2576 static u_int32_t    nstat_userland_quic_watchers = 0;
2577 static u_int32_t    nstat_userland_udp_watchers = 0;
2578 static u_int32_t    nstat_userland_tcp_watchers = 0;
2579 
2580 static u_int32_t    nstat_userland_quic_shadows = 0;
2581 static u_int32_t    nstat_userland_udp_shadows = 0;
2582 static u_int32_t    nstat_userland_tcp_shadows = 0;
2583 
2584 static nstat_provider   nstat_userland_quic_provider;
2585 static nstat_provider   nstat_userland_udp_provider;
2586 static nstat_provider   nstat_userland_tcp_provider;
2587 
2588 enum nstat_rnf_override {
2589 	nstat_rnf_override_not_set,
2590 	nstat_rnf_override_enabled,
2591 	nstat_rnf_override_disabled
2592 };
2593 
2594 struct nstat_tu_shadow {
2595 	tailq_entry_tu_shadow                   shad_link;
2596 	userland_stats_request_vals_fn          *shad_getvals_fn;
2597 	userland_stats_request_extension_fn     *shad_get_extension_fn;
2598 	userland_stats_provider_context         *shad_provider_context;
2599 	u_int64_t                               shad_properties;
2600 	u_int64_t                               shad_start_timestamp;
2601 	nstat_provider_id_t                     shad_provider;
2602 	struct nstat_procdetails                *shad_procdetails;
2603 	bool                                    shad_live;  // false if defunct
2604 	enum nstat_rnf_override                 shad_rnf_override;
2605 	uint32_t                                shad_magic;
2606 };
2607 
2608 // Magic number checking should remain in place until the userland provider has been fully proven
2609 #define TU_SHADOW_MAGIC             0xfeedf00d
2610 #define TU_SHADOW_UNMAGIC           0xdeaddeed
2611 
2612 static tailq_head_tu_shadow nstat_userprot_shad_head = TAILQ_HEAD_INITIALIZER(nstat_userprot_shad_head);
2613 
2614 static errno_t
nstat_userland_tu_lookup(__unused const void * data,__unused u_int32_t length,__unused nstat_provider_cookie_t * out_cookie)2615 nstat_userland_tu_lookup(
2616 	__unused const void                 *data,
2617 	__unused u_int32_t                  length,
2618 	__unused nstat_provider_cookie_t    *out_cookie)
2619 {
2620 	// Looking up a specific connection is not supported
2621 	return ENOTSUP;
2622 }
2623 
2624 static int
nstat_userland_tu_gone(__unused nstat_provider_cookie_t cookie)2625 nstat_userland_tu_gone(
2626 	__unused nstat_provider_cookie_t    cookie)
2627 {
2628 	// Returns non-zero if the source has gone.
2629 	// We don't keep a source hanging around, so the answer is always 0
2630 	return 0;
2631 }
2632 
2633 static errno_t
nstat_userland_tu_counts(nstat_provider_cookie_t cookie,struct nstat_counts * out_counts,int * out_gone)2634 nstat_userland_tu_counts(
2635 	nstat_provider_cookie_t cookie,
2636 	struct nstat_counts     *out_counts,
2637 	int                     *out_gone)
2638 {
2639 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)cookie;
2640 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
2641 	assert(shad->shad_live);
2642 
2643 	bool result = (*shad->shad_getvals_fn)(shad->shad_provider_context, NULL, NULL, out_counts, NULL);
2644 
2645 	if (out_gone) {
2646 		*out_gone = 0;
2647 	}
2648 
2649 	return (result)? 0 : EIO;
2650 }
2651 
2652 
2653 static errno_t
nstat_userland_tu_copy_descriptor(nstat_provider_cookie_t cookie,void * __sized_by (len)data,__unused size_t len)2654 nstat_userland_tu_copy_descriptor(
2655 	nstat_provider_cookie_t cookie,
2656 	void                    *__sized_by(len)data,
2657 	__unused size_t         len)
2658 {
2659 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)cookie;
2660 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
2661 	assert(shad->shad_live);
2662 	struct nstat_procdetails *procdetails = shad->shad_procdetails;
2663 	assert(procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
2664 
2665 	bool result = (*shad->shad_getvals_fn)(shad->shad_provider_context, NULL, NULL, NULL, data);
2666 
2667 	switch (shad->shad_provider) {
2668 	case NSTAT_PROVIDER_TCP_USERLAND:
2669 	{
2670 		nstat_tcp_descriptor *desc = (nstat_tcp_descriptor *)data;
2671 		desc->pid = procdetails->pdet_pid;
2672 		desc->upid = procdetails->pdet_upid;
2673 		uuid_copy(desc->uuid, procdetails->pdet_uuid);
2674 		strbufcpy(desc->pname, procdetails->pdet_procname);
2675 		if (shad->shad_rnf_override == nstat_rnf_override_enabled) {
2676 			desc->ifnet_properties |= NSTAT_IFNET_VIA_CELLFALLBACK;
2677 			desc->fallback_mode = SO_FALLBACK_MODE_FAST;
2678 		} else if (shad->shad_rnf_override == nstat_rnf_override_disabled) {
2679 			desc->ifnet_properties &= ~NSTAT_IFNET_VIA_CELLFALLBACK;
2680 			desc->fallback_mode = SO_FALLBACK_MODE_NONE;
2681 		}
2682 		desc->ifnet_properties |= (uint32_t)shad->shad_properties;
2683 		desc->start_timestamp = shad->shad_start_timestamp;
2684 		desc->timestamp = mach_continuous_time();
2685 	}
2686 	break;
2687 	case NSTAT_PROVIDER_UDP_USERLAND:
2688 	{
2689 		nstat_udp_descriptor *desc = (nstat_udp_descriptor *)data;
2690 		desc->pid = procdetails->pdet_pid;
2691 		desc->upid = procdetails->pdet_upid;
2692 		uuid_copy(desc->uuid, procdetails->pdet_uuid);
2693 		strbufcpy(desc->pname, procdetails->pdet_procname);
2694 		if (shad->shad_rnf_override == nstat_rnf_override_enabled) {
2695 			desc->ifnet_properties |= NSTAT_IFNET_VIA_CELLFALLBACK;
2696 			desc->fallback_mode = SO_FALLBACK_MODE_FAST;
2697 		} else if (shad->shad_rnf_override == nstat_rnf_override_disabled) {
2698 			desc->ifnet_properties &= ~NSTAT_IFNET_VIA_CELLFALLBACK;
2699 			desc->fallback_mode = SO_FALLBACK_MODE_NONE;
2700 		}
2701 		desc->ifnet_properties |= (uint32_t)shad->shad_properties;
2702 		desc->start_timestamp = shad->shad_start_timestamp;
2703 		desc->timestamp = mach_continuous_time();
2704 	}
2705 	break;
2706 	case NSTAT_PROVIDER_QUIC_USERLAND:
2707 	{
2708 		nstat_quic_descriptor *desc = (nstat_quic_descriptor *)data;
2709 		desc->pid = procdetails->pdet_pid;
2710 		desc->upid = procdetails->pdet_upid;
2711 		uuid_copy(desc->uuid, procdetails->pdet_uuid);
2712 		strbufcpy(desc->pname, procdetails->pdet_procname);
2713 		if (shad->shad_rnf_override == nstat_rnf_override_enabled) {
2714 			desc->ifnet_properties |= NSTAT_IFNET_VIA_CELLFALLBACK;
2715 			desc->fallback_mode = SO_FALLBACK_MODE_FAST;
2716 		} else if (shad->shad_rnf_override == nstat_rnf_override_disabled) {
2717 			desc->ifnet_properties &= ~NSTAT_IFNET_VIA_CELLFALLBACK;
2718 			desc->fallback_mode = SO_FALLBACK_MODE_NONE;
2719 		}
2720 		desc->ifnet_properties |= (uint32_t)shad->shad_properties;
2721 		desc->start_timestamp = shad->shad_start_timestamp;
2722 		desc->timestamp = mach_continuous_time();
2723 	}
2724 	break;
2725 	default:
2726 		break;
2727 	}
2728 	return (result)? 0 : EIO;
2729 }
2730 
2731 static void
nstat_userland_tu_release(__unused nstat_provider_cookie_t cookie,__unused int locked)2732 nstat_userland_tu_release(
2733 	__unused nstat_provider_cookie_t    cookie,
2734 	__unused int locked)
2735 {
2736 	// Called when a nstat_src is detached.
2737 	// We don't reference count or ask for delayed release so nothing to do here.
2738 	// Note that any associated nstat_tu_shadow may already have been released.
2739 }
2740 
2741 static bool
check_reporting_for_user(nstat_provider_filter * filter,pid_t pid,pid_t epid,uuid_t * uuid,uuid_t * euuid)2742 check_reporting_for_user(nstat_provider_filter *filter, pid_t pid, pid_t epid, uuid_t *uuid, uuid_t *euuid)
2743 {
2744 	bool retval = true;
2745 
2746 	if ((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER) != 0) {
2747 		retval = false;
2748 
2749 		if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_PID) != 0) &&
2750 		    (filter->npf_pid == pid)) {
2751 			retval = true;
2752 		} else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_EPID) != 0) &&
2753 		    (filter->npf_pid == epid)) {
2754 			retval = true;
2755 		} else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_UUID) != 0) &&
2756 		    (memcmp(filter->npf_uuid, uuid, sizeof(*uuid)) == 0)) {
2757 			retval = true;
2758 		} else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_EUUID) != 0) &&
2759 		    (memcmp(filter->npf_uuid, euuid, sizeof(*euuid)) == 0)) {
2760 			retval = true;
2761 		}
2762 	}
2763 	return retval;
2764 }
2765 
2766 static bool
nstat_userland_tcp_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,__unused u_int64_t suppression_flags)2767 nstat_userland_tcp_reporting_allowed(
2768 	nstat_provider_cookie_t cookie,
2769 	nstat_provider_filter *filter,
2770 	__unused u_int64_t suppression_flags)
2771 {
2772 	bool retval = true;
2773 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)cookie;
2774 
2775 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
2776 
2777 	if ((filter->npf_flags & NSTAT_FILTER_IFNET_FLAGS) != 0) {
2778 		u_int32_t ifflags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
2779 
2780 		if ((*shad->shad_getvals_fn)(shad->shad_provider_context, &ifflags, NULL, NULL, NULL)) {
2781 			if ((filter->npf_flags & ifflags) == 0) {
2782 				return false;
2783 			}
2784 		}
2785 	}
2786 
2787 	if ((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER) != 0) {
2788 		nstat_tcp_descriptor tcp_desc;  // Stack allocation - OK or pushing the limits too far?
2789 		if ((*shad->shad_getvals_fn)(shad->shad_provider_context, NULL, NULL, NULL, &tcp_desc)) {
2790 			retval = check_reporting_for_user(filter, (pid_t)tcp_desc.pid, (pid_t)tcp_desc.epid,
2791 			    &tcp_desc.uuid, &tcp_desc.euuid);
2792 		} else {
2793 			retval = false; // No further information, so might as well give up now.
2794 		}
2795 	}
2796 
2797 	return retval;
2798 }
2799 
2800 static size_t
nstat_userland_extensions(nstat_provider_cookie_t cookie,u_int32_t extension_id,void * __sized_by (len)buf,size_t len)2801 nstat_userland_extensions(nstat_provider_cookie_t cookie, u_int32_t extension_id, void *__sized_by(len)buf, size_t len)
2802 {
2803 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)cookie;
2804 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
2805 	assert(shad->shad_live);
2806 	assert(shad->shad_procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
2807 
2808 	return shad->shad_get_extension_fn(shad->shad_provider_context, extension_id, buf, len);
2809 }
2810 
2811 
2812 static bool
nstat_userland_udp_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,__unused u_int64_t suppression_flags)2813 nstat_userland_udp_reporting_allowed(
2814 	nstat_provider_cookie_t cookie,
2815 	nstat_provider_filter *filter,
2816 	__unused u_int64_t suppression_flags)
2817 {
2818 	bool retval = true;
2819 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)cookie;
2820 
2821 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
2822 
2823 	if ((filter->npf_flags & NSTAT_FILTER_IFNET_FLAGS) != 0) {
2824 		u_int32_t ifflags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
2825 
2826 		if ((*shad->shad_getvals_fn)(shad->shad_provider_context, &ifflags, NULL, NULL, NULL)) {
2827 			if ((filter->npf_flags & ifflags) == 0) {
2828 				return false;
2829 			}
2830 		}
2831 	}
2832 	if ((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER) != 0) {
2833 		nstat_udp_descriptor udp_desc;  // Stack allocation - OK or pushing the limits too far?
2834 		if ((*shad->shad_getvals_fn)(shad->shad_provider_context, NULL, NULL, NULL, &udp_desc)) {
2835 			retval = check_reporting_for_user(filter, (pid_t)udp_desc.pid, (pid_t)udp_desc.epid,
2836 			    &udp_desc.uuid, &udp_desc.euuid);
2837 		} else {
2838 			retval = false; // No further information, so might as well give up now.
2839 		}
2840 	}
2841 	return retval;
2842 }
2843 
2844 static bool
nstat_userland_quic_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,__unused u_int64_t suppression_flags)2845 nstat_userland_quic_reporting_allowed(
2846 	nstat_provider_cookie_t cookie,
2847 	nstat_provider_filter *filter,
2848 	__unused u_int64_t suppression_flags)
2849 {
2850 	bool retval = true;
2851 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)cookie;
2852 
2853 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
2854 
2855 	if ((filter->npf_flags & NSTAT_FILTER_IFNET_FLAGS) != 0) {
2856 		u_int32_t ifflags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
2857 
2858 		if ((*shad->shad_getvals_fn)(shad->shad_provider_context, &ifflags, NULL, NULL, NULL)) {
2859 			if ((filter->npf_flags & ifflags) == 0) {
2860 				return false;
2861 			}
2862 		}
2863 	}
2864 	if ((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER) != 0) {
2865 		nstat_quic_descriptor quic_desc;  // Stack allocation - OK or pushing the limits too far?
2866 		if ((*shad->shad_getvals_fn)(shad->shad_provider_context, NULL, NULL, NULL, &quic_desc)) {
2867 			retval = check_reporting_for_user(filter, (pid_t)quic_desc.pid, (pid_t)quic_desc.epid,
2868 			    &quic_desc.uuid, &quic_desc.euuid);
2869 		} else {
2870 			retval = false; // No further information, so might as well give up now.
2871 		}
2872 	}
2873 	return retval;
2874 }
2875 
2876 static errno_t
nstat_userland_protocol_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req,nstat_provider_type_t nstat_provider_type,nstat_provider * nstat_provider,u_int32_t * proto_watcher_cnt)2877 nstat_userland_protocol_add_watcher(
2878 	nstat_client            *client,
2879 	nstat_msg_add_all_srcs  *req,
2880 	nstat_provider_type_t   nstat_provider_type,
2881 	nstat_provider          *nstat_provider,
2882 	u_int32_t               *proto_watcher_cnt)
2883 {
2884 	errno_t result;
2885 
2886 	NSTAT_LOCK_EXCLUSIVE();
2887 	result = nstat_set_provider_filter(client, req);
2888 
2889 	if (result == 0) {
2890 		struct nstat_tu_shadow *shad;
2891 
2892 		OSIncrementAtomic(proto_watcher_cnt);
2893 
2894 		TAILQ_FOREACH(shad, &nstat_userprot_shad_head, shad_link) {
2895 			assert(shad->shad_magic == TU_SHADOW_MAGIC);
2896 
2897 			if ((shad->shad_provider == nstat_provider_type) && (shad->shad_live)) {
2898 				result = nstat_client_source_add(0, client, nstat_provider, shad, NSTAT_LOCK_HELD);
2899 				if (result != 0) {
2900 					NSTAT_LOG_ERROR("nstat_client_source_add returned %d for "
2901 					    "provider type: %d", result, nstat_provider_type);
2902 					break;
2903 				}
2904 			}
2905 		}
2906 	}
2907 	NSTAT_UNLOCK_EXCLUSIVE();
2908 
2909 	return result;
2910 }
2911 
2912 static errno_t
nstat_userland_tcp_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req)2913 nstat_userland_tcp_add_watcher(
2914 	nstat_client            *client,
2915 	nstat_msg_add_all_srcs  *req)
2916 {
2917 	return nstat_userland_protocol_add_watcher(client, req, NSTAT_PROVIDER_TCP_USERLAND,
2918 	           &nstat_userland_tcp_provider, &nstat_userland_tcp_watchers);
2919 }
2920 
2921 static errno_t
nstat_userland_udp_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req)2922 nstat_userland_udp_add_watcher(
2923 	nstat_client            *client,
2924 	nstat_msg_add_all_srcs *req)
2925 {
2926 	return nstat_userland_protocol_add_watcher(client, req, NSTAT_PROVIDER_UDP_USERLAND,
2927 	           &nstat_userland_udp_provider, &nstat_userland_udp_watchers);
2928 }
2929 
2930 static errno_t
nstat_userland_quic_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req)2931 nstat_userland_quic_add_watcher(
2932 	nstat_client            *client,
2933 	nstat_msg_add_all_srcs  *req)
2934 {
2935 	return nstat_userland_protocol_add_watcher(client, req, NSTAT_PROVIDER_QUIC_USERLAND,
2936 	           &nstat_userland_quic_provider, &nstat_userland_quic_watchers);
2937 }
2938 
2939 static void
nstat_userland_tcp_remove_watcher(__unused nstat_client * client)2940 nstat_userland_tcp_remove_watcher(
2941 	__unused nstat_client    *client)
2942 {
2943 	OSDecrementAtomic(&nstat_userland_tcp_watchers);
2944 }
2945 
2946 static void
nstat_userland_udp_remove_watcher(__unused nstat_client * client)2947 nstat_userland_udp_remove_watcher(
2948 	__unused nstat_client    *client)
2949 {
2950 	OSDecrementAtomic(&nstat_userland_udp_watchers);
2951 }
2952 
2953 static void
nstat_userland_quic_remove_watcher(__unused nstat_client * client)2954 nstat_userland_quic_remove_watcher(
2955 	__unused nstat_client    *client)
2956 {
2957 	OSDecrementAtomic(&nstat_userland_quic_watchers);
2958 }
2959 
2960 
2961 static void
nstat_init_userland_tcp_provider(void)2962 nstat_init_userland_tcp_provider(void)
2963 {
2964 	bzero(&nstat_userland_tcp_provider, sizeof(nstat_userland_tcp_provider));
2965 	nstat_userland_tcp_provider.nstat_descriptor_length = sizeof(nstat_tcp_descriptor);
2966 	nstat_userland_tcp_provider.nstat_provider_id = NSTAT_PROVIDER_TCP_USERLAND;
2967 	nstat_userland_tcp_provider.nstat_lookup = nstat_userland_tu_lookup;
2968 	nstat_userland_tcp_provider.nstat_gone = nstat_userland_tu_gone;
2969 	nstat_userland_tcp_provider.nstat_counts = nstat_userland_tu_counts;
2970 	nstat_userland_tcp_provider.nstat_release = nstat_userland_tu_release;
2971 	nstat_userland_tcp_provider.nstat_watcher_add = nstat_userland_tcp_add_watcher;
2972 	nstat_userland_tcp_provider.nstat_watcher_remove = nstat_userland_tcp_remove_watcher;
2973 	nstat_userland_tcp_provider.nstat_copy_descriptor = nstat_userland_tu_copy_descriptor;
2974 	nstat_userland_tcp_provider.nstat_reporting_allowed = nstat_userland_tcp_reporting_allowed;
2975 	nstat_userland_tcp_provider.nstat_copy_extension = nstat_userland_extensions;
2976 	nstat_userland_tcp_provider.next = nstat_providers;
2977 	nstat_providers = &nstat_userland_tcp_provider;
2978 }
2979 
2980 
2981 static void
nstat_init_userland_udp_provider(void)2982 nstat_init_userland_udp_provider(void)
2983 {
2984 	bzero(&nstat_userland_udp_provider, sizeof(nstat_userland_udp_provider));
2985 	nstat_userland_udp_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor);
2986 	nstat_userland_udp_provider.nstat_provider_id = NSTAT_PROVIDER_UDP_USERLAND;
2987 	nstat_userland_udp_provider.nstat_lookup = nstat_userland_tu_lookup;
2988 	nstat_userland_udp_provider.nstat_gone = nstat_userland_tu_gone;
2989 	nstat_userland_udp_provider.nstat_counts = nstat_userland_tu_counts;
2990 	nstat_userland_udp_provider.nstat_release = nstat_userland_tu_release;
2991 	nstat_userland_udp_provider.nstat_watcher_add = nstat_userland_udp_add_watcher;
2992 	nstat_userland_udp_provider.nstat_watcher_remove = nstat_userland_udp_remove_watcher;
2993 	nstat_userland_udp_provider.nstat_copy_descriptor = nstat_userland_tu_copy_descriptor;
2994 	nstat_userland_udp_provider.nstat_reporting_allowed = nstat_userland_udp_reporting_allowed;
2995 	nstat_userland_udp_provider.nstat_copy_extension = nstat_userland_extensions;
2996 	nstat_userland_udp_provider.next = nstat_providers;
2997 	nstat_providers = &nstat_userland_udp_provider;
2998 }
2999 
3000 static void
nstat_init_userland_quic_provider(void)3001 nstat_init_userland_quic_provider(void)
3002 {
3003 	bzero(&nstat_userland_quic_provider, sizeof(nstat_userland_quic_provider));
3004 	nstat_userland_quic_provider.nstat_descriptor_length = sizeof(nstat_quic_descriptor);
3005 	nstat_userland_quic_provider.nstat_provider_id = NSTAT_PROVIDER_QUIC_USERLAND;
3006 	nstat_userland_quic_provider.nstat_lookup = nstat_userland_tu_lookup;
3007 	nstat_userland_quic_provider.nstat_gone = nstat_userland_tu_gone;
3008 	nstat_userland_quic_provider.nstat_counts = nstat_userland_tu_counts;
3009 	nstat_userland_quic_provider.nstat_release = nstat_userland_tu_release;
3010 	nstat_userland_quic_provider.nstat_watcher_add = nstat_userland_quic_add_watcher;
3011 	nstat_userland_quic_provider.nstat_watcher_remove = nstat_userland_quic_remove_watcher;
3012 	nstat_userland_quic_provider.nstat_copy_descriptor = nstat_userland_tu_copy_descriptor;
3013 	nstat_userland_quic_provider.nstat_reporting_allowed = nstat_userland_quic_reporting_allowed;
3014 	nstat_userland_quic_provider.nstat_copy_extension = nstat_userland_extensions;
3015 	nstat_userland_quic_provider.next = nstat_providers;
3016 	nstat_providers = &nstat_userland_quic_provider;
3017 }
3018 
3019 
3020 // Things get started with a call to netstats to say that there’s a new connection:
3021 __private_extern__ nstat_userland_context
ntstat_userland_stats_open(userland_stats_provider_context * ctx,int provider_id,u_int64_t properties,userland_stats_request_vals_fn req_fn,userland_stats_request_extension_fn req_extension_fn)3022 ntstat_userland_stats_open(userland_stats_provider_context *ctx,
3023     int provider_id,
3024     u_int64_t properties,
3025     userland_stats_request_vals_fn req_fn,
3026     userland_stats_request_extension_fn req_extension_fn)
3027 {
3028 	struct nstat_tu_shadow *shad;
3029 	struct nstat_procdetails *procdetails;
3030 	nstat_provider *provider;
3031 
3032 	if ((provider_id != NSTAT_PROVIDER_TCP_USERLAND) &&
3033 	    (provider_id != NSTAT_PROVIDER_UDP_USERLAND) &&
3034 	    (provider_id != NSTAT_PROVIDER_QUIC_USERLAND)) {
3035 		NSTAT_LOG_ERROR("incorrect provider is supplied, %d", provider_id);
3036 		return NULL;
3037 	}
3038 
3039 	shad = kalloc_type(struct nstat_tu_shadow, Z_WAITOK | Z_NOFAIL);
3040 
3041 	procdetails = nstat_retain_curprocdetails();
3042 
3043 	if (procdetails == NULL) {
3044 		kfree_type(struct nstat_tu_shadow, shad);
3045 		return NULL;
3046 	}
3047 
3048 	shad->shad_getvals_fn         = req_fn;
3049 	shad->shad_get_extension_fn   = req_extension_fn;
3050 	shad->shad_provider_context   = ctx;
3051 	shad->shad_provider           = provider_id;
3052 	shad->shad_properties         = properties;
3053 	shad->shad_procdetails        = procdetails;
3054 	shad->shad_rnf_override       = nstat_rnf_override_not_set;
3055 	shad->shad_start_timestamp    = mach_continuous_time();
3056 	shad->shad_live               = true;
3057 	shad->shad_magic              = TU_SHADOW_MAGIC;
3058 
3059 	NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_tu_shad_allocs);
3060 	NSTAT_GLOBAL_COUNT_INCREMENT_WITH_MAX(nstat_global_tu_shad_current, nstat_global_tu_shad_max);
3061 
3062 	NSTAT_LOCK_EXCLUSIVE();
3063 	nstat_client *client;
3064 
3065 	// Even if there are no watchers, we save the shadow structure
3066 	TAILQ_INSERT_HEAD(&nstat_userprot_shad_head, shad, shad_link);
3067 
3068 	if (provider_id == NSTAT_PROVIDER_TCP_USERLAND) {
3069 		nstat_userland_tcp_shadows++;
3070 		provider = &nstat_userland_tcp_provider;
3071 	} else if (provider_id == NSTAT_PROVIDER_UDP_USERLAND) {
3072 		nstat_userland_udp_shadows++;
3073 		provider = &nstat_userland_udp_provider;
3074 	} else {
3075 		nstat_userland_quic_shadows++;
3076 		provider = &nstat_userland_quic_provider;
3077 	}
3078 
3079 	for (client = nstat_clients; client; client = client->ntc_next) {
3080 		if ((client->ntc_watching & (1 << provider_id)) != 0) {
3081 			// this client is watching tcp/udp/quic userland
3082 			// Link to it.
3083 			int result = nstat_client_source_add(0, client, provider, shad, NSTAT_LOCK_HELD);
3084 			if (result != 0) {
3085 				// There should be some kind of statistics for failures like this.
3086 				// <rdar://problem/31377195> The kernel ntstat component should keep some
3087 				// internal counters reflecting operational state for eventual AWD reporting
3088 			}
3089 		}
3090 	}
3091 	NSTAT_UNLOCK_EXCLUSIVE();
3092 
3093 	return (nstat_userland_context)shad;
3094 }
3095 
3096 
3097 __private_extern__ void
ntstat_userland_stats_close(nstat_userland_context nstat_ctx)3098 ntstat_userland_stats_close(nstat_userland_context nstat_ctx)
3099 {
3100 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)nstat_ctx;
3101 	tailq_head_nstat_src dead_list;
3102 	nstat_src *src;
3103 
3104 	if (shad == NULL) {
3105 		return;
3106 	}
3107 
3108 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
3109 	TAILQ_INIT(&dead_list);
3110 
3111 	NSTAT_LOCK_EXCLUSIVE();
3112 	if (nstat_userland_udp_watchers != 0 ||
3113 	    nstat_userland_tcp_watchers != 0 ||
3114 	    nstat_userland_quic_watchers != 0) {
3115 		nstat_client *client;
3116 		errno_t result;
3117 
3118 		for (client = nstat_clients; client; client = client->ntc_next) {
3119 			TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
3120 			{
3121 				if (shad == (struct nstat_tu_shadow *)src->nts_cookie) {
3122 					nstat_provider_id_t provider_id = src->nts_provider->nstat_provider_id;
3123 					if (provider_id == NSTAT_PROVIDER_TCP_USERLAND ||
3124 					    provider_id == NSTAT_PROVIDER_UDP_USERLAND ||
3125 					    provider_id == NSTAT_PROVIDER_QUIC_USERLAND) {
3126 						break;
3127 					}
3128 				}
3129 			}
3130 
3131 			if (src) {
3132 				result = nstat_client_send_goodbye(client, src);
3133 
3134 				TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
3135 				TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
3136 			}
3137 		}
3138 	}
3139 	TAILQ_REMOVE(&nstat_userprot_shad_head, shad, shad_link);
3140 
3141 	if (shad->shad_live) {
3142 		if (shad->shad_provider == NSTAT_PROVIDER_TCP_USERLAND) {
3143 			nstat_userland_tcp_shadows--;
3144 		} else if (shad->shad_provider == NSTAT_PROVIDER_UDP_USERLAND) {
3145 			nstat_userland_udp_shadows--;
3146 		} else {
3147 			nstat_userland_quic_shadows--;
3148 		}
3149 	}
3150 
3151 	NSTAT_UNLOCK_EXCLUSIVE();
3152 
3153 	while ((src = TAILQ_FIRST(&dead_list))) {
3154 		TAILQ_REMOVE(&dead_list, src, nts_client_link);
3155 		nstat_client_cleanup_source(NULL, src, TRUE);
3156 	}
3157 	nstat_release_procdetails(shad->shad_procdetails);
3158 	shad->shad_magic = TU_SHADOW_UNMAGIC;
3159 	NSTAT_GLOBAL_COUNT_DECREMENT(nstat_global_tu_shad_current);
3160 	kfree_type(struct nstat_tu_shadow, shad);
3161 }
3162 
3163 static void
ntstat_userland_stats_event_locked(struct nstat_tu_shadow * shad,uint64_t event)3164 ntstat_userland_stats_event_locked(
3165 	struct nstat_tu_shadow *shad,
3166 	uint64_t event)
3167 {
3168 	nstat_client *client;
3169 	nstat_src *src;
3170 	errno_t result;
3171 	nstat_provider_id_t provider_id;
3172 
3173 	NSTAT_ASSERT_LOCKED_EXCLUSIVE();
3174 
3175 	if (nstat_userland_udp_watchers != 0 || nstat_userland_tcp_watchers != 0 || nstat_userland_quic_watchers != 0) {
3176 		for (client = nstat_clients; client; client = client->ntc_next) {
3177 			if (((client->ntc_provider_filters[NSTAT_PROVIDER_TCP_USERLAND].npf_events & event) == 0) &&
3178 			    ((client->ntc_provider_filters[NSTAT_PROVIDER_UDP_USERLAND].npf_events & event) == 0) &&
3179 			    ((client->ntc_provider_filters[NSTAT_PROVIDER_QUIC_USERLAND].npf_events & event) == 0)) {
3180 				continue;
3181 			}
3182 			TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link) {
3183 				provider_id = src->nts_provider->nstat_provider_id;
3184 				if (provider_id == NSTAT_PROVIDER_TCP_USERLAND || provider_id == NSTAT_PROVIDER_UDP_USERLAND ||
3185 				    provider_id == NSTAT_PROVIDER_QUIC_USERLAND) {
3186 					if (shad == (struct nstat_tu_shadow *)src->nts_cookie) {
3187 						break;
3188 					}
3189 				}
3190 			}
3191 			if (src && ((client->ntc_provider_filters[provider_id].npf_events & event) != 0)) {
3192 				result = nstat_client_send_event(client, src, event);
3193 			}
3194 		}
3195 	}
3196 }
3197 
3198 __private_extern__ void
ntstat_userland_stats_event(nstat_userland_context nstat_ctx,uint64_t event)3199 ntstat_userland_stats_event(
3200 	nstat_userland_context nstat_ctx,
3201 	uint64_t event)
3202 {
3203 	// This will need refinement for when we do genuine stats filtering
3204 	// See <rdar://problem/23022832> NetworkStatistics should provide opt-in notifications
3205 	// For now it deals only with events that potentially cause any traditional netstat sources to be closed
3206 
3207 	struct nstat_tu_shadow *shad = (struct nstat_tu_shadow *)nstat_ctx;
3208 	tailq_head_nstat_src dead_list;
3209 	nstat_src *src;
3210 
3211 	if (shad == NULL) {
3212 		return;
3213 	}
3214 
3215 	assert(shad->shad_magic == TU_SHADOW_MAGIC);
3216 
3217 	if (event & NECP_CLIENT_STATISTICS_EVENT_TIME_WAIT) {
3218 		TAILQ_INIT(&dead_list);
3219 
3220 		NSTAT_LOCK_EXCLUSIVE();
3221 		if (nstat_userland_udp_watchers != 0 ||
3222 		    nstat_userland_tcp_watchers != 0 ||
3223 		    nstat_userland_quic_watchers != 0) {
3224 			nstat_client *client;
3225 			errno_t result;
3226 
3227 			for (client = nstat_clients; client; client = client->ntc_next) {
3228 				TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
3229 				{
3230 					if (shad == (struct nstat_tu_shadow *)src->nts_cookie) {
3231 						break;
3232 					}
3233 				}
3234 
3235 				if (src) {
3236 					if (!(src->nts_filter & NSTAT_FILTER_TCP_NO_EARLY_CLOSE)) {
3237 						result = nstat_client_send_goodbye(client, src);
3238 
3239 						TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
3240 						TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
3241 					}
3242 				}
3243 			}
3244 		}
3245 		NSTAT_UNLOCK_EXCLUSIVE();
3246 
3247 		while ((src = TAILQ_FIRST(&dead_list))) {
3248 			TAILQ_REMOVE(&dead_list, src, nts_client_link);
3249 			nstat_client_cleanup_source(NULL, src, TRUE);
3250 		}
3251 	}
3252 }
3253 
3254 __private_extern__ void
nstats_userland_stats_defunct_for_process(int pid)3255 nstats_userland_stats_defunct_for_process(int pid)
3256 {
3257 	// Note that this can be called multiple times for the same process
3258 	tailq_head_nstat_src dead_list;
3259 	nstat_src *src, *tmpsrc;
3260 	struct nstat_tu_shadow *shad;
3261 
3262 	TAILQ_INIT(&dead_list);
3263 
3264 	NSTAT_LOCK_EXCLUSIVE();
3265 
3266 	if (nstat_userland_udp_watchers != 0 ||
3267 	    nstat_userland_tcp_watchers != 0 ||
3268 	    nstat_userland_quic_watchers != 0) {
3269 		nstat_client *client;
3270 		errno_t result;
3271 
3272 		for (client = nstat_clients; client; client = client->ntc_next) {
3273 			TAILQ_FOREACH_SAFE(src, &client->ntc_src_queue, nts_client_link, tmpsrc)
3274 			{
3275 				nstat_provider_id_t provider_id = src->nts_provider->nstat_provider_id;
3276 				if (provider_id == NSTAT_PROVIDER_TCP_USERLAND ||
3277 				    provider_id == NSTAT_PROVIDER_UDP_USERLAND ||
3278 				    provider_id == NSTAT_PROVIDER_QUIC_USERLAND) {
3279 					shad = (struct nstat_tu_shadow *)src->nts_cookie;
3280 					if (shad->shad_procdetails->pdet_pid == pid) {
3281 						result = nstat_client_send_goodbye(client, src);
3282 
3283 						TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
3284 						TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
3285 					}
3286 				}
3287 			}
3288 		}
3289 	}
3290 
3291 	TAILQ_FOREACH(shad, &nstat_userprot_shad_head, shad_link) {
3292 		assert(shad->shad_magic == TU_SHADOW_MAGIC);
3293 
3294 		if (shad->shad_live) {
3295 			if (shad->shad_procdetails->pdet_pid == pid) {
3296 				shad->shad_live = false;
3297 				if (shad->shad_provider == NSTAT_PROVIDER_TCP_USERLAND) {
3298 					nstat_userland_tcp_shadows--;
3299 				} else if (shad->shad_provider == NSTAT_PROVIDER_UDP_USERLAND) {
3300 					nstat_userland_udp_shadows--;
3301 				} else {
3302 					nstat_userland_quic_shadows--;
3303 				}
3304 			}
3305 		}
3306 	}
3307 
3308 	NSTAT_UNLOCK_EXCLUSIVE();
3309 
3310 	while ((src = TAILQ_FIRST(&dead_list))) {
3311 		TAILQ_REMOVE(&dead_list, src, nts_client_link);
3312 		nstat_client_cleanup_source(NULL, src, TRUE);
3313 	}
3314 }
3315 
3316 errno_t
nstat_userland_mark_rnf_override(uuid_t target_fuuid,bool rnf_override)3317 nstat_userland_mark_rnf_override(uuid_t target_fuuid, bool rnf_override)
3318 {
3319 	// Note that this can be called multiple times for the same process
3320 	struct nstat_tu_shadow *shad;
3321 	uuid_t fuuid;
3322 	errno_t result;
3323 
3324 	NSTAT_LOCK_EXCLUSIVE();
3325 	// We set the fallback state regardles of watchers as there may be future ones that need to know
3326 	TAILQ_FOREACH(shad, &nstat_userprot_shad_head, shad_link) {
3327 		assert(shad->shad_magic == TU_SHADOW_MAGIC);
3328 		assert(shad->shad_procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
3329 		if (shad->shad_get_extension_fn(shad->shad_provider_context, NSTAT_EXTENDED_UPDATE_TYPE_FUUID, fuuid, sizeof(fuuid))) {
3330 			if (uuid_compare(fuuid, target_fuuid) == 0) {
3331 				break;
3332 			}
3333 		}
3334 	}
3335 	if (shad) {
3336 		if (shad->shad_procdetails->pdet_pid != proc_selfpid()) {
3337 			result = EPERM;
3338 		} else {
3339 			result = 0;
3340 			// It would be possible but awkward to check the previous value
3341 			// for RNF override, and send an event only if changed.
3342 			// In practice it's fine to send an event regardless,
3343 			// which "pushes" the last statistics for the previous mode
3344 			shad->shad_rnf_override = rnf_override ? nstat_rnf_override_enabled
3345 			    : nstat_rnf_override_disabled;
3346 			ntstat_userland_stats_event_locked(shad,
3347 			    rnf_override ? NSTAT_EVENT_SRC_ENTER_CELLFALLBACK
3348 			    : NSTAT_EVENT_SRC_EXIT_CELLFALLBACK);
3349 		}
3350 	} else {
3351 		result = EEXIST;
3352 	}
3353 
3354 	NSTAT_UNLOCK_EXCLUSIVE();
3355 
3356 	return result;
3357 }
3358 
3359 #pragma mark -- Generic Providers --
3360 
3361 static nstat_provider   nstat_userland_conn_provider;
3362 static nstat_provider   nstat_udp_subflow_provider;
3363 
3364 static u_int32_t    nstat_generic_provider_watchers[NSTAT_PROVIDER_COUNT];
3365 
3366 struct nstat_generic_shadow {
3367 	tailq_entry_generic_shadow              gshad_link;
3368 	nstat_provider_context                  gshad_provider_context;
3369 	nstat_provider_request_vals_fn          *gshad_getvals_fn;
3370 	nstat_provider_request_extensions_fn    *gshad_getextensions_fn;
3371 	u_int64_t                               gshad_properties;
3372 	u_int64_t                               gshad_start_timestamp;
3373 	struct nstat_procdetails                *gshad_procdetails;
3374 	nstat_provider_id_t                     gshad_provider;
3375 	int32_t                                 gshad_refcnt;
3376 	uint32_t                                gshad_magic;
3377 };
3378 
3379 // Magic number checking should remain in place until the userland provider has been fully proven
3380 #define NSTAT_GENERIC_SHADOW_MAGIC             0xfadef00d
3381 #define NSTAT_GENERIC_SHADOW_UNMAGIC           0xfadedead
3382 
3383 static tailq_head_generic_shadow nstat_gshad_head = TAILQ_HEAD_INITIALIZER(nstat_gshad_head);
3384 
3385 static inline void
nstat_retain_gshad(struct nstat_generic_shadow * gshad)3386 nstat_retain_gshad(
3387 	struct nstat_generic_shadow *gshad)
3388 {
3389 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3390 
3391 	OSIncrementAtomic(&gshad->gshad_refcnt);
3392 }
3393 
3394 static void
nstat_release_gshad(struct nstat_generic_shadow * gshad)3395 nstat_release_gshad(
3396 	struct nstat_generic_shadow *gshad)
3397 {
3398 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3399 
3400 	if (OSDecrementAtomic(&gshad->gshad_refcnt) == 1) {
3401 		nstat_release_procdetails(gshad->gshad_procdetails);
3402 		gshad->gshad_magic = NSTAT_GENERIC_SHADOW_UNMAGIC;
3403 		NSTAT_GLOBAL_COUNT_DECREMENT(nstat_global_gshad_current);
3404 		kfree_type(struct nstat_generic_shadow, gshad);
3405 	}
3406 }
3407 
3408 static errno_t
nstat_generic_provider_lookup(__unused const void * data,__unused u_int32_t length,__unused nstat_provider_cookie_t * out_cookie)3409 nstat_generic_provider_lookup(
3410 	__unused const void                 *data,
3411 	__unused u_int32_t                  length,
3412 	__unused nstat_provider_cookie_t    *out_cookie)
3413 {
3414 	// Looking up a specific connection is not supported
3415 	return ENOTSUP;
3416 }
3417 
3418 static int
nstat_generic_provider_gone(__unused nstat_provider_cookie_t cookie)3419 nstat_generic_provider_gone(
3420 	__unused nstat_provider_cookie_t    cookie)
3421 {
3422 	// Returns non-zero if the source has gone.
3423 	// We don't keep a source hanging around, so the answer is always 0
3424 	return 0;
3425 }
3426 
3427 static errno_t
nstat_generic_provider_counts(nstat_provider_cookie_t cookie,struct nstat_counts * out_counts,int * out_gone)3428 nstat_generic_provider_counts(
3429 	nstat_provider_cookie_t cookie,
3430 	struct nstat_counts     *out_counts,
3431 	int                     *out_gone)
3432 {
3433 	struct nstat_generic_shadow *gshad = (struct nstat_generic_shadow *)cookie;
3434 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3435 
3436 	memset(out_counts, 0, sizeof(*out_counts));
3437 
3438 	bool result = (*gshad->gshad_getvals_fn)(gshad->gshad_provider_context, NULL, out_counts, NULL);
3439 
3440 	if (out_gone) {
3441 		*out_gone = 0;
3442 	}
3443 	return (result)? 0 : EIO;
3444 }
3445 
3446 
3447 static errno_t
nstat_generic_provider_copy_descriptor(nstat_provider_cookie_t cookie,void * __sized_by (len)data,__unused size_t len)3448 nstat_generic_provider_copy_descriptor(
3449 	nstat_provider_cookie_t cookie,
3450 	void                    *__sized_by(len)data,
3451 	__unused size_t         len)
3452 {
3453 	struct nstat_generic_shadow *gshad = (struct nstat_generic_shadow *)cookie;
3454 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3455 	struct nstat_procdetails *procdetails = gshad->gshad_procdetails;
3456 	assert(procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
3457 
3458 	bool result = (*gshad->gshad_getvals_fn)(gshad->gshad_provider_context, NULL, NULL, data);
3459 
3460 	switch (gshad->gshad_provider) {
3461 	case NSTAT_PROVIDER_CONN_USERLAND:
3462 	{
3463 		nstat_connection_descriptor *desc = (nstat_connection_descriptor *)data;
3464 		desc->pid = procdetails->pdet_pid;
3465 		desc->upid = procdetails->pdet_upid;
3466 		uuid_copy(desc->uuid, procdetails->pdet_uuid);
3467 		strbufcpy(desc->pname, procdetails->pdet_procname);
3468 		desc->start_timestamp = gshad->gshad_start_timestamp;
3469 		desc->timestamp = mach_continuous_time();
3470 		break;
3471 	}
3472 	case NSTAT_PROVIDER_UDP_SUBFLOW:
3473 	{
3474 		nstat_udp_descriptor *desc = (nstat_udp_descriptor *)data;
3475 		desc->pid = procdetails->pdet_pid;
3476 		desc->upid = procdetails->pdet_upid;
3477 		uuid_copy(desc->uuid, procdetails->pdet_uuid);
3478 		strbufcpy(desc->pname, procdetails->pdet_procname);
3479 		desc->start_timestamp = gshad->gshad_start_timestamp;
3480 		desc->timestamp = mach_continuous_time();
3481 		break;
3482 	}
3483 	default:
3484 		break;
3485 	}
3486 	return (result)? 0 : EIO;
3487 }
3488 
3489 static void
nstat_generic_provider_release(__unused nstat_provider_cookie_t cookie,__unused int locked)3490 nstat_generic_provider_release(
3491 	__unused nstat_provider_cookie_t    cookie,
3492 	__unused int locked)
3493 {
3494 	// Called when a nstat_src is detached.
3495 	struct nstat_generic_shadow *gshad = (struct nstat_generic_shadow *)cookie;
3496 
3497 	nstat_release_gshad(gshad);
3498 }
3499 
3500 static bool
nstat_generic_provider_reporting_allowed(nstat_provider_cookie_t cookie,nstat_provider_filter * filter,u_int64_t suppression_flags)3501 nstat_generic_provider_reporting_allowed(
3502 	nstat_provider_cookie_t cookie,
3503 	nstat_provider_filter *filter,
3504 	u_int64_t suppression_flags)
3505 {
3506 	struct nstat_generic_shadow *gshad = (struct nstat_generic_shadow *)cookie;
3507 
3508 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3509 
3510 	if ((filter->npf_flags & NSTAT_FILTER_SUPPRESS_BORING_FLAGS) != 0) {
3511 		if ((filter->npf_flags & suppression_flags) != 0) {
3512 			return false;
3513 		}
3514 	}
3515 
3516 	// Filter based on interface and connection flags
3517 	// If a provider doesn't support flags, a client shouldn't attempt to use filtering
3518 	if ((filter->npf_flags & NSTAT_FILTER_IFNET_AND_CONN_FLAGS) != 0) {
3519 		u_int32_t ifflags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
3520 
3521 		if ((*gshad->gshad_getvals_fn)(gshad->gshad_provider_context, &ifflags, NULL, NULL)) {
3522 			if ((filter->npf_flags & ifflags) == 0) {
3523 				return false;
3524 			}
3525 		}
3526 	}
3527 
3528 	if ((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER) != 0) {
3529 		struct nstat_procdetails *procdetails = gshad->gshad_procdetails;
3530 		assert(procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
3531 
3532 		// Check details that we have readily to hand before asking the provider for descriptor items
3533 		if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_PID) != 0) &&
3534 		    (filter->npf_pid == procdetails->pdet_pid)) {
3535 			return true;
3536 		}
3537 		if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_UUID) != 0) &&
3538 		    (memcmp(filter->npf_uuid, &procdetails->pdet_uuid, sizeof(filter->npf_uuid)) == 0)) {
3539 			return true;
3540 		}
3541 		if ((filter->npf_flags & (NSTAT_FILTER_SPECIFIC_USER_BY_EPID | NSTAT_FILTER_SPECIFIC_USER_BY_EUUID)) != 0) {
3542 			nstat_udp_descriptor udp_desc;  // Stack allocation - OK or pushing the limits too far?
3543 			switch (gshad->gshad_provider) {
3544 			case NSTAT_PROVIDER_CONN_USERLAND:
3545 				// Filtering by effective uuid or effective pid is currently not supported
3546 				filter->npf_flags &= ~((uint64_t)(NSTAT_FILTER_SPECIFIC_USER_BY_EPID | NSTAT_FILTER_SPECIFIC_USER_BY_EUUID));
3547 				NSTAT_LOG("attempt to filter conn provider by effective pid/uuid, not supported");
3548 				return true;
3549 
3550 			case NSTAT_PROVIDER_UDP_SUBFLOW:
3551 				if ((*gshad->gshad_getvals_fn)(gshad->gshad_provider_context, NULL, NULL, &udp_desc)) {
3552 					if (check_reporting_for_user(filter, procdetails->pdet_pid, (pid_t)udp_desc.epid,
3553 					    &procdetails->pdet_uuid, &udp_desc.euuid)) {
3554 						return true;
3555 					}
3556 				}
3557 				break;
3558 			default:
3559 				break;
3560 			}
3561 		}
3562 		return false;
3563 	}
3564 	return true;
3565 }
3566 
3567 static size_t
nstat_generic_extensions(nstat_provider_cookie_t cookie,u_int32_t extension_id,void * __sized_by (len)buf,size_t len)3568 nstat_generic_extensions(nstat_provider_cookie_t cookie, u_int32_t extension_id, void *__sized_by(len)buf, size_t len)
3569 {
3570 	struct nstat_generic_shadow *gshad = (struct nstat_generic_shadow *)cookie;
3571 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3572 	assert(gshad->gshad_procdetails->pdet_magic == NSTAT_PROCDETAILS_MAGIC);
3573 
3574 	if (gshad->gshad_getextensions_fn == NULL) {
3575 		return 0;
3576 	}
3577 	return gshad->gshad_getextensions_fn(gshad->gshad_provider_context, extension_id, buf, len);
3578 }
3579 
3580 static errno_t
nstat_generic_provider_add_watcher(nstat_client * client,nstat_msg_add_all_srcs * req)3581 nstat_generic_provider_add_watcher(
3582 	nstat_client            *client,
3583 	nstat_msg_add_all_srcs  *req)
3584 {
3585 	errno_t result;
3586 	nstat_provider_id_t  provider_id = req->provider;
3587 	nstat_provider *provider;
3588 
3589 	switch (provider_id) {
3590 	case NSTAT_PROVIDER_CONN_USERLAND:
3591 		provider = &nstat_userland_conn_provider;
3592 		break;
3593 	case NSTAT_PROVIDER_UDP_SUBFLOW:
3594 		provider = &nstat_udp_subflow_provider;
3595 		break;
3596 	default:
3597 		return ENOTSUP;
3598 	}
3599 
3600 	NSTAT_LOCK_EXCLUSIVE();
3601 	result = nstat_set_provider_filter(client, req);
3602 
3603 	if (result == 0) {
3604 		struct nstat_generic_shadow *gshad;
3605 		nstat_provider_filter *filter = &client->ntc_provider_filters[provider_id];
3606 
3607 		OSIncrementAtomic(&nstat_generic_provider_watchers[provider_id]);
3608 
3609 		TAILQ_FOREACH(gshad, &nstat_gshad_head, gshad_link) {
3610 			assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3611 
3612 			if (gshad->gshad_provider == provider_id) {
3613 				if (filter->npf_flags & NSTAT_FILTER_INITIAL_PROPERTIES) {
3614 					u_int64_t npf_flags = filter->npf_flags & NSTAT_FILTER_IFNET_AND_CONN_FLAGS;
3615 					if ((npf_flags != 0) && ((npf_flags & gshad->gshad_properties) == 0)) {
3616 						// Skip this one
3617 						// Note - no filtering by pid or UUID supported at this point, for simplicity
3618 						continue;
3619 					}
3620 				}
3621 				nstat_retain_gshad(gshad);
3622 				result = nstat_client_source_add(0, client, provider, gshad, NSTAT_LOCK_HELD);
3623 				if (result != 0) {
3624 					NSTAT_LOG_ERROR("nstat_client_source_add returned %d for "
3625 					    "provider type: %d", result, provider_id);
3626 					nstat_release_gshad(gshad);
3627 					break;
3628 				}
3629 			}
3630 		}
3631 	}
3632 	NSTAT_UNLOCK_EXCLUSIVE();
3633 
3634 	return result;
3635 }
3636 
3637 static void
nstat_userland_conn_remove_watcher(__unused nstat_client * client)3638 nstat_userland_conn_remove_watcher(
3639 	__unused nstat_client    *client)
3640 {
3641 	OSDecrementAtomic(&nstat_generic_provider_watchers[NSTAT_PROVIDER_CONN_USERLAND]);
3642 }
3643 
3644 static void
nstat_udp_subflow_remove_watcher(__unused nstat_client * client)3645 nstat_udp_subflow_remove_watcher(
3646 	__unused nstat_client    *client)
3647 {
3648 	OSDecrementAtomic(&nstat_generic_provider_watchers[NSTAT_PROVIDER_UDP_SUBFLOW]);
3649 }
3650 
3651 static void
nstat_init_userland_conn_provider(void)3652 nstat_init_userland_conn_provider(void)
3653 {
3654 	bzero(&nstat_userland_conn_provider, sizeof(nstat_userland_conn_provider));
3655 	nstat_userland_conn_provider.nstat_descriptor_length = sizeof(nstat_connection_descriptor);
3656 	nstat_userland_conn_provider.nstat_provider_id = NSTAT_PROVIDER_CONN_USERLAND;
3657 	nstat_userland_conn_provider.nstat_lookup = nstat_generic_provider_lookup;
3658 	nstat_userland_conn_provider.nstat_gone = nstat_generic_provider_gone;
3659 	nstat_userland_conn_provider.nstat_counts = nstat_generic_provider_counts;
3660 	nstat_userland_conn_provider.nstat_release = nstat_generic_provider_release;
3661 	nstat_userland_conn_provider.nstat_watcher_add = nstat_generic_provider_add_watcher;
3662 	nstat_userland_conn_provider.nstat_watcher_remove = nstat_userland_conn_remove_watcher;
3663 	nstat_userland_conn_provider.nstat_copy_descriptor = nstat_generic_provider_copy_descriptor;
3664 	nstat_userland_conn_provider.nstat_reporting_allowed = nstat_generic_provider_reporting_allowed;
3665 	nstat_userland_conn_provider.nstat_copy_extension = nstat_generic_extensions;
3666 	nstat_userland_conn_provider.next = nstat_providers;
3667 	nstat_providers = &nstat_userland_conn_provider;
3668 }
3669 
3670 static void
nstat_init_udp_subflow_provider(void)3671 nstat_init_udp_subflow_provider(void)
3672 {
3673 	bzero(&nstat_udp_subflow_provider, sizeof(nstat_udp_subflow_provider));
3674 	nstat_udp_subflow_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor);
3675 	nstat_udp_subflow_provider.nstat_provider_id = NSTAT_PROVIDER_UDP_SUBFLOW;
3676 	nstat_udp_subflow_provider.nstat_lookup = nstat_generic_provider_lookup;
3677 	nstat_udp_subflow_provider.nstat_gone = nstat_generic_provider_gone;
3678 	nstat_udp_subflow_provider.nstat_counts = nstat_generic_provider_counts;
3679 	nstat_udp_subflow_provider.nstat_release = nstat_generic_provider_release;
3680 	nstat_udp_subflow_provider.nstat_watcher_add = nstat_generic_provider_add_watcher;
3681 	nstat_udp_subflow_provider.nstat_watcher_remove = nstat_udp_subflow_remove_watcher;
3682 	nstat_udp_subflow_provider.nstat_copy_descriptor = nstat_generic_provider_copy_descriptor;
3683 	nstat_udp_subflow_provider.nstat_reporting_allowed = nstat_generic_provider_reporting_allowed;
3684 	nstat_udp_subflow_provider.nstat_copy_extension = nstat_generic_extensions;
3685 	nstat_udp_subflow_provider.next = nstat_providers;
3686 	nstat_providers = &nstat_udp_subflow_provider;
3687 }
3688 
3689 // Things get started with a call from the provider to netstats to say that there’s a new source
3690 __private_extern__ nstat_context
nstat_provider_stats_open(nstat_provider_context ctx,int provider_id,u_int64_t properties,nstat_provider_request_vals_fn req_fn,nstat_provider_request_extensions_fn req_extensions_fn)3691 nstat_provider_stats_open(nstat_provider_context ctx,
3692     int provider_id,
3693     u_int64_t properties,
3694     nstat_provider_request_vals_fn req_fn,
3695     nstat_provider_request_extensions_fn req_extensions_fn)
3696 {
3697 	struct nstat_generic_shadow *gshad;
3698 	struct nstat_procdetails *procdetails;
3699 	nstat_provider *provider = nstat_find_provider_by_id(provider_id);
3700 
3701 	gshad = kalloc_type(struct nstat_generic_shadow, Z_WAITOK | Z_NOFAIL);
3702 
3703 	procdetails = nstat_retain_curprocdetails();
3704 
3705 	if (procdetails == NULL) {
3706 		kfree_type(struct nstat_generic_shadow, gshad);
3707 		return NULL;
3708 	}
3709 
3710 	gshad->gshad_getvals_fn         = req_fn;
3711 	gshad->gshad_getextensions_fn   = req_extensions_fn;
3712 	gshad->gshad_provider_context   = ctx;
3713 	gshad->gshad_properties         = properties;
3714 	gshad->gshad_procdetails        = procdetails;
3715 	gshad->gshad_provider           = provider_id;
3716 	gshad->gshad_start_timestamp    = mach_continuous_time();
3717 	gshad->gshad_refcnt             = 0;
3718 	gshad->gshad_magic              = NSTAT_GENERIC_SHADOW_MAGIC;
3719 	nstat_retain_gshad(gshad);
3720 	NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_gshad_allocs);
3721 	NSTAT_GLOBAL_COUNT_INCREMENT_WITH_MAX(nstat_global_gshad_current, nstat_global_gshad_max);
3722 
3723 	NSTAT_LOCK_EXCLUSIVE();
3724 	nstat_client     *client;
3725 
3726 	// Even if there are no watchers, we save the shadow structure
3727 	TAILQ_INSERT_HEAD(&nstat_gshad_head, gshad, gshad_link);
3728 
3729 	for (client = nstat_clients; client; client = client->ntc_next) {
3730 		if ((client->ntc_watching & (1 << provider_id)) != 0) {
3731 			// Does this client want an initial filtering to be made?
3732 			u_int64_t npf_flags = client->ntc_provider_filters[provider->nstat_provider_id].npf_flags;
3733 			if (npf_flags & NSTAT_FILTER_INITIAL_PROPERTIES) {
3734 				npf_flags &= NSTAT_FILTER_IFNET_AND_CONN_FLAGS;
3735 				if ((npf_flags != 0) && ((npf_flags & properties) == 0)) {
3736 					// Skip this one
3737 					// Note - no filtering by pid or UUID supported at this point, for simplicity
3738 					continue;
3739 				}
3740 			}
3741 			// this client is watching, so link to it.
3742 			nstat_retain_gshad(gshad);
3743 			int result = nstat_client_source_add(0, client, provider, gshad, NSTAT_LOCK_HELD);
3744 			if (result != 0) {
3745 				// There should be some kind of statistics for failures like this.
3746 				// <rdar://problem/31377195> The kernel ntstat component should keep some
3747 				// internal counters reflecting operational state for eventual AWD reporting
3748 				nstat_release_gshad(gshad);
3749 			}
3750 		}
3751 	}
3752 	NSTAT_UNLOCK_EXCLUSIVE();
3753 
3754 	return (nstat_context) gshad;
3755 }
3756 
3757 
3758 // When the source is closed, netstats will make one last call on the request functions to retrieve final values
3759 __private_extern__ void
nstat_provider_stats_close(nstat_context nstat_ctx)3760 nstat_provider_stats_close(nstat_context nstat_ctx)
3761 {
3762 	tailq_head_nstat_src dead_list;
3763 	nstat_src *src;
3764 	struct nstat_generic_shadow *gshad = (struct nstat_generic_shadow *)nstat_ctx;
3765 
3766 	if (gshad == NULL) {
3767 		NSTAT_LOG_ERROR("called with null reference");
3768 		return;
3769 	}
3770 
3771 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3772 
3773 	if (gshad->gshad_magic != NSTAT_GENERIC_SHADOW_MAGIC) {
3774 		NSTAT_LOG_ERROR("called with incorrect shadow magic 0x%x", gshad->gshad_magic);
3775 	}
3776 
3777 	TAILQ_INIT(&dead_list);
3778 
3779 	NSTAT_LOCK_EXCLUSIVE();
3780 
3781 	TAILQ_REMOVE(&nstat_gshad_head, gshad, gshad_link);
3782 
3783 	int32_t num_srcs = gshad->gshad_refcnt - 1;
3784 	if ((nstat_generic_provider_watchers[gshad->gshad_provider] != 0) && (num_srcs > 0)) {
3785 		nstat_client *client;
3786 		errno_t result;
3787 
3788 		for (client = nstat_clients; client; client = client->ntc_next) {
3789 			// Only scan further if this client is watching
3790 			if ((client->ntc_watching & (1 << gshad->gshad_provider)) != 0) {
3791 				TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
3792 				{
3793 					if ((gshad == (struct nstat_generic_shadow *)src->nts_cookie) &&
3794 					    (gshad->gshad_provider == src->nts_provider->nstat_provider_id)) {
3795 						break;
3796 					}
3797 				}
3798 				if (src) {
3799 					result = nstat_client_send_goodbye(client, src);
3800 					// There is currently no recovery possible from failure to send,
3801 					// so no need to check the return code.
3802 					// rdar://28312774 (Scalability and resilience issues in ntstat.c)
3803 
3804 					TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
3805 					TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
3806 					--num_srcs;
3807 				}
3808 
3809 				// Performance optimization, don't scan full lists if no chance of presence
3810 				if (num_srcs == 0) {
3811 					break;
3812 				}
3813 			}
3814 		}
3815 	}
3816 	NSTAT_UNLOCK_EXCLUSIVE();
3817 
3818 	while ((src = TAILQ_FIRST(&dead_list))) {
3819 		TAILQ_REMOVE(&dead_list, src, nts_client_link);
3820 		nstat_client_cleanup_source(NULL, src, TRUE);
3821 	}
3822 	nstat_release_gshad(gshad);
3823 }
3824 
3825 // Events that cause a significant change may be reported via a flags word
3826 void
nstat_provider_stats_event(__unused nstat_context nstat_ctx,__unused uint64_t event)3827 nstat_provider_stats_event(__unused nstat_context nstat_ctx, __unused uint64_t event)
3828 {
3829 	nstat_src *src;
3830 	struct nstat_generic_shadow *gshad = (struct nstat_generic_shadow *)nstat_ctx;
3831 
3832 	if (gshad == NULL) {
3833 		NSTAT_LOG_ERROR("called with null reference");
3834 		return;
3835 	}
3836 
3837 	assert(gshad->gshad_magic == NSTAT_GENERIC_SHADOW_MAGIC);
3838 
3839 	if (gshad->gshad_magic != NSTAT_GENERIC_SHADOW_MAGIC) {
3840 		NSTAT_LOG_ERROR("called with incorrect shadow magic 0x%x", gshad->gshad_magic);
3841 	}
3842 
3843 	NSTAT_LOCK_EXCLUSIVE();
3844 
3845 	if (nstat_generic_provider_watchers[gshad->gshad_provider] != 0) {
3846 		nstat_client *client;
3847 		errno_t result;
3848 		nstat_provider_id_t provider_id = gshad->gshad_provider;
3849 
3850 		for (client = nstat_clients; client; client = client->ntc_next) {
3851 			// Only scan further if this client is watching and has interest in the event
3852 			// or the client has requested "boring" unchanged status to be ignored
3853 			if (((client->ntc_watching & (1 << provider_id)) != 0) &&
3854 			    (((client->ntc_provider_filters[provider_id].npf_events & event) != 0) ||
3855 			    ((client->ntc_provider_filters[provider_id].npf_flags & NSTAT_FILTER_SUPPRESS_BORING_FLAGS) != 0))) {
3856 				TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
3857 				{
3858 					if (gshad == (struct nstat_generic_shadow *)src->nts_cookie) {
3859 						break;
3860 					}
3861 				}
3862 
3863 				if (src) {
3864 					src->nts_reported = false;
3865 					if ((client->ntc_provider_filters[provider_id].npf_events & event) != 0) {
3866 						result = nstat_client_send_event(client, src, event);
3867 						// There is currently no recovery possible from failure to send,
3868 						// so no need to check the return code.
3869 						// rdar://28312774 (Scalability and resilience issues in ntstat.c)
3870 					}
3871 				}
3872 			}
3873 		}
3874 	}
3875 	NSTAT_UNLOCK_EXCLUSIVE();
3876 }
3877 
3878 #endif /* SKYWALK */
3879 
3880 
3881 #pragma mark -- ifnet Provider --
3882 
3883 static nstat_provider   nstat_ifnet_provider;
3884 
3885 /*
3886  * We store a pointer to the ifnet and the original threshold
3887  * requested by the client.
3888  */
3889 struct nstat_ifnet_cookie {
3890 	struct ifnet    *ifp;
3891 	uint64_t        threshold;
3892 };
3893 
3894 static errno_t
nstat_ifnet_lookup(const void * data,u_int32_t length,nstat_provider_cookie_t * out_cookie)3895 nstat_ifnet_lookup(
3896 	const void              *data,
3897 	u_int32_t               length,
3898 	nstat_provider_cookie_t *out_cookie)
3899 {
3900 	const nstat_ifnet_add_param *param = (const nstat_ifnet_add_param *)data;
3901 	struct ifnet *ifp;
3902 	boolean_t changed = FALSE;
3903 	nstat_client *client;
3904 	nstat_src *src;
3905 	struct nstat_ifnet_cookie *cookie;
3906 
3907 	if (length < sizeof(*param) || param->threshold < 1024 * 1024) {
3908 		return EINVAL;
3909 	}
3910 	if (nstat_privcheck != 0) {
3911 		errno_t result = priv_check_cred(kauth_cred_get(),
3912 		    PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
3913 		if (result != 0) {
3914 			return result;
3915 		}
3916 	}
3917 	cookie = kalloc_type(struct nstat_ifnet_cookie,
3918 	    Z_WAITOK | Z_ZERO | Z_NOFAIL);
3919 
3920 	ifnet_head_lock_shared();
3921 	TAILQ_FOREACH(ifp, &ifnet_head, if_link)
3922 	{
3923 		if (!ifnet_is_attached(ifp, 1)) {
3924 			continue;
3925 		}
3926 		ifnet_lock_exclusive(ifp);
3927 		if (ifp->if_index == param->ifindex) {
3928 			cookie->ifp = ifp;
3929 			cookie->threshold = param->threshold;
3930 			*out_cookie = cookie;
3931 			if (!ifp->if_data_threshold ||
3932 			    ifp->if_data_threshold > param->threshold) {
3933 				changed = TRUE;
3934 				ifp->if_data_threshold = param->threshold;
3935 			}
3936 			ifnet_lock_done(ifp);
3937 			ifnet_reference(ifp);
3938 			ifnet_decr_iorefcnt(ifp);
3939 			break;
3940 		}
3941 		ifnet_lock_done(ifp);
3942 		ifnet_decr_iorefcnt(ifp);
3943 	}
3944 	ifnet_head_done();
3945 
3946 	/*
3947 	 * When we change the threshold to something smaller, we notify
3948 	 * all of our clients with a description message.
3949 	 * We won't send a message to the client we are currently serving
3950 	 * because it has no `ifnet source' yet.
3951 	 */
3952 	if (changed) {
3953 		NSTAT_LOCK_EXCLUSIVE();
3954 		for (client = nstat_clients; client; client = client->ntc_next) {
3955 			TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
3956 			{
3957 				if (src->nts_provider != &nstat_ifnet_provider) {
3958 					continue;
3959 				}
3960 				nstat_client_send_description(client, src, 0, 0);
3961 			}
3962 		}
3963 		NSTAT_UNLOCK_EXCLUSIVE();
3964 	}
3965 	if (cookie->ifp == NULL) {
3966 		kfree_type(struct nstat_ifnet_cookie, cookie);
3967 	}
3968 
3969 	return ifp ? 0 : EINVAL;
3970 }
3971 
3972 static int
nstat_ifnet_gone(nstat_provider_cookie_t cookie)3973 nstat_ifnet_gone(
3974 	nstat_provider_cookie_t cookie)
3975 {
3976 	struct ifnet *ifp;
3977 	struct nstat_ifnet_cookie *ifcookie =
3978 	    (struct nstat_ifnet_cookie *)cookie;
3979 
3980 	ifnet_head_lock_shared();
3981 	TAILQ_FOREACH(ifp, &ifnet_head, if_link)
3982 	{
3983 		if (ifp == ifcookie->ifp) {
3984 			break;
3985 		}
3986 	}
3987 	ifnet_head_done();
3988 
3989 	return ifp ? 0 : 1;
3990 }
3991 
3992 static errno_t
nstat_ifnet_counts(nstat_provider_cookie_t cookie,struct nstat_counts * out_counts,int * out_gone)3993 nstat_ifnet_counts(
3994 	nstat_provider_cookie_t cookie,
3995 	struct nstat_counts     *out_counts,
3996 	int                     *out_gone)
3997 {
3998 	struct nstat_ifnet_cookie *ifcookie =
3999 	    (struct nstat_ifnet_cookie *)cookie;
4000 	struct ifnet *ifp = ifcookie->ifp;
4001 
4002 	if (out_gone) {
4003 		*out_gone = 0;
4004 	}
4005 
4006 	// if the ifnet is gone, we should stop using it
4007 	if (nstat_ifnet_gone(cookie)) {
4008 		if (out_gone) {
4009 			*out_gone = 1;
4010 		}
4011 		return EINVAL;
4012 	}
4013 
4014 	bzero(out_counts, sizeof(*out_counts));
4015 	out_counts->nstat_rxpackets = ifp->if_ipackets;
4016 	out_counts->nstat_rxbytes = ifp->if_ibytes;
4017 	out_counts->nstat_txpackets = ifp->if_opackets;
4018 	out_counts->nstat_txbytes = ifp->if_obytes;
4019 	out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
4020 	return 0;
4021 }
4022 
4023 static void
nstat_ifnet_release(nstat_provider_cookie_t cookie,__unused int locked)4024 nstat_ifnet_release(
4025 	nstat_provider_cookie_t cookie,
4026 	__unused int            locked)
4027 {
4028 	struct nstat_ifnet_cookie *ifcookie;
4029 	struct ifnet *ifp;
4030 	nstat_client *client;
4031 	nstat_src *src;
4032 	uint64_t minthreshold = UINT64_MAX;
4033 
4034 	/*
4035 	 * Find all the clients that requested a threshold
4036 	 * for this ifnet and re-calculate if_data_threshold.
4037 	 */
4038 	NSTAT_LOCK_SHARED();
4039 	for (client = nstat_clients; client; client = client->ntc_next) {
4040 		TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
4041 		{
4042 			/* Skip the provider we are about to detach. */
4043 			if (src->nts_provider != &nstat_ifnet_provider ||
4044 			    src->nts_cookie == cookie) {
4045 				continue;
4046 			}
4047 			ifcookie = (struct nstat_ifnet_cookie *)src->nts_cookie;
4048 			if (ifcookie->threshold < minthreshold) {
4049 				minthreshold = ifcookie->threshold;
4050 			}
4051 		}
4052 	}
4053 	NSTAT_UNLOCK_SHARED();
4054 	/*
4055 	 * Reset if_data_threshold or disable it.
4056 	 */
4057 	ifcookie = (struct nstat_ifnet_cookie *)cookie;
4058 	ifp = ifcookie->ifp;
4059 	if (ifnet_is_attached(ifp, 1)) {
4060 		ifnet_lock_exclusive(ifp);
4061 		if (minthreshold == UINT64_MAX) {
4062 			ifp->if_data_threshold = 0;
4063 		} else {
4064 			ifp->if_data_threshold = minthreshold;
4065 		}
4066 		ifnet_lock_done(ifp);
4067 		ifnet_decr_iorefcnt(ifp);
4068 	}
4069 	ifnet_release(ifp);
4070 	kfree_type(struct nstat_ifnet_cookie, ifcookie);
4071 }
4072 
4073 static void
nstat_ifnet_copy_link_status(struct ifnet * ifp,struct nstat_ifnet_descriptor * desc)4074 nstat_ifnet_copy_link_status(
4075 	struct ifnet                    *ifp,
4076 	struct nstat_ifnet_descriptor   *desc)
4077 {
4078 	struct if_link_status *ifsr = ifp->if_link_status;
4079 	nstat_ifnet_desc_link_status *link_status = &desc->link_status;
4080 
4081 	link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_NONE;
4082 	if (ifsr == NULL) {
4083 		return;
4084 	}
4085 
4086 	lck_rw_lock_shared(&ifp->if_link_status_lock);
4087 
4088 	if (ifp->if_type == IFT_CELLULAR) {
4089 		nstat_ifnet_desc_cellular_status *cell_status = &link_status->u.cellular;
4090 		struct if_cellular_status_v1 *if_cell_sr =
4091 		    &ifsr->ifsr_u.ifsr_cell.if_cell_u.if_status_v1;
4092 
4093 		if (ifsr->ifsr_version != IF_CELLULAR_STATUS_REPORT_VERSION_1) {
4094 			goto done;
4095 		}
4096 
4097 		link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR;
4098 
4099 		if (if_cell_sr->valid_bitmask & IF_CELL_LINK_QUALITY_METRIC_VALID) {
4100 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_LINK_QUALITY_METRIC_VALID;
4101 			cell_status->link_quality_metric = if_cell_sr->link_quality_metric;
4102 		}
4103 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_BANDWIDTH_VALID) {
4104 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_BANDWIDTH_VALID;
4105 			cell_status->ul_effective_bandwidth = if_cell_sr->ul_effective_bandwidth;
4106 		}
4107 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_BANDWIDTH_VALID) {
4108 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_BANDWIDTH_VALID;
4109 			cell_status->ul_max_bandwidth = if_cell_sr->ul_max_bandwidth;
4110 		}
4111 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_LATENCY_VALID) {
4112 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_LATENCY_VALID;
4113 			cell_status->ul_min_latency = if_cell_sr->ul_min_latency;
4114 		}
4115 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_LATENCY_VALID) {
4116 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_LATENCY_VALID;
4117 			cell_status->ul_effective_latency = if_cell_sr->ul_effective_latency;
4118 		}
4119 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_LATENCY_VALID) {
4120 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_LATENCY_VALID;
4121 			cell_status->ul_max_latency = if_cell_sr->ul_max_latency;
4122 		}
4123 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_RETXT_LEVEL_VALID) {
4124 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
4125 			if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_NONE) {
4126 				cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_NONE;
4127 			} else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_LOW) {
4128 				cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_LOW;
4129 			} else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_MEDIUM) {
4130 				cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_MEDIUM;
4131 			} else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_HIGH) {
4132 				cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_HIGH;
4133 			} else {
4134 				cell_status->valid_bitmask &= ~NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
4135 			}
4136 		}
4137 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_BYTES_LOST_VALID) {
4138 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_BYTES_LOST_VALID;
4139 			cell_status->ul_bytes_lost = if_cell_sr->ul_bytes_lost;
4140 		}
4141 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_QUEUE_SIZE_VALID) {
4142 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_QUEUE_SIZE_VALID;
4143 			cell_status->ul_min_queue_size = if_cell_sr->ul_min_queue_size;
4144 		}
4145 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_AVG_QUEUE_SIZE_VALID) {
4146 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_AVG_QUEUE_SIZE_VALID;
4147 			cell_status->ul_avg_queue_size = if_cell_sr->ul_avg_queue_size;
4148 		}
4149 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_QUEUE_SIZE_VALID) {
4150 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_QUEUE_SIZE_VALID;
4151 			cell_status->ul_max_queue_size = if_cell_sr->ul_max_queue_size;
4152 		}
4153 		if (if_cell_sr->valid_bitmask & IF_CELL_DL_EFFECTIVE_BANDWIDTH_VALID) {
4154 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_EFFECTIVE_BANDWIDTH_VALID;
4155 			cell_status->dl_effective_bandwidth = if_cell_sr->dl_effective_bandwidth;
4156 		}
4157 		if (if_cell_sr->valid_bitmask & IF_CELL_DL_MAX_BANDWIDTH_VALID) {
4158 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_MAX_BANDWIDTH_VALID;
4159 			cell_status->dl_max_bandwidth = if_cell_sr->dl_max_bandwidth;
4160 		}
4161 		if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_INACTIVITY_TIME_VALID) {
4162 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_INACTIVITY_TIME_VALID;
4163 			cell_status->config_inactivity_time = if_cell_sr->config_inactivity_time;
4164 		}
4165 		if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_BACKOFF_TIME_VALID) {
4166 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_BACKOFF_TIME_VALID;
4167 			cell_status->config_backoff_time = if_cell_sr->config_backoff_time;
4168 		}
4169 		if (if_cell_sr->valid_bitmask & IF_CELL_UL_MSS_RECOMMENDED_VALID) {
4170 			cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_MSS_RECOMMENDED_VALID;
4171 			cell_status->mss_recommended = if_cell_sr->mss_recommended;
4172 		}
4173 	} else if (IFNET_IS_WIFI(ifp)) {
4174 		nstat_ifnet_desc_wifi_status *wifi_status = &link_status->u.wifi;
4175 		struct if_wifi_status_v1 *if_wifi_sr =
4176 		    &ifsr->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
4177 
4178 		if (ifsr->ifsr_version != IF_WIFI_STATUS_REPORT_VERSION_1) {
4179 			goto done;
4180 		}
4181 
4182 		link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI;
4183 
4184 		if (if_wifi_sr->valid_bitmask & IF_WIFI_LINK_QUALITY_METRIC_VALID) {
4185 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_LINK_QUALITY_METRIC_VALID;
4186 			wifi_status->link_quality_metric = if_wifi_sr->link_quality_metric;
4187 		}
4188 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID) {
4189 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID;
4190 			wifi_status->ul_effective_bandwidth = if_wifi_sr->ul_effective_bandwidth;
4191 		}
4192 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_BANDWIDTH_VALID) {
4193 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_BANDWIDTH_VALID;
4194 			wifi_status->ul_max_bandwidth = if_wifi_sr->ul_max_bandwidth;
4195 		}
4196 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MIN_LATENCY_VALID) {
4197 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MIN_LATENCY_VALID;
4198 			wifi_status->ul_min_latency = if_wifi_sr->ul_min_latency;
4199 		}
4200 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_LATENCY_VALID) {
4201 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_LATENCY_VALID;
4202 			wifi_status->ul_effective_latency = if_wifi_sr->ul_effective_latency;
4203 		}
4204 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_LATENCY_VALID) {
4205 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_LATENCY_VALID;
4206 			wifi_status->ul_max_latency = if_wifi_sr->ul_max_latency;
4207 		}
4208 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_RETXT_LEVEL_VALID) {
4209 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
4210 			if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_NONE) {
4211 				wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_NONE;
4212 			} else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_LOW) {
4213 				wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_LOW;
4214 			} else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_MEDIUM) {
4215 				wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_MEDIUM;
4216 			} else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_HIGH) {
4217 				wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_HIGH;
4218 			} else {
4219 				wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
4220 			}
4221 		}
4222 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_BYTES_LOST_VALID) {
4223 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_BYTES_LOST_VALID;
4224 			wifi_status->ul_bytes_lost = if_wifi_sr->ul_bytes_lost;
4225 		}
4226 		if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_ERROR_RATE_VALID) {
4227 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_ERROR_RATE_VALID;
4228 			wifi_status->ul_error_rate = if_wifi_sr->ul_error_rate;
4229 		}
4230 		if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID) {
4231 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID;
4232 			wifi_status->dl_effective_bandwidth = if_wifi_sr->dl_effective_bandwidth;
4233 		}
4234 		if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_BANDWIDTH_VALID) {
4235 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_BANDWIDTH_VALID;
4236 			wifi_status->dl_max_bandwidth = if_wifi_sr->dl_max_bandwidth;
4237 		}
4238 		if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MIN_LATENCY_VALID) {
4239 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MIN_LATENCY_VALID;
4240 			wifi_status->dl_min_latency = if_wifi_sr->dl_min_latency;
4241 		}
4242 		if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_LATENCY_VALID) {
4243 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_LATENCY_VALID;
4244 			wifi_status->dl_effective_latency = if_wifi_sr->dl_effective_latency;
4245 		}
4246 		if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_LATENCY_VALID) {
4247 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_LATENCY_VALID;
4248 			wifi_status->dl_max_latency = if_wifi_sr->dl_max_latency;
4249 		}
4250 		if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_ERROR_RATE_VALID) {
4251 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_ERROR_RATE_VALID;
4252 			wifi_status->dl_error_rate = if_wifi_sr->dl_error_rate;
4253 		}
4254 		if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_FREQUENCY_VALID) {
4255 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
4256 			if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_2_4_GHZ) {
4257 				wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_2_4_GHZ;
4258 			} else if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_5_0_GHZ) {
4259 				wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_5_0_GHZ;
4260 			} else {
4261 				wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
4262 			}
4263 		}
4264 		if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_MULTICAST_RATE_VALID) {
4265 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_MULTICAST_RATE_VALID;
4266 			wifi_status->config_multicast_rate = if_wifi_sr->config_multicast_rate;
4267 		}
4268 		if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_COUNT_VALID) {
4269 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_COUNT_VALID;
4270 			wifi_status->scan_count = if_wifi_sr->scan_count;
4271 		}
4272 		if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_DURATION_VALID) {
4273 			wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_DURATION_VALID;
4274 			wifi_status->scan_duration = if_wifi_sr->scan_duration;
4275 		}
4276 	}
4277 
4278 done:
4279 	lck_rw_done(&ifp->if_link_status_lock);
4280 }
4281 
4282 /* Some thresholds to determine Low Internet mode */
4283 #define NSTAT_LIM_DL_MAX_BANDWIDTH_THRESHOLD    1000000 /* 1 Mbps */
4284 #define NSTAT_LIM_UL_MAX_BANDWIDTH_THRESHOLD    500000  /* 500 Kbps */
4285 #define NSTAT_LIM_UL_MIN_RTT_THRESHOLD          1000    /* 1 second */
4286 #define NSTAT_LIM_CONN_TIMEOUT_PERCENT_THRESHOLD (10 << 10) /* 10 percent connection timeouts */
4287 #define NSTAT_LIM_PACKET_LOSS_PERCENT_THRESHOLD (2 << 10) /* 2 percent packet loss rate */
4288 
4289 static boolean_t
nstat_lim_activity_check(struct if_lim_perf_stat * st)4290 nstat_lim_activity_check(struct if_lim_perf_stat *st)
4291 {
4292 	/* check that the current activity is enough to report stats */
4293 	if (st->lim_total_txpkts < nstat_lim_min_tx_pkts ||
4294 	    st->lim_total_rxpkts < nstat_lim_min_rx_pkts ||
4295 	    st->lim_conn_attempts == 0) {
4296 		return FALSE;
4297 	}
4298 
4299 	/*
4300 	 * Compute percentages if there was enough activity. Use
4301 	 * shift-left by 10 to preserve precision.
4302 	 */
4303 	st->lim_packet_loss_percent = ((st->lim_total_retxpkts << 10) /
4304 	    st->lim_total_txpkts) * 100;
4305 
4306 	st->lim_packet_ooo_percent = ((st->lim_total_oopkts << 10) /
4307 	    st->lim_total_rxpkts) * 100;
4308 
4309 	st->lim_conn_timeout_percent = ((st->lim_conn_timeouts << 10) /
4310 	    st->lim_conn_attempts) * 100;
4311 
4312 	/*
4313 	 * Is Low Internet detected? First order metrics are bandwidth
4314 	 * and RTT. If these metrics are below the minimum thresholds
4315 	 * defined then the network attachment can be classified as
4316 	 * having Low Internet capacity.
4317 	 *
4318 	 * High connection timeout rate also indicates Low Internet
4319 	 * capacity.
4320 	 */
4321 	if (st->lim_dl_max_bandwidth > 0 &&
4322 	    st->lim_dl_max_bandwidth <= NSTAT_LIM_DL_MAX_BANDWIDTH_THRESHOLD) {
4323 		st->lim_dl_detected = 1;
4324 	}
4325 
4326 	if ((st->lim_ul_max_bandwidth > 0 &&
4327 	    st->lim_ul_max_bandwidth <= NSTAT_LIM_UL_MAX_BANDWIDTH_THRESHOLD) ||
4328 	    st->lim_rtt_min >= NSTAT_LIM_UL_MIN_RTT_THRESHOLD) {
4329 		st->lim_ul_detected = 1;
4330 	}
4331 
4332 	if (st->lim_conn_attempts > 20 &&
4333 	    st->lim_conn_timeout_percent >=
4334 	    NSTAT_LIM_CONN_TIMEOUT_PERCENT_THRESHOLD) {
4335 		st->lim_ul_detected = 1;
4336 	}
4337 	/*
4338 	 * Second order metrics: If there was high packet loss even after
4339 	 * using delay based algorithms then we classify it as Low Internet
4340 	 * again
4341 	 */
4342 	if (st->lim_bk_txpkts >= nstat_lim_min_tx_pkts &&
4343 	    st->lim_packet_loss_percent >=
4344 	    NSTAT_LIM_PACKET_LOSS_PERCENT_THRESHOLD) {
4345 		st->lim_ul_detected = 1;
4346 	}
4347 	return TRUE;
4348 }
4349 
4350 static u_int64_t nstat_lim_last_report_time = 0;
4351 static void
nstat_ifnet_report_lim_stats(void)4352 nstat_ifnet_report_lim_stats(void)
4353 {
4354 	u_int64_t uptime;
4355 	struct nstat_sysinfo_data data;
4356 	struct nstat_sysinfo_lim_stats *st;
4357 	struct ifnet *ifp;
4358 	int err;
4359 
4360 	uptime = net_uptime();
4361 
4362 	if ((u_int32_t)(uptime - nstat_lim_last_report_time) <
4363 	    nstat_lim_interval) {
4364 		return;
4365 	}
4366 
4367 	nstat_lim_last_report_time = uptime;
4368 	data.flags = NSTAT_SYSINFO_LIM_STATS;
4369 	st = &data.u.lim_stats;
4370 	data.unsent_data_cnt = 0;
4371 
4372 	ifnet_head_lock_shared();
4373 	TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
4374 		if (!IF_FULLY_ATTACHED(ifp)) {
4375 			continue;
4376 		}
4377 
4378 		/* Limit reporting to Wifi, Ethernet and cellular */
4379 		if (!(IFNET_IS_ETHERNET(ifp) || IFNET_IS_CELLULAR(ifp))) {
4380 			continue;
4381 		}
4382 
4383 		if (!nstat_lim_activity_check(&ifp->if_lim_stat)) {
4384 			continue;
4385 		}
4386 
4387 		bzero(st, sizeof(*st));
4388 		st->ifnet_siglen = sizeof(st->ifnet_signature);
4389 		err = ifnet_get_netsignature(ifp, AF_INET,
4390 		    (u_int8_t *)&st->ifnet_siglen, NULL,
4391 		    st->ifnet_signature);
4392 		if (err != 0) {
4393 			err = ifnet_get_netsignature(ifp, AF_INET6,
4394 			    (u_int8_t *)&st->ifnet_siglen, NULL,
4395 			    st->ifnet_signature);
4396 			if (err != 0) {
4397 				continue;
4398 			}
4399 		}
4400 		ifnet_lock_shared(ifp);
4401 		if (IFNET_IS_CELLULAR(ifp)) {
4402 			st->ifnet_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR;
4403 		} else if (IFNET_IS_WIFI(ifp)) {
4404 			st->ifnet_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI;
4405 		} else {
4406 			st->ifnet_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_ETHERNET;
4407 		}
4408 		bcopy(&ifp->if_lim_stat, &st->lim_stat,
4409 		    sizeof(st->lim_stat));
4410 
4411 		/* Zero the stats in ifp */
4412 		bzero(&ifp->if_lim_stat, sizeof(ifp->if_lim_stat));
4413 		if (nstat_debug != 0) {
4414 			NSTAT_LOG_DEBUG("Reporting LIM stats for ifindex %u", ifp->if_index);
4415 		}
4416 		ifnet_lock_done(ifp);
4417 		nstat_sysinfo_send_data(&data);
4418 	}
4419 	ifnet_head_done();
4420 }
4421 
4422 static errno_t
nstat_ifnet_copy_descriptor(nstat_provider_cookie_t cookie,void * __sized_by (len)data,size_t len)4423 nstat_ifnet_copy_descriptor(
4424 	nstat_provider_cookie_t cookie,
4425 	void                    *__sized_by(len)data,
4426 	size_t                  len)
4427 {
4428 	nstat_ifnet_descriptor *desc = (nstat_ifnet_descriptor *)data;
4429 	struct nstat_ifnet_cookie *ifcookie =
4430 	    (struct nstat_ifnet_cookie *)cookie;
4431 	struct ifnet *ifp = ifcookie->ifp;
4432 
4433 	if (len < sizeof(nstat_ifnet_descriptor)) {
4434 		return EINVAL;
4435 	}
4436 
4437 	if (nstat_ifnet_gone(cookie)) {
4438 		return EINVAL;
4439 	}
4440 
4441 	bzero(desc, sizeof(*desc));
4442 	ifnet_lock_shared(ifp);
4443 	strlcpy(desc->name, ifp->if_xname, sizeof(desc->name));
4444 	desc->ifindex = ifp->if_index;
4445 	desc->threshold = ifp->if_data_threshold;
4446 	desc->type = ifp->if_type;
4447 	if (ifp->if_desc.ifd_len < sizeof(desc->description)) {
4448 		memcpy(desc->description, ifp->if_desc.ifd_desc,
4449 		    sizeof(desc->description));
4450 	}
4451 	nstat_ifnet_copy_link_status(ifp, desc);
4452 	ifnet_lock_done(ifp);
4453 	return 0;
4454 }
4455 
4456 static bool
nstat_ifnet_cookie_equal(nstat_provider_cookie_t cookie1,nstat_provider_cookie_t cookie2)4457 nstat_ifnet_cookie_equal(
4458 	nstat_provider_cookie_t cookie1,
4459 	nstat_provider_cookie_t cookie2)
4460 {
4461 	struct nstat_ifnet_cookie *c1 = (struct nstat_ifnet_cookie *)cookie1;
4462 	struct nstat_ifnet_cookie *c2 = (struct nstat_ifnet_cookie *)cookie2;
4463 
4464 	return (c1->ifp->if_index == c2->ifp->if_index) ? true : false;
4465 }
4466 
4467 static void
nstat_init_ifnet_provider(void)4468 nstat_init_ifnet_provider(void)
4469 {
4470 	bzero(&nstat_ifnet_provider, sizeof(nstat_ifnet_provider));
4471 	nstat_ifnet_provider.nstat_provider_id = NSTAT_PROVIDER_IFNET;
4472 	nstat_ifnet_provider.nstat_descriptor_length = sizeof(nstat_ifnet_descriptor);
4473 	nstat_ifnet_provider.nstat_lookup = nstat_ifnet_lookup;
4474 	nstat_ifnet_provider.nstat_gone = nstat_ifnet_gone;
4475 	nstat_ifnet_provider.nstat_counts = nstat_ifnet_counts;
4476 	nstat_ifnet_provider.nstat_watcher_add = NULL;
4477 	nstat_ifnet_provider.nstat_watcher_remove = NULL;
4478 	nstat_ifnet_provider.nstat_copy_descriptor = nstat_ifnet_copy_descriptor;
4479 	nstat_ifnet_provider.nstat_cookie_equal = nstat_ifnet_cookie_equal;
4480 	nstat_ifnet_provider.nstat_release = nstat_ifnet_release;
4481 	nstat_ifnet_provider.next = nstat_providers;
4482 	nstat_providers = &nstat_ifnet_provider;
4483 }
4484 
4485 __private_extern__ void
nstat_ifnet_threshold_reached(unsigned int ifindex)4486 nstat_ifnet_threshold_reached(unsigned int ifindex)
4487 {
4488 	nstat_client *client;
4489 	nstat_src *src;
4490 	struct ifnet *ifp;
4491 	struct nstat_ifnet_cookie *ifcookie;
4492 
4493 	NSTAT_LOCK_EXCLUSIVE();
4494 	for (client = nstat_clients; client; client = client->ntc_next) {
4495 		TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
4496 		{
4497 			if (src->nts_provider != &nstat_ifnet_provider) {
4498 				continue;
4499 			}
4500 			ifcookie = (struct nstat_ifnet_cookie *)src->nts_cookie;
4501 			ifp = ifcookie->ifp;
4502 			if (ifp->if_index != ifindex) {
4503 				continue;
4504 			}
4505 			nstat_client_send_counts(client, src, 0, 0, NULL);
4506 		}
4507 	}
4508 	NSTAT_UNLOCK_EXCLUSIVE();
4509 }
4510 
4511 #pragma mark -- Sysinfo --
4512 static void
nstat_set_keyval_scalar(nstat_sysinfo_keyval * kv,int key,u_int32_t val)4513 nstat_set_keyval_scalar(nstat_sysinfo_keyval *kv, int key, u_int32_t val)
4514 {
4515 	kv->nstat_sysinfo_key = key;
4516 	kv->nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
4517 	kv->u.nstat_sysinfo_scalar = val;
4518 	kv->nstat_sysinfo_valsize = sizeof(kv->u.nstat_sysinfo_scalar);
4519 }
4520 
4521 static void
nstat_set_keyval_u64_scalar(nstat_sysinfo_keyval * kv,int key,u_int64_t val)4522 nstat_set_keyval_u64_scalar(nstat_sysinfo_keyval *kv, int key, u_int64_t val)
4523 {
4524 	kv->nstat_sysinfo_key = key;
4525 	kv->nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
4526 	kv->u.nstat_sysinfo_scalar = val;
4527 	kv->nstat_sysinfo_valsize = sizeof(kv->u.nstat_sysinfo_scalar);
4528 }
4529 
4530 static void
nstat_set_keyval_string(nstat_sysinfo_keyval * kv,int key,u_int8_t * __counted_by (len)buf,u_int32_t len)4531 nstat_set_keyval_string(nstat_sysinfo_keyval *kv, int key, u_int8_t *__counted_by(len)buf,
4532     u_int32_t len)
4533 {
4534 	kv->nstat_sysinfo_key = key;
4535 	kv->nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_STRING;
4536 	kv->nstat_sysinfo_valsize = len;
4537 	bcopy(buf, kv->u.nstat_sysinfo_string, kv->nstat_sysinfo_valsize);
4538 }
4539 
4540 static void
nstat_sysinfo_send_data_internal(nstat_client * client,nstat_sysinfo_data * data)4541 nstat_sysinfo_send_data_internal(
4542 	nstat_client *client,
4543 	nstat_sysinfo_data *data)
4544 {
4545 	nstat_msg_sysinfo_counts *syscnt = NULL;
4546 	size_t allocsize = 0, countsize = 0, nkeyvals = 0, finalsize = 0;
4547 	nstat_sysinfo_keyval *kv;
4548 	errno_t result = 0;
4549 	size_t i = 0;
4550 
4551 	allocsize = offsetof(nstat_msg_sysinfo_counts, counts);
4552 	countsize = offsetof(nstat_sysinfo_counts, nstat_sysinfo_keyvals);
4553 	finalsize = allocsize;
4554 
4555 	/* get number of key-vals for each kind of stat */
4556 	switch (data->flags) {
4557 	case NSTAT_SYSINFO_TCP_STATS:
4558 		nkeyvals = NSTAT_SYSINFO_TCP_STATS_COUNT;
4559 		break;
4560 	case NSTAT_SYSINFO_LIM_STATS:
4561 		nkeyvals = NSTAT_LIM_STAT_KEYVAL_COUNT;
4562 		break;
4563 	case NSTAT_SYSINFO_NET_API_STATS:
4564 		nkeyvals = NSTAT_NET_API_STAT_KEYVAL_COUNT;
4565 		break;
4566 	default:
4567 		return;
4568 	}
4569 	countsize += sizeof(nstat_sysinfo_keyval) * nkeyvals;
4570 	allocsize += countsize;
4571 
4572 	syscnt = (nstat_msg_sysinfo_counts *) kalloc_data(allocsize,
4573 	    Z_WAITOK | Z_ZERO);
4574 	if (syscnt == NULL) {
4575 		return;
4576 	}
4577 
4578 	kv = nstat_sysinfo_get_keyvals(syscnt);
4579 
4580 	switch (data->flags) {
4581 	case NSTAT_SYSINFO_TCP_STATS:
4582 	{
4583 		nstat_set_keyval_scalar(&kv[i++],
4584 		    NSTAT_SYSINFO_KEY_IPV4_AVGRTT,
4585 		    data->u.tcp_stats.ipv4_avgrtt);
4586 		nstat_set_keyval_scalar(&kv[i++],
4587 		    NSTAT_SYSINFO_KEY_IPV6_AVGRTT,
4588 		    data->u.tcp_stats.ipv6_avgrtt);
4589 		nstat_set_keyval_scalar(&kv[i++],
4590 		    NSTAT_SYSINFO_KEY_SEND_PLR,
4591 		    data->u.tcp_stats.send_plr);
4592 		nstat_set_keyval_scalar(&kv[i++],
4593 		    NSTAT_SYSINFO_KEY_RECV_PLR,
4594 		    data->u.tcp_stats.recv_plr);
4595 		nstat_set_keyval_scalar(&kv[i++],
4596 		    NSTAT_SYSINFO_KEY_SEND_TLRTO,
4597 		    data->u.tcp_stats.send_tlrto_rate);
4598 		nstat_set_keyval_scalar(&kv[i++],
4599 		    NSTAT_SYSINFO_KEY_SEND_REORDERRATE,
4600 		    data->u.tcp_stats.send_reorder_rate);
4601 		nstat_set_keyval_scalar(&kv[i++],
4602 		    NSTAT_SYSINFO_CONNECTION_ATTEMPTS,
4603 		    data->u.tcp_stats.connection_attempts);
4604 		nstat_set_keyval_scalar(&kv[i++],
4605 		    NSTAT_SYSINFO_CONNECTION_ACCEPTS,
4606 		    data->u.tcp_stats.connection_accepts);
4607 		nstat_set_keyval_scalar(&kv[i++],
4608 		    NSTAT_SYSINFO_ECN_CLIENT_ENABLED,
4609 		    data->u.tcp_stats.ecn_client_enabled);
4610 		nstat_set_keyval_scalar(&kv[i++],
4611 		    NSTAT_SYSINFO_ECN_SERVER_ENABLED,
4612 		    data->u.tcp_stats.ecn_server_enabled);
4613 		nstat_set_keyval_scalar(&kv[i++],
4614 		    NSTAT_SYSINFO_ECN_CLIENT_SETUP,
4615 		    data->u.tcp_stats.ecn_client_setup);
4616 		nstat_set_keyval_scalar(&kv[i++],
4617 		    NSTAT_SYSINFO_ECN_SERVER_SETUP,
4618 		    data->u.tcp_stats.ecn_server_setup);
4619 		nstat_set_keyval_scalar(&kv[i++],
4620 		    NSTAT_SYSINFO_ECN_CLIENT_SUCCESS,
4621 		    data->u.tcp_stats.ecn_client_success);
4622 		nstat_set_keyval_scalar(&kv[i++],
4623 		    NSTAT_SYSINFO_ECN_SERVER_SUCCESS,
4624 		    data->u.tcp_stats.ecn_server_success);
4625 		nstat_set_keyval_scalar(&kv[i++],
4626 		    NSTAT_SYSINFO_ECN_NOT_SUPPORTED,
4627 		    data->u.tcp_stats.ecn_not_supported);
4628 		nstat_set_keyval_scalar(&kv[i++],
4629 		    NSTAT_SYSINFO_ECN_LOST_SYN,
4630 		    data->u.tcp_stats.ecn_lost_syn);
4631 		nstat_set_keyval_scalar(&kv[i++],
4632 		    NSTAT_SYSINFO_ECN_LOST_SYNACK,
4633 		    data->u.tcp_stats.ecn_lost_synack);
4634 		nstat_set_keyval_scalar(&kv[i++],
4635 		    NSTAT_SYSINFO_ECN_RECV_CE,
4636 		    data->u.tcp_stats.ecn_recv_ce);
4637 		nstat_set_keyval_scalar(&kv[i++],
4638 		    NSTAT_SYSINFO_ECN_RECV_ECE,
4639 		    data->u.tcp_stats.ecn_recv_ece);
4640 		nstat_set_keyval_scalar(&kv[i++],
4641 		    NSTAT_SYSINFO_ECN_SENT_ECE,
4642 		    data->u.tcp_stats.ecn_sent_ece);
4643 		nstat_set_keyval_scalar(&kv[i++],
4644 		    NSTAT_SYSINFO_ECN_CONN_RECV_CE,
4645 		    data->u.tcp_stats.ecn_conn_recv_ce);
4646 		nstat_set_keyval_scalar(&kv[i++],
4647 		    NSTAT_SYSINFO_ECN_CONN_RECV_ECE,
4648 		    data->u.tcp_stats.ecn_conn_recv_ece);
4649 		nstat_set_keyval_scalar(&kv[i++],
4650 		    NSTAT_SYSINFO_ECN_CONN_PLNOCE,
4651 		    data->u.tcp_stats.ecn_conn_plnoce);
4652 		nstat_set_keyval_scalar(&kv[i++],
4653 		    NSTAT_SYSINFO_ECN_CONN_PL_CE,
4654 		    data->u.tcp_stats.ecn_conn_pl_ce);
4655 		nstat_set_keyval_scalar(&kv[i++],
4656 		    NSTAT_SYSINFO_ECN_CONN_NOPL_CE,
4657 		    data->u.tcp_stats.ecn_conn_nopl_ce);
4658 		nstat_set_keyval_scalar(&kv[i++],
4659 		    NSTAT_SYSINFO_ECN_FALLBACK_SYNLOSS,
4660 		    data->u.tcp_stats.ecn_fallback_synloss);
4661 		nstat_set_keyval_scalar(&kv[i++],
4662 		    NSTAT_SYSINFO_ECN_FALLBACK_REORDER,
4663 		    data->u.tcp_stats.ecn_fallback_reorder);
4664 		nstat_set_keyval_scalar(&kv[i++],
4665 		    NSTAT_SYSINFO_ECN_FALLBACK_CE,
4666 		    data->u.tcp_stats.ecn_fallback_ce);
4667 		nstat_set_keyval_scalar(&kv[i++],
4668 		    NSTAT_SYSINFO_TFO_SYN_DATA_RCV,
4669 		    data->u.tcp_stats.tfo_syn_data_rcv);
4670 		nstat_set_keyval_scalar(&kv[i++],
4671 		    NSTAT_SYSINFO_TFO_COOKIE_REQ_RCV,
4672 		    data->u.tcp_stats.tfo_cookie_req_rcv);
4673 		nstat_set_keyval_scalar(&kv[i++],
4674 		    NSTAT_SYSINFO_TFO_COOKIE_SENT,
4675 		    data->u.tcp_stats.tfo_cookie_sent);
4676 		nstat_set_keyval_scalar(&kv[i++],
4677 		    NSTAT_SYSINFO_TFO_COOKIE_INVALID,
4678 		    data->u.tcp_stats.tfo_cookie_invalid);
4679 		nstat_set_keyval_scalar(&kv[i++],
4680 		    NSTAT_SYSINFO_TFO_COOKIE_REQ,
4681 		    data->u.tcp_stats.tfo_cookie_req);
4682 		nstat_set_keyval_scalar(&kv[i++],
4683 		    NSTAT_SYSINFO_TFO_COOKIE_RCV,
4684 		    data->u.tcp_stats.tfo_cookie_rcv);
4685 		nstat_set_keyval_scalar(&kv[i++],
4686 		    NSTAT_SYSINFO_TFO_SYN_DATA_SENT,
4687 		    data->u.tcp_stats.tfo_syn_data_sent);
4688 		nstat_set_keyval_scalar(&kv[i++],
4689 		    NSTAT_SYSINFO_TFO_SYN_DATA_ACKED,
4690 		    data->u.tcp_stats.tfo_syn_data_acked);
4691 		nstat_set_keyval_scalar(&kv[i++],
4692 		    NSTAT_SYSINFO_TFO_SYN_LOSS,
4693 		    data->u.tcp_stats.tfo_syn_loss);
4694 		nstat_set_keyval_scalar(&kv[i++],
4695 		    NSTAT_SYSINFO_TFO_BLACKHOLE,
4696 		    data->u.tcp_stats.tfo_blackhole);
4697 		nstat_set_keyval_scalar(&kv[i++],
4698 		    NSTAT_SYSINFO_TFO_COOKIE_WRONG,
4699 		    data->u.tcp_stats.tfo_cookie_wrong);
4700 		nstat_set_keyval_scalar(&kv[i++],
4701 		    NSTAT_SYSINFO_TFO_NO_COOKIE_RCV,
4702 		    data->u.tcp_stats.tfo_no_cookie_rcv);
4703 		nstat_set_keyval_scalar(&kv[i++],
4704 		    NSTAT_SYSINFO_TFO_HEURISTICS_DISABLE,
4705 		    data->u.tcp_stats.tfo_heuristics_disable);
4706 		nstat_set_keyval_scalar(&kv[i++],
4707 		    NSTAT_SYSINFO_TFO_SEND_BLACKHOLE,
4708 		    data->u.tcp_stats.tfo_sndblackhole);
4709 		nstat_set_keyval_scalar(&kv[i++],
4710 		    NSTAT_SYSINFO_MPTCP_HANDOVER_ATTEMPT,
4711 		    data->u.tcp_stats.mptcp_handover_attempt);
4712 		nstat_set_keyval_scalar(&kv[i++],
4713 		    NSTAT_SYSINFO_MPTCP_INTERACTIVE_ATTEMPT,
4714 		    data->u.tcp_stats.mptcp_interactive_attempt);
4715 		nstat_set_keyval_scalar(&kv[i++],
4716 		    NSTAT_SYSINFO_MPTCP_AGGREGATE_ATTEMPT,
4717 		    data->u.tcp_stats.mptcp_aggregate_attempt);
4718 		nstat_set_keyval_scalar(&kv[i++],
4719 		    NSTAT_SYSINFO_MPTCP_FP_HANDOVER_ATTEMPT,
4720 		    data->u.tcp_stats.mptcp_fp_handover_attempt);
4721 		nstat_set_keyval_scalar(&kv[i++],
4722 		    NSTAT_SYSINFO_MPTCP_FP_INTERACTIVE_ATTEMPT,
4723 		    data->u.tcp_stats.mptcp_fp_interactive_attempt);
4724 		nstat_set_keyval_scalar(&kv[i++],
4725 		    NSTAT_SYSINFO_MPTCP_FP_AGGREGATE_ATTEMPT,
4726 		    data->u.tcp_stats.mptcp_fp_aggregate_attempt);
4727 		nstat_set_keyval_scalar(&kv[i++],
4728 		    NSTAT_SYSINFO_MPTCP_HEURISTIC_FALLBACK,
4729 		    data->u.tcp_stats.mptcp_heuristic_fallback);
4730 		nstat_set_keyval_scalar(&kv[i++],
4731 		    NSTAT_SYSINFO_MPTCP_FP_HEURISTIC_FALLBACK,
4732 		    data->u.tcp_stats.mptcp_fp_heuristic_fallback);
4733 		nstat_set_keyval_scalar(&kv[i++],
4734 		    NSTAT_SYSINFO_MPTCP_HANDOVER_SUCCESS_WIFI,
4735 		    data->u.tcp_stats.mptcp_handover_success_wifi);
4736 		nstat_set_keyval_scalar(&kv[i++],
4737 		    NSTAT_SYSINFO_MPTCP_HANDOVER_SUCCESS_CELL,
4738 		    data->u.tcp_stats.mptcp_handover_success_cell);
4739 		nstat_set_keyval_scalar(&kv[i++],
4740 		    NSTAT_SYSINFO_MPTCP_INTERACTIVE_SUCCESS,
4741 		    data->u.tcp_stats.mptcp_interactive_success);
4742 		nstat_set_keyval_scalar(&kv[i++],
4743 		    NSTAT_SYSINFO_MPTCP_AGGREGATE_SUCCESS,
4744 		    data->u.tcp_stats.mptcp_aggregate_success);
4745 		nstat_set_keyval_scalar(&kv[i++],
4746 		    NSTAT_SYSINFO_MPTCP_FP_HANDOVER_SUCCESS_WIFI,
4747 		    data->u.tcp_stats.mptcp_fp_handover_success_wifi);
4748 		nstat_set_keyval_scalar(&kv[i++],
4749 		    NSTAT_SYSINFO_MPTCP_FP_HANDOVER_SUCCESS_CELL,
4750 		    data->u.tcp_stats.mptcp_fp_handover_success_cell);
4751 		nstat_set_keyval_scalar(&kv[i++],
4752 		    NSTAT_SYSINFO_MPTCP_FP_INTERACTIVE_SUCCESS,
4753 		    data->u.tcp_stats.mptcp_fp_interactive_success);
4754 		nstat_set_keyval_scalar(&kv[i++],
4755 		    NSTAT_SYSINFO_MPTCP_FP_AGGREGATE_SUCCESS,
4756 		    data->u.tcp_stats.mptcp_fp_aggregate_success);
4757 		nstat_set_keyval_scalar(&kv[i++],
4758 		    NSTAT_SYSINFO_MPTCP_HANDOVER_CELL_FROM_WIFI,
4759 		    data->u.tcp_stats.mptcp_handover_cell_from_wifi);
4760 		nstat_set_keyval_scalar(&kv[i++],
4761 		    NSTAT_SYSINFO_MPTCP_HANDOVER_WIFI_FROM_CELL,
4762 		    data->u.tcp_stats.mptcp_handover_wifi_from_cell);
4763 		nstat_set_keyval_scalar(&kv[i++],
4764 		    NSTAT_SYSINFO_MPTCP_INTERACTIVE_CELL_FROM_WIFI,
4765 		    data->u.tcp_stats.mptcp_interactive_cell_from_wifi);
4766 		nstat_set_keyval_u64_scalar(&kv[i++],
4767 		    NSTAT_SYSINFO_MPTCP_HANDOVER_CELL_BYTES,
4768 		    data->u.tcp_stats.mptcp_handover_cell_bytes);
4769 		nstat_set_keyval_u64_scalar(&kv[i++],
4770 		    NSTAT_SYSINFO_MPTCP_INTERACTIVE_CELL_BYTES,
4771 		    data->u.tcp_stats.mptcp_interactive_cell_bytes);
4772 		nstat_set_keyval_u64_scalar(&kv[i++],
4773 		    NSTAT_SYSINFO_MPTCP_AGGREGATE_CELL_BYTES,
4774 		    data->u.tcp_stats.mptcp_aggregate_cell_bytes);
4775 		nstat_set_keyval_u64_scalar(&kv[i++],
4776 		    NSTAT_SYSINFO_MPTCP_HANDOVER_ALL_BYTES,
4777 		    data->u.tcp_stats.mptcp_handover_all_bytes);
4778 		nstat_set_keyval_u64_scalar(&kv[i++],
4779 		    NSTAT_SYSINFO_MPTCP_INTERACTIVE_ALL_BYTES,
4780 		    data->u.tcp_stats.mptcp_interactive_all_bytes);
4781 		nstat_set_keyval_u64_scalar(&kv[i++],
4782 		    NSTAT_SYSINFO_MPTCP_AGGREGATE_ALL_BYTES,
4783 		    data->u.tcp_stats.mptcp_aggregate_all_bytes);
4784 		nstat_set_keyval_scalar(&kv[i++],
4785 		    NSTAT_SYSINFO_MPTCP_BACK_TO_WIFI,
4786 		    data->u.tcp_stats.mptcp_back_to_wifi);
4787 		nstat_set_keyval_scalar(&kv[i++],
4788 		    NSTAT_SYSINFO_MPTCP_WIFI_PROXY,
4789 		    data->u.tcp_stats.mptcp_wifi_proxy);
4790 		nstat_set_keyval_scalar(&kv[i++],
4791 		    NSTAT_SYSINFO_MPTCP_CELL_PROXY,
4792 		    data->u.tcp_stats.mptcp_cell_proxy);
4793 		nstat_set_keyval_scalar(&kv[i++],
4794 		    NSTAT_SYSINFO_MPTCP_TRIGGERED_CELL,
4795 		    data->u.tcp_stats.mptcp_triggered_cell);
4796 		VERIFY(i == nkeyvals);
4797 		break;
4798 	}
4799 	case NSTAT_SYSINFO_LIM_STATS:
4800 	{
4801 		nstat_set_keyval_string(&kv[i++],
4802 		    NSTAT_SYSINFO_LIM_IFNET_SIGNATURE,
4803 		    data->u.lim_stats.ifnet_signature,
4804 		    min(data->u.lim_stats.ifnet_siglen, NSTAT_SYSINFO_KEYVAL_STRING_MAXSIZE));
4805 		nstat_set_keyval_u64_scalar(&kv[i++],
4806 		    NSTAT_SYSINFO_LIM_IFNET_DL_MAX_BANDWIDTH,
4807 		    data->u.lim_stats.lim_stat.lim_dl_max_bandwidth);
4808 		nstat_set_keyval_u64_scalar(&kv[i++],
4809 		    NSTAT_SYSINFO_LIM_IFNET_UL_MAX_BANDWIDTH,
4810 		    data->u.lim_stats.lim_stat.lim_ul_max_bandwidth);
4811 		nstat_set_keyval_u64_scalar(&kv[i++],
4812 		    NSTAT_SYSINFO_LIM_IFNET_PACKET_LOSS_PERCENT,
4813 		    data->u.lim_stats.lim_stat.lim_packet_loss_percent);
4814 		nstat_set_keyval_u64_scalar(&kv[i++],
4815 		    NSTAT_SYSINFO_LIM_IFNET_PACKET_OOO_PERCENT,
4816 		    data->u.lim_stats.lim_stat.lim_packet_ooo_percent);
4817 		nstat_set_keyval_u64_scalar(&kv[i++],
4818 		    NSTAT_SYSINFO_LIM_IFNET_RTT_VARIANCE,
4819 		    data->u.lim_stats.lim_stat.lim_rtt_variance);
4820 		nstat_set_keyval_u64_scalar(&kv[i++],
4821 		    NSTAT_SYSINFO_LIM_IFNET_RTT_MIN,
4822 		    data->u.lim_stats.lim_stat.lim_rtt_min);
4823 		nstat_set_keyval_u64_scalar(&kv[i++],
4824 		    NSTAT_SYSINFO_LIM_IFNET_RTT_AVG,
4825 		    data->u.lim_stats.lim_stat.lim_rtt_average);
4826 		nstat_set_keyval_u64_scalar(&kv[i++],
4827 		    NSTAT_SYSINFO_LIM_IFNET_CONN_TIMEOUT_PERCENT,
4828 		    data->u.lim_stats.lim_stat.lim_conn_timeout_percent);
4829 		nstat_set_keyval_scalar(&kv[i++],
4830 		    NSTAT_SYSINFO_LIM_IFNET_DL_DETECTED,
4831 		    data->u.lim_stats.lim_stat.lim_dl_detected);
4832 		nstat_set_keyval_scalar(&kv[i++],
4833 		    NSTAT_SYSINFO_LIM_IFNET_UL_DETECTED,
4834 		    data->u.lim_stats.lim_stat.lim_ul_detected);
4835 		nstat_set_keyval_scalar(&kv[i++],
4836 		    NSTAT_SYSINFO_LIM_IFNET_TYPE,
4837 		    data->u.lim_stats.ifnet_type);
4838 		break;
4839 	}
4840 	case NSTAT_SYSINFO_NET_API_STATS:
4841 	{
4842 		nstat_set_keyval_u64_scalar(&kv[i++],
4843 		    NSTAT_SYSINFO_API_IF_FLTR_ATTACH,
4844 		    data->u.net_api_stats.net_api_stats.nas_iflt_attach_total);
4845 		nstat_set_keyval_u64_scalar(&kv[i++],
4846 		    NSTAT_SYSINFO_API_IF_FLTR_ATTACH_OS,
4847 		    data->u.net_api_stats.net_api_stats.nas_iflt_attach_os_total);
4848 		nstat_set_keyval_u64_scalar(&kv[i++],
4849 		    NSTAT_SYSINFO_API_IP_FLTR_ADD,
4850 		    data->u.net_api_stats.net_api_stats.nas_ipf_add_total);
4851 		nstat_set_keyval_u64_scalar(&kv[i++],
4852 		    NSTAT_SYSINFO_API_IP_FLTR_ADD_OS,
4853 		    data->u.net_api_stats.net_api_stats.nas_ipf_add_os_total);
4854 		nstat_set_keyval_u64_scalar(&kv[i++],
4855 		    NSTAT_SYSINFO_API_SOCK_FLTR_ATTACH,
4856 		    data->u.net_api_stats.net_api_stats.nas_sfltr_register_total);
4857 		nstat_set_keyval_u64_scalar(&kv[i++],
4858 		    NSTAT_SYSINFO_API_SOCK_FLTR_ATTACH_OS,
4859 		    data->u.net_api_stats.net_api_stats.nas_sfltr_register_os_total);
4860 
4861 
4862 		nstat_set_keyval_u64_scalar(&kv[i++],
4863 		    NSTAT_SYSINFO_API_SOCK_ALLOC_TOTAL,
4864 		    data->u.net_api_stats.net_api_stats.nas_socket_alloc_total);
4865 		nstat_set_keyval_u64_scalar(&kv[i++],
4866 		    NSTAT_SYSINFO_API_SOCK_ALLOC_KERNEL,
4867 		    data->u.net_api_stats.net_api_stats.nas_socket_in_kernel_total);
4868 		nstat_set_keyval_u64_scalar(&kv[i++],
4869 		    NSTAT_SYSINFO_API_SOCK_ALLOC_KERNEL_OS,
4870 		    data->u.net_api_stats.net_api_stats.nas_socket_in_kernel_os_total);
4871 		nstat_set_keyval_u64_scalar(&kv[i++],
4872 		    NSTAT_SYSINFO_API_SOCK_NECP_CLIENTUUID,
4873 		    data->u.net_api_stats.net_api_stats.nas_socket_necp_clientuuid_total);
4874 
4875 		nstat_set_keyval_u64_scalar(&kv[i++],
4876 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_LOCAL,
4877 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_local_total);
4878 		nstat_set_keyval_u64_scalar(&kv[i++],
4879 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_ROUTE,
4880 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_route_total);
4881 		nstat_set_keyval_u64_scalar(&kv[i++],
4882 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_INET,
4883 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_inet_total);
4884 		nstat_set_keyval_u64_scalar(&kv[i++],
4885 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_INET6,
4886 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_inet6_total);
4887 		nstat_set_keyval_u64_scalar(&kv[i++],
4888 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_SYSTEM,
4889 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_system_total);
4890 		nstat_set_keyval_u64_scalar(&kv[i++],
4891 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_MULTIPATH,
4892 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_multipath_total);
4893 		nstat_set_keyval_u64_scalar(&kv[i++],
4894 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_KEY,
4895 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_key_total);
4896 		nstat_set_keyval_u64_scalar(&kv[i++],
4897 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_NDRV,
4898 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_ndrv_total);
4899 		nstat_set_keyval_u64_scalar(&kv[i++],
4900 		    NSTAT_SYSINFO_API_SOCK_DOMAIN_OTHER,
4901 		    data->u.net_api_stats.net_api_stats.nas_socket_domain_other_total);
4902 
4903 		nstat_set_keyval_u64_scalar(&kv[i++],
4904 		    NSTAT_SYSINFO_API_SOCK_INET_STREAM,
4905 		    data->u.net_api_stats.net_api_stats.nas_socket_inet_stream_total);
4906 		nstat_set_keyval_u64_scalar(&kv[i++],
4907 		    NSTAT_SYSINFO_API_SOCK_INET_DGRAM,
4908 		    data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_total);
4909 		nstat_set_keyval_u64_scalar(&kv[i++],
4910 		    NSTAT_SYSINFO_API_SOCK_INET_DGRAM_CONNECTED,
4911 		    data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_connected);
4912 		nstat_set_keyval_u64_scalar(&kv[i++],
4913 		    NSTAT_SYSINFO_API_SOCK_INET_DGRAM_DNS,
4914 		    data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_dns);
4915 		nstat_set_keyval_u64_scalar(&kv[i++],
4916 		    NSTAT_SYSINFO_API_SOCK_INET_DGRAM_NO_DATA,
4917 		    data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_no_data);
4918 
4919 		nstat_set_keyval_u64_scalar(&kv[i++],
4920 		    NSTAT_SYSINFO_API_SOCK_INET6_STREAM,
4921 		    data->u.net_api_stats.net_api_stats.nas_socket_inet6_stream_total);
4922 		nstat_set_keyval_u64_scalar(&kv[i++],
4923 		    NSTAT_SYSINFO_API_SOCK_INET6_DGRAM,
4924 		    data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_total);
4925 		nstat_set_keyval_u64_scalar(&kv[i++],
4926 		    NSTAT_SYSINFO_API_SOCK_INET6_DGRAM_CONNECTED,
4927 		    data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_connected);
4928 		nstat_set_keyval_u64_scalar(&kv[i++],
4929 		    NSTAT_SYSINFO_API_SOCK_INET6_DGRAM_DNS,
4930 		    data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_dns);
4931 		nstat_set_keyval_u64_scalar(&kv[i++],
4932 		    NSTAT_SYSINFO_API_SOCK_INET6_DGRAM_NO_DATA,
4933 		    data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_no_data);
4934 
4935 		nstat_set_keyval_u64_scalar(&kv[i++],
4936 		    NSTAT_SYSINFO_API_SOCK_INET_MCAST_JOIN,
4937 		    data->u.net_api_stats.net_api_stats.nas_socket_mcast_join_total);
4938 		nstat_set_keyval_u64_scalar(&kv[i++],
4939 		    NSTAT_SYSINFO_API_SOCK_INET_MCAST_JOIN_OS,
4940 		    data->u.net_api_stats.net_api_stats.nas_socket_mcast_join_os_total);
4941 
4942 		nstat_set_keyval_u64_scalar(&kv[i++],
4943 		    NSTAT_SYSINFO_API_NEXUS_FLOW_INET_STREAM,
4944 		    data->u.net_api_stats.net_api_stats.nas_nx_flow_inet_stream_total);
4945 		nstat_set_keyval_u64_scalar(&kv[i++],
4946 		    NSTAT_SYSINFO_API_NEXUS_FLOW_INET_DATAGRAM,
4947 		    data->u.net_api_stats.net_api_stats.nas_nx_flow_inet_dgram_total);
4948 
4949 		nstat_set_keyval_u64_scalar(&kv[i++],
4950 		    NSTAT_SYSINFO_API_NEXUS_FLOW_INET6_STREAM,
4951 		    data->u.net_api_stats.net_api_stats.nas_nx_flow_inet6_stream_total);
4952 		nstat_set_keyval_u64_scalar(&kv[i++],
4953 		    NSTAT_SYSINFO_API_NEXUS_FLOW_INET6_DATAGRAM,
4954 		    data->u.net_api_stats.net_api_stats.nas_nx_flow_inet6_dgram_total);
4955 
4956 		nstat_set_keyval_u64_scalar(&kv[i++],
4957 		    NSTAT_SYSINFO_API_IFNET_ALLOC,
4958 		    data->u.net_api_stats.net_api_stats.nas_ifnet_alloc_total);
4959 		nstat_set_keyval_u64_scalar(&kv[i++],
4960 		    NSTAT_SYSINFO_API_IFNET_ALLOC_OS,
4961 		    data->u.net_api_stats.net_api_stats.nas_ifnet_alloc_os_total);
4962 
4963 		nstat_set_keyval_u64_scalar(&kv[i++],
4964 		    NSTAT_SYSINFO_API_PF_ADDRULE,
4965 		    data->u.net_api_stats.net_api_stats.nas_pf_addrule_total);
4966 		nstat_set_keyval_u64_scalar(&kv[i++],
4967 		    NSTAT_SYSINFO_API_PF_ADDRULE_OS,
4968 		    data->u.net_api_stats.net_api_stats.nas_pf_addrule_os);
4969 
4970 		nstat_set_keyval_u64_scalar(&kv[i++],
4971 		    NSTAT_SYSINFO_API_VMNET_START,
4972 		    data->u.net_api_stats.net_api_stats.nas_vmnet_total);
4973 
4974 #if SKYWALK
4975 		nstat_set_keyval_scalar(&kv[i++],
4976 		    NSTAT_SYSINFO_API_IF_NETAGENT_ENABLED,
4977 		    if_is_fsw_transport_netagent_enabled());
4978 #endif /* SKYWALK */
4979 
4980 		nstat_set_keyval_scalar(&kv[i++],
4981 		    NSTAT_SYSINFO_API_REPORT_INTERVAL,
4982 		    data->u.net_api_stats.report_interval);
4983 
4984 		break;
4985 	}
4986 	}
4987 	if (syscnt != NULL) {
4988 		VERIFY(i > 0 && i <= nkeyvals);
4989 		countsize = offsetof(nstat_sysinfo_counts,
4990 		    nstat_sysinfo_keyvals) +
4991 		    sizeof(nstat_sysinfo_keyval) * i;
4992 		finalsize += countsize;
4993 		syscnt->hdr.type = NSTAT_MSG_TYPE_SYSINFO_COUNTS;
4994 		assert(finalsize <= MAX_NSTAT_MSG_HDR_LENGTH);
4995 		syscnt->hdr.length = (u_int16_t)finalsize;
4996 		syscnt->counts.nstat_sysinfo_len = (u_int32_t)countsize;
4997 
4998 		result = ctl_enqueuedata(client->ntc_kctl,
4999 		    client->ntc_unit, syscnt, finalsize, CTL_DATA_EOR);
5000 		if (result != 0) {
5001 			nstat_stats.nstat_sysinfofailures += 1;
5002 		}
5003 		kfree_data(syscnt, allocsize);
5004 	}
5005 	return;
5006 }
5007 
5008 __private_extern__ void
nstat_sysinfo_send_data(nstat_sysinfo_data * data)5009 nstat_sysinfo_send_data(
5010 	nstat_sysinfo_data *data)
5011 {
5012 	if (nstat_debug != 0) {
5013 		NSTAT_LOG_DEBUG("Sending stats report for %x", data->flags);
5014 	}
5015 
5016 	nstat_client *client;
5017 
5018 	NSTAT_LOCK_EXCLUSIVE();
5019 	for (client = nstat_clients; client; client = client->ntc_next) {
5020 		if ((client->ntc_flags & NSTAT_FLAG_SYSINFO_SUBSCRIBED) != 0) {
5021 			nstat_sysinfo_send_data_internal(client, data);
5022 		}
5023 	}
5024 	NSTAT_UNLOCK_EXCLUSIVE();
5025 }
5026 
5027 static void
nstat_sysinfo_generate_report(void)5028 nstat_sysinfo_generate_report(void)
5029 {
5030 	tcp_report_stats();
5031 	nstat_ifnet_report_lim_stats();
5032 	nstat_net_api_report_stats();
5033 }
5034 
5035 #pragma mark -- net_api --
5036 
5037 static struct net_api_stats net_api_stats_before;
5038 static u_int64_t net_api_stats_last_report_time;
5039 
5040 static void
nstat_net_api_report_stats(void)5041 nstat_net_api_report_stats(void)
5042 {
5043 	struct nstat_sysinfo_data data;
5044 	struct nstat_sysinfo_net_api_stats *st = &data.u.net_api_stats;
5045 	u_int64_t uptime;
5046 
5047 	uptime = net_uptime();
5048 
5049 	if ((u_int32_t)(uptime - net_api_stats_last_report_time) <
5050 	    net_api_stats_report_interval) {
5051 		return;
5052 	}
5053 
5054 	st->report_interval = (u_int32_t)(uptime - net_api_stats_last_report_time);
5055 	net_api_stats_last_report_time = uptime;
5056 
5057 	data.flags = NSTAT_SYSINFO_NET_API_STATS;
5058 	data.unsent_data_cnt = 0;
5059 
5060 	/*
5061 	 * Some of the fields in the report are the current value and
5062 	 * other fields are the delta from the last report:
5063 	 * - Report difference for the per flow counters as they increase
5064 	 *   with time
5065 	 * - Report current value for other counters as they tend not to change
5066 	 *   much with time
5067 	 */
5068 #define STATCOPY(f) \
5069 	(st->net_api_stats.f = net_api_stats.f)
5070 #define STATDIFF(f) \
5071 	(st->net_api_stats.f = net_api_stats.f - net_api_stats_before.f)
5072 
5073 	STATCOPY(nas_iflt_attach_count);
5074 	STATCOPY(nas_iflt_attach_total);
5075 	STATCOPY(nas_iflt_attach_os_total);
5076 
5077 	STATCOPY(nas_ipf_add_count);
5078 	STATCOPY(nas_ipf_add_total);
5079 	STATCOPY(nas_ipf_add_os_total);
5080 
5081 	STATCOPY(nas_sfltr_register_count);
5082 	STATCOPY(nas_sfltr_register_total);
5083 	STATCOPY(nas_sfltr_register_os_total);
5084 
5085 	STATDIFF(nas_socket_alloc_total);
5086 	STATDIFF(nas_socket_in_kernel_total);
5087 	STATDIFF(nas_socket_in_kernel_os_total);
5088 	STATDIFF(nas_socket_necp_clientuuid_total);
5089 
5090 	STATDIFF(nas_socket_domain_local_total);
5091 	STATDIFF(nas_socket_domain_route_total);
5092 	STATDIFF(nas_socket_domain_inet_total);
5093 	STATDIFF(nas_socket_domain_inet6_total);
5094 	STATDIFF(nas_socket_domain_system_total);
5095 	STATDIFF(nas_socket_domain_multipath_total);
5096 	STATDIFF(nas_socket_domain_key_total);
5097 	STATDIFF(nas_socket_domain_ndrv_total);
5098 	STATDIFF(nas_socket_domain_other_total);
5099 
5100 	STATDIFF(nas_socket_inet_stream_total);
5101 	STATDIFF(nas_socket_inet_dgram_total);
5102 	STATDIFF(nas_socket_inet_dgram_connected);
5103 	STATDIFF(nas_socket_inet_dgram_dns);
5104 	STATDIFF(nas_socket_inet_dgram_no_data);
5105 
5106 	STATDIFF(nas_socket_inet6_stream_total);
5107 	STATDIFF(nas_socket_inet6_dgram_total);
5108 	STATDIFF(nas_socket_inet6_dgram_connected);
5109 	STATDIFF(nas_socket_inet6_dgram_dns);
5110 	STATDIFF(nas_socket_inet6_dgram_no_data);
5111 
5112 	STATDIFF(nas_socket_mcast_join_total);
5113 	STATDIFF(nas_socket_mcast_join_os_total);
5114 
5115 	STATDIFF(nas_sock_inet6_stream_exthdr_in);
5116 	STATDIFF(nas_sock_inet6_stream_exthdr_out);
5117 	STATDIFF(nas_sock_inet6_dgram_exthdr_in);
5118 	STATDIFF(nas_sock_inet6_dgram_exthdr_out);
5119 
5120 	STATDIFF(nas_nx_flow_inet_stream_total);
5121 	STATDIFF(nas_nx_flow_inet_dgram_total);
5122 
5123 	STATDIFF(nas_nx_flow_inet6_stream_total);
5124 	STATDIFF(nas_nx_flow_inet6_dgram_total);
5125 
5126 	STATCOPY(nas_ifnet_alloc_count);
5127 	STATCOPY(nas_ifnet_alloc_total);
5128 	STATCOPY(nas_ifnet_alloc_os_count);
5129 	STATCOPY(nas_ifnet_alloc_os_total);
5130 
5131 	STATCOPY(nas_pf_addrule_total);
5132 	STATCOPY(nas_pf_addrule_os);
5133 
5134 	STATCOPY(nas_vmnet_total);
5135 
5136 #undef STATCOPY
5137 #undef STATDIFF
5138 
5139 	nstat_sysinfo_send_data(&data);
5140 
5141 	/*
5142 	 * Save a copy of the current fields so we can diff them the next time
5143 	 */
5144 	memcpy(&net_api_stats_before, &net_api_stats,
5145 	    sizeof(struct net_api_stats));
5146 	_CASSERT(sizeof(net_api_stats_before) == sizeof(net_api_stats));
5147 }
5148 
5149 
5150 #pragma mark -- Kernel Control Socket --
5151 
5152 static kern_ctl_ref     nstat_ctlref = NULL;
5153 
5154 static errno_t  nstat_client_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo);
5155 static errno_t  nstat_client_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo);
5156 static errno_t  nstat_client_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags);
5157 
5158 static errno_t
nstat_enqueue_success(uint64_t context,nstat_client * client,u_int16_t flags)5159 nstat_enqueue_success(
5160 	uint64_t context,
5161 	nstat_client *client,
5162 	u_int16_t flags)
5163 {
5164 	nstat_msg_hdr success;
5165 	errno_t result;
5166 
5167 	bzero(&success, sizeof(success));
5168 	success.context = context;
5169 	success.type = NSTAT_MSG_TYPE_SUCCESS;
5170 	success.length = sizeof(success);
5171 	success.flags = flags;
5172 	result = ctl_enqueuedata(client->ntc_kctl, client->ntc_unit, &success,
5173 	    sizeof(success), CTL_DATA_EOR | CTL_DATA_CRIT);
5174 	if (result != 0) {
5175 		if (nstat_debug != 0) {
5176 			NSTAT_LOG_ERROR("could not enqueue success message %d", result);
5177 		}
5178 		nstat_stats.nstat_successmsgfailures += 1;
5179 	}
5180 	return result;
5181 }
5182 
5183 static errno_t
nstat_client_send_event(nstat_client * client,nstat_src * src,u_int64_t event)5184 nstat_client_send_event(
5185 	nstat_client    *client,
5186 	nstat_src       *src,
5187 	u_int64_t       event)
5188 {
5189 	errno_t result = ENOTSUP;
5190 	int failed = 0;
5191 
5192 	if (nstat_client_reporting_allowed(client, src, 0)) {
5193 		if ((client->ntc_flags & NSTAT_FLAG_SUPPORTS_UPDATES) != 0) {
5194 			result = nstat_client_send_update(client, src, 0, event, 0, NULL);
5195 			if (result != 0) {
5196 				failed = 1;
5197 				if (nstat_debug != 0) {
5198 					NSTAT_LOG_ERROR("nstat_client_send_event() %d", result);
5199 				}
5200 			}
5201 		} else {
5202 			if (nstat_debug != 0) {
5203 				NSTAT_LOG_ERROR("nstat_client_send_event() used when updates not supported");
5204 			}
5205 		}
5206 	}
5207 	return result;
5208 }
5209 
5210 static errno_t
nstat_client_send_goodbye(nstat_client * client,nstat_src * src)5211 nstat_client_send_goodbye(
5212 	nstat_client    *client,
5213 	nstat_src       *src)
5214 {
5215 	errno_t result = 0;
5216 	int failed = 0;
5217 	u_int16_t hdr_flags = NSTAT_MSG_HDR_FLAG_CLOSED_AFTER_FILTER;
5218 
5219 	if (nstat_client_reporting_allowed(client, src, (src->nts_reported)? NSTAT_FILTER_SUPPRESS_BORING_CLOSE: 0)) {
5220 		hdr_flags = 0;
5221 		if ((client->ntc_flags & NSTAT_FLAG_SUPPORTS_UPDATES) != 0) {
5222 			result = nstat_client_send_update(client, src, 0, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
5223 			if (result != 0) {
5224 				failed = 1;
5225 				hdr_flags = NSTAT_MSG_HDR_FLAG_CLOSED_AFTER_DROP;
5226 				if (nstat_debug != 0) {
5227 					NSTAT_LOG_ERROR("nstat_client_send_update() %d", result);
5228 				}
5229 			}
5230 		} else {
5231 			// send one last counts notification
5232 			result = nstat_client_send_counts(client, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
5233 			if (result != 0) {
5234 				failed = 1;
5235 				hdr_flags = NSTAT_MSG_HDR_FLAG_CLOSED_AFTER_DROP;
5236 				if (nstat_debug != 0) {
5237 					NSTAT_LOG_ERROR("nstat_client_send_counts() %d", result);
5238 				}
5239 			}
5240 
5241 			// send a last description
5242 			result = nstat_client_send_description(client, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING);
5243 			if (result != 0) {
5244 				failed = 1;
5245 				hdr_flags = NSTAT_MSG_HDR_FLAG_CLOSED_AFTER_DROP;
5246 				if (nstat_debug != 0) {
5247 					NSTAT_LOG_ERROR("nstat_client_send_description() %d", result);
5248 				}
5249 			}
5250 		}
5251 	}
5252 
5253 	// send the source removed notification
5254 	result = nstat_client_send_removed(client, src, hdr_flags);
5255 	if (result != 0 && nstat_debug) {
5256 		failed = 1;
5257 		if (nstat_debug != 0) {
5258 			NSTAT_LOG_ERROR("nstat_client_send_removed() %d", result);
5259 		}
5260 	}
5261 
5262 	if (failed != 0) {
5263 		nstat_stats.nstat_control_send_goodbye_failures++;
5264 	}
5265 
5266 
5267 	return result;
5268 }
5269 
5270 static errno_t
nstat_flush_accumulated_msgs(nstat_client * client)5271 nstat_flush_accumulated_msgs(
5272 	nstat_client     *client)
5273 {
5274 	errno_t result = 0;
5275 	if (client->ntc_accumulated != NULL && mbuf_len(client->ntc_accumulated) > 0) {
5276 		mbuf_pkthdr_setlen(client->ntc_accumulated, mbuf_len(client->ntc_accumulated));
5277 		result = ctl_enqueuembuf(client->ntc_kctl, client->ntc_unit, client->ntc_accumulated, CTL_DATA_EOR);
5278 		if (result != 0) {
5279 			nstat_stats.nstat_flush_accumulated_msgs_failures++;
5280 			if (nstat_debug != 0) {
5281 				NSTAT_LOG_ERROR("ctl_enqueuembuf failed: %d", result);
5282 			}
5283 			mbuf_freem(client->ntc_accumulated);
5284 		}
5285 		client->ntc_accumulated = NULL;
5286 	}
5287 	return result;
5288 }
5289 
5290 static errno_t
nstat_accumulate_msg(nstat_client * client,uint8_t * __sized_by (length)msg,size_t length)5291 nstat_accumulate_msg(
5292 	nstat_client    *client,
5293 	uint8_t         *__sized_by(length) msg,
5294 	size_t          length)
5295 {
5296 	assert(length <= MAX_NSTAT_MSG_HDR_LENGTH);
5297 
5298 	if (client->ntc_accumulated && mbuf_trailingspace(client->ntc_accumulated) < length) {
5299 		// Will send the current mbuf
5300 		nstat_flush_accumulated_msgs(client);
5301 	}
5302 
5303 	errno_t result = 0;
5304 
5305 	if (client->ntc_accumulated == NULL) {
5306 		unsigned int one = 1;
5307 		if (mbuf_allocpacket(MBUF_DONTWAIT, NSTAT_MAX_MSG_SIZE, &one, &client->ntc_accumulated) != 0) {
5308 			if (nstat_debug != 0) {
5309 				NSTAT_LOG_ERROR("mbuf_allocpacket failed");
5310 			}
5311 			result = ENOMEM;
5312 		} else {
5313 			mbuf_setlen(client->ntc_accumulated, 0);
5314 		}
5315 	}
5316 
5317 	if (result == 0) {
5318 		result = mbuf_copyback(client->ntc_accumulated, mbuf_len(client->ntc_accumulated),
5319 		    length, msg, MBUF_DONTWAIT);
5320 	}
5321 
5322 	if (result != 0) {
5323 		nstat_flush_accumulated_msgs(client);
5324 		if (nstat_debug != 0) {
5325 			NSTAT_LOG_ERROR("resorting to ctl_enqueuedata");
5326 		}
5327 		result = ctl_enqueuedata(client->ntc_kctl, client->ntc_unit, msg, length, CTL_DATA_EOR);
5328 	}
5329 
5330 	if (result != 0) {
5331 		nstat_stats.nstat_accumulate_msg_failures++;
5332 	}
5333 
5334 	return result;
5335 }
5336 
5337 static void
nstat_idle_check(__unused thread_call_param_t p0,__unused thread_call_param_t p1)5338 nstat_idle_check(
5339 	__unused thread_call_param_t p0,
5340 	__unused thread_call_param_t p1)
5341 {
5342 	nstat_client *client;
5343 	nstat_src  *src, *tmpsrc;
5344 	tailq_head_nstat_src dead_list;
5345 	TAILQ_INIT(&dead_list);
5346 
5347 	NSTAT_LOCK_EXCLUSIVE();
5348 
5349 	nstat_idle_time = 0;
5350 
5351 	for (client = nstat_clients; client; client = client->ntc_next) {
5352 		TAILQ_FOREACH_SAFE(src, &client->ntc_src_queue, nts_client_link, tmpsrc)
5353 		{
5354 			if (src->nts_provider->nstat_gone(src->nts_cookie)) {
5355 				errno_t result;
5356 
5357 				// Pull it off the list
5358 				NSTAT_NOTE_SRC(nstat_src_gone_idlecheck, client, src);
5359 				TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
5360 
5361 				result = nstat_client_send_goodbye(client, src);
5362 
5363 				// Put this on the list to release later
5364 				TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
5365 			}
5366 		}
5367 	}
5368 
5369 	if (nstat_clients) {
5370 		clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
5371 		thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
5372 	}
5373 
5374 	NSTAT_UNLOCK_EXCLUSIVE();
5375 
5376 	/* Generate any system level reports, if needed */
5377 	nstat_sysinfo_generate_report();
5378 
5379 	// Release the sources now that we aren't holding lots of locks
5380 	while ((src = TAILQ_FIRST(&dead_list))) {
5381 		TAILQ_REMOVE(&dead_list, src, nts_client_link);
5382 		NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_src_idlecheck_gone);
5383 		nstat_client_cleanup_source(NULL, src, FALSE);
5384 	}
5385 
5386 	nstat_prune_procdetails();
5387 }
5388 
5389 static void
nstat_client_register(void)5390 nstat_client_register(void)
5391 {
5392 	// Register the control
5393 	struct kern_ctl_reg     nstat_control;
5394 	bzero(&nstat_control, sizeof(nstat_control));
5395 	strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name));
5396 	nstat_control.ctl_flags = CTL_FLAG_REG_EXTENDED | CTL_FLAG_REG_CRIT;
5397 	nstat_control.ctl_sendsize = nstat_sendspace;
5398 	nstat_control.ctl_recvsize = nstat_recvspace;
5399 	nstat_control.ctl_connect = nstat_client_connect;
5400 	nstat_control.ctl_disconnect = nstat_client_disconnect;
5401 	nstat_control.ctl_send = nstat_client_send;
5402 
5403 	ctl_register(&nstat_control, &nstat_ctlref);
5404 }
5405 
5406 static void
nstat_client_cleanup_source(nstat_client * client,struct nstat_src * src,boolean_t locked)5407 nstat_client_cleanup_source(
5408 	nstat_client        *client,
5409 	struct nstat_src    *src,
5410 	boolean_t           locked)
5411 {
5412 	errno_t result;
5413 
5414 	if (client) {
5415 		result = nstat_client_send_removed(client, src, 0);
5416 		if (result != 0) {
5417 			nstat_stats.nstat_control_cleanup_source_failures++;
5418 			if (nstat_debug != 0) {
5419 				NSTAT_LOG_ERROR("nstat_client_send_removed() %d", result);
5420 			}
5421 		}
5422 	}
5423 	// Cleanup the source if we found it.
5424 	src->nts_provider->nstat_release(src->nts_cookie, locked);
5425 	NSTAT_GLOBAL_COUNT_DECREMENT(nstat_global_src_current);
5426 	kfree_type(struct nstat_src, src);
5427 }
5428 
5429 static bool
nstat_client_reporting_allowed(nstat_client * client,nstat_src * src,u_int64_t suppression_flags)5430 nstat_client_reporting_allowed(
5431 	nstat_client *client,
5432 	nstat_src *src,
5433 	u_int64_t suppression_flags)
5434 {
5435 	if (src->nts_provider->nstat_reporting_allowed == NULL) {
5436 		return TRUE;
5437 	}
5438 
5439 	return src->nts_provider->nstat_reporting_allowed(src->nts_cookie,
5440 	           &client->ntc_provider_filters[src->nts_provider->nstat_provider_id], suppression_flags);
5441 }
5442 
5443 
5444 static errno_t
nstat_client_connect(kern_ctl_ref kctl,struct sockaddr_ctl * sac,void ** uinfo)5445 nstat_client_connect(
5446 	kern_ctl_ref        kctl,
5447 	struct sockaddr_ctl *sac,
5448 	void                **uinfo)
5449 {
5450 	nstat_client *client = kalloc_type(nstat_client,
5451 	    Z_WAITOK | Z_ZERO);
5452 	if (client == NULL) {
5453 		return ENOMEM;
5454 	}
5455 
5456 	lck_mtx_init(&client->ntc_user_mtx, &nstat_lck_grp, NULL);
5457 	client->ntc_kctl = kctl;
5458 	client->ntc_unit = sac->sc_unit;
5459 	client->ntc_flags = NSTAT_FLAG_REQCOUNTS;
5460 	client->ntc_procdetails = nstat_retain_curprocdetails();
5461 	*uinfo = client;
5462 
5463 	NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_client_allocs);
5464 	NSTAT_GLOBAL_COUNT_INCREMENT_WITH_MAX(nstat_global_client_current, nstat_global_client_max);
5465 #if NSTAT_TRACE_ENABLED
5466 	client->ntc_trace = (nstat_cyclic_trace *)kalloc_data(sizeof(nstat_cyclic_trace), Z_WAITOK | Z_ZERO);
5467 #endif
5468 	NSTAT_LOCK_EXCLUSIVE();
5469 	client->ntc_next = nstat_clients;
5470 	nstat_clients = client;
5471 
5472 	if (nstat_idle_time == 0) {
5473 		clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
5474 		thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
5475 	}
5476 
5477 	merge_current_event_filters();
5478 	NSTAT_UNLOCK_EXCLUSIVE();
5479 
5480 	return 0;
5481 }
5482 
5483 static errno_t
nstat_client_disconnect(__unused kern_ctl_ref kctl,__unused u_int32_t unit,void * uinfo)5484 nstat_client_disconnect(
5485 	__unused kern_ctl_ref   kctl,
5486 	__unused u_int32_t      unit,
5487 	void                    *uinfo)
5488 {
5489 	u_int32_t   watching;
5490 	nstat_client *client = (nstat_client*)uinfo;
5491 	tailq_head_nstat_src cleanup_list;
5492 	nstat_src *src;
5493 
5494 	TAILQ_INIT(&cleanup_list);
5495 
5496 	// pull it out of the global list of clients
5497 	NSTAT_LOCK_EXCLUSIVE();
5498 	nstat_client     **clientpp;
5499 	for (clientpp = &nstat_clients; *clientpp; clientpp = &(*clientpp)->ntc_next) {
5500 		if (*clientpp == client) {
5501 			*clientpp = client->ntc_next;
5502 			break;
5503 		}
5504 	}
5505 	merge_current_event_filters();
5506 
5507 	// Stop watching for sources
5508 	nstat_provider  *provider;
5509 	watching = client->ntc_watching;
5510 	client->ntc_watching = 0;
5511 	for (provider = nstat_providers; provider && watching; provider = provider->next) {
5512 		if ((watching & (1 << provider->nstat_provider_id)) != 0) {
5513 			watching &= ~(1 << provider->nstat_provider_id);
5514 			provider->nstat_watcher_remove(client);
5515 		}
5516 	}
5517 
5518 	// set cleanup flags
5519 	client->ntc_flags |= NSTAT_FLAG_CLEANUP;
5520 
5521 	if (client->ntc_accumulated) {
5522 		mbuf_freem(client->ntc_accumulated);
5523 		client->ntc_accumulated = NULL;
5524 	}
5525 
5526 	// Copy out the list of sources
5527 	TAILQ_CONCAT(&cleanup_list, &client->ntc_src_queue, nts_client_link);
5528 
5529 	NSTAT_UNLOCK_EXCLUSIVE();
5530 
5531 	while ((src = TAILQ_FIRST(&cleanup_list))) {
5532 		TAILQ_REMOVE(&cleanup_list, src, nts_client_link);
5533 		nstat_client_cleanup_source(NULL, src, FALSE);
5534 	}
5535 
5536 	lck_mtx_destroy(&client->ntc_user_mtx, &nstat_lck_grp);
5537 	nstat_release_procdetails(client->ntc_procdetails);
5538 	nstat_accumulate_client_metrics(client);
5539 #if NSTAT_TRACE_ENABLED
5540 	if (client->ntc_trace != NULL) {
5541 		kfree_data(client->ntc_trace, sizeof(nstat_cyclic_trace));
5542 	}
5543 #endif
5544 	NSTAT_GLOBAL_COUNT_DECREMENT(nstat_global_client_current);
5545 	kfree_type(struct nstat_client, client);
5546 
5547 	return 0;
5548 }
5549 
5550 static nstat_src_ref_t
nstat_client_next_src_ref(nstat_client * client)5551 nstat_client_next_src_ref(
5552 	nstat_client     *client)
5553 {
5554 	return ++client->ntc_next_srcref;
5555 }
5556 
5557 static errno_t
nstat_client_send_counts(nstat_client * client,nstat_src * src,unsigned long long context,u_int16_t hdr_flags,int * gone)5558 nstat_client_send_counts(
5559 	nstat_client        *client,
5560 	nstat_src           *src,
5561 	unsigned long long  context,
5562 	u_int16_t           hdr_flags,
5563 	int                 *gone)
5564 {
5565 	nstat_msg_src_counts counts;
5566 	errno_t result = 0;
5567 
5568 	/* Some providers may not have any counts to send */
5569 	if (src->nts_provider->nstat_counts == NULL) {
5570 		return 0;
5571 	}
5572 
5573 	bzero(&counts, sizeof(counts));
5574 	counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
5575 	counts.hdr.length = sizeof(counts);
5576 	counts.hdr.flags = hdr_flags;
5577 	counts.hdr.context = context;
5578 	counts.srcref = src->nts_srcref;
5579 	counts.event_flags = 0;
5580 
5581 	if (src->nts_provider->nstat_counts(src->nts_cookie, &counts.counts, gone) == 0) {
5582 		if ((src->nts_filter & NSTAT_FILTER_NOZEROBYTES) &&
5583 		    counts.counts.nstat_rxbytes == 0 &&
5584 		    counts.counts.nstat_txbytes == 0) {
5585 			result = EAGAIN;
5586 		} else {
5587 			result = ctl_enqueuedata(client->ntc_kctl,
5588 			    client->ntc_unit, &counts, sizeof(counts),
5589 			    CTL_DATA_EOR);
5590 			if (result != 0) {
5591 				nstat_stats.nstat_sendcountfailures += 1;
5592 			}
5593 		}
5594 	}
5595 	return result;
5596 }
5597 
5598 static errno_t
nstat_client_append_counts(nstat_client * client,nstat_src * src,int * gone)5599 nstat_client_append_counts(
5600 	nstat_client    *client,
5601 	nstat_src       *src,
5602 	int             *gone)
5603 {
5604 	/* Some providers may not have any counts to send */
5605 	if (!src->nts_provider->nstat_counts) {
5606 		return 0;
5607 	}
5608 
5609 	nstat_msg_src_counts counts;
5610 	bzero(&counts, sizeof(counts));
5611 	counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
5612 	counts.hdr.length = sizeof(counts);
5613 	counts.srcref = src->nts_srcref;
5614 	counts.event_flags = 0;
5615 
5616 	errno_t result = 0;
5617 	result = src->nts_provider->nstat_counts(src->nts_cookie, &counts.counts, gone);
5618 	if (result != 0) {
5619 		return result;
5620 	}
5621 
5622 	if ((src->nts_filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
5623 	    counts.counts.nstat_rxbytes == 0 && counts.counts.nstat_txbytes == 0) {
5624 		return EAGAIN;
5625 	}
5626 
5627 	return nstat_accumulate_msg(client, (uint8_t *)&counts, sizeof(counts));
5628 }
5629 
5630 static int
nstat_client_send_description(nstat_client * client,nstat_src * src,u_int64_t context,u_int16_t hdr_flags)5631 nstat_client_send_description(
5632 	nstat_client    *client,
5633 	nstat_src       *src,
5634 	u_int64_t       context,
5635 	u_int16_t       hdr_flags)
5636 {
5637 	// Provider doesn't support getting the descriptor? Done.
5638 	if (src->nts_provider->nstat_descriptor_length == 0 ||
5639 	    src->nts_provider->nstat_copy_descriptor == NULL) {
5640 		return EOPNOTSUPP;
5641 	}
5642 
5643 	// Allocate storage for the descriptor message
5644 	mbuf_ref_t          msg;
5645 	unsigned int    one = 1;
5646 	size_t          size = offsetof(nstat_msg_src_description, data) + src->nts_provider->nstat_descriptor_length;
5647 	assert(size <= MAX_NSTAT_MSG_HDR_LENGTH);
5648 
5649 	if (mbuf_allocpacket(MBUF_DONTWAIT, size, &one, &msg) != 0) {
5650 		return ENOMEM;
5651 	}
5652 
5653 
5654 	bzero(m_mtod_current(msg), size);
5655 	mbuf_setlen(msg, size);
5656 	mbuf_pkthdr_setlen(msg, mbuf_len(msg));
5657 
5658 	// Query the provider for the provider specific bits
5659 	nstat_msg_src_description *desc = mtod(msg, nstat_msg_src_description *);
5660 	desc->hdr.context = context;
5661 	desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
5662 	desc->hdr.length = (u_int16_t)size;
5663 	desc->hdr.flags = hdr_flags;
5664 	desc->srcref = src->nts_srcref;
5665 	desc->event_flags = 0;
5666 	desc->provider = src->nts_provider->nstat_provider_id;
5667 
5668 	uint8_t *desc_data_ptr = nstat_get_data(desc);
5669 	errno_t result = src->nts_provider->nstat_copy_descriptor(src->nts_cookie, desc_data_ptr, src->nts_provider->nstat_descriptor_length);
5670 
5671 	if (result != 0) {
5672 		mbuf_freem(msg);
5673 		return result;
5674 	}
5675 
5676 	result = ctl_enqueuembuf(client->ntc_kctl, client->ntc_unit, msg, CTL_DATA_EOR);
5677 	if (result != 0) {
5678 		nstat_stats.nstat_descriptionfailures += 1;
5679 		mbuf_freem(msg);
5680 	}
5681 
5682 	return result;
5683 }
5684 
5685 static errno_t
nstat_client_append_description(nstat_client * client,nstat_src * src)5686 nstat_client_append_description(
5687 	nstat_client    *client,
5688 	nstat_src       *src)
5689 {
5690 	size_t  size = offsetof(nstat_msg_src_description, data) + src->nts_provider->nstat_descriptor_length;
5691 	if (size > 512 || src->nts_provider->nstat_descriptor_length == 0 ||
5692 	    src->nts_provider->nstat_copy_descriptor == NULL) {
5693 		return EOPNOTSUPP;
5694 	}
5695 
5696 	// Fill out a buffer on the stack, we will copy to the mbuf later
5697 	u_int64_t buffer[size / sizeof(u_int64_t)  + 1]; // u_int64_t to ensure alignment
5698 	bzero(buffer, size);
5699 
5700 	nstat_msg_src_description *desc = (nstat_msg_src_description*)buffer;
5701 	desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
5702 	desc->hdr.length = (u_int16_t)size;
5703 	desc->srcref = src->nts_srcref;
5704 	desc->event_flags = 0;
5705 	desc->provider = src->nts_provider->nstat_provider_id;
5706 
5707 	errno_t result = 0;
5708 	// Fill in the description
5709 	// Query the provider for the provider specific bits
5710 	uint8_t *desc_data_ptr = nstat_get_data(desc);
5711 	result = src->nts_provider->nstat_copy_descriptor(src->nts_cookie, desc_data_ptr,
5712 	    src->nts_provider->nstat_descriptor_length);
5713 	if (result != 0) {
5714 		return result;
5715 	}
5716 
5717 	return nstat_accumulate_msg(client, (uint8_t *)buffer, size);
5718 }
5719 
5720 static uint64_t
nstat_extension_flags_for_source(nstat_client * client,nstat_src * src)5721 nstat_extension_flags_for_source(
5722 	nstat_client    *client,
5723 	nstat_src       *src)
5724 {
5725 	VERIFY(client != NULL & src != NULL);
5726 	nstat_provider_id_t provider_id = src->nts_provider->nstat_provider_id;
5727 
5728 	return client->ntc_provider_filters[provider_id].npf_extensions;
5729 }
5730 
5731 static int
nstat_client_send_update(nstat_client * client,nstat_src * src,u_int64_t context,u_int64_t event,u_int16_t hdr_flags,int * gone)5732 nstat_client_send_update(
5733 	nstat_client    *client,
5734 	nstat_src       *src,
5735 	u_int64_t       context,
5736 	u_int64_t       event,
5737 	u_int16_t       hdr_flags,
5738 	int             *gone)
5739 {
5740 	// Provider doesn't support getting the descriptor or counts? Done.
5741 	if ((src->nts_provider->nstat_descriptor_length == 0 ||
5742 	    src->nts_provider->nstat_copy_descriptor == NULL) &&
5743 	    src->nts_provider->nstat_counts == NULL) {
5744 		return EOPNOTSUPP;
5745 	}
5746 
5747 	// Allocate storage for the descriptor message
5748 	mbuf_ref_t      msg;
5749 	unsigned int    one = 1;
5750 	size_t          size = offsetof(nstat_msg_src_update, data) +
5751 	    src->nts_provider->nstat_descriptor_length;
5752 	size_t          total_extension_size = 0;
5753 	u_int32_t       num_extensions = 0;
5754 	u_int64_t       extension_mask = nstat_extension_flags_for_source(client, src);
5755 
5756 	if ((extension_mask != 0) && (src->nts_provider->nstat_copy_extension != NULL)) {
5757 		uint32_t extension_id = 0;
5758 		for (extension_id = NSTAT_EXTENDED_UPDATE_TYPE_MIN; extension_id <= NSTAT_EXTENDED_UPDATE_TYPE_MAX; extension_id++) {
5759 			if ((extension_mask & (1ull << extension_id)) != 0) {
5760 				size_t extension_size = src->nts_provider->nstat_copy_extension(src->nts_cookie, extension_id, NULL, 0);
5761 				if (extension_size == 0) {
5762 					extension_mask &= ~(1ull << extension_id);
5763 				} else {
5764 					num_extensions++;
5765 					total_extension_size += ROUNDUP64(extension_size);
5766 				}
5767 			}
5768 		}
5769 		size += total_extension_size + (sizeof(nstat_msg_src_extended_item_hdr) * num_extensions);
5770 	}
5771 	assert(size <= MAX_NSTAT_MSG_HDR_LENGTH);
5772 
5773 	/*
5774 	 * XXX Would be interesting to see how extended updates affect mbuf
5775 	 * allocations, given the max segments defined as 1, one may get
5776 	 * allocations with higher fragmentation.
5777 	 */
5778 	if (mbuf_allocpacket(MBUF_DONTWAIT, size, &one, &msg) != 0) {
5779 		return ENOMEM;
5780 	}
5781 
5782 	/* zero out for nstat_msg_src_update */
5783 	bzero(m_mtod_current(msg), size);
5784 
5785 	nstat_msg_src_update *desc = mtod(msg, nstat_msg_src_update *);
5786 	desc->hdr.context = context;
5787 	desc->hdr.type = (num_extensions == 0) ? NSTAT_MSG_TYPE_SRC_UPDATE :
5788 	    NSTAT_MSG_TYPE_SRC_EXTENDED_UPDATE;
5789 	desc->hdr.length = (u_int16_t)size;
5790 	desc->hdr.flags = hdr_flags;
5791 	desc->srcref = src->nts_srcref;
5792 	desc->event_flags = event;
5793 	desc->provider = src->nts_provider->nstat_provider_id;
5794 
5795 	/*
5796 	 * XXX The following two lines are only valid when max-segments is passed
5797 	 * as one.
5798 	 * Other computations with offset also depend on that being true.
5799 	 * Be aware of that before making any modifications that changes that
5800 	 * behavior.
5801 	 */
5802 	mbuf_setlen(msg, size);
5803 	mbuf_pkthdr_setlen(msg, mbuf_len(msg));
5804 
5805 	errno_t result = 0;
5806 	if (src->nts_provider->nstat_descriptor_length != 0 && src->nts_provider->nstat_copy_descriptor) {
5807 		// Query the provider for the provider specific bits
5808 		u_int8_t *desc_data_ptr = nstat_get_data(desc);
5809 		result = src->nts_provider->nstat_copy_descriptor(src->nts_cookie, desc_data_ptr,
5810 		    src->nts_provider->nstat_descriptor_length);
5811 		if (result != 0) {
5812 			mbuf_freem(msg);
5813 			return result;
5814 		}
5815 	}
5816 
5817 	if (num_extensions > 0) {
5818 		nstat_msg_src_extended_item_hdr *p_extension_hdr = (nstat_msg_src_extended_item_hdr *)mtodo(msg, sizeof(nstat_msg_src_update_hdr) + src->nts_provider->nstat_descriptor_length);
5819 		uint32_t extension_id = 0;
5820 
5821 		bzero(p_extension_hdr, total_extension_size + (sizeof(nstat_msg_src_extended_item_hdr) * num_extensions));
5822 
5823 		for (extension_id = NSTAT_EXTENDED_UPDATE_TYPE_MIN; extension_id <= NSTAT_EXTENDED_UPDATE_TYPE_MAX; extension_id++) {
5824 			if ((extension_mask & (1ull << extension_id)) != 0) {
5825 				void *buf = (void *)(p_extension_hdr + 1);
5826 				size_t extension_size = src->nts_provider->nstat_copy_extension(src->nts_cookie, extension_id, buf, total_extension_size);
5827 				if ((extension_size == 0) || (extension_size > total_extension_size)) {
5828 					// Something has gone wrong. Instead of attempting to wind back the excess buffer space, mark it as unused
5829 					p_extension_hdr->type = NSTAT_EXTENDED_UPDATE_TYPE_UNKNOWN;
5830 					p_extension_hdr->length = total_extension_size + (sizeof(nstat_msg_src_extended_item_hdr) * (num_extensions - 1));
5831 					break;
5832 				} else {
5833 					// The extension may be of any size alignment, reported as such in the extension header,
5834 					// but we pad to ensure that whatever comes next is suitably aligned
5835 					p_extension_hdr->type = extension_id;
5836 					p_extension_hdr->length = extension_size;
5837 					extension_size = ROUNDUP64(extension_size);
5838 					total_extension_size -= extension_size;
5839 					p_extension_hdr = (nstat_msg_src_extended_item_hdr *)(void *)((char *)buf + extension_size);
5840 					num_extensions--;
5841 				}
5842 			}
5843 		}
5844 	}
5845 
5846 	if (src->nts_provider->nstat_counts) {
5847 		result = src->nts_provider->nstat_counts(src->nts_cookie, &desc->counts, gone);
5848 		if (result == 0) {
5849 			if ((src->nts_filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
5850 			    desc->counts.nstat_rxbytes == 0 && desc->counts.nstat_txbytes == 0) {
5851 				result = EAGAIN;
5852 			} else {
5853 				result = ctl_enqueuembuf(client->ntc_kctl, client->ntc_unit, msg, CTL_DATA_EOR);
5854 			}
5855 		}
5856 	}
5857 
5858 	if (result != 0) {
5859 		nstat_stats.nstat_srcupatefailures += 1;
5860 		mbuf_freem(msg);
5861 	} else {
5862 		src->nts_reported = true;
5863 	}
5864 
5865 	return result;
5866 }
5867 
5868 static errno_t
nstat_client_append_update(nstat_client * client,nstat_src * src,int * gone)5869 nstat_client_append_update(
5870 	nstat_client    *client,
5871 	nstat_src       *src,
5872 	int             *gone)
5873 {
5874 	if ((src->nts_provider->nstat_descriptor_length == 0 ||
5875 	    src->nts_provider->nstat_copy_descriptor == NULL) &&
5876 	    src->nts_provider->nstat_counts == NULL) {
5877 		return EOPNOTSUPP;
5878 	}
5879 
5880 	size_t      size = offsetof(nstat_msg_src_update, data) + src->nts_provider->nstat_descriptor_length;
5881 	size_t      total_extension_size = 0;
5882 	u_int32_t   num_extensions = 0;
5883 	u_int64_t   extension_mask = nstat_extension_flags_for_source(client, src);
5884 
5885 	if ((extension_mask != 0) && (src->nts_provider->nstat_copy_extension != NULL)) {
5886 		uint32_t extension_id = 0;
5887 		for (extension_id = NSTAT_EXTENDED_UPDATE_TYPE_MIN; extension_id <= NSTAT_EXTENDED_UPDATE_TYPE_MAX; extension_id++) {
5888 			if ((extension_mask & (1ull << extension_id)) != 0) {
5889 				size_t extension_size = src->nts_provider->nstat_copy_extension(src->nts_cookie, extension_id, NULL, 0);
5890 				if (extension_size == 0) {
5891 					extension_mask &= ~(1ull << extension_id);
5892 				} else {
5893 					num_extensions++;
5894 					total_extension_size += ROUNDUP64(extension_size);
5895 				}
5896 			}
5897 		}
5898 		size += total_extension_size + (sizeof(nstat_msg_src_extended_item_hdr) * num_extensions);
5899 	}
5900 
5901 	/*
5902 	 * This kind of limits extensions.
5903 	 * The optimization is around being able to deliver multiple
5904 	 * of updates bundled together.
5905 	 * Increasing the size runs the risk of too much stack usage.
5906 	 * One could potentially changed the allocation below to be on heap.
5907 	 * For now limiting it to half of NSTAT_MAX_MSG_SIZE.
5908 	 */
5909 	if (size > (NSTAT_MAX_MSG_SIZE >> 1)) {
5910 		return EOPNOTSUPP;
5911 	}
5912 
5913 	// Fill out a buffer on the stack, we will copy to the mbuf later
5914 	u_int64_t buffer[size / sizeof(u_int64_t)  + 1]; // u_int64_t to ensure alignment
5915 	bzero(buffer, size);
5916 
5917 	nstat_msg_src_update    *desc = (nstat_msg_src_update*)buffer;
5918 	desc->hdr.type = (num_extensions == 0) ? NSTAT_MSG_TYPE_SRC_UPDATE :
5919 	    NSTAT_MSG_TYPE_SRC_EXTENDED_UPDATE;
5920 	desc->hdr.length = (u_int16_t)size;
5921 	desc->srcref = src->nts_srcref;
5922 	desc->event_flags = 0;
5923 	desc->provider = src->nts_provider->nstat_provider_id;
5924 
5925 	errno_t result = 0;
5926 	// Fill in the description
5927 	if (src->nts_provider->nstat_descriptor_length != 0 && src->nts_provider->nstat_copy_descriptor) {
5928 		// Query the provider for the provider specific bits
5929 		u_int8_t *desc_data_ptr = nstat_get_data(desc);
5930 		result = src->nts_provider->nstat_copy_descriptor(src->nts_cookie, desc_data_ptr,
5931 		    src->nts_provider->nstat_descriptor_length);
5932 		if (result != 0) {
5933 			nstat_stats.nstat_copy_descriptor_failures++;
5934 			if (nstat_debug != 0) {
5935 				NSTAT_LOG_ERROR("src->nts_provider->nstat_copy_descriptor: %d", result);
5936 			}
5937 			return result;
5938 		}
5939 	}
5940 
5941 	if (num_extensions > 0) {
5942 		nstat_msg_src_extended_item_hdr *p_extension_hdr = (nstat_msg_src_extended_item_hdr *)(void *)((char *)buffer +
5943 		    sizeof(nstat_msg_src_update_hdr) + src->nts_provider->nstat_descriptor_length);
5944 		uint32_t extension_id = 0;
5945 		bzero(p_extension_hdr, total_extension_size + (sizeof(nstat_msg_src_extended_item_hdr) * num_extensions));
5946 
5947 		for (extension_id = NSTAT_EXTENDED_UPDATE_TYPE_MIN; extension_id <= NSTAT_EXTENDED_UPDATE_TYPE_MAX; extension_id++) {
5948 			if ((extension_mask & (1ull << extension_id)) != 0) {
5949 				void *buf = (void *)(p_extension_hdr + 1);
5950 				size_t extension_size = src->nts_provider->nstat_copy_extension(src->nts_cookie, extension_id, buf, total_extension_size);
5951 				if ((extension_size == 0) || (extension_size > total_extension_size)) {
5952 					// Something has gone wrong. Instead of attempting to wind back the excess buffer space, mark it as unused
5953 					p_extension_hdr->type = NSTAT_EXTENDED_UPDATE_TYPE_UNKNOWN;
5954 					p_extension_hdr->length = total_extension_size + (sizeof(nstat_msg_src_extended_item_hdr) * (num_extensions - 1));
5955 					break;
5956 				} else {
5957 					extension_size = ROUNDUP64(extension_size);
5958 					p_extension_hdr->type = extension_id;
5959 					p_extension_hdr->length = extension_size;
5960 					total_extension_size -= extension_size;
5961 					p_extension_hdr = (nstat_msg_src_extended_item_hdr *)(void *)((char *)buf + extension_size);
5962 					num_extensions--;
5963 				}
5964 			}
5965 		}
5966 	}
5967 
5968 	if (src->nts_provider->nstat_counts) {
5969 		result = src->nts_provider->nstat_counts(src->nts_cookie, &desc->counts, gone);
5970 		if (result != 0) {
5971 			nstat_stats.nstat_provider_counts_failures++;
5972 			if (nstat_debug != 0) {
5973 				NSTAT_LOG_ERROR("src->nts_provider->nstat_counts: %d", result);
5974 			}
5975 			return result;
5976 		}
5977 
5978 		if ((src->nts_filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
5979 		    desc->counts.nstat_rxbytes == 0 && desc->counts.nstat_txbytes == 0) {
5980 			return EAGAIN;
5981 		}
5982 	}
5983 
5984 	result = nstat_accumulate_msg(client, (uint8_t *)buffer, size);
5985 	if (result == 0) {
5986 		src->nts_reported = true;
5987 	}
5988 	return result;
5989 }
5990 
5991 static errno_t
nstat_client_send_removed(nstat_client * client,nstat_src * src,u_int16_t hdr_flags)5992 nstat_client_send_removed(
5993 	nstat_client    *client,
5994 	nstat_src       *src,
5995 	u_int16_t       hdr_flags)
5996 {
5997 	nstat_msg_src_removed removed;
5998 	errno_t result;
5999 
6000 	bzero(&removed, sizeof(removed));
6001 	removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED;
6002 	removed.hdr.length = sizeof(removed);
6003 	removed.hdr.context = 0;
6004 	removed.hdr.flags = hdr_flags;
6005 	removed.srcref = src->nts_srcref;
6006 	result = ctl_enqueuedata(client->ntc_kctl, client->ntc_unit, &removed,
6007 	    sizeof(removed), CTL_DATA_EOR | CTL_DATA_CRIT);
6008 	if (result != 0) {
6009 		nstat_stats.nstat_msgremovedfailures += 1;
6010 	}
6011 
6012 	return result;
6013 }
6014 
6015 static errno_t
nstat_client_handle_add_request(nstat_client * client,mbuf_t m)6016 nstat_client_handle_add_request(
6017 	nstat_client    *client,
6018 	mbuf_t          m)
6019 {
6020 	errno_t result;
6021 
6022 	// Verify the header fits in the first mbuf
6023 	if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param)) {
6024 		return EINVAL;
6025 	}
6026 
6027 	// Calculate the length of the parameter field
6028 	ssize_t paramlength = mbuf_pkthdr_len(m) - offsetof(nstat_msg_add_src_req, param);
6029 	if (paramlength < 0 || paramlength > 2 * 1024) {
6030 		return EINVAL;
6031 	}
6032 
6033 	nstat_provider          *__single provider = NULL;
6034 	nstat_provider_cookie_t __single cookie = NULL;
6035 	nstat_msg_add_src_req *req = mtod(m, nstat_msg_add_src_req *);
6036 	if (mbuf_pkthdr_len(m) > mbuf_len(m)) {
6037 		// parameter is too large, we need to make a contiguous copy
6038 		void *data = (void *) kalloc_data(paramlength, Z_WAITOK);
6039 
6040 		if (!data) {
6041 			return ENOMEM;
6042 		}
6043 		result = mbuf_copydata(m, offsetof(nstat_msg_add_src_req, param), paramlength, data);
6044 		if (result == 0) {
6045 			result = nstat_lookup_entry(req->provider, data, paramlength, &provider, &cookie);
6046 		}
6047 		kfree_data(data, paramlength);
6048 	} else {
6049 		uint8_t *req_param_ptr = nstat_get_data(req);
6050 		result = nstat_lookup_entry(req->provider, req_param_ptr, paramlength, &provider, &cookie);
6051 	}
6052 
6053 	if (result != 0) {
6054 		return result;
6055 	}
6056 
6057 	// sanitize cookie
6058 	nstat_client_sanitize_cookie(client, provider->nstat_provider_id, cookie);
6059 
6060 	result = nstat_client_source_add(req->hdr.context, client, provider, cookie, NSTAT_LOCK_NOTHELD);
6061 
6062 	if (result != 0) {
6063 		provider->nstat_release(cookie, 0);
6064 	}
6065 
6066 	// Set the flag if a provider added a single source
6067 	os_atomic_or(&client->ntc_added_src, (1 << provider->nstat_provider_id), relaxed);
6068 
6069 	return result;
6070 }
6071 
6072 static errno_t
nstat_set_provider_filter(nstat_client * client,nstat_msg_add_all_srcs * req)6073 nstat_set_provider_filter(
6074 	nstat_client            *client,
6075 	nstat_msg_add_all_srcs  *req)
6076 {
6077 	nstat_provider_id_t provider_id = req->provider;
6078 
6079 	u_int32_t prev_ntc_watching = os_atomic_or_orig(&client->ntc_watching, (1 << provider_id), relaxed);
6080 
6081 	// Reject it if the client is already watching all the sources.
6082 	if ((prev_ntc_watching & (1 << provider_id)) != 0) {
6083 		return EALREADY;
6084 	}
6085 
6086 	// Reject it if any single source has already been added.
6087 	u_int32_t ntc_added_src = os_atomic_load(&client->ntc_added_src, relaxed);
6088 	if ((ntc_added_src & (1 << provider_id)) != 0) {
6089 		return EALREADY;
6090 	}
6091 
6092 	client->ntc_watching |= (1 << provider_id);
6093 	client->ntc_provider_filters[provider_id].npf_events = req->events;
6094 	client->ntc_provider_filters[provider_id].npf_flags  = req->filter;
6095 	client->ntc_provider_filters[provider_id].npf_pid    = req->target_pid;
6096 	uuid_copy(client->ntc_provider_filters[provider_id].npf_uuid, req->target_uuid);
6097 
6098 	// The extensions should be populated by a more direct mechanism
6099 	// Using the top 32 bits of the filter flags reduces the namespace of both,
6100 	// but is a convenient workaround that avoids ntstat.h changes that would require rebuild of all clients
6101 	// Extensions give away additional privacy information and are subject to unconditional privilege check,
6102 	// unconstrained by the value of nstat_privcheck
6103 	if (priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0) == 0) {
6104 		client->ntc_provider_filters[provider_id].npf_extensions = (req->filter >> NSTAT_FILTER_ALLOWED_EXTENSIONS_SHIFT) & NSTAT_EXTENDED_UPDATE_FLAG_MASK;
6105 	}
6106 	return 0;
6107 }
6108 
6109 static errno_t
nstat_client_handle_add_all(nstat_client * client,mbuf_t m)6110 nstat_client_handle_add_all(
6111 	nstat_client    *client,
6112 	mbuf_t          m)
6113 {
6114 	errno_t result = 0;
6115 
6116 	// Verify the header fits in the first mbuf
6117 	if (mbuf_len(m) < sizeof(nstat_msg_add_all_srcs)) {
6118 		return EINVAL;
6119 	}
6120 
6121 	nstat_msg_add_all_srcs *req = mtod(m, nstat_msg_add_all_srcs *);
6122 	if (req->provider > NSTAT_PROVIDER_LAST) {
6123 		return ENOENT;
6124 	}
6125 
6126 	nstat_provider *provider = nstat_find_provider_by_id(req->provider);
6127 
6128 	if (!provider) {
6129 		return ENOENT;
6130 	}
6131 	if (provider->nstat_watcher_add == NULL) {
6132 		return ENOTSUP;
6133 	}
6134 
6135 	// Traditionally the nstat_privcheck value allowed for easy access to ntstat on the Mac.
6136 	// Keep backwards compatibility while being more stringent with recent providers
6137 	if ((nstat_privcheck != 0) || (req->provider == NSTAT_PROVIDER_UDP_SUBFLOW) || (req->provider == NSTAT_PROVIDER_CONN_USERLAND)) {
6138 		result = priv_check_cred(kauth_cred_get(),
6139 		    PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
6140 		if (result != 0) {
6141 			return result;
6142 		}
6143 	}
6144 
6145 	NSTAT_LOCK_EXCLUSIVE();
6146 	if (req->filter & NSTAT_FILTER_SUPPRESS_SRC_ADDED) {
6147 		// Suppression of source messages implicitly requires the use of update messages
6148 		client->ntc_flags |= NSTAT_FLAG_SUPPORTS_UPDATES;
6149 	}
6150 	NSTAT_UNLOCK_EXCLUSIVE();
6151 
6152 	// rdar://problem/30301300   Different providers require different synchronization
6153 	// to ensure that a new entry does not get double counted due to being added prior
6154 	// to all current provider entries being added.  Hence pass the provider the details
6155 	// in the original request for this to be applied atomically
6156 
6157 	result = provider->nstat_watcher_add(client, req);
6158 
6159 	if (result == 0) {
6160 		nstat_enqueue_success(req->hdr.context, client, 0);
6161 	}
6162 
6163 	return result;
6164 }
6165 
6166 static errno_t
nstat_client_source_add(u_int64_t context,nstat_client * client,nstat_provider * provider,nstat_provider_cookie_t cookie,nstat_lock_status lock_status)6167 nstat_client_source_add(
6168 	u_int64_t               context,
6169 	nstat_client            *client,
6170 	nstat_provider          *provider,
6171 	nstat_provider_cookie_t cookie,
6172 	nstat_lock_status lock_status)
6173 {
6174 	if (lock_status == NSTAT_LOCK_NOTHELD) {
6175 		NSTAT_LOCK_EXCLUSIVE();
6176 	} else {
6177 		NSTAT_ASSERT_LOCKED_EXCLUSIVE();
6178 	}
6179 
6180 	// Fill out source added message if appropriate
6181 	errno_t result = 0;
6182 	mbuf_ref_t msg = NULL;
6183 	nstat_src_ref_t *srcrefp = NULL;
6184 
6185 	u_int64_t provider_filter_flags = client->ntc_provider_filters[provider->nstat_provider_id].npf_flags;
6186 	boolean_t tell_user = ((provider_filter_flags & NSTAT_FILTER_SUPPRESS_SRC_ADDED) == 0);
6187 	u_int32_t src_filter = (provider_filter_flags & NSTAT_FILTER_PROVIDER_NOZEROBYTES)? NSTAT_FILTER_NOZEROBYTES : 0;
6188 
6189 	if (provider_filter_flags & NSTAT_FILTER_TCP_NO_EARLY_CLOSE) {
6190 		src_filter |= NSTAT_FILTER_TCP_NO_EARLY_CLOSE;
6191 	}
6192 
6193 	do {
6194 		if (tell_user) {
6195 			unsigned int one = 1;
6196 
6197 			if (mbuf_allocpacket(MBUF_DONTWAIT, sizeof(nstat_msg_src_added),
6198 			    &one, &msg) != 0) {
6199 				NSTAT_NOTE_QUAL(nstat_src_add_no_buf, client, 0);
6200 				result = ENOMEM;
6201 				break;
6202 			}
6203 
6204 			mbuf_setlen(msg, sizeof(nstat_msg_src_added));
6205 			mbuf_pkthdr_setlen(msg, mbuf_len(msg));
6206 			bzero(m_mtod_current(msg), sizeof(nstat_msg_src_added));
6207 
6208 			nstat_msg_src_added *add = mtod(msg, nstat_msg_src_added *);
6209 			add->hdr.type = NSTAT_MSG_TYPE_SRC_ADDED;
6210 			assert(mbuf_len(msg) <= MAX_NSTAT_MSG_HDR_LENGTH);
6211 			add->hdr.length = (u_int16_t)mbuf_len(msg);
6212 			add->hdr.context = context;
6213 			add->provider = provider->nstat_provider_id;
6214 			srcrefp = &add->srcref;
6215 		}
6216 
6217 		// Allocate storage for the source
6218 		nstat_src *src = kalloc_type(struct nstat_src, Z_WAITOK);
6219 		if (src == NULL) {
6220 			NSTAT_NOTE_QUAL(nstat_src_add_no_src_mem, client, 0);
6221 			if (msg) {
6222 				mbuf_freem(msg);
6223 			}
6224 			result = ENOMEM;
6225 			break;
6226 		}
6227 
6228 		// Fill in the source, including picking an unused source ref
6229 
6230 		src->nts_srcref = nstat_client_next_src_ref(client);
6231 		if (srcrefp) {
6232 			*srcrefp = src->nts_srcref;
6233 		}
6234 
6235 		if (client->ntc_flags & NSTAT_FLAG_CLEANUP || src->nts_srcref == NSTAT_SRC_REF_INVALID) {
6236 			NSTAT_NOTE_QUAL(nstat_src_add_while_cleanup, client, 0);
6237 			kfree_type(struct nstat_src, src);
6238 			if (msg) {
6239 				mbuf_freem(msg);
6240 			}
6241 			result = EINVAL;
6242 			break;
6243 		}
6244 		src->nts_provider = provider;
6245 		src->nts_cookie = cookie;
6246 		src->nts_filter = src_filter;
6247 		src->nts_seq = 0;
6248 
6249 		if (msg) {
6250 			// send the source added message if appropriate
6251 			result = ctl_enqueuembuf(client->ntc_kctl, client->ntc_unit, msg, CTL_DATA_EOR);
6252 			if (result != 0) {
6253 				NSTAT_NOTE_SRC(nstat_src_add_send_err, client, src);
6254 				nstat_stats.nstat_srcaddedfailures += 1;
6255 				kfree_type(struct nstat_src, src);
6256 				mbuf_freem(msg);
6257 				break;
6258 			}
6259 		}
6260 		// Put the source in the list
6261 		TAILQ_INSERT_HEAD(&client->ntc_src_queue, src, nts_client_link);
6262 		src->nts_client = client;
6263 
6264 		NSTAT_GLOBAL_COUNT_INCREMENT(nstat_global_src_allocs);
6265 		NSTAT_GLOBAL_COUNT_INCREMENT_WITH_MAX(nstat_global_src_current, nstat_global_src_max);
6266 
6267 		client->ntc_metrics.nstat_src_current++;
6268 		if (client->ntc_metrics.nstat_src_current > client->ntc_metrics.nstat_src_max) {
6269 			client->ntc_metrics.nstat_src_max = client->ntc_metrics.nstat_src_current;
6270 		}
6271 
6272 		NSTAT_NOTE_SRC(nstat_src_add_success, client, src);
6273 	} while (0);
6274 
6275 	if (lock_status == NSTAT_LOCK_NOTHELD) {
6276 		NSTAT_UNLOCK_EXCLUSIVE();
6277 	}
6278 	return result;
6279 }
6280 
6281 static errno_t
nstat_client_handle_remove_request(nstat_client * client,mbuf_t m)6282 nstat_client_handle_remove_request(
6283 	nstat_client    *client,
6284 	mbuf_t          m)
6285 {
6286 	nstat_src_ref_t srcref = NSTAT_SRC_REF_INVALID;
6287 	nstat_src *src;
6288 
6289 	if (mbuf_copydata(m, offsetof(nstat_msg_rem_src_req, srcref), sizeof(srcref), &srcref) != 0) {
6290 		return EINVAL;
6291 	}
6292 
6293 	NSTAT_LOCK_EXCLUSIVE();
6294 
6295 	// Remove this source as we look for it
6296 	TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
6297 	{
6298 		if (src->nts_srcref == srcref) {
6299 			break;
6300 		}
6301 	}
6302 	if (src) {
6303 		TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
6304 	}
6305 
6306 	NSTAT_UNLOCK_EXCLUSIVE();
6307 
6308 	if (src) {
6309 		nstat_client_cleanup_source(client, src, FALSE);
6310 		NSTAT_NOTE_QUAL(nstat_remove_src_found, client, srcref);
6311 	} else {
6312 		NSTAT_NOTE_QUAL(nstat_remove_src_missed, client, srcref);
6313 	}
6314 
6315 	return src ? 0 : ENOENT;
6316 }
6317 
6318 static errno_t
nstat_client_handle_query_request(nstat_client * client,mbuf_t m)6319 nstat_client_handle_query_request(
6320 	nstat_client    *client,
6321 	mbuf_t          m)
6322 {
6323 	// TBD: handle this from another thread so we can enqueue a lot of data
6324 	// As written, if a client requests query all, this function will be
6325 	// called from their send of the request message. We will attempt to write
6326 	// responses and succeed until the buffer fills up. Since the clients thread
6327 	// is blocked on send, it won't be reading unless the client has two threads
6328 	// using this socket, one for read and one for write. Two threads probably
6329 	// won't work with this code anyhow since we don't have proper locking in
6330 	// place yet.
6331 	tailq_head_nstat_src    dead_list;
6332 	errno_t                 result = ENOENT;
6333 	nstat_msg_query_src_req req;
6334 
6335 	if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) {
6336 		return EINVAL;
6337 	}
6338 
6339 	TAILQ_INIT(&dead_list);
6340 	const boolean_t  all_srcs = (req.srcref == NSTAT_SRC_REF_ALL);
6341 
6342 	if (all_srcs) {
6343 		NSTAT_NOTE_QUAL(nstat_query_request_all, client, 0);
6344 	} else {
6345 		NSTAT_NOTE_QUAL(nstat_query_request_one, client, req.srcref);
6346 	}
6347 
6348 	NSTAT_LOCK_SHARED();
6349 
6350 	if (all_srcs) {
6351 		client->ntc_flags |= NSTAT_FLAG_REQCOUNTS;
6352 	}
6353 	nstat_src       *src, *tmpsrc;
6354 	u_int64_t       src_count = 0;
6355 	boolean_t       partial = FALSE;
6356 
6357 	/*
6358 	 * Error handling policy and sequence number generation is folded into
6359 	 * nstat_client_begin_query.
6360 	 */
6361 	partial = nstat_client_begin_query(client, &req.hdr);
6362 
6363 
6364 	TAILQ_FOREACH_SAFE(src, &client->ntc_src_queue, nts_client_link, tmpsrc)
6365 	{
6366 		int     gone = 0;
6367 
6368 		// XXX ignore IFACE types?
6369 		if (all_srcs || src->nts_srcref == req.srcref) {
6370 			if (nstat_client_reporting_allowed(client, src, 0)
6371 			    && (!partial || !all_srcs || src->nts_seq != client->ntc_seq)) {
6372 				if (all_srcs) {
6373 					result = nstat_client_append_counts(client, src, &gone);
6374 				} else {
6375 					result = nstat_client_send_counts(client, src, req.hdr.context, 0, &gone);
6376 				}
6377 
6378 				if (ENOMEM == result || ENOBUFS == result) {
6379 					/*
6380 					 * If the counts message failed to
6381 					 * enqueue then we should clear our flag so
6382 					 * that a client doesn't miss anything on
6383 					 * idle cleanup.  We skip the "gone"
6384 					 * processing in the hope that we may
6385 					 * catch it another time.
6386 					 */
6387 					NSTAT_NOTE_SRC(nstat_query_request_nobuf, client, src);
6388 					client->ntc_flags &= ~NSTAT_FLAG_REQCOUNTS;
6389 					break;
6390 				}
6391 				if (partial) {
6392 					/*
6393 					 * We skip over hard errors and
6394 					 * filtered sources.
6395 					 */
6396 					src->nts_seq = client->ntc_seq;
6397 					src_count++;
6398 				}
6399 			}
6400 		}
6401 
6402 		if (gone) {
6403 			// send one last descriptor message so client may see last state
6404 			// If we can't send the notification now, it
6405 			// will be sent in the idle cleanup.
6406 			if (NSTAT_LOCK_SHARED_TO_EXCLUSIVE()) {
6407 				NSTAT_NOTE_SRC(nstat_query_request_upgrade, client, src);
6408 				// Successfully upgraded the lock, now we can remove the source from the client
6409 				result = nstat_client_send_description(client, src, 0, 0);
6410 				if (result != 0) {
6411 					nstat_stats.nstat_control_send_description_failures++;
6412 					if (nstat_debug != 0) {
6413 						NSTAT_LOG_ERROR("nstat_client_send_description() %d", result);
6414 					}
6415 					client->ntc_flags &= ~NSTAT_FLAG_REQCOUNTS;
6416 					NSTAT_NOTE_SRC(nstat_query_request_nodesc, client, src);
6417 					NSTAT_LOCK_EXCLUSIVE_TO_SHARED();
6418 					break;
6419 				}
6420 
6421 				// pull src out of the list
6422 				TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
6423 				TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
6424 				NSTAT_LOCK_EXCLUSIVE_TO_SHARED();
6425 			} else {
6426 				// The upgrade failed and the shared lock has been dropped
6427 				// This should be rare.  Simply drop out here and have user level retry
6428 				// the poll, have the idle cleanup catch the "gone" source
6429 				NSTAT_NOTE_SRC(nstat_query_request_noupgrade, client, src);
6430 				NSTAT_LOCK_SHARED();
6431 				break;
6432 			}
6433 		}
6434 
6435 		if (all_srcs) {
6436 			if (src_count >= QUERY_CONTINUATION_SRC_COUNT) {
6437 				NSTAT_NOTE_SRC(nstat_query_request_limit, client, src);
6438 				break;
6439 			}
6440 			if ((src_count >= QUERY_CONTINUATION_MIN_SRC_COUNT) &&
6441 			    (NSTAT_LOCK_WOULD_YIELD())) {
6442 				// A possibly higher priority thread is waiting
6443 				// Exit from here and have user level initiate the next fragment
6444 				NSTAT_NOTE_SRC(nstat_query_request_yield, client, src);
6445 				break;
6446 			}
6447 		} else if (req.srcref == src->nts_srcref) {
6448 			break;
6449 		}
6450 	}
6451 
6452 	nstat_flush_accumulated_msgs(client);
6453 
6454 	u_int16_t flags = 0;
6455 	if (req.srcref == NSTAT_SRC_REF_ALL) {
6456 		flags = nstat_client_end_query(client, src, partial);
6457 	}
6458 
6459 	NSTAT_UNLOCK_SHARED();
6460 
6461 	/*
6462 	 * If an error occurred enqueueing data, then allow the error to
6463 	 * propagate to nstat_client_send. This way, the error is sent to
6464 	 * user-level.
6465 	 */
6466 	if (all_srcs && ENOMEM != result && ENOBUFS != result) {
6467 		nstat_enqueue_success(req.hdr.context, client, flags);
6468 		result = 0;
6469 	}
6470 
6471 	while ((src = TAILQ_FIRST(&dead_list))) {
6472 		TAILQ_REMOVE(&dead_list, src, nts_client_link);
6473 		nstat_client_cleanup_source(client, src, FALSE);
6474 	}
6475 
6476 	return result;
6477 }
6478 
6479 static errno_t
nstat_client_handle_get_src_description(nstat_client * client,mbuf_t m)6480 nstat_client_handle_get_src_description(
6481 	nstat_client    *client,
6482 	mbuf_t          m)
6483 {
6484 	nstat_msg_get_src_description   req;
6485 	errno_t result = ENOENT;
6486 	nstat_src *src;
6487 
6488 	if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) {
6489 		return EINVAL;
6490 	}
6491 
6492 	NSTAT_LOCK_SHARED();
6493 	u_int64_t src_count = 0;
6494 	boolean_t partial = FALSE;
6495 	const boolean_t all_srcs = (req.srcref == NSTAT_SRC_REF_ALL);
6496 
6497 	if (all_srcs) {
6498 		NSTAT_NOTE_QUAL(nstat_query_description_all, client, 0);
6499 	} else {
6500 		NSTAT_NOTE_QUAL(nstat_query_description_one, client, req.srcref);
6501 	}
6502 
6503 	/*
6504 	 * Error handling policy and sequence number generation is folded into
6505 	 * nstat_client_begin_query.
6506 	 */
6507 	partial = nstat_client_begin_query(client, &req.hdr);
6508 
6509 	TAILQ_FOREACH(src, &client->ntc_src_queue, nts_client_link)
6510 	{
6511 		if (all_srcs || src->nts_srcref == req.srcref) {
6512 			if (nstat_client_reporting_allowed(client, src, 0)
6513 			    && (!all_srcs || !partial || src->nts_seq != client->ntc_seq)) {
6514 				if (all_srcs) {
6515 					result = nstat_client_append_description(client, src);
6516 				} else {
6517 					result = nstat_client_send_description(client, src, req.hdr.context, 0);
6518 				}
6519 
6520 				if (ENOMEM == result || ENOBUFS == result) {
6521 					/*
6522 					 * If the description message failed to
6523 					 * enqueue then we give up for now.
6524 					 */
6525 					break;
6526 				}
6527 				if (partial) {
6528 					/*
6529 					 * Note, we skip over hard errors and
6530 					 * filtered sources.
6531 					 */
6532 					src->nts_seq = client->ntc_seq;
6533 					src_count++;
6534 					if (src_count >= QUERY_CONTINUATION_SRC_COUNT) {
6535 						NSTAT_NOTE_SRC(nstat_query_description_limit, client, src);
6536 						break;
6537 					}
6538 					if ((src_count >= QUERY_CONTINUATION_MIN_SRC_COUNT) &&
6539 					    (NSTAT_LOCK_WOULD_YIELD())) {
6540 						// A possibly higher priority thread is waiting
6541 						// Exit from here and have user level initiate the next fragment
6542 						NSTAT_NOTE_SRC(nstat_query_description_yield, client, src);
6543 						break;
6544 					}
6545 				}
6546 			}
6547 
6548 			if (!all_srcs) {
6549 				break;
6550 			}
6551 		}
6552 	}
6553 	nstat_flush_accumulated_msgs(client);
6554 
6555 	u_int16_t flags = 0;
6556 	if (req.srcref == NSTAT_SRC_REF_ALL) {
6557 		flags = nstat_client_end_query(client, src, partial);
6558 	}
6559 
6560 	NSTAT_UNLOCK_SHARED();
6561 	/*
6562 	 * If an error occurred enqueueing data, then allow the error to
6563 	 * propagate to nstat_client_send. This way, the error is sent to
6564 	 * user-level.
6565 	 */
6566 	if (all_srcs && ENOMEM != result && ENOBUFS != result) {
6567 		nstat_enqueue_success(req.hdr.context, client, flags);
6568 		result = 0;
6569 	}
6570 
6571 	return result;
6572 }
6573 
6574 static void
nstat_send_error(nstat_client * client,u_int64_t context,u_int32_t error)6575 nstat_send_error(
6576 	nstat_client *client,
6577 	u_int64_t context,
6578 	u_int32_t error)
6579 {
6580 	errno_t result;
6581 	struct nstat_msg_error  err;
6582 
6583 	bzero(&err, sizeof(err));
6584 	err.hdr.type = NSTAT_MSG_TYPE_ERROR;
6585 	err.hdr.length = sizeof(err);
6586 	err.hdr.context = context;
6587 	err.error = error;
6588 
6589 	result = ctl_enqueuedata(client->ntc_kctl, client->ntc_unit, &err,
6590 	    sizeof(err), CTL_DATA_EOR | CTL_DATA_CRIT);
6591 	if (result != 0) {
6592 		nstat_stats.nstat_msgerrorfailures++;
6593 	}
6594 }
6595 
6596 static boolean_t
nstat_client_begin_query(nstat_client * client,const nstat_msg_hdr * hdrp)6597 nstat_client_begin_query(
6598 	nstat_client        *client,
6599 	const nstat_msg_hdr *hdrp)
6600 {
6601 	boolean_t partial = FALSE;
6602 
6603 	if (hdrp->flags & NSTAT_MSG_HDR_FLAG_CONTINUATION) {
6604 		/* A partial query all has been requested. */
6605 		partial = TRUE;
6606 
6607 		if (client->ntc_context != hdrp->context) {
6608 			if (client->ntc_context != 0) {
6609 				nstat_send_error(client, client->ntc_context, EAGAIN);
6610 			}
6611 
6612 			/* Initialize client for a partial query all. */
6613 			client->ntc_context = hdrp->context;
6614 			client->ntc_seq++;
6615 		}
6616 	}
6617 
6618 	return partial;
6619 }
6620 
6621 static u_int16_t
nstat_client_end_query(nstat_client * client,nstat_src * last_src,boolean_t partial)6622 nstat_client_end_query(
6623 	nstat_client *client,
6624 	nstat_src *last_src,
6625 	boolean_t partial)
6626 {
6627 	u_int16_t flags = 0;
6628 
6629 	if (last_src == NULL || !partial) {
6630 		/*
6631 		 * We iterated through the entire srcs list or exited early
6632 		 * from the loop when a partial update was not requested (an
6633 		 * error occurred), so clear context to indicate internally
6634 		 * that the query is finished.
6635 		 */
6636 		client->ntc_context = 0;
6637 	} else {
6638 		/*
6639 		 * Indicate to userlevel to make another partial request as
6640 		 * there are still sources left to be reported.
6641 		 */
6642 		flags |= NSTAT_MSG_HDR_FLAG_CONTINUATION;
6643 	}
6644 
6645 	return flags;
6646 }
6647 
6648 static errno_t
nstat_client_handle_get_update(nstat_client * client,mbuf_t m)6649 nstat_client_handle_get_update(
6650 	nstat_client    *client,
6651 	mbuf_t          m)
6652 {
6653 	nstat_msg_query_src_req req;
6654 
6655 	if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) {
6656 		return EINVAL;
6657 	}
6658 
6659 	NSTAT_LOCK_SHARED();
6660 
6661 	client->ntc_flags |= NSTAT_FLAG_SUPPORTS_UPDATES;
6662 
6663 	errno_t         result = ENOENT;
6664 	nstat_src       *src, *tmpsrc;
6665 	tailq_head_nstat_src dead_list;
6666 	u_int64_t src_count = 0;
6667 	boolean_t partial = FALSE;
6668 	const boolean_t all_srcs = (req.srcref == NSTAT_SRC_REF_ALL);
6669 	TAILQ_INIT(&dead_list);
6670 
6671 	if (all_srcs) {
6672 		NSTAT_NOTE_QUAL(nstat_query_update_all, client, 0);
6673 	} else {
6674 		NSTAT_NOTE_QUAL(nstat_query_update_one, client, req.srcref);
6675 	}
6676 
6677 	/*
6678 	 * Error handling policy and sequence number generation is folded into
6679 	 * nstat_client_begin_query.
6680 	 */
6681 	partial = nstat_client_begin_query(client, &req.hdr);
6682 
6683 	TAILQ_FOREACH_SAFE(src, &client->ntc_src_queue, nts_client_link, tmpsrc) {
6684 		int gone = 0;
6685 		if (all_srcs) {
6686 			// Check to see if we should handle this source or if we're still skipping to find where to continue
6687 			if ((FALSE == partial || src->nts_seq != client->ntc_seq)) {
6688 				u_int64_t suppression_flags = (src->nts_reported)? NSTAT_FILTER_SUPPRESS_BORING_POLL: 0;
6689 				if (nstat_client_reporting_allowed(client, src, suppression_flags)) {
6690 					result = nstat_client_append_update(client, src, &gone);
6691 					if (ENOMEM == result || ENOBUFS == result) {
6692 						/*
6693 						 * If the update message failed to
6694 						 * enqueue then give up.
6695 						 */
6696 						NSTAT_NOTE_SRC(nstat_query_update_nobuf, client, src);
6697 						break;
6698 					}
6699 					if (partial) {
6700 						/*
6701 						 * We skip over hard errors and
6702 						 * filtered sources.
6703 						 */
6704 						src->nts_seq = client->ntc_seq;
6705 						src_count++;
6706 					}
6707 				}
6708 			}
6709 		} else if (src->nts_srcref == req.srcref) {
6710 			if (nstat_client_reporting_allowed(client, src, 0)) {
6711 				result = nstat_client_send_update(client, src, req.hdr.context, 0, 0, &gone);
6712 			}
6713 		}
6714 
6715 		if (gone) {
6716 			if (NSTAT_LOCK_SHARED_TO_EXCLUSIVE()) {
6717 				// Successfully upgraded the lock, now we can remove the source from the client
6718 				// pull src out of the list
6719 				TAILQ_REMOVE(&client->ntc_src_queue, src, nts_client_link);
6720 				TAILQ_INSERT_TAIL(&dead_list, src, nts_client_link);
6721 				NSTAT_LOCK_EXCLUSIVE_TO_SHARED();
6722 				NSTAT_NOTE_SRC(nstat_query_update_upgrade, client, src);
6723 			} else {
6724 				// The upgrade failed and the shared lock has been dropped
6725 				// This should be rare.  Simply drop out here and have user level retry
6726 				// the poll, have the idle cleanup catch the "gone" source
6727 				NSTAT_NOTE_SRC(nstat_query_update_noupgrade, client, src);
6728 				NSTAT_LOCK_SHARED();
6729 				break;
6730 			}
6731 		}
6732 
6733 		if (!all_srcs && req.srcref == src->nts_srcref) {
6734 			break;
6735 		}
6736 		if (src_count >= QUERY_CONTINUATION_SRC_COUNT) {
6737 			NSTAT_NOTE_SRC(nstat_query_update_limit, client, src);
6738 			break;
6739 		}
6740 		if ((src_count >= QUERY_CONTINUATION_MIN_SRC_COUNT) &&
6741 		    (NSTAT_LOCK_WOULD_YIELD())) {
6742 			// A possibly higher priority thread is waiting
6743 			// Exit from here and have user level initiate the next fragment
6744 			NSTAT_NOTE_SRC(nstat_query_update_yield, client, src);
6745 			break;
6746 		}
6747 	}
6748 
6749 	nstat_flush_accumulated_msgs(client);
6750 
6751 
6752 	u_int16_t flags = 0;
6753 	if (req.srcref == NSTAT_SRC_REF_ALL) {
6754 		flags = nstat_client_end_query(client, src, partial);
6755 	}
6756 	NSTAT_UNLOCK_SHARED();
6757 
6758 	/*
6759 	 * If an error occurred enqueueing data, then allow the error to
6760 	 * propagate to nstat_client_send. This way, the error is sent to
6761 	 * user-level.
6762 	 */
6763 	if (all_srcs && ENOMEM != result && ENOBUFS != result) {
6764 		nstat_enqueue_success(req.hdr.context, client, flags);
6765 		result = 0;
6766 	}
6767 
6768 	while ((src = TAILQ_FIRST(&dead_list))) {
6769 		TAILQ_REMOVE(&dead_list, src, nts_client_link);
6770 		// release src and send notification
6771 		nstat_client_cleanup_source(client, src, FALSE);
6772 	}
6773 
6774 	return result;
6775 }
6776 
6777 static errno_t
nstat_client_handle_subscribe_sysinfo(nstat_client * client)6778 nstat_client_handle_subscribe_sysinfo(
6779 	nstat_client    *client)
6780 {
6781 	errno_t result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
6782 
6783 	if (result != 0) {
6784 		return result;
6785 	}
6786 
6787 	NSTAT_LOCK_EXCLUSIVE();
6788 	client->ntc_flags |= NSTAT_FLAG_SYSINFO_SUBSCRIBED;
6789 	NSTAT_UNLOCK_EXCLUSIVE();
6790 
6791 	return 0;
6792 }
6793 
6794 static errno_t
nstat_client_send(kern_ctl_ref kctl,u_int32_t unit,void * uinfo,mbuf_t m,__unused int flags)6795 nstat_client_send(
6796 	kern_ctl_ref    kctl,
6797 	u_int32_t       unit,
6798 	void            *uinfo,
6799 	mbuf_t          m,
6800 	__unused int    flags)
6801 {
6802 	nstat_client     *client = (nstat_client*)uinfo;
6803 	struct nstat_msg_hdr    *hdr;
6804 	struct nstat_msg_hdr    storage;
6805 	errno_t                                 result = 0;
6806 
6807 	if (mbuf_pkthdr_len(m) < sizeof(*hdr)) {
6808 		// Is this the right thing to do?
6809 		mbuf_freem(m);
6810 		return EINVAL;
6811 	}
6812 
6813 	if (mbuf_len(m) >= sizeof(*hdr)) {
6814 		hdr = mtod(m, struct nstat_msg_hdr *);
6815 	} else {
6816 		mbuf_copydata(m, 0, sizeof(storage), &storage);
6817 		hdr = &storage;
6818 	}
6819 
6820 	// Legacy clients may not set the length
6821 	// Those clients are likely not setting the flags either
6822 	// Fix everything up so old clients continue to work
6823 	if (hdr->length != mbuf_pkthdr_len(m)) {
6824 		hdr->flags = 0;
6825 		assert(mbuf_pkthdr_len(m) <= MAX_NSTAT_MSG_HDR_LENGTH);
6826 		hdr->length = (u_int16_t)mbuf_pkthdr_len(m);
6827 		if (hdr == &storage) {
6828 			mbuf_copyback(m, 0, sizeof(*hdr), hdr, MBUF_DONTWAIT);
6829 		}
6830 	}
6831 
6832 	lck_mtx_lock(&client->ntc_user_mtx); /* Prevent misbehaving clients from overlapping calls */
6833 	switch (hdr->type) {
6834 	case NSTAT_MSG_TYPE_ADD_SRC:
6835 		result = nstat_client_handle_add_request(client, m);
6836 		break;
6837 
6838 	case NSTAT_MSG_TYPE_ADD_ALL_SRCS:
6839 		result = nstat_client_handle_add_all(client, m);
6840 		break;
6841 
6842 	case NSTAT_MSG_TYPE_REM_SRC:
6843 		result = nstat_client_handle_remove_request(client, m);
6844 		break;
6845 
6846 	case NSTAT_MSG_TYPE_QUERY_SRC:
6847 		result = nstat_client_handle_query_request(client, m);
6848 		break;
6849 
6850 	case NSTAT_MSG_TYPE_GET_SRC_DESC:
6851 		result = nstat_client_handle_get_src_description(client, m);
6852 		break;
6853 
6854 	case NSTAT_MSG_TYPE_GET_UPDATE:
6855 		result = nstat_client_handle_get_update(client, m);
6856 		break;
6857 
6858 	case NSTAT_MSG_TYPE_SUBSCRIBE_SYSINFO:
6859 		result = nstat_client_handle_subscribe_sysinfo(client);
6860 		break;
6861 
6862 	default:
6863 		result = EINVAL;
6864 		break;
6865 	}
6866 
6867 	if (result != 0) {
6868 		struct nstat_msg_error  err;
6869 
6870 		bzero(&err, sizeof(err));
6871 		err.hdr.type = NSTAT_MSG_TYPE_ERROR;
6872 		err.hdr.length = (u_int16_t)(sizeof(err) + mbuf_pkthdr_len(m));
6873 		err.hdr.context = hdr->context;
6874 		err.error = result;
6875 
6876 		if (mbuf_prepend(&m, sizeof(err), MBUF_DONTWAIT) == 0 &&
6877 		    mbuf_copyback(m, 0, sizeof(err), &err, MBUF_DONTWAIT) == 0) {
6878 			result = ctl_enqueuembuf(kctl, unit, m, CTL_DATA_EOR | CTL_DATA_CRIT);
6879 			if (result != 0) {
6880 				mbuf_freem(m);
6881 			}
6882 			m = NULL;
6883 		}
6884 
6885 		if (result != 0) {
6886 			// Unable to prepend the error to the request - just send the error
6887 			err.hdr.length = sizeof(err);
6888 			result = ctl_enqueuedata(kctl, unit, &err, sizeof(err),
6889 			    CTL_DATA_EOR | CTL_DATA_CRIT);
6890 			if (result != 0) {
6891 				nstat_stats.nstat_msgerrorfailures += 1;
6892 			}
6893 		}
6894 		nstat_stats.nstat_handle_msg_failures += 1;
6895 	}
6896 	lck_mtx_unlock(&client->ntc_user_mtx);
6897 
6898 	if (m) {
6899 		mbuf_freem(m);
6900 	}
6901 	return result;
6902 }
6903 
6904 
6905 /* Performs interface matching based on NSTAT_IFNET_IS… filter flags provided by an external caller */
6906 static bool
nstat_interface_matches_filter_flag(uint32_t filter_flags,struct ifnet * ifp)6907 nstat_interface_matches_filter_flag(uint32_t filter_flags, struct ifnet *ifp)
6908 {
6909 	bool result = false;
6910 
6911 	if (ifp) {
6912 		uint32_t flag_mask = (NSTAT_FILTER_IFNET_FLAGS & ~(NSTAT_IFNET_IS_NON_LOCAL | NSTAT_IFNET_IS_LOCAL));
6913 		filter_flags &= flag_mask;
6914 
6915 		uint32_t flags = nstat_ifnet_to_flags(ifp);
6916 		if (filter_flags & flags) {
6917 			result = true;
6918 		}
6919 	}
6920 	return result;
6921 }
6922 
6923 
6924 static int
progress_indicators_for_interface(unsigned int ifindex,uint64_t recentflow_maxduration,uint32_t filter_flags,uint64_t transport_protocol_mask,struct nstat_progress_indicators * indicators)6925 progress_indicators_for_interface(unsigned int ifindex, uint64_t recentflow_maxduration, uint32_t filter_flags, uint64_t transport_protocol_mask, struct nstat_progress_indicators *indicators)
6926 {
6927 	int error = 0;
6928 	struct inpcb *inp;
6929 	uint64_t min_recent_start_time;
6930 	bool update_tcp_indicators = ((transport_protocol_mask & PR_PROTO_TCP) == PR_PROTO_TCP) || (transport_protocol_mask == 0);
6931 	bool update_quic_indicators = (transport_protocol_mask & PR_PROTO_QUIC) == PR_PROTO_QUIC;
6932 #if SKYWALK
6933 	struct nstat_tu_shadow *shad;
6934 #endif /* SKYWALK */
6935 
6936 	min_recent_start_time = mach_continuous_time() - recentflow_maxduration;
6937 	bzero(indicators, sizeof(*indicators));
6938 
6939 	if (nstat_debug != 0) {
6940 		/* interface index -1 may be passed in to only match against the filters specified in the flags */
6941 		if (ifindex < UINT_MAX) {
6942 			NSTAT_LOG_DEBUG("for interface index %u with flags %x", ifindex, filter_flags);
6943 		} else {
6944 			NSTAT_LOG_DEBUG("for matching interface with flags %x", filter_flags);
6945 		}
6946 	}
6947 
6948 	if (update_tcp_indicators) {
6949 		lck_rw_lock_shared(&tcbinfo.ipi_lock);
6950 		/*
6951 		 * For progress indicators we don't need to special case TCP to collect time wait connections
6952 		 */
6953 		LIST_FOREACH(inp, tcbinfo.ipi_listhead, inp_list)
6954 		{
6955 			struct tcpcb  *tp = intotcpcb(inp);
6956 			/* radar://57100452
6957 			 * The conditional logic implemented below performs an *inclusive* match based on the desired interface index in addition to any filter values.
6958 			 * While the general expectation is that only one criteria normally is used for queries, the capability exists satisfy any eccentric future needs.
6959 			 */
6960 			if (tp && inp->inp_state != INPCB_STATE_DEAD && inp->inp_last_outifp &&
6961 			    /* matches the given interface index, or against any provided filter flags */
6962 			    (((inp->inp_last_outifp->if_index == ifindex) ||
6963 			    nstat_interface_matches_filter_flag(filter_flags, inp->inp_last_outifp)) &&
6964 			    /* perform flow state matching based any provided filter flags */
6965 			    (((filter_flags & (NSTAT_IFNET_IS_NON_LOCAL | NSTAT_IFNET_IS_LOCAL)) == 0) ||
6966 			    ((filter_flags & NSTAT_IFNET_IS_NON_LOCAL) && !(tp->t_flags & TF_LOCAL)) ||
6967 			    ((filter_flags & NSTAT_IFNET_IS_LOCAL) && (tp->t_flags & TF_LOCAL))))) {
6968 				struct tcp_conn_status connstatus;
6969 
6970 				if (nstat_debug != 0) {
6971 					NSTAT_LOG_DEBUG("*matched non-Skywalk* [filter match: %d]", nstat_interface_matches_filter_flag(filter_flags, inp->inp_last_outifp));
6972 				}
6973 
6974 				indicators->np_numflows++;
6975 				tcp_get_connectivity_status(tp, &connstatus);
6976 				if (connstatus.write_probe_failed) {
6977 					indicators->np_write_probe_fails++;
6978 				}
6979 				if (connstatus.read_probe_failed) {
6980 					indicators->np_read_probe_fails++;
6981 				}
6982 				if (connstatus.conn_probe_failed) {
6983 					indicators->np_conn_probe_fails++;
6984 				}
6985 				if (inp->inp_start_timestamp > min_recent_start_time) {
6986 					uint64_t flow_count;
6987 
6988 					indicators->np_recentflows++;
6989 					flow_count = os_atomic_load(&inp->inp_stat->rxbytes, relaxed);
6990 					indicators->np_recentflows_rxbytes += flow_count;
6991 					flow_count = os_atomic_load(&inp->inp_stat->txbytes, relaxed);
6992 					indicators->np_recentflows_txbytes += flow_count;
6993 
6994 					indicators->np_recentflows_rxooo += tp->t_stat.rxoutoforderbytes;
6995 					indicators->np_recentflows_rxdup += tp->t_stat.rxduplicatebytes;
6996 					indicators->np_recentflows_retx += tp->t_stat.txretransmitbytes;
6997 					if (tp->snd_max - tp->snd_una) {
6998 						indicators->np_recentflows_unacked++;
6999 					}
7000 				}
7001 			}
7002 		}
7003 		lck_rw_done(&tcbinfo.ipi_lock);
7004 	}
7005 
7006 #if SKYWALK
7007 	u_int32_t locality_flags = (filter_flags & (NSTAT_IFNET_IS_LOCAL | NSTAT_IFNET_IS_NON_LOCAL));
7008 	u_int32_t flag_mask = (NSTAT_FILTER_IFNET_FLAGS & ~(NSTAT_IFNET_IS_NON_LOCAL | NSTAT_IFNET_IS_LOCAL));
7009 	u_int32_t interface_flags = (filter_flags & flag_mask);
7010 
7011 	NSTAT_LOCK_SHARED();
7012 
7013 	TAILQ_FOREACH(shad, &nstat_userprot_shad_head, shad_link) {
7014 		assert(shad->shad_magic == TU_SHADOW_MAGIC);
7015 
7016 		bool consider_shad = false;
7017 		if (shad->shad_live) {
7018 			if (shad->shad_provider == NSTAT_PROVIDER_QUIC_USERLAND) {
7019 				consider_shad = update_quic_indicators;
7020 			} else if (shad->shad_provider == NSTAT_PROVIDER_TCP_USERLAND) {
7021 				consider_shad = update_tcp_indicators;
7022 			}
7023 		}
7024 
7025 		if (consider_shad) {
7026 			u_int32_t ifflags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
7027 			nstat_progress_digest digest;
7028 			bzero(&digest, sizeof(digest));
7029 
7030 			// fetch ifflags and digest from necp_client
7031 			bool result = (*shad->shad_getvals_fn)(shad->shad_provider_context, &ifflags, &digest, NULL, NULL);
7032 			error = (result)? 0 : EIO;
7033 			if (error) {
7034 				NSTAT_LOG_ERROR("nstat get ifflags and progressdigest returned %d", error);
7035 				continue;
7036 			}
7037 			if (ifflags & (NSTAT_IFNET_FLOWSWITCH_VALUE_UNOBTAINABLE | NSTAT_IFNET_ROUTE_VALUE_UNOBTAINABLE)) {
7038 				NSTAT_LOG_ERROR("nstat get ifflags and progressdigest resulted in Skywalk error, ifflags=%x", ifflags);
7039 				continue;
7040 			}
7041 
7042 			if ((digest.ifindex == (u_int32_t)ifindex) ||
7043 			    ((ifindex == UINT_MAX) && ((ifflags & interface_flags) != 0))) {
7044 				// a match on either a requested interface index or on interface type
7045 				if ((locality_flags != 0) && ((locality_flags & ifflags) == 0)) {
7046 					continue;
7047 				}
7048 
7049 				if (nstat_debug != 0) {
7050 					NSTAT_LOG_DEBUG("*matched Skywalk* [ifindex=%u digest.ifindex=%u filter_flags=%x ifflags=%x]", ifindex, digest.ifindex, filter_flags, ifflags);
7051 				}
7052 
7053 				indicators->np_numflows++;
7054 				if (digest.connstatus.write_probe_failed) {
7055 					indicators->np_write_probe_fails++;
7056 				}
7057 				if (digest.connstatus.read_probe_failed) {
7058 					indicators->np_read_probe_fails++;
7059 				}
7060 				if (digest.connstatus.conn_probe_failed) {
7061 					indicators->np_conn_probe_fails++;
7062 				}
7063 				if (shad->shad_start_timestamp > min_recent_start_time) {
7064 					indicators->np_recentflows++;
7065 					indicators->np_recentflows_rxbytes += digest.rxbytes;
7066 					indicators->np_recentflows_txbytes += digest.txbytes;
7067 					indicators->np_recentflows_rxooo += digest.rxoutoforderbytes;
7068 					indicators->np_recentflows_rxdup += digest.rxduplicatebytes;
7069 					indicators->np_recentflows_retx += digest.txretransmit;
7070 					if (digest.txunacked) {
7071 						indicators->np_recentflows_unacked++;
7072 					}
7073 				}
7074 			}
7075 		}
7076 	}
7077 
7078 	NSTAT_UNLOCK_SHARED();
7079 
7080 #endif /* SKYWALK */
7081 	return error;
7082 }
7083 
7084 
7085 static int
tcp_progress_probe_enable_for_interface(unsigned int ifindex,uint32_t filter_flags,uint32_t enable_flags)7086 tcp_progress_probe_enable_for_interface(unsigned int ifindex, uint32_t filter_flags, uint32_t enable_flags)
7087 {
7088 	int error = 0;
7089 	struct ifnet *ifp;
7090 
7091 	if (nstat_debug != 0) {
7092 		NSTAT_LOG_DEBUG("for interface index %u with flags %d", ifindex, filter_flags);
7093 	}
7094 
7095 	ifnet_head_lock_shared();
7096 	TAILQ_FOREACH(ifp, &ifnet_head, if_link)
7097 	{
7098 		if ((ifp->if_index == ifindex) ||
7099 		    nstat_interface_matches_filter_flag(filter_flags, ifp)) {
7100 			if (nstat_debug != 0) {
7101 				NSTAT_LOG_DEBUG("*matched* interface index %d, enable: %d", ifp->if_index, enable_flags);
7102 			}
7103 			error = if_probe_connectivity(ifp, enable_flags);
7104 			if (error) {
7105 				NSTAT_LOG_ERROR("(%d) - nstat set tcp probe %d for interface index %d", error, enable_flags, ifp->if_index);
7106 			}
7107 		}
7108 	}
7109 	ifnet_head_done();
7110 
7111 	return error;
7112 }
7113 
7114 
7115 static int
ntstat_progress_indicators(struct sysctl_req * req)7116 ntstat_progress_indicators(struct sysctl_req *req)
7117 {
7118 	struct nstat_progress_indicators indicators = {};
7119 	int error = 0;
7120 	struct nstat_progress_req requested;
7121 
7122 	if (priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0) != 0) {
7123 		return EACCES;
7124 	}
7125 	if (req->newptr == USER_ADDR_NULL) {
7126 		return EINVAL;
7127 	}
7128 	if (req->newlen < sizeof(requested)) {
7129 		return EINVAL;
7130 	}
7131 	error = SYSCTL_IN(req, &requested, sizeof(requested));
7132 	if (error != 0) {
7133 		return error;
7134 	}
7135 	error = progress_indicators_for_interface((unsigned int)requested.np_ifindex, requested.np_recentflow_maxduration, (uint32_t)requested.np_filter_flags, requested.np_transport_protocol_mask, &indicators);
7136 	if (error != 0) {
7137 		return error;
7138 	}
7139 	error = SYSCTL_OUT(req, &indicators, sizeof(indicators));
7140 	return error;
7141 }
7142 
7143 
7144 __private_extern__ int
ntstat_tcp_progress_enable(struct sysctl_req * req)7145 ntstat_tcp_progress_enable(struct sysctl_req *req)
7146 {
7147 	int error = 0;
7148 	struct tcpprobereq requested;
7149 
7150 	if (priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0) != 0) {
7151 		return EACCES;
7152 	}
7153 	if (req->newptr == USER_ADDR_NULL) {
7154 		return EINVAL;
7155 	}
7156 	if (req->newlen < sizeof(requested)) {
7157 		return EINVAL;
7158 	}
7159 	error = SYSCTL_IN(req, &requested, sizeof(requested));
7160 	if (error != 0) {
7161 		return error;
7162 	}
7163 	error = tcp_progress_probe_enable_for_interface((unsigned int)requested.ifindex, (uint32_t)requested.filter_flags, (uint32_t)requested.enable);
7164 
7165 	return error;
7166 }
7167 
7168 
7169 #if SKYWALK
7170 
7171 #pragma mark -- netstat support for user level providers --
7172 
7173 static int
nstat_gather_flow_data(nstat_provider_id_t provider,nstat_flow_data * __counted_by (n)flow_data_start,int n)7174 nstat_gather_flow_data(nstat_provider_id_t provider, nstat_flow_data *__counted_by(n)flow_data_start, int n)
7175 {
7176 	nstat_flow_data * flow_data = flow_data_start;
7177 	struct nstat_tu_shadow *shad;
7178 	int prepared = 0;
7179 	errno_t err;
7180 
7181 	NSTAT_ASSERT_LOCKED();
7182 
7183 	TAILQ_FOREACH(shad, &nstat_userprot_shad_head, shad_link) {
7184 		assert(shad->shad_magic == TU_SHADOW_MAGIC);
7185 
7186 		if ((shad->shad_provider == provider) && (shad->shad_live)) {
7187 			if (prepared >= n) {
7188 				break;
7189 			}
7190 			err = nstat_userland_tu_copy_descriptor((nstat_provider_cookie_t) shad,
7191 			    &flow_data->flow_descriptor, sizeof(flow_data->flow_descriptor));
7192 
7193 			if (err != 0) {
7194 				NSTAT_LOG_ERROR("nstat_userland_tu_copy_descriptor  returned %d", err);
7195 			}
7196 			err = nstat_userland_tu_counts((nstat_provider_cookie_t) shad,
7197 			    &flow_data->counts, NULL);
7198 			if (err != 0) {
7199 				NSTAT_LOG_ERROR("nstat_userland_tu_counts  returned %d", err);
7200 			}
7201 			flow_data++;
7202 			prepared++;
7203 		}
7204 	}
7205 	return prepared;
7206 }
7207 
7208 static void
nstat_userland_to_xinpcb_n(nstat_provider_id_t provider,nstat_flow_data * flow_data,struct xinpcb_n * xinp)7209 nstat_userland_to_xinpcb_n(nstat_provider_id_t provider, nstat_flow_data *flow_data, struct xinpcb_n *xinp)
7210 {
7211 	xinp->xi_len = sizeof(struct xinpcb_n);
7212 	xinp->xi_kind = XSO_INPCB;
7213 
7214 	if (provider == NSTAT_PROVIDER_TCP_USERLAND) {
7215 		nstat_tcp_descriptor *desc = &flow_data->flow_descriptor.tcp_descriptor;
7216 		struct sockaddr_in *sa = &desc->local.v4;
7217 		if (sa->sin_family == AF_INET) {
7218 			xinp->inp_vflag = INP_IPV4;
7219 			xinp->inp_laddr = desc->local.v4.sin_addr;
7220 			xinp->inp_lport = desc->local.v4.sin_port;
7221 			xinp->inp_faddr = desc->remote.v4.sin_addr;
7222 			xinp->inp_fport = desc->remote.v4.sin_port;
7223 		} else if (sa->sin_family == AF_INET6) {
7224 			xinp->inp_vflag = INP_IPV6;
7225 			xinp->in6p_laddr = desc->local.v6.sin6_addr;
7226 			xinp->in6p_lport = desc->local.v6.sin6_port;
7227 			xinp->in6p_faddr = desc->remote.v6.sin6_addr;
7228 			xinp->in6p_fport = desc->remote.v6.sin6_port;
7229 		}
7230 	} else if (provider == NSTAT_PROVIDER_UDP_USERLAND) {
7231 		nstat_udp_descriptor *desc = &flow_data->flow_descriptor.udp_descriptor;
7232 		struct sockaddr_in *sa = &desc->local.v4;
7233 		if (sa->sin_family == AF_INET) {
7234 			xinp->inp_vflag = INP_IPV4;
7235 			xinp->inp_laddr = desc->local.v4.sin_addr;
7236 			xinp->inp_lport = desc->local.v4.sin_port;
7237 			xinp->inp_faddr = desc->remote.v4.sin_addr;
7238 			xinp->inp_fport = desc->remote.v4.sin_port;
7239 		} else if (sa->sin_family == AF_INET6) {
7240 			xinp->inp_vflag = INP_IPV6;
7241 			xinp->in6p_laddr = desc->local.v6.sin6_addr;
7242 			xinp->in6p_lport = desc->local.v6.sin6_port;
7243 			xinp->in6p_faddr = desc->remote.v6.sin6_addr;
7244 			xinp->in6p_fport = desc->remote.v6.sin6_port;
7245 		}
7246 	}
7247 }
7248 
7249 static void
nstat_userland_to_xsocket_n(nstat_provider_id_t provider,nstat_flow_data * flow_data,struct xsocket_n * xso)7250 nstat_userland_to_xsocket_n(nstat_provider_id_t provider, nstat_flow_data *flow_data, struct xsocket_n *xso)
7251 {
7252 	xso->xso_len = sizeof(struct xsocket_n);
7253 	xso->xso_kind = XSO_SOCKET;
7254 
7255 	if (provider == NSTAT_PROVIDER_TCP_USERLAND) {
7256 		nstat_tcp_descriptor *desc = &flow_data->flow_descriptor.tcp_descriptor;
7257 		xso->xso_protocol = IPPROTO_TCP;
7258 		xso->so_e_pid = desc->epid;
7259 		xso->so_last_pid = desc->pid;
7260 	} else {
7261 		nstat_udp_descriptor *desc = &flow_data->flow_descriptor.udp_descriptor;
7262 		xso->xso_protocol = IPPROTO_UDP;
7263 		xso->so_e_pid = desc->epid;
7264 		xso->so_last_pid = desc->pid;
7265 	}
7266 }
7267 
7268 static void
nstat_userland_to_rcv_xsockbuf_n(nstat_provider_id_t provider,nstat_flow_data * flow_data,struct xsockbuf_n * xsbrcv)7269 nstat_userland_to_rcv_xsockbuf_n(nstat_provider_id_t provider, nstat_flow_data *flow_data, struct xsockbuf_n *xsbrcv)
7270 {
7271 	xsbrcv->xsb_len = sizeof(struct xsockbuf_n);
7272 	xsbrcv->xsb_kind = XSO_RCVBUF;
7273 
7274 	if (provider == NSTAT_PROVIDER_TCP_USERLAND) {
7275 		nstat_tcp_descriptor *desc = &flow_data->flow_descriptor.tcp_descriptor;
7276 		xsbrcv->sb_hiwat = desc->rcvbufsize;
7277 		xsbrcv->sb_cc = desc->rcvbufused;
7278 	} else {
7279 		nstat_udp_descriptor *desc = &flow_data->flow_descriptor.udp_descriptor;
7280 		xsbrcv->sb_hiwat = desc->rcvbufsize;
7281 		xsbrcv->sb_cc = desc->rcvbufused;
7282 	}
7283 }
7284 
7285 static void
nstat_userland_to_snd_xsockbuf_n(nstat_provider_id_t provider,nstat_flow_data * flow_data,struct xsockbuf_n * xsbsnd)7286 nstat_userland_to_snd_xsockbuf_n(nstat_provider_id_t provider, nstat_flow_data *flow_data, struct xsockbuf_n *xsbsnd)
7287 {
7288 	xsbsnd->xsb_len = sizeof(struct xsockbuf_n);
7289 	xsbsnd->xsb_kind = XSO_SNDBUF;
7290 
7291 	if (provider == NSTAT_PROVIDER_TCP_USERLAND) {
7292 		nstat_tcp_descriptor *desc = &flow_data->flow_descriptor.tcp_descriptor;
7293 		xsbsnd->sb_hiwat = desc->sndbufsize;
7294 		xsbsnd->sb_cc = desc->sndbufused;
7295 	} else {
7296 	}
7297 }
7298 
7299 static void
nstat_userland_to_xsockstat_n(nstat_flow_data * flow_data,struct xsockstat_n * xst)7300 nstat_userland_to_xsockstat_n(nstat_flow_data *flow_data, struct xsockstat_n *xst)
7301 {
7302 	xst->xst_len = sizeof(struct xsockstat_n);
7303 	xst->xst_kind = XSO_STATS;
7304 
7305 	// The kernel version supports an array of counts, here we only support one and map to first entry
7306 	xst->xst_tc_stats[0].rxpackets = flow_data->counts.nstat_rxpackets;
7307 	xst->xst_tc_stats[0].rxbytes   = flow_data->counts.nstat_rxbytes;
7308 	xst->xst_tc_stats[0].txpackets = flow_data->counts.nstat_txpackets;
7309 	xst->xst_tc_stats[0].txbytes   = flow_data->counts.nstat_txbytes;
7310 }
7311 
7312 static void
nstat_userland_to_xtcpcb_n(nstat_flow_data * flow_data,struct xtcpcb_n * xt)7313 nstat_userland_to_xtcpcb_n(nstat_flow_data *flow_data, struct  xtcpcb_n *xt)
7314 {
7315 	nstat_tcp_descriptor *desc = &flow_data->flow_descriptor.tcp_descriptor;
7316 	xt->xt_len = sizeof(struct xtcpcb_n);
7317 	xt->xt_kind = XSO_TCPCB;
7318 	xt->t_state = desc->state;
7319 	xt->snd_wnd = desc->txwindow;
7320 	xt->snd_cwnd = desc->txcwindow;
7321 }
7322 
7323 
7324 __private_extern__ int
ntstat_userland_count(short proto)7325 ntstat_userland_count(short proto)
7326 {
7327 	int n = 0;
7328 	if (proto == IPPROTO_TCP) {
7329 		n = nstat_userland_tcp_shadows;
7330 	} else if (proto == IPPROTO_UDP) {
7331 		n = nstat_userland_udp_shadows;
7332 	}
7333 	return n;
7334 }
7335 
7336 __private_extern__ int
nstat_userland_get_snapshot(short proto,void * __sized_by (* snapshot_size)* snapshotp,size_t * snapshot_size,int * countp)7337 nstat_userland_get_snapshot(short proto, void *__sized_by(*snapshot_size) * snapshotp, size_t *snapshot_size, int *countp)
7338 {
7339 	int error = 0;
7340 	int n = 0;
7341 	size_t data_size = 0;
7342 	nstat_provider_id_t provider;
7343 	nstat_flow_data *flow_data = NULL;
7344 
7345 	NSTAT_LOCK_SHARED();
7346 	if (proto == IPPROTO_TCP) {
7347 		n = nstat_userland_tcp_shadows;
7348 		provider = NSTAT_PROVIDER_TCP_USERLAND;
7349 	} else if (proto == IPPROTO_UDP) {
7350 		n = nstat_userland_udp_shadows;
7351 		provider = NSTAT_PROVIDER_UDP_USERLAND;
7352 	}
7353 	if (n == 0) {
7354 		goto done;
7355 	}
7356 
7357 	data_size = n * sizeof(*flow_data);
7358 	flow_data = (nstat_flow_data *) kalloc_data(data_size,
7359 	    Z_WAITOK | Z_ZERO);
7360 	if (flow_data) {
7361 		n = nstat_gather_flow_data(provider, flow_data, n);
7362 	} else {
7363 		error = ENOMEM;
7364 	}
7365 done:
7366 	NSTAT_UNLOCK_SHARED();
7367 	*snapshotp = flow_data;
7368 	*snapshot_size = data_size;
7369 	*countp = n;
7370 	return error;
7371 }
7372 
7373 // nstat_userland_list_snapshot() does most of the work for a sysctl that uses a return format
7374 // as per get_pcblist_n() even though the vast majority of fields are unused.
7375 // Additional items are required in the sysctl output before and after the data added
7376 // by this function.
7377 __private_extern__ int
nstat_userland_list_snapshot(short proto,struct sysctl_req * req,void * __sized_by (n * sizeof (nstat_flow_data))userlandsnapshot,int n)7378 nstat_userland_list_snapshot(short proto, struct sysctl_req *req, void *__sized_by(n * sizeof(nstat_flow_data))userlandsnapshot, int n)
7379 {
7380 	int error = 0;
7381 	int i;
7382 	nstat_provider_id_t provider;
7383 	void *buf = NULL;
7384 	nstat_flow_data *flow_data, *flow_data_array = NULL;
7385 	size_t item_size = ROUNDUP64(sizeof(struct xinpcb_n)) +
7386 	    ROUNDUP64(sizeof(struct xsocket_n)) +
7387 	    2 * ROUNDUP64(sizeof(struct xsockbuf_n)) +
7388 	    ROUNDUP64(sizeof(struct xsockstat_n));
7389 
7390 	if ((n == 0) || (userlandsnapshot == NULL)) {
7391 		goto done;
7392 	}
7393 
7394 	if (proto == IPPROTO_TCP) {
7395 		item_size += ROUNDUP64(sizeof(struct xtcpcb_n));
7396 		provider = NSTAT_PROVIDER_TCP_USERLAND;
7397 	} else if (proto == IPPROTO_UDP) {
7398 		provider = NSTAT_PROVIDER_UDP_USERLAND;
7399 	} else {
7400 		error = EINVAL;
7401 		goto done;
7402 	}
7403 
7404 	buf = (void *) kalloc_data(item_size, Z_WAITOK);
7405 	if (buf) {
7406 		struct xinpcb_n *xi = (struct xinpcb_n *)buf;
7407 		struct xsocket_n *xso = (struct xsocket_n *) ADVANCE64(xi, sizeof(*xi));
7408 		struct xsockbuf_n *xsbrcv = (struct xsockbuf_n *) ADVANCE64(xso, sizeof(*xso));
7409 		struct xsockbuf_n *xsbsnd = (struct xsockbuf_n *) ADVANCE64(xsbrcv, sizeof(*xsbrcv));
7410 		struct xsockstat_n *xsostats = (struct xsockstat_n *) ADVANCE64(xsbsnd, sizeof(*xsbsnd));
7411 		struct  xtcpcb_n *xt = (struct xtcpcb_n *) ADVANCE64(xsostats, sizeof(*xsostats));
7412 
7413 		flow_data_array = (nstat_flow_data *)userlandsnapshot;
7414 
7415 		for (i = 0; i < n; i++) {
7416 			flow_data = &flow_data_array[i];
7417 			bzero(buf, item_size);
7418 
7419 			nstat_userland_to_xinpcb_n(provider, flow_data, xi);
7420 			nstat_userland_to_xsocket_n(provider, flow_data, xso);
7421 			nstat_userland_to_rcv_xsockbuf_n(provider, flow_data, xsbrcv);
7422 			nstat_userland_to_snd_xsockbuf_n(provider, flow_data, xsbsnd);
7423 			nstat_userland_to_xsockstat_n(flow_data, xsostats);
7424 			if (proto == IPPROTO_TCP) {
7425 				nstat_userland_to_xtcpcb_n(flow_data, xt);
7426 			}
7427 			error = SYSCTL_OUT(req, buf, item_size);
7428 			if (error) {
7429 				break;
7430 			}
7431 		}
7432 		kfree_data(buf, item_size);
7433 	} else {
7434 		error = ENOMEM;
7435 	}
7436 done:
7437 	return error;
7438 }
7439 
7440 __private_extern__ void
nstat_userland_release_snapshot(void * snapshot,int nuserland)7441 nstat_userland_release_snapshot(void *snapshot, int nuserland)
7442 {
7443 	if (snapshot != NULL) {
7444 		kfree_data(snapshot, nuserland * sizeof(nstat_flow_data));
7445 	}
7446 }
7447 
7448 #if NTSTAT_SUPPORTS_STANDALONE_SYSCTL
7449 
7450 __private_extern__ int
ntstat_userland_list_n(short proto,struct sysctl_req * req)7451 ntstat_userland_list_n(short proto, struct sysctl_req *req)
7452 {
7453 	int error = 0;
7454 	int n;
7455 	struct xinpgen xig;
7456 	void *snapshot = NULL;
7457 	size_t item_size = ROUNDUP64(sizeof(struct xinpcb_n)) +
7458 	    ROUNDUP64(sizeof(struct xsocket_n)) +
7459 	    2 * ROUNDUP64(sizeof(struct xsockbuf_n)) +
7460 	    ROUNDUP64(sizeof(struct xsockstat_n));
7461 
7462 	if (proto == IPPROTO_TCP) {
7463 		item_size += ROUNDUP64(sizeof(struct xtcpcb_n));
7464 	}
7465 
7466 	if (req->oldptr == USER_ADDR_NULL) {
7467 		n = ntstat_userland_count(proto);
7468 		req->oldidx = 2 * (sizeof(xig)) + (n + 1 + n / 8) * item_size;
7469 		goto done;
7470 	}
7471 
7472 	if (req->newptr != USER_ADDR_NULL) {
7473 		error = EPERM;
7474 		goto done;
7475 	}
7476 
7477 	error = nstat_userland_get_snapshot(proto, &snapshot, &n);
7478 
7479 	if (error) {
7480 		goto done;
7481 	}
7482 
7483 	bzero(&xig, sizeof(xig));
7484 	xig.xig_len = sizeof(xig);
7485 	xig.xig_gen = 0;
7486 	xig.xig_sogen = 0;
7487 	xig.xig_count = n;
7488 	error = SYSCTL_OUT(req, &xig, sizeof(xig));
7489 	if (error) {
7490 		goto done;
7491 	}
7492 	/*
7493 	 * We are done if there are no flows
7494 	 */
7495 	if (n == 0) {
7496 		goto done;
7497 	}
7498 
7499 	error = nstat_userland_list_snapshot(proto, req, snapshot, n);
7500 
7501 	if (!error) {
7502 		/*
7503 		 * Give the user an updated idea of our state,
7504 		 * which is unchanged
7505 		 */
7506 		error = SYSCTL_OUT(req, &xig, sizeof(xig));
7507 	}
7508 done:
7509 	nstat_userland_release_snapshot(snapshot, n);
7510 	return error;
7511 }
7512 
7513 #endif /* NTSTAT_SUPPORTS_STANDALONE_SYSCTL */
7514 #endif /* SKYWALK */
7515