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