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