xref: /xnu-12377.81.4/bsd/net/ntstat.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
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(&param->dst.v4);
1314 	mask = param->mask.v4.sin_family ? __DECONST_SA(&param->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