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