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