]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/ntstat.c
xnu-6153.121.1.tar.gz
[apple/xnu.git] / bsd / net / ntstat.c
CommitLineData
6d2010ae 1/*
cb323159 2 * Copyright (c) 2010-2019 Apple Inc. All rights reserved.
6d2010ae
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
39037602 5 *
6d2010ae
A
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.
39037602 14 *
6d2010ae
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
39037602 17 *
6d2010ae
A
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.
39037602 25 *
6d2010ae
A
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>
39236c6e
A
37#include <sys/queue.h>
38#include <sys/priv.h>
fe8ab488 39#include <sys/protosw.h>
6d2010ae
A
40
41#include <kern/clock.h>
42#include <kern/debug.h>
43
44#include <libkern/libkern.h>
45#include <libkern/OSMalloc.h>
46#include <libkern/OSAtomic.h>
47#include <libkern/locks.h>
48
49#include <net/if.h>
39236c6e
A
50#include <net/if_var.h>
51#include <net/if_types.h>
6d2010ae 52#include <net/route.h>
5ba3f43e
A
53
54// These includes appear in ntstat.h but we include them here first so they won't trigger
55// any clang diagnostic errors.
56#include <netinet/in.h>
57#include <netinet/in_stat.h>
58#include <netinet/tcp.h>
59
60#pragma clang diagnostic push
61#pragma clang diagnostic error "-Wpadded"
62#pragma clang diagnostic error "-Wpacked"
63// This header defines structures shared with user space, so we need to ensure there is
64// no compiler inserted padding in case the user space process isn't using the same
65// architecture as the kernel (example: i386 process with x86_64 kernel).
6d2010ae 66#include <net/ntstat.h>
5ba3f43e 67#pragma clang diagnostic pop
6d2010ae
A
68
69#include <netinet/ip_var.h>
70#include <netinet/in_pcb.h>
71#include <netinet/in_var.h>
6d2010ae
A
72#include <netinet/tcp_var.h>
73#include <netinet/tcp_fsm.h>
fe8ab488 74#include <netinet/tcp_cc.h>
6d2010ae
A
75#include <netinet/udp.h>
76#include <netinet/udp_var.h>
77#include <netinet6/in6_pcb.h>
78#include <netinet6/in6_var.h>
79
0a7de745 80__private_extern__ int nstat_collect = 1;
d190cdc3
A
81
82#if (DEBUG || DEVELOPMENT)
6d2010ae
A
83SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
84 &nstat_collect, 0, "Collect detailed statistics");
d190cdc3 85#endif /* (DEBUG || DEVELOPMENT) */
6d2010ae 86
5ba3f43e
A
87#if CONFIG_EMBEDDED
88static int nstat_privcheck = 1;
89#else
39236c6e 90static int nstat_privcheck = 0;
5ba3f43e 91#endif
39236c6e
A
92SYSCTL_INT(_net, OID_AUTO, statistics_privcheck, CTLFLAG_RW | CTLFLAG_LOCKED,
93 &nstat_privcheck, 0, "Entitlement check");
94
fe8ab488 95SYSCTL_NODE(_net, OID_AUTO, stats,
0a7de745 96 CTLFLAG_RW | CTLFLAG_LOCKED, 0, "network statistics");
fe8ab488
A
97
98static int nstat_debug = 0;
99SYSCTL_INT(_net_stats, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
100 &nstat_debug, 0, "");
101
102static int nstat_sendspace = 2048;
103SYSCTL_INT(_net_stats, OID_AUTO, sendspace, CTLFLAG_RW | CTLFLAG_LOCKED,
104 &nstat_sendspace, 0, "");
105
106static int nstat_recvspace = 8192;
107SYSCTL_INT(_net_stats, OID_AUTO, recvspace, CTLFLAG_RW | CTLFLAG_LOCKED,
108 &nstat_recvspace, 0, "");
109
3e170ce0
A
110static struct nstat_stats nstat_stats;
111SYSCTL_STRUCT(_net_stats, OID_AUTO, stats, CTLFLAG_RD | CTLFLAG_LOCKED,
112 &nstat_stats, nstat_stats, "");
fe8ab488 113
5ba3f43e
A
114static u_int32_t nstat_lim_interval = 30 * 60; /* Report interval, seconds */
115static u_int32_t nstat_lim_min_tx_pkts = 100;
116static u_int32_t nstat_lim_min_rx_pkts = 100;
117#if (DEBUG || DEVELOPMENT)
118SYSCTL_INT(_net_stats, OID_AUTO, lim_report_interval,
119 CTLFLAG_RW | CTLFLAG_LOCKED, &nstat_lim_interval, 0,
120 "Low internet stat report interval");
121
122SYSCTL_INT(_net_stats, OID_AUTO, lim_min_tx_pkts,
123 CTLFLAG_RW | CTLFLAG_LOCKED, &nstat_lim_min_tx_pkts, 0,
124 "Low Internet, min transmit packets threshold");
125
126SYSCTL_INT(_net_stats, OID_AUTO, lim_min_rx_pkts,
127 CTLFLAG_RW | CTLFLAG_LOCKED, &nstat_lim_min_rx_pkts, 0,
128 "Low Internet, min receive packets threshold");
129#endif /* DEBUG || DEVELOPMENT */
130
131static struct net_api_stats net_api_stats_before;
132static u_int64_t net_api_stats_last_report_time;
133#define NET_API_STATS_REPORT_INTERVAL (12 * 60 * 60) /* 12 hours, in seconds */
134static u_int32_t net_api_stats_report_interval = NET_API_STATS_REPORT_INTERVAL;
135
136#if (DEBUG || DEVELOPMENT)
137SYSCTL_UINT(_net_stats, OID_AUTO, api_report_interval,
138 CTLFLAG_RW | CTLFLAG_LOCKED, &net_api_stats_report_interval, 0, "");
139#endif /* DEBUG || DEVELOPMENT */
140
0a7de745 141enum{
cb323159
A
142 NSTAT_FLAG_CLEANUP = (1 << 0),
143 NSTAT_FLAG_REQCOUNTS = (1 << 1),
144 NSTAT_FLAG_SUPPORTS_UPDATES = (1 << 2),
0a7de745 145 NSTAT_FLAG_SYSINFO_SUBSCRIBED = (1 << 3),
316670eb
A
146};
147
5ba3f43e
A
148#if CONFIG_EMBEDDED
149#define QUERY_CONTINUATION_SRC_COUNT 50
150#else
3e170ce0 151#define QUERY_CONTINUATION_SRC_COUNT 100
5ba3f43e
A
152#endif
153
cb323159
A
154typedef TAILQ_HEAD(, nstat_src) tailq_head_nstat_src;
155typedef TAILQ_ENTRY(nstat_src) tailq_entry_nstat_src;
3e170ce0 156
0a7de745
A
157typedef struct nstat_provider_filter {
158 u_int64_t npf_flags;
159 u_int64_t npf_events;
160 pid_t npf_pid;
161 uuid_t npf_uuid;
39037602
A
162} nstat_provider_filter;
163
164
0a7de745
A
165typedef struct nstat_control_state {
166 struct nstat_control_state *ncs_next;
cb323159 167 u_int32_t ncs_watching;
5ba3f43e 168 decl_lck_mtx_data(, ncs_mtx);
cb323159
A
169 kern_ctl_ref ncs_kctl;
170 u_int32_t ncs_unit;
171 nstat_src_ref_t ncs_next_srcref;
0a7de745 172 tailq_head_nstat_src ncs_src_queue;
cb323159
A
173 mbuf_t ncs_accumulated;
174 u_int32_t ncs_flags;
0a7de745 175 nstat_provider_filter ncs_provider_filters[NSTAT_PROVIDER_COUNT];
3e170ce0 176 /* state maintained for partial query requests */
cb323159
A
177 u_int64_t ncs_context;
178 u_int64_t ncs_seq;
6d2010ae
A
179} nstat_control_state;
180
0a7de745
A
181typedef struct nstat_provider {
182 struct nstat_provider *next;
cb323159
A
183 nstat_provider_id_t nstat_provider_id;
184 size_t nstat_descriptor_length;
185 errno_t (*nstat_lookup)(const void *data, u_int32_t length, nstat_provider_cookie_t *out_cookie);
186 int (*nstat_gone)(nstat_provider_cookie_t cookie);
187 errno_t (*nstat_counts)(nstat_provider_cookie_t cookie, struct nstat_counts *out_counts, int *out_gone);
188 errno_t (*nstat_watcher_add)(nstat_control_state *state, nstat_msg_add_all_srcs *req);
189 void (*nstat_watcher_remove)(nstat_control_state *state);
190 errno_t (*nstat_copy_descriptor)(nstat_provider_cookie_t cookie, void *data, u_int32_t len);
191 void (*nstat_release)(nstat_provider_cookie_t cookie, boolean_t locked);
192 bool (*nstat_reporting_allowed)(nstat_provider_cookie_t cookie, nstat_provider_filter *filter);
316670eb
A
193} nstat_provider;
194
cb323159
A
195typedef STAILQ_HEAD(, nstat_src) stailq_head_nstat_src;
196typedef STAILQ_ENTRY(nstat_src) stailq_entry_nstat_src;
39037602 197
0a7de745
A
198typedef TAILQ_HEAD(, nstat_tu_shadow) tailq_head_tu_shadow;
199typedef TAILQ_ENTRY(nstat_tu_shadow) tailq_entry_tu_shadow;
316670eb 200
0a7de745
A
201typedef TAILQ_HEAD(, nstat_procdetails) tailq_head_procdetails;
202typedef TAILQ_ENTRY(nstat_procdetails) tailq_entry_procdetails;
5ba3f43e 203
0a7de745
A
204typedef struct nstat_src {
205 tailq_entry_nstat_src ns_control_link; // All sources for the nstat_control_state, for iterating over.
cb323159
A
206 nstat_control_state *ns_control; // The nstat_control_state that this is a source for
207 nstat_src_ref_t srcref;
208 nstat_provider *provider;
209 nstat_provider_cookie_t cookie;
210 uint32_t filter;
211 uint64_t seq;
316670eb
A
212} nstat_src;
213
cb323159
A
214static errno_t nstat_control_send_counts(nstat_control_state *, nstat_src *, unsigned long long, u_int16_t, int *);
215static int nstat_control_send_description(nstat_control_state *state, nstat_src *src, u_int64_t context, u_int16_t hdr_flags);
216static 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);
217static errno_t nstat_control_send_removed(nstat_control_state *, nstat_src *);
218static errno_t nstat_control_send_goodbye(nstat_control_state *state, nstat_src *src);
219static void nstat_control_cleanup_source(nstat_control_state *state, nstat_src *src, boolean_t);
220static bool nstat_control_reporting_allowed(nstat_control_state *state, nstat_src *src);
221static boolean_t nstat_control_begin_query(nstat_control_state *state, const nstat_msg_hdr *hdrp);
222static u_int16_t nstat_control_end_query(nstat_control_state *state, nstat_src *last_src, boolean_t partial);
223static void nstat_ifnet_report_ecn_stats(void);
224static void nstat_ifnet_report_lim_stats(void);
225static void nstat_net_api_report_stats(void);
226static errno_t nstat_set_provider_filter( nstat_control_state *state, nstat_msg_add_all_srcs *req);
227static errno_t nstat_control_send_event(nstat_control_state *state, nstat_src *src, u_int64_t event);
228
229static u_int32_t nstat_udp_watchers = 0;
230static u_int32_t nstat_tcp_watchers = 0;
316670eb 231
6d2010ae
A
232static void nstat_control_register(void);
233
fe8ab488
A
234/*
235 * The lock order is as follows:
236 *
237 * socket_lock (inpcb)
238 * nstat_mtx
5ba3f43e 239 * state->ncs_mtx
fe8ab488 240 */
0a7de745
A
241static volatile OSMallocTag nstat_malloc_tag = NULL;
242static nstat_control_state *nstat_controls = NULL;
243static uint64_t nstat_idle_time = 0;
6d2010ae
A
244static decl_lck_mtx_data(, nstat_mtx);
245
fe8ab488
A
246/* some extern definitions */
247extern void mbuf_report_peak_usage(void);
248extern void tcp_report_stats(void);
249
6d2010ae
A
250static void
251nstat_copy_sa_out(
0a7de745
A
252 const struct sockaddr *src,
253 struct sockaddr *dst,
254 int maxlen)
6d2010ae 255{
0a7de745
A
256 if (src->sa_len > maxlen) {
257 return;
258 }
39037602 259
6d2010ae
A
260 bcopy(src, dst, src->sa_len);
261 if (src->sa_family == AF_INET6 &&
0a7de745
A
262 src->sa_len >= sizeof(struct sockaddr_in6)) {
263 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(void *)dst;
264 if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
265 if (sin6->sin6_scope_id == 0) {
fe8ab488 266 sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
0a7de745 267 }
fe8ab488 268 sin6->sin6_addr.s6_addr16[1] = 0;
6d2010ae
A
269 }
270 }
271}
272
273static void
274nstat_ip_to_sockaddr(
0a7de745 275 const struct in_addr *ip,
cb323159
A
276 u_int16_t port,
277 struct sockaddr_in *sin,
278 u_int32_t maxlen)
6d2010ae 279{
0a7de745 280 if (maxlen < sizeof(struct sockaddr_in)) {
6d2010ae 281 return;
0a7de745 282 }
39037602 283
6d2010ae
A
284 sin->sin_family = AF_INET;
285 sin->sin_len = sizeof(*sin);
286 sin->sin_port = port;
287 sin->sin_addr = *ip;
288}
289
5ba3f43e 290u_int16_t
39037602
A
291nstat_ifnet_to_flags(
292 struct ifnet *ifp)
293{
294 u_int16_t flags = 0;
295 u_int32_t functional_type = if_functional_type(ifp, FALSE);
296
297 /* Panic if someone adds a functional type without updating ntstat. */
298 VERIFY(0 <= functional_type && functional_type <= IFRTYPE_FUNCTIONAL_LAST);
299
0a7de745 300 switch (functional_type) {
39037602
A
301 case IFRTYPE_FUNCTIONAL_UNKNOWN:
302 flags |= NSTAT_IFNET_IS_UNKNOWN_TYPE;
303 break;
304 case IFRTYPE_FUNCTIONAL_LOOPBACK:
305 flags |= NSTAT_IFNET_IS_LOOPBACK;
306 break;
307 case IFRTYPE_FUNCTIONAL_WIRED:
743345f9 308 case IFRTYPE_FUNCTIONAL_INTCOPROC:
39037602
A
309 flags |= NSTAT_IFNET_IS_WIRED;
310 break;
311 case IFRTYPE_FUNCTIONAL_WIFI_INFRA:
312 flags |= NSTAT_IFNET_IS_WIFI;
313 break;
314 case IFRTYPE_FUNCTIONAL_WIFI_AWDL:
315 flags |= NSTAT_IFNET_IS_WIFI;
316 flags |= NSTAT_IFNET_IS_AWDL;
317 break;
318 case IFRTYPE_FUNCTIONAL_CELLULAR:
319 flags |= NSTAT_IFNET_IS_CELLULAR;
320 break;
cb323159
A
321 case IFRTYPE_FUNCTIONAL_COMPANIONLINK:
322 flags |= NSTAT_IFNET_IS_COMPANIONLINK;
323 break;
39037602
A
324 }
325
0a7de745 326 if (IFNET_IS_EXPENSIVE(ifp)) {
39037602
A
327 flags |= NSTAT_IFNET_IS_EXPENSIVE;
328 }
cb323159
A
329 if (IFNET_IS_CONSTRAINED(ifp)) {
330 flags |= NSTAT_IFNET_IS_CONSTRAINED;
331 }
39037602
A
332
333 return flags;
334}
335
3e170ce0
A
336static u_int16_t
337nstat_inpcb_to_flags(
338 const struct inpcb *inp)
339{
340 u_int16_t flags = 0;
341
cb323159
A
342 if (inp != NULL) {
343 if (inp->inp_last_outifp != NULL) {
344 struct ifnet *ifp = inp->inp_last_outifp;
345 flags = nstat_ifnet_to_flags(ifp);
3e170ce0 346
cb323159
A
347 struct tcpcb *tp = intotcpcb(inp);
348 if (tp) {
349 if (tp->t_flags & TF_LOCAL) {
350 flags |= NSTAT_IFNET_IS_LOCAL;
351 } else {
352 flags |= NSTAT_IFNET_IS_NON_LOCAL;
353 }
0a7de745 354 }
cb323159
A
355 } else {
356 flags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
357 }
358 if (inp->inp_socket != NULL &&
359 (inp->inp_socket->so_flags1 & SOF1_CELLFALLBACK)) {
360 flags |= NSTAT_IFNET_VIA_CELLFALLBACK;
3e170ce0 361 }
3e170ce0 362 }
3e170ce0
A
363 return flags;
364}
365
6d2010ae
A
366#pragma mark -- Network Statistic Providers --
367
6d2010ae 368static errno_t nstat_control_source_add(u_int64_t context, nstat_control_state *state, nstat_provider *provider, nstat_provider_cookie_t cookie);
0a7de745 369struct nstat_provider *nstat_providers = NULL;
6d2010ae
A
370
371static struct nstat_provider*
372nstat_find_provider_by_id(
0a7de745 373 nstat_provider_id_t id)
6d2010ae 374{
0a7de745 375 struct nstat_provider *provider;
39037602 376
0a7de745
A
377 for (provider = nstat_providers; provider != NULL; provider = provider->next) {
378 if (provider->nstat_provider_id == id) {
6d2010ae 379 break;
0a7de745 380 }
6d2010ae 381 }
39037602 382
6d2010ae
A
383 return provider;
384}
385
386static errno_t
387nstat_lookup_entry(
cb323159
A
388 nstat_provider_id_t id,
389 const void *data,
390 u_int32_t length,
391 nstat_provider **out_provider,
0a7de745 392 nstat_provider_cookie_t *out_cookie)
6d2010ae
A
393{
394 *out_provider = nstat_find_provider_by_id(id);
0a7de745 395 if (*out_provider == NULL) {
6d2010ae
A
396 return ENOENT;
397 }
39037602 398
6d2010ae
A
399 return (*out_provider)->nstat_lookup(data, length, out_cookie);
400}
401
402static void nstat_init_route_provider(void);
403static void nstat_init_tcp_provider(void);
404static void nstat_init_udp_provider(void);
39236c6e 405static void nstat_init_ifnet_provider(void);
6d2010ae 406
316670eb 407__private_extern__ void
6d2010ae
A
408nstat_init(void)
409{
0a7de745
A
410 if (nstat_malloc_tag != NULL) {
411 return;
412 }
39037602 413
6d2010ae 414 OSMallocTag tag = OSMalloc_Tagalloc(NET_STAT_CONTROL_NAME, OSMT_DEFAULT);
0a7de745 415 if (!OSCompareAndSwapPtr(NULL, tag, &nstat_malloc_tag)) {
6d2010ae
A
416 OSMalloc_Tagfree(tag);
417 tag = nstat_malloc_tag;
0a7de745 418 } else {
6d2010ae
A
419 // we need to initialize other things, we do it here as this code path will only be hit once;
420 nstat_init_route_provider();
421 nstat_init_tcp_provider();
422 nstat_init_udp_provider();
39236c6e 423 nstat_init_ifnet_provider();
6d2010ae
A
424 nstat_control_register();
425 }
426}
427
428#pragma mark -- Aligned Buffer Allocation --
429
0a7de745
A
430struct align_header {
431 u_int32_t offset;
432 u_int32_t length;
6d2010ae
A
433};
434
435static void*
436nstat_malloc_aligned(
0a7de745
A
437 u_int32_t length,
438 u_int8_t alignment,
439 OSMallocTag tag)
6d2010ae 440{
0a7de745 441 struct align_header *hdr = NULL;
cb323159 442 u_int32_t size = length + sizeof(*hdr) + alignment - 1;
39037602 443
cb323159 444 u_int8_t *buffer = OSMalloc(size, tag);
0a7de745
A
445 if (buffer == NULL) {
446 return NULL;
447 }
39037602 448
cb323159 449 u_int8_t *aligned = buffer + sizeof(*hdr);
6d2010ae 450 aligned = (u_int8_t*)P2ROUNDUP(aligned, alignment);
39037602 451
316670eb 452 hdr = (struct align_header*)(void *)(aligned - sizeof(*hdr));
6d2010ae
A
453 hdr->offset = aligned - buffer;
454 hdr->length = size;
39037602 455
6d2010ae
A
456 return aligned;
457}
458
459static void
460nstat_free_aligned(
0a7de745
A
461 void *buffer,
462 OSMallocTag tag)
6d2010ae 463{
316670eb 464 struct align_header *hdr = (struct align_header*)(void *)((u_int8_t*)buffer - sizeof(*hdr));
6d2010ae
A
465 OSFree(((char*)buffer) - hdr->offset, hdr->length, tag);
466}
467
468#pragma mark -- Route Provider --
469
0a7de745 470static nstat_provider nstat_route_provider;
6d2010ae
A
471
472static errno_t
473nstat_route_lookup(
cb323159
A
474 const void *data,
475 u_int32_t length,
0a7de745 476 nstat_provider_cookie_t *out_cookie)
6d2010ae
A
477{
478 // rt_lookup doesn't take const params but it doesn't modify the parameters for
479 // the lookup. So...we use a union to eliminate the warning.
0a7de745 480 union{
6d2010ae
A
481 struct sockaddr *sa;
482 const struct sockaddr *const_sa;
483 } dst, mask;
39037602 484
0a7de745 485 const nstat_route_add_param *param = (const nstat_route_add_param*)data;
6d2010ae 486 *out_cookie = NULL;
39037602 487
0a7de745 488 if (length < sizeof(*param)) {
6d2010ae
A
489 return EINVAL;
490 }
39037602 491
6d2010ae 492 if (param->dst.v4.sin_family == 0 ||
0a7de745
A
493 param->dst.v4.sin_family > AF_MAX ||
494 (param->mask.v4.sin_family != 0 && param->mask.v4.sin_family != param->dst.v4.sin_family)) {
6d2010ae
A
495 return EINVAL;
496 }
39037602 497
6d2010ae 498 if (param->dst.v4.sin_len > sizeof(param->dst) ||
0a7de745 499 (param->mask.v4.sin_family && param->mask.v4.sin_len > sizeof(param->mask.v4.sin_len))) {
316670eb 500 return EINVAL;
6d2010ae 501 }
fe8ab488
A
502 if ((param->dst.v4.sin_family == AF_INET &&
503 param->dst.v4.sin_len < sizeof(struct sockaddr_in)) ||
504 (param->dst.v6.sin6_family == AF_INET6 &&
0a7de745 505 param->dst.v6.sin6_len < sizeof(struct sockaddr_in6))) {
fe8ab488
A
506 return EINVAL;
507 }
39037602 508
6d2010ae
A
509 dst.const_sa = (const struct sockaddr*)&param->dst;
510 mask.const_sa = param->mask.v4.sin_family ? (const struct sockaddr*)&param->mask : NULL;
39037602 511
0a7de745
A
512 struct radix_node_head *rnh = rt_tables[dst.sa->sa_family];
513 if (rnh == NULL) {
514 return EAFNOSUPPORT;
515 }
39037602 516
6d2010ae
A
517 lck_mtx_lock(rnh_lock);
518 struct rtentry *rt = rt_lookup(TRUE, dst.sa, mask.sa, rnh, param->ifindex);
519 lck_mtx_unlock(rnh_lock);
39037602 520
0a7de745
A
521 if (rt) {
522 *out_cookie = (nstat_provider_cookie_t)rt;
523 }
39037602 524
6d2010ae
A
525 return rt ? 0 : ENOENT;
526}
527
528static int
529nstat_route_gone(
0a7de745 530 nstat_provider_cookie_t cookie)
6d2010ae 531{
0a7de745 532 struct rtentry *rt = (struct rtentry*)cookie;
6d2010ae
A
533 return ((rt->rt_flags & RTF_UP) == 0) ? 1 : 0;
534}
535
536static errno_t
537nstat_route_counts(
0a7de745 538 nstat_provider_cookie_t cookie,
cb323159
A
539 struct nstat_counts *out_counts,
540 int *out_gone)
6d2010ae 541{
0a7de745
A
542 struct rtentry *rt = (struct rtentry*)cookie;
543 struct nstat_counts *rt_stats = rt->rt_stats;
39037602 544
0a7de745
A
545 if (out_gone) {
546 *out_gone = 0;
547 }
39037602 548
0a7de745
A
549 if (out_gone && (rt->rt_flags & RTF_UP) == 0) {
550 *out_gone = 1;
551 }
39037602 552
0a7de745 553 if (rt_stats) {
6d2010ae
A
554 atomic_get_64(out_counts->nstat_rxpackets, &rt_stats->nstat_rxpackets);
555 atomic_get_64(out_counts->nstat_rxbytes, &rt_stats->nstat_rxbytes);
556 atomic_get_64(out_counts->nstat_txpackets, &rt_stats->nstat_txpackets);
557 atomic_get_64(out_counts->nstat_txbytes, &rt_stats->nstat_txbytes);
558 out_counts->nstat_rxduplicatebytes = rt_stats->nstat_rxduplicatebytes;
559 out_counts->nstat_rxoutoforderbytes = rt_stats->nstat_rxoutoforderbytes;
560 out_counts->nstat_txretransmit = rt_stats->nstat_txretransmit;
561 out_counts->nstat_connectattempts = rt_stats->nstat_connectattempts;
562 out_counts->nstat_connectsuccesses = rt_stats->nstat_connectsuccesses;
563 out_counts->nstat_min_rtt = rt_stats->nstat_min_rtt;
564 out_counts->nstat_avg_rtt = rt_stats->nstat_avg_rtt;
565 out_counts->nstat_var_rtt = rt_stats->nstat_var_rtt;
39236c6e 566 out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
0a7de745 567 } else {
6d2010ae 568 bzero(out_counts, sizeof(*out_counts));
3e170ce0 569 }
39037602 570
6d2010ae
A
571 return 0;
572}
573
574static void
575nstat_route_release(
316670eb
A
576 nstat_provider_cookie_t cookie,
577 __unused int locked)
6d2010ae
A
578{
579 rtfree((struct rtentry*)cookie);
580}
581
cb323159 582static u_int32_t nstat_route_watchers = 0;
6d2010ae
A
583
584static int
585nstat_route_walktree_add(
0a7de745
A
586 struct radix_node *rn,
587 void *context)
6d2010ae 588{
0a7de745 589 errno_t result = 0;
6d2010ae 590 struct rtentry *rt = (struct rtentry *)rn;
0a7de745 591 nstat_control_state *state = (nstat_control_state*)context;
6d2010ae 592
5ba3f43e 593 LCK_MTX_ASSERT(rnh_lock, LCK_MTX_ASSERT_OWNED);
6d2010ae
A
594
595 /* RTF_UP can't change while rnh_lock is held */
0a7de745 596 if ((rt->rt_flags & RTF_UP) != 0) {
6d2010ae
A
597 /* Clear RTPRF_OURS if the route is still usable */
598 RT_LOCK(rt);
599 if (rt_validate(rt)) {
600 RT_ADDREF_LOCKED(rt);
601 RT_UNLOCK(rt);
602 } else {
603 RT_UNLOCK(rt);
604 rt = NULL;
605 }
606
607 /* Otherwise if RTF_CONDEMNED, treat it as if it were down */
0a7de745
A
608 if (rt == NULL) {
609 return 0;
610 }
6d2010ae
A
611
612 result = nstat_control_source_add(0, state, &nstat_route_provider, rt);
0a7de745 613 if (result != 0) {
6d2010ae 614 rtfree_locked(rt);
0a7de745 615 }
6d2010ae 616 }
39037602 617
6d2010ae
A
618 return result;
619}
620
621static errno_t
622nstat_route_add_watcher(
cb323159 623 nstat_control_state *state,
5ba3f43e 624 nstat_msg_add_all_srcs *req)
6d2010ae
A
625{
626 int i;
627 errno_t result = 0;
39037602 628
6d2010ae 629 lck_mtx_lock(rnh_lock);
5ba3f43e
A
630
631 result = nstat_set_provider_filter(state, req);
0a7de745 632 if (result == 0) {
5ba3f43e 633 OSIncrementAtomic(&nstat_route_watchers);
39037602 634
0a7de745 635 for (i = 1; i < AF_MAX; i++) {
5ba3f43e
A
636 struct radix_node_head *rnh;
637 rnh = rt_tables[i];
0a7de745
A
638 if (!rnh) {
639 continue;
640 }
5ba3f43e
A
641
642 result = rnh->rnh_walktree(rnh, nstat_route_walktree_add, state);
0a7de745 643 if (result != 0) {
5ba3f43e
A
644 // This is probably resource exhaustion.
645 // There currently isn't a good way to recover from this.
646 // Least bad seems to be to give up on the add-all but leave
647 // the watcher in place.
648 break;
649 }
6d2010ae
A
650 }
651 }
652 lck_mtx_unlock(rnh_lock);
39037602 653
6d2010ae
A
654 return result;
655}
656
657__private_extern__ void
658nstat_route_new_entry(
0a7de745 659 struct rtentry *rt)
6d2010ae 660{
0a7de745 661 if (nstat_route_watchers == 0) {
6d2010ae 662 return;
0a7de745 663 }
39037602 664
6d2010ae 665 lck_mtx_lock(&nstat_mtx);
0a7de745
A
666 if ((rt->rt_flags & RTF_UP) != 0) {
667 nstat_control_state *state;
668 for (state = nstat_controls; state; state = state->ncs_next) {
669 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_ROUTE)) != 0) {
6d2010ae
A
670 // this client is watching routes
671 // acquire a reference for the route
672 RT_ADDREF(rt);
39037602 673
6d2010ae 674 // add the source, if that fails, release the reference
0a7de745 675 if (nstat_control_source_add(0, state, &nstat_route_provider, rt) != 0) {
6d2010ae 676 RT_REMREF(rt);
0a7de745 677 }
6d2010ae
A
678 }
679 }
680 }
681 lck_mtx_unlock(&nstat_mtx);
682}
683
684static void
685nstat_route_remove_watcher(
0a7de745 686 __unused nstat_control_state *state)
6d2010ae
A
687{
688 OSDecrementAtomic(&nstat_route_watchers);
689}
690
691static errno_t
692nstat_route_copy_descriptor(
0a7de745 693 nstat_provider_cookie_t cookie,
cb323159
A
694 void *data,
695 u_int32_t len)
6d2010ae 696{
0a7de745
A
697 nstat_route_descriptor *desc = (nstat_route_descriptor*)data;
698 if (len < sizeof(*desc)) {
6d2010ae
A
699 return EINVAL;
700 }
701 bzero(desc, sizeof(*desc));
39037602 702
0a7de745 703 struct rtentry *rt = (struct rtentry*)cookie;
fe8ab488
A
704 desc->id = (uint64_t)VM_KERNEL_ADDRPERM(rt);
705 desc->parent_id = (uint64_t)VM_KERNEL_ADDRPERM(rt->rt_parent);
706 desc->gateway_id = (uint64_t)VM_KERNEL_ADDRPERM(rt->rt_gwroute);
6d2010ae 707
39037602 708
6d2010ae 709 // key/dest
0a7de745
A
710 struct sockaddr *sa;
711 if ((sa = rt_key(rt))) {
6d2010ae 712 nstat_copy_sa_out(sa, &desc->dst.sa, sizeof(desc->dst));
0a7de745 713 }
39037602 714
6d2010ae 715 // mask
0a7de745 716 if ((sa = rt_mask(rt)) && sa->sa_len <= sizeof(desc->mask)) {
6d2010ae 717 memcpy(&desc->mask, sa, sa->sa_len);
0a7de745 718 }
39037602 719
6d2010ae 720 // gateway
0a7de745 721 if ((sa = rt->rt_gateway)) {
6d2010ae 722 nstat_copy_sa_out(sa, &desc->gateway.sa, sizeof(desc->gateway));
0a7de745 723 }
39037602 724
0a7de745 725 if (rt->rt_ifp) {
6d2010ae 726 desc->ifindex = rt->rt_ifp->if_index;
0a7de745 727 }
39037602 728
6d2010ae 729 desc->flags = rt->rt_flags;
39037602 730
6d2010ae
A
731 return 0;
732}
733
39037602
A
734static bool
735nstat_route_reporting_allowed(nstat_provider_cookie_t cookie, nstat_provider_filter *filter)
736{
737 bool retval = true;
738
0a7de745
A
739 if ((filter->npf_flags & NSTAT_FILTER_IFNET_FLAGS) != 0) {
740 struct rtentry *rt = (struct rtentry*)cookie;
39037602
A
741 struct ifnet *ifp = rt->rt_ifp;
742
0a7de745 743 if (ifp) {
39037602
A
744 uint16_t interface_properties = nstat_ifnet_to_flags(ifp);
745
0a7de745 746 if ((filter->npf_flags & interface_properties) == 0) {
39037602
A
747 retval = false;
748 }
749 }
750 }
751 return retval;
752}
753
6d2010ae
A
754static void
755nstat_init_route_provider(void)
756{
757 bzero(&nstat_route_provider, sizeof(nstat_route_provider));
758 nstat_route_provider.nstat_descriptor_length = sizeof(nstat_route_descriptor);
759 nstat_route_provider.nstat_provider_id = NSTAT_PROVIDER_ROUTE;
760 nstat_route_provider.nstat_lookup = nstat_route_lookup;
761 nstat_route_provider.nstat_gone = nstat_route_gone;
762 nstat_route_provider.nstat_counts = nstat_route_counts;
763 nstat_route_provider.nstat_release = nstat_route_release;
764 nstat_route_provider.nstat_watcher_add = nstat_route_add_watcher;
765 nstat_route_provider.nstat_watcher_remove = nstat_route_remove_watcher;
766 nstat_route_provider.nstat_copy_descriptor = nstat_route_copy_descriptor;
39037602 767 nstat_route_provider.nstat_reporting_allowed = nstat_route_reporting_allowed;
6d2010ae
A
768 nstat_route_provider.next = nstat_providers;
769 nstat_providers = &nstat_route_provider;
770}
771
772#pragma mark -- Route Collection --
773
5ba3f43e 774__private_extern__ struct nstat_counts*
6d2010ae 775nstat_route_attach(
0a7de745 776 struct rtentry *rte)
6d2010ae
A
777{
778 struct nstat_counts *result = rte->rt_stats;
0a7de745
A
779 if (result) {
780 return result;
781 }
39037602 782
0a7de745
A
783 if (nstat_malloc_tag == NULL) {
784 nstat_init();
785 }
39037602 786
6d2010ae 787 result = nstat_malloc_aligned(sizeof(*result), sizeof(u_int64_t), nstat_malloc_tag);
0a7de745
A
788 if (!result) {
789 return result;
790 }
39037602 791
6d2010ae 792 bzero(result, sizeof(*result));
39037602 793
0a7de745 794 if (!OSCompareAndSwapPtr(NULL, result, &rte->rt_stats)) {
6d2010ae
A
795 nstat_free_aligned(result, nstat_malloc_tag);
796 result = rte->rt_stats;
797 }
39037602 798
6d2010ae
A
799 return result;
800}
801
802__private_extern__ void
803nstat_route_detach(
0a7de745 804 struct rtentry *rte)
6d2010ae 805{
0a7de745 806 if (rte->rt_stats) {
6d2010ae
A
807 nstat_free_aligned(rte->rt_stats, nstat_malloc_tag);
808 rte->rt_stats = NULL;
809 }
810}
811
812__private_extern__ void
813nstat_route_connect_attempt(
0a7de745 814 struct rtentry *rte)
6d2010ae 815{
0a7de745
A
816 while (rte) {
817 struct nstat_counts* stats = nstat_route_attach(rte);
818 if (stats) {
6d2010ae
A
819 OSIncrementAtomic(&stats->nstat_connectattempts);
820 }
39037602 821
6d2010ae
A
822 rte = rte->rt_parent;
823 }
824}
825
826__private_extern__ void
827nstat_route_connect_success(
0a7de745 828 struct rtentry *rte)
6d2010ae
A
829{
830 // This route
0a7de745
A
831 while (rte) {
832 struct nstat_counts* stats = nstat_route_attach(rte);
833 if (stats) {
6d2010ae
A
834 OSIncrementAtomic(&stats->nstat_connectsuccesses);
835 }
39037602 836
6d2010ae
A
837 rte = rte->rt_parent;
838 }
839}
840
841__private_extern__ void
842nstat_route_tx(
0a7de745 843 struct rtentry *rte,
cb323159
A
844 u_int32_t packets,
845 u_int32_t bytes,
846 u_int32_t flags)
6d2010ae 847{
0a7de745
A
848 while (rte) {
849 struct nstat_counts* stats = nstat_route_attach(rte);
850 if (stats) {
851 if ((flags & NSTAT_TX_FLAG_RETRANSMIT) != 0) {
6d2010ae 852 OSAddAtomic(bytes, &stats->nstat_txretransmit);
0a7de745 853 } else {
6d2010ae
A
854 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_txpackets);
855 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_txbytes);
856 }
857 }
39037602 858
6d2010ae
A
859 rte = rte->rt_parent;
860 }
861}
862
863__private_extern__ void
864nstat_route_rx(
0a7de745 865 struct rtentry *rte,
cb323159
A
866 u_int32_t packets,
867 u_int32_t bytes,
868 u_int32_t flags)
6d2010ae 869{
0a7de745
A
870 while (rte) {
871 struct nstat_counts* stats = nstat_route_attach(rte);
872 if (stats) {
873 if (flags == 0) {
6d2010ae
A
874 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_rxpackets);
875 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_rxbytes);
0a7de745
A
876 } else {
877 if (flags & NSTAT_RX_FLAG_OUT_OF_ORDER) {
6d2010ae 878 OSAddAtomic(bytes, &stats->nstat_rxoutoforderbytes);
0a7de745
A
879 }
880 if (flags & NSTAT_RX_FLAG_DUPLICATE) {
6d2010ae 881 OSAddAtomic(bytes, &stats->nstat_rxduplicatebytes);
0a7de745 882 }
6d2010ae
A
883 }
884 }
39037602 885
6d2010ae
A
886 rte = rte->rt_parent;
887 }
888}
889
5ba3f43e 890/* atomically average current value at _val_addr with _new_val and store */
0a7de745
A
891#define NSTAT_EWMA_ATOMIC(_val_addr, _new_val, _decay) do { \
892 volatile uint32_t _old_val; \
893 volatile uint32_t _avg; \
894 do { \
895 _old_val = *_val_addr; \
896 if (_old_val == 0) \
897 { \
898 _avg = _new_val; \
899 } \
900 else \
901 { \
902 _avg = _old_val - (_old_val >> _decay) + (_new_val >> _decay); \
903 } \
904 if (_old_val == _avg) break; \
905 } while (!OSCompareAndSwap(_old_val, _avg, _val_addr)); \
5ba3f43e
A
906} while (0);
907
908/* atomically compute minimum of current value at _val_addr with _new_val and store */
0a7de745
A
909#define NSTAT_MIN_ATOMIC(_val_addr, _new_val) do { \
910 volatile uint32_t _old_val; \
911 do { \
912 _old_val = *_val_addr; \
913 if (_old_val != 0 && _old_val < _new_val) \
914 { \
915 break; \
916 } \
917 } while (!OSCompareAndSwap(_old_val, _new_val, _val_addr)); \
5ba3f43e
A
918} while (0);
919
6d2010ae
A
920__private_extern__ void
921nstat_route_rtt(
0a7de745
A
922 struct rtentry *rte,
923 u_int32_t rtt,
924 u_int32_t rtt_var)
6d2010ae 925{
5ba3f43e 926 const uint32_t decay = 3;
39037602 927
0a7de745
A
928 while (rte) {
929 struct nstat_counts* stats = nstat_route_attach(rte);
930 if (stats) {
5ba3f43e
A
931 NSTAT_EWMA_ATOMIC(&stats->nstat_avg_rtt, rtt, decay);
932 NSTAT_MIN_ATOMIC(&stats->nstat_min_rtt, rtt);
933 NSTAT_EWMA_ATOMIC(&stats->nstat_var_rtt, rtt_var, decay);
934 }
935 rte = rte->rt_parent;
936 }
937}
39037602 938
5ba3f43e
A
939__private_extern__ void
940nstat_route_update(
0a7de745
A
941 struct rtentry *rte,
942 uint32_t connect_attempts,
943 uint32_t connect_successes,
944 uint32_t rx_packets,
945 uint32_t rx_bytes,
946 uint32_t rx_duplicatebytes,
947 uint32_t rx_outoforderbytes,
948 uint32_t tx_packets,
949 uint32_t tx_bytes,
950 uint32_t tx_retransmit,
951 uint32_t rtt,
952 uint32_t rtt_var)
5ba3f43e
A
953{
954 const uint32_t decay = 3;
39037602 955
0a7de745
A
956 while (rte) {
957 struct nstat_counts* stats = nstat_route_attach(rte);
958 if (stats) {
5ba3f43e
A
959 OSAddAtomic(connect_attempts, &stats->nstat_connectattempts);
960 OSAddAtomic(connect_successes, &stats->nstat_connectsuccesses);
961 OSAddAtomic64((SInt64)tx_packets, (SInt64*)&stats->nstat_txpackets);
962 OSAddAtomic64((SInt64)tx_bytes, (SInt64*)&stats->nstat_txbytes);
963 OSAddAtomic(tx_retransmit, &stats->nstat_txretransmit);
964 OSAddAtomic64((SInt64)rx_packets, (SInt64*)&stats->nstat_rxpackets);
965 OSAddAtomic64((SInt64)rx_bytes, (SInt64*)&stats->nstat_rxbytes);
966 OSAddAtomic(rx_outoforderbytes, &stats->nstat_rxoutoforderbytes);
967 OSAddAtomic(rx_duplicatebytes, &stats->nstat_rxduplicatebytes);
968
969 if (rtt != 0) {
970 NSTAT_EWMA_ATOMIC(&stats->nstat_avg_rtt, rtt, decay);
971 NSTAT_MIN_ATOMIC(&stats->nstat_min_rtt, rtt);
972 NSTAT_EWMA_ATOMIC(&stats->nstat_var_rtt, rtt_var, decay);
973 }
6d2010ae 974 }
6d2010ae
A
975 rte = rte->rt_parent;
976 }
977}
978
39037602 979#pragma mark -- TCP Kernel Provider --
6d2010ae 980
39236c6e
A
981/*
982 * Due to the way the kernel deallocates a process (the process structure
983 * might be gone by the time we get the PCB detach notification),
984 * we need to cache the process name. Without this, proc_name() would
985 * return null and the process name would never be sent to userland.
fe8ab488
A
986 *
987 * For UDP sockets, we also store the cached the connection tuples along with
988 * the interface index. This is necessary because when UDP sockets are
989 * disconnected, the connection tuples are forever lost from the inpcb, thus
990 * we need to keep track of the last call to connect() in ntstat.
39236c6e 991 */
fe8ab488 992struct nstat_tucookie {
0a7de745
A
993 struct inpcb *inp;
994 char pname[MAXCOMLEN + 1];
995 bool cached;
996 union{
997 struct sockaddr_in v4;
998 struct sockaddr_in6 v6;
fe8ab488 999 } local;
0a7de745
A
1000 union{
1001 struct sockaddr_in v4;
1002 struct sockaddr_in6 v6;
fe8ab488 1003 } remote;
0a7de745
A
1004 unsigned int if_index;
1005 uint16_t ifnet_properties;
39236c6e
A
1006};
1007
fe8ab488
A
1008static struct nstat_tucookie *
1009nstat_tucookie_alloc_internal(
0a7de745
A
1010 struct inpcb *inp,
1011 bool ref,
1012 bool locked)
39236c6e 1013{
fe8ab488 1014 struct nstat_tucookie *cookie;
39236c6e
A
1015
1016 cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag);
0a7de745 1017 if (cookie == NULL) {
39236c6e 1018 return NULL;
0a7de745
A
1019 }
1020 if (!locked) {
5ba3f43e 1021 LCK_MTX_ASSERT(&nstat_mtx, LCK_MTX_ASSERT_NOTOWNED);
0a7de745
A
1022 }
1023 if (ref && in_pcb_checkstate(inp, WNT_ACQUIRE, locked) == WNT_STOPUSING) {
39236c6e
A
1024 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
1025 return NULL;
1026 }
1027 bzero(cookie, sizeof(*cookie));
1028 cookie->inp = inp;
1029 proc_name(inp->inp_socket->last_pid, cookie->pname,
1030 sizeof(cookie->pname));
fe8ab488
A
1031 /*
1032 * We only increment the reference count for UDP sockets because we
1033 * only cache UDP socket tuples.
1034 */
0a7de745 1035 if (SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP) {
fe8ab488 1036 OSIncrementAtomic(&inp->inp_nstat_refcnt);
0a7de745 1037 }
39236c6e
A
1038
1039 return cookie;
1040}
1041
fe8ab488
A
1042static struct nstat_tucookie *
1043nstat_tucookie_alloc(
0a7de745 1044 struct inpcb *inp)
fe8ab488
A
1045{
1046 return nstat_tucookie_alloc_internal(inp, false, false);
1047}
1048
1049static struct nstat_tucookie *
1050nstat_tucookie_alloc_ref(
0a7de745 1051 struct inpcb *inp)
fe8ab488
A
1052{
1053 return nstat_tucookie_alloc_internal(inp, true, false);
1054}
1055
1056static struct nstat_tucookie *
1057nstat_tucookie_alloc_ref_locked(
0a7de745 1058 struct inpcb *inp)
fe8ab488
A
1059{
1060 return nstat_tucookie_alloc_internal(inp, true, true);
1061}
1062
39236c6e 1063static void
fe8ab488 1064nstat_tucookie_release_internal(
0a7de745
A
1065 struct nstat_tucookie *cookie,
1066 int inplock)
39236c6e 1067{
0a7de745 1068 if (SOCK_PROTO(cookie->inp->inp_socket) == IPPROTO_UDP) {
fe8ab488 1069 OSDecrementAtomic(&cookie->inp->inp_nstat_refcnt);
0a7de745 1070 }
39236c6e
A
1071 in_pcb_checkstate(cookie->inp, WNT_RELEASE, inplock);
1072 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
1073}
1074
fe8ab488
A
1075static void
1076nstat_tucookie_release(
0a7de745 1077 struct nstat_tucookie *cookie)
fe8ab488
A
1078{
1079 nstat_tucookie_release_internal(cookie, false);
1080}
1081
1082static void
1083nstat_tucookie_release_locked(
0a7de745 1084 struct nstat_tucookie *cookie)
fe8ab488
A
1085{
1086 nstat_tucookie_release_internal(cookie, true);
1087}
1088
1089
0a7de745 1090static nstat_provider nstat_tcp_provider;
6d2010ae
A
1091
1092static errno_t
1093nstat_tcpudp_lookup(
0a7de745
A
1094 struct inpcbinfo *inpinfo,
1095 const void *data,
1096 u_int32_t length,
1097 nstat_provider_cookie_t *out_cookie)
6d2010ae 1098{
39236c6e
A
1099 struct inpcb *inp = NULL;
1100
6d2010ae 1101 // parameter validation
0a7de745
A
1102 const nstat_tcp_add_param *param = (const nstat_tcp_add_param*)data;
1103 if (length < sizeof(*param)) {
6d2010ae
A
1104 return EINVAL;
1105 }
39037602 1106
6d2010ae
A
1107 // src and dst must match
1108 if (param->remote.v4.sin_family != 0 &&
0a7de745 1109 param->remote.v4.sin_family != param->local.v4.sin_family) {
6d2010ae
A
1110 return EINVAL;
1111 }
39037602
A
1112
1113
0a7de745
A
1114 switch (param->local.v4.sin_family) {
1115 case AF_INET:
6d2010ae 1116 {
0a7de745
A
1117 if (param->local.v4.sin_len != sizeof(param->local.v4) ||
1118 (param->remote.v4.sin_family != 0 &&
1119 param->remote.v4.sin_len != sizeof(param->remote.v4))) {
1120 return EINVAL;
6d2010ae 1121 }
0a7de745
A
1122
1123 inp = in_pcblookup_hash(inpinfo, param->remote.v4.sin_addr, param->remote.v4.sin_port,
1124 param->local.v4.sin_addr, param->local.v4.sin_port, 1, NULL);
1125 }
1126 break;
39037602 1127
6d2010ae 1128#if INET6
0a7de745
A
1129 case AF_INET6:
1130 {
1131 union{
1132 const struct in6_addr *in6c;
1133 struct in6_addr *in6;
1134 } local, remote;
39037602 1135
0a7de745
A
1136 if (param->local.v6.sin6_len != sizeof(param->local.v6) ||
1137 (param->remote.v6.sin6_family != 0 &&
1138 param->remote.v6.sin6_len != sizeof(param->remote.v6))) {
1139 return EINVAL;
1140 }
39037602 1141
0a7de745
A
1142 local.in6c = &param->local.v6.sin6_addr;
1143 remote.in6c = &param->remote.v6.sin6_addr;
39037602 1144
0a7de745
A
1145 inp = in6_pcblookup_hash(inpinfo, remote.in6, param->remote.v6.sin6_port,
1146 local.in6, param->local.v6.sin6_port, 1, NULL);
1147 }
1148 break;
6d2010ae 1149#endif
39037602 1150
0a7de745
A
1151 default:
1152 return EINVAL;
6d2010ae 1153 }
39037602 1154
0a7de745 1155 if (inp == NULL) {
39236c6e 1156 return ENOENT;
0a7de745 1157 }
39037602 1158
6d2010ae 1159 // At this point we have a ref to the inpcb
fe8ab488 1160 *out_cookie = nstat_tucookie_alloc(inp);
0a7de745 1161 if (*out_cookie == NULL) {
39236c6e 1162 in_pcb_checkstate(inp, WNT_RELEASE, 0);
0a7de745 1163 }
39236c6e 1164
6d2010ae
A
1165 return 0;
1166}
1167
1168static errno_t
1169nstat_tcp_lookup(
cb323159
A
1170 const void *data,
1171 u_int32_t length,
0a7de745 1172 nstat_provider_cookie_t *out_cookie)
6d2010ae
A
1173{
1174 return nstat_tcpudp_lookup(&tcbinfo, data, length, out_cookie);
1175}
1176
1177static int
1178nstat_tcp_gone(
0a7de745 1179 nstat_provider_cookie_t cookie)
6d2010ae 1180{
fe8ab488
A
1181 struct nstat_tucookie *tucookie =
1182 (struct nstat_tucookie *)cookie;
39236c6e
A
1183 struct inpcb *inp;
1184 struct tcpcb *tp;
39037602 1185
39236c6e 1186 return (!(inp = tucookie->inp) ||
0a7de745
A
1187 !(tp = intotcpcb(inp)) ||
1188 inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
6d2010ae
A
1189}
1190
1191static errno_t
1192nstat_tcp_counts(
0a7de745 1193 nstat_provider_cookie_t cookie,
cb323159
A
1194 struct nstat_counts *out_counts,
1195 int *out_gone)
6d2010ae 1196{
fe8ab488
A
1197 struct nstat_tucookie *tucookie =
1198 (struct nstat_tucookie *)cookie;
39236c6e
A
1199 struct inpcb *inp;
1200
6d2010ae 1201 bzero(out_counts, sizeof(*out_counts));
39037602 1202
0a7de745
A
1203 if (out_gone) {
1204 *out_gone = 0;
1205 }
39037602 1206
6d2010ae 1207 // if the pcb is in the dead state, we should stop using it
0a7de745
A
1208 if (nstat_tcp_gone(cookie)) {
1209 if (out_gone) {
1210 *out_gone = 1;
1211 }
1212 if (!(inp = tucookie->inp) || !intotcpcb(inp)) {
39236c6e 1213 return EINVAL;
0a7de745 1214 }
39037602 1215 }
39236c6e
A
1216 inp = tucookie->inp;
1217 struct tcpcb *tp = intotcpcb(inp);
39037602 1218
316670eb
A
1219 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
1220 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
1221 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
1222 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
1223 out_counts->nstat_rxduplicatebytes = tp->t_stat.rxduplicatebytes;
1224 out_counts->nstat_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes;
1225 out_counts->nstat_txretransmit = tp->t_stat.txretransmitbytes;
1226 out_counts->nstat_connectattempts = tp->t_state >= TCPS_SYN_SENT ? 1 : 0;
1227 out_counts->nstat_connectsuccesses = tp->t_state >= TCPS_ESTABLISHED ? 1 : 0;
1228 out_counts->nstat_avg_rtt = tp->t_srtt;
1229 out_counts->nstat_min_rtt = tp->t_rttbest;
1230 out_counts->nstat_var_rtt = tp->t_rttvar;
0a7de745 1231 if (out_counts->nstat_avg_rtt < out_counts->nstat_min_rtt) {
316670eb 1232 out_counts->nstat_min_rtt = out_counts->nstat_avg_rtt;
0a7de745 1233 }
39236c6e
A
1234 atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes);
1235 atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes);
1236 atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes);
1237 atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes);
fe8ab488
A
1238 atomic_get_64(out_counts->nstat_wired_rxbytes, &inp->inp_Wstat->rxbytes);
1239 atomic_get_64(out_counts->nstat_wired_txbytes, &inp->inp_Wstat->txbytes);
39037602 1240
6d2010ae
A
1241 return 0;
1242}
1243
1244static void
1245nstat_tcp_release(
0a7de745 1246 nstat_provider_cookie_t cookie,
316670eb 1247 int locked)
6d2010ae 1248{
fe8ab488
A
1249 struct nstat_tucookie *tucookie =
1250 (struct nstat_tucookie *)cookie;
39236c6e 1251
fe8ab488 1252 nstat_tucookie_release_internal(tucookie, locked);
6d2010ae
A
1253}
1254
6d2010ae
A
1255static errno_t
1256nstat_tcp_add_watcher(
0a7de745 1257 nstat_control_state *state,
cb323159 1258 nstat_msg_add_all_srcs *req)
5ba3f43e
A
1259{
1260 // There is a tricky issue around getting all TCP sockets added once
1261 // and only once. nstat_tcp_new_pcb() is called prior to the new item
1262 // being placed on any lists where it might be found.
1263 // By locking the tcbinfo.ipi_lock prior to marking the state as a watcher,
1264 // it should be impossible for a new socket to be added twice.
1265 // On the other hand, there is still a timing issue where a new socket
1266 // results in a call to nstat_tcp_new_pcb() before this watcher
1267 // is instantiated and yet the socket doesn't make it into ipi_listhead
1268 // prior to the scan. <rdar://problem/30361716>
39037602 1269
5ba3f43e 1270 errno_t result;
39037602 1271
5ba3f43e
A
1272 lck_rw_lock_shared(tcbinfo.ipi_lock);
1273 result = nstat_set_provider_filter(state, req);
1274 if (result == 0) {
1275 OSIncrementAtomic(&nstat_tcp_watchers);
1276
1277 // Add all current tcp inpcbs. Ignore those in timewait
1278 struct inpcb *inp;
1279 struct nstat_tucookie *cookie;
1280 LIST_FOREACH(inp, tcbinfo.ipi_listhead, inp_list)
6d2010ae 1281 {
5ba3f43e 1282 cookie = nstat_tucookie_alloc_ref(inp);
0a7de745 1283 if (cookie == NULL) {
5ba3f43e 1284 continue;
0a7de745 1285 }
5ba3f43e 1286 if (nstat_control_source_add(0, state, &nstat_tcp_provider,
0a7de745 1287 cookie) != 0) {
5ba3f43e
A
1288 nstat_tucookie_release(cookie);
1289 break;
1290 }
6d2010ae
A
1291 }
1292 }
39037602 1293
39236c6e 1294 lck_rw_done(tcbinfo.ipi_lock);
39037602 1295
5ba3f43e 1296 return result;
6d2010ae
A
1297}
1298
1299static void
1300nstat_tcp_remove_watcher(
0a7de745 1301 __unused nstat_control_state *state)
6d2010ae
A
1302{
1303 OSDecrementAtomic(&nstat_tcp_watchers);
1304}
1305
1306__private_extern__ void
1307nstat_tcp_new_pcb(
0a7de745 1308 struct inpcb *inp)
6d2010ae 1309{
fe8ab488 1310 struct nstat_tucookie *cookie;
39236c6e 1311
5ba3f43e
A
1312 inp->inp_start_timestamp = mach_continuous_time();
1313
0a7de745 1314 if (nstat_tcp_watchers == 0) {
6d2010ae 1315 return;
0a7de745 1316 }
fe8ab488
A
1317
1318 socket_lock(inp->inp_socket, 0);
6d2010ae 1319 lck_mtx_lock(&nstat_mtx);
0a7de745
A
1320 nstat_control_state *state;
1321 for (state = nstat_controls; state; state = state->ncs_next) {
1322 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_TCP_KERNEL)) != 0) {
6d2010ae
A
1323 // this client is watching tcp
1324 // acquire a reference for it
fe8ab488 1325 cookie = nstat_tucookie_alloc_ref_locked(inp);
0a7de745 1326 if (cookie == NULL) {
39236c6e 1327 continue;
0a7de745 1328 }
6d2010ae 1329 // add the source, if that fails, release the reference
39236c6e 1330 if (nstat_control_source_add(0, state,
0a7de745 1331 &nstat_tcp_provider, cookie) != 0) {
fe8ab488 1332 nstat_tucookie_release_locked(cookie);
6d2010ae
A
1333 break;
1334 }
1335 }
1336 }
1337 lck_mtx_unlock(&nstat_mtx);
fe8ab488 1338 socket_unlock(inp->inp_socket, 0);
6d2010ae
A
1339}
1340
316670eb
A
1341__private_extern__ void
1342nstat_pcb_detach(struct inpcb *inp)
1343{
1344 nstat_control_state *state;
5ba3f43e
A
1345 nstat_src *src;
1346 tailq_head_nstat_src dead_list;
fe8ab488
A
1347 struct nstat_tucookie *tucookie;
1348 errno_t result;
316670eb 1349
0a7de745 1350 if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0)) {
316670eb 1351 return;
0a7de745 1352 }
316670eb 1353
5ba3f43e 1354 TAILQ_INIT(&dead_list);
316670eb 1355 lck_mtx_lock(&nstat_mtx);
0a7de745 1356 for (state = nstat_controls; state; state = state->ncs_next) {
5ba3f43e
A
1357 lck_mtx_lock(&state->ncs_mtx);
1358 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
39236c6e 1359 {
d190cdc3 1360 nstat_provider_id_t provider_id = src->provider->nstat_provider_id;
0a7de745 1361 if (provider_id == NSTAT_PROVIDER_TCP_KERNEL || provider_id == NSTAT_PROVIDER_UDP_KERNEL) {
d190cdc3 1362 tucookie = (struct nstat_tucookie *)src->cookie;
0a7de745 1363 if (tucookie->inp == inp) {
d190cdc3 1364 break;
0a7de745 1365 }
d190cdc3 1366 }
39236c6e 1367 }
316670eb 1368
0a7de745 1369 if (src) {
3e170ce0 1370 result = nstat_control_send_goodbye(state, src);
39037602 1371
5ba3f43e
A
1372 TAILQ_REMOVE(&state->ncs_src_queue, src, ns_control_link);
1373 TAILQ_INSERT_TAIL(&dead_list, src, ns_control_link);
316670eb 1374 }
5ba3f43e 1375 lck_mtx_unlock(&state->ncs_mtx);
316670eb
A
1376 }
1377 lck_mtx_unlock(&nstat_mtx);
1378
0a7de745 1379 while ((src = TAILQ_FIRST(&dead_list))) {
5ba3f43e 1380 TAILQ_REMOVE(&dead_list, src, ns_control_link);
316670eb
A
1381 nstat_control_cleanup_source(NULL, src, TRUE);
1382 }
1383}
1384
cb323159
A
1385__private_extern__ void
1386nstat_pcb_event(struct inpcb *inp, u_int64_t event)
1387{
1388 nstat_control_state *state;
1389 nstat_src *src;
1390 struct nstat_tucookie *tucookie;
1391 errno_t result;
1392 nstat_provider_id_t provider_id;
1393
1394 if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0)) {
1395 return;
1396 }
1397
1398 lck_mtx_lock(&nstat_mtx);
1399 for (state = nstat_controls; state; state = state->ncs_next) {
1400 if (((state->ncs_provider_filters[NSTAT_PROVIDER_TCP_KERNEL].npf_events & event) == 0) &&
1401 ((state->ncs_provider_filters[NSTAT_PROVIDER_UDP_KERNEL].npf_events & event) == 0)) {
1402 continue;
1403 }
1404 lck_mtx_lock(&state->ncs_mtx);
1405 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
1406 {
1407 provider_id = src->provider->nstat_provider_id;
1408 if (provider_id == NSTAT_PROVIDER_TCP_KERNEL || provider_id == NSTAT_PROVIDER_UDP_KERNEL) {
1409 tucookie = (struct nstat_tucookie *)src->cookie;
1410 if (tucookie->inp == inp) {
1411 break;
1412 }
1413 }
1414 }
1415
1416 if (src && ((state->ncs_provider_filters[provider_id].npf_events & event) != 0)) {
1417 result = nstat_control_send_event(state, src, event);
1418 }
1419 lck_mtx_unlock(&state->ncs_mtx);
1420 }
1421 lck_mtx_unlock(&nstat_mtx);
1422}
1423
1424
fe8ab488
A
1425__private_extern__ void
1426nstat_pcb_cache(struct inpcb *inp)
1427{
1428 nstat_control_state *state;
1429 nstat_src *src;
1430 struct nstat_tucookie *tucookie;
1431
39037602 1432 if (inp == NULL || nstat_udp_watchers == 0 ||
0a7de745 1433 inp->inp_nstat_refcnt == 0) {
fe8ab488 1434 return;
0a7de745 1435 }
fe8ab488
A
1436 VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
1437 lck_mtx_lock(&nstat_mtx);
1438 for (state = nstat_controls; state; state = state->ncs_next) {
5ba3f43e
A
1439 lck_mtx_lock(&state->ncs_mtx);
1440 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
fe8ab488
A
1441 {
1442 tucookie = (struct nstat_tucookie *)src->cookie;
0a7de745
A
1443 if (tucookie->inp == inp) {
1444 if (inp->inp_vflag & INP_IPV6) {
5ba3f43e 1445 in6_ip6_to_sockaddr(&inp->in6p_laddr,
39037602 1446 inp->inp_lport,
fe8ab488
A
1447 &tucookie->local.v6,
1448 sizeof(tucookie->local));
5ba3f43e 1449 in6_ip6_to_sockaddr(&inp->in6p_faddr,
fe8ab488
A
1450 inp->inp_fport,
1451 &tucookie->remote.v6,
1452 sizeof(tucookie->remote));
0a7de745 1453 } else if (inp->inp_vflag & INP_IPV4) {
fe8ab488 1454 nstat_ip_to_sockaddr(&inp->inp_laddr,
39037602 1455 inp->inp_lport,
fe8ab488
A
1456 &tucookie->local.v4,
1457 sizeof(tucookie->local));
1458 nstat_ip_to_sockaddr(&inp->inp_faddr,
39037602 1459 inp->inp_fport,
fe8ab488
A
1460 &tucookie->remote.v4,
1461 sizeof(tucookie->remote));
1462 }
0a7de745 1463 if (inp->inp_last_outifp) {
39037602 1464 tucookie->if_index =
fe8ab488 1465 inp->inp_last_outifp->if_index;
0a7de745 1466 }
3e170ce0
A
1467
1468 tucookie->ifnet_properties = nstat_inpcb_to_flags(inp);
fe8ab488
A
1469 tucookie->cached = true;
1470 break;
1471 }
1472 }
5ba3f43e 1473 lck_mtx_unlock(&state->ncs_mtx);
fe8ab488
A
1474 }
1475 lck_mtx_unlock(&nstat_mtx);
1476}
1477
1478__private_extern__ void
1479nstat_pcb_invalidate_cache(struct inpcb *inp)
1480{
1481 nstat_control_state *state;
1482 nstat_src *src;
1483 struct nstat_tucookie *tucookie;
1484
1485 if (inp == NULL || nstat_udp_watchers == 0 ||
0a7de745 1486 inp->inp_nstat_refcnt == 0) {
fe8ab488 1487 return;
0a7de745 1488 }
fe8ab488
A
1489 VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
1490 lck_mtx_lock(&nstat_mtx);
1491 for (state = nstat_controls; state; state = state->ncs_next) {
5ba3f43e
A
1492 lck_mtx_lock(&state->ncs_mtx);
1493 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
fe8ab488
A
1494 {
1495 tucookie = (struct nstat_tucookie *)src->cookie;
0a7de745 1496 if (tucookie->inp == inp) {
fe8ab488
A
1497 tucookie->cached = false;
1498 break;
1499 }
1500 }
5ba3f43e 1501 lck_mtx_unlock(&state->ncs_mtx);
fe8ab488
A
1502 }
1503 lck_mtx_unlock(&nstat_mtx);
1504}
1505
6d2010ae
A
1506static errno_t
1507nstat_tcp_copy_descriptor(
0a7de745
A
1508 nstat_provider_cookie_t cookie,
1509 void *data,
1510 u_int32_t len)
6d2010ae 1511{
0a7de745 1512 if (len < sizeof(nstat_tcp_descriptor)) {
6d2010ae
A
1513 return EINVAL;
1514 }
316670eb 1515
0a7de745 1516 if (nstat_tcp_gone(cookie)) {
316670eb 1517 return EINVAL;
0a7de745 1518 }
39037602 1519
0a7de745 1520 nstat_tcp_descriptor *desc = (nstat_tcp_descriptor*)data;
fe8ab488
A
1521 struct nstat_tucookie *tucookie =
1522 (struct nstat_tucookie *)cookie;
0a7de745
A
1523 struct inpcb *inp = tucookie->inp;
1524 struct tcpcb *tp = intotcpcb(inp);
6d2010ae 1525 bzero(desc, sizeof(*desc));
39037602 1526
0a7de745 1527 if (inp->inp_vflag & INP_IPV6) {
5ba3f43e 1528 in6_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
0a7de745 1529 &desc->local.v6, sizeof(desc->local));
5ba3f43e 1530 in6_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
0a7de745
A
1531 &desc->remote.v6, sizeof(desc->remote));
1532 } else if (inp->inp_vflag & INP_IPV4) {
6d2010ae 1533 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
0a7de745 1534 &desc->local.v4, sizeof(desc->local));
6d2010ae 1535 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
0a7de745 1536 &desc->remote.v4, sizeof(desc->remote));
6d2010ae 1537 }
39037602 1538
6d2010ae 1539 desc->state = intotcpcb(inp)->t_state;
316670eb
A
1540 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
1541 inp->inp_last_outifp->if_index;
39037602 1542
6d2010ae
A
1543 // danger - not locked, values could be bogus
1544 desc->txunacked = tp->snd_max - tp->snd_una;
1545 desc->txwindow = tp->snd_wnd;
1546 desc->txcwindow = tp->snd_cwnd;
fe8ab488
A
1547
1548 if (CC_ALGO(tp)->name != NULL) {
1549 strlcpy(desc->cc_algo, CC_ALGO(tp)->name,
1550 sizeof(desc->cc_algo));
1551 }
39037602 1552
6d2010ae 1553 struct socket *so = inp->inp_socket;
0a7de745 1554 if (so) {
6d2010ae
A
1555 // TBD - take the socket lock around these to make sure
1556 // they're in sync?
1557 desc->upid = so->last_upid;
1558 desc->pid = so->last_pid;
316670eb 1559 desc->traffic_class = so->so_traffic_class;
0a7de745 1560 if ((so->so_flags1 & SOF1_TRAFFIC_MGT_SO_BACKGROUND)) {
39037602 1561 desc->traffic_mgt_flags |= TRAFFIC_MGT_SO_BACKGROUND;
0a7de745
A
1562 }
1563 if ((so->so_flags1 & SOF1_TRAFFIC_MGT_TCP_RECVBG)) {
39037602 1564 desc->traffic_mgt_flags |= TRAFFIC_MGT_TCP_RECVBG;
0a7de745 1565 }
6d2010ae 1566 proc_name(desc->pid, desc->pname, sizeof(desc->pname));
0a7de745 1567 if (desc->pname[0] == 0) {
39236c6e
A
1568 strlcpy(desc->pname, tucookie->pname,
1569 sizeof(desc->pname));
0a7de745 1570 } else {
39236c6e
A
1571 desc->pname[sizeof(desc->pname) - 1] = 0;
1572 strlcpy(tucookie->pname, desc->pname,
1573 sizeof(tucookie->pname));
1574 }
1575 memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
fe8ab488 1576 memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
39236c6e
A
1577 if (so->so_flags & SOF_DELEGATED) {
1578 desc->eupid = so->e_upid;
1579 desc->epid = so->e_pid;
1580 memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
1581 } else {
1582 desc->eupid = desc->upid;
1583 desc->epid = desc->pid;
1584 memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
1585 }
6d2010ae
A
1586 desc->sndbufsize = so->so_snd.sb_hiwat;
1587 desc->sndbufused = so->so_snd.sb_cc;
1588 desc->rcvbufsize = so->so_rcv.sb_hiwat;
1589 desc->rcvbufused = so->so_rcv.sb_cc;
1590 }
3e170ce0
A
1591
1592 tcp_get_connectivity_status(tp, &desc->connstatus);
1593 desc->ifnet_properties = nstat_inpcb_to_flags(inp);
5ba3f43e
A
1594 inp_get_activity_bitmap(inp, &desc->activity_bitmap);
1595 desc->start_timestamp = inp->inp_start_timestamp;
1596 desc->timestamp = mach_continuous_time();
6d2010ae
A
1597 return 0;
1598}
1599
3e170ce0 1600static bool
39037602 1601nstat_tcpudp_reporting_allowed(nstat_provider_cookie_t cookie, nstat_provider_filter *filter, bool is_UDP)
3e170ce0
A
1602{
1603 bool retval = true;
1604
0a7de745 1605 if ((filter->npf_flags & (NSTAT_FILTER_IFNET_FLAGS | NSTAT_FILTER_SPECIFIC_USER)) != 0) {
3e170ce0 1606 struct nstat_tucookie *tucookie = (struct nstat_tucookie *)cookie;
39037602
A
1607 struct inpcb *inp = tucookie->inp;
1608
1609 /* Only apply interface filter if at least one is allowed. */
0a7de745 1610 if ((filter->npf_flags & NSTAT_FILTER_IFNET_FLAGS) != 0) {
39037602
A
1611 uint16_t interface_properties = nstat_inpcb_to_flags(inp);
1612
0a7de745 1613 if ((filter->npf_flags & interface_properties) == 0) {
39037602
A
1614 // For UDP, we could have an undefined interface and yet transfers may have occurred.
1615 // We allow reporting if there have been transfers of the requested kind.
1616 // This is imperfect as we cannot account for the expensive attribute over wifi.
1617 // We also assume that cellular is expensive and we have no way to select for AWDL
0a7de745
A
1618 if (is_UDP) {
1619 do{
1620 if ((filter->npf_flags & (NSTAT_FILTER_ACCEPT_CELLULAR | NSTAT_FILTER_ACCEPT_EXPENSIVE)) &&
1621 (inp->inp_cstat->rxbytes || inp->inp_cstat->txbytes)) {
39037602
A
1622 break;
1623 }
1624 if ((filter->npf_flags & NSTAT_FILTER_ACCEPT_WIFI) &&
0a7de745 1625 (inp->inp_wstat->rxbytes || inp->inp_wstat->txbytes)) {
39037602
A
1626 break;
1627 }
1628 if ((filter->npf_flags & NSTAT_FILTER_ACCEPT_WIRED) &&
0a7de745 1629 (inp->inp_Wstat->rxbytes || inp->inp_Wstat->txbytes)) {
39037602
A
1630 break;
1631 }
1632 return false;
1633 } while (0);
0a7de745 1634 } else {
39037602
A
1635 return false;
1636 }
1637 }
1638 }
3e170ce0 1639
0a7de745 1640 if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER) != 0) && (retval)) {
39037602
A
1641 struct socket *so = inp->inp_socket;
1642 retval = false;
3e170ce0 1643
0a7de745 1644 if (so) {
39037602 1645 if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_PID) != 0) &&
0a7de745 1646 (filter->npf_pid == so->last_pid)) {
39037602 1647 retval = true;
0a7de745
A
1648 } else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_EPID) != 0) &&
1649 (filter->npf_pid == (so->so_flags & SOF_DELEGATED)? so->e_upid : so->last_pid)) {
39037602 1650 retval = true;
0a7de745
A
1651 } else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_UUID) != 0) &&
1652 (memcmp(filter->npf_uuid, so->last_uuid, sizeof(so->last_uuid)) == 0)) {
39037602 1653 retval = true;
0a7de745
A
1654 } else if (((filter->npf_flags & NSTAT_FILTER_SPECIFIC_USER_BY_EUUID) != 0) &&
1655 (memcmp(filter->npf_uuid, (so->so_flags & SOF_DELEGATED)? so->e_uuid : so->last_uuid,
1656 sizeof(so->last_uuid)) == 0)) {
39037602
A
1657 retval = true;
1658 }
1659 }
1660 }
3e170ce0
A
1661 }
1662 return retval;
1663}
1664
39037602
A
1665static bool
1666nstat_tcp_reporting_allowed(nstat_provider_cookie_t cookie, nstat_provider_filter *filter)
1667{
1668 return nstat_tcpudp_reporting_allowed(cookie, filter, FALSE);
1669}
1670
6d2010ae
A
1671static void
1672nstat_init_tcp_provider(void)
1673{
1674 bzero(&nstat_tcp_provider, sizeof(nstat_tcp_provider));
1675 nstat_tcp_provider.nstat_descriptor_length = sizeof(nstat_tcp_descriptor);
39037602 1676 nstat_tcp_provider.nstat_provider_id = NSTAT_PROVIDER_TCP_KERNEL;
6d2010ae
A
1677 nstat_tcp_provider.nstat_lookup = nstat_tcp_lookup;
1678 nstat_tcp_provider.nstat_gone = nstat_tcp_gone;
1679 nstat_tcp_provider.nstat_counts = nstat_tcp_counts;
1680 nstat_tcp_provider.nstat_release = nstat_tcp_release;
1681 nstat_tcp_provider.nstat_watcher_add = nstat_tcp_add_watcher;
1682 nstat_tcp_provider.nstat_watcher_remove = nstat_tcp_remove_watcher;
1683 nstat_tcp_provider.nstat_copy_descriptor = nstat_tcp_copy_descriptor;
39037602 1684 nstat_tcp_provider.nstat_reporting_allowed = nstat_tcp_reporting_allowed;
6d2010ae
A
1685 nstat_tcp_provider.next = nstat_providers;
1686 nstat_providers = &nstat_tcp_provider;
1687}
1688
1689#pragma mark -- UDP Provider --
1690
0a7de745 1691static nstat_provider nstat_udp_provider;
6d2010ae
A
1692
1693static errno_t
1694nstat_udp_lookup(
cb323159
A
1695 const void *data,
1696 u_int32_t length,
0a7de745 1697 nstat_provider_cookie_t *out_cookie)
6d2010ae
A
1698{
1699 return nstat_tcpudp_lookup(&udbinfo, data, length, out_cookie);
1700}
1701
1702static int
1703nstat_udp_gone(
0a7de745 1704 nstat_provider_cookie_t cookie)
6d2010ae 1705{
fe8ab488
A
1706 struct nstat_tucookie *tucookie =
1707 (struct nstat_tucookie *)cookie;
39236c6e
A
1708 struct inpcb *inp;
1709
1710 return (!(inp = tucookie->inp) ||
0a7de745 1711 inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
6d2010ae
A
1712}
1713
1714static errno_t
1715nstat_udp_counts(
0a7de745
A
1716 nstat_provider_cookie_t cookie,
1717 struct nstat_counts *out_counts,
1718 int *out_gone)
6d2010ae 1719{
fe8ab488
A
1720 struct nstat_tucookie *tucookie =
1721 (struct nstat_tucookie *)cookie;
39037602 1722
0a7de745
A
1723 if (out_gone) {
1724 *out_gone = 0;
1725 }
39037602 1726
6d2010ae 1727 // if the pcb is in the dead state, we should stop using it
0a7de745
A
1728 if (nstat_udp_gone(cookie)) {
1729 if (out_gone) {
1730 *out_gone = 1;
1731 }
1732 if (!tucookie->inp) {
39236c6e 1733 return EINVAL;
0a7de745 1734 }
6d2010ae 1735 }
39236c6e 1736 struct inpcb *inp = tucookie->inp;
39037602 1737
6d2010ae
A
1738 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
1739 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
1740 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
1741 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
39236c6e
A
1742 atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes);
1743 atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes);
1744 atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes);
1745 atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes);
fe8ab488
A
1746 atomic_get_64(out_counts->nstat_wired_rxbytes, &inp->inp_Wstat->rxbytes);
1747 atomic_get_64(out_counts->nstat_wired_txbytes, &inp->inp_Wstat->txbytes);
39037602 1748
6d2010ae
A
1749 return 0;
1750}
1751
1752static void
1753nstat_udp_release(
0a7de745 1754 nstat_provider_cookie_t cookie,
316670eb 1755 int locked)
6d2010ae 1756{
fe8ab488
A
1757 struct nstat_tucookie *tucookie =
1758 (struct nstat_tucookie *)cookie;
39236c6e 1759
fe8ab488 1760 nstat_tucookie_release_internal(tucookie, locked);
6d2010ae
A
1761}
1762
6d2010ae
A
1763static errno_t
1764nstat_udp_add_watcher(
0a7de745 1765 nstat_control_state *state,
cb323159 1766 nstat_msg_add_all_srcs *req)
5ba3f43e
A
1767{
1768 // There is a tricky issue around getting all UDP sockets added once
1769 // and only once. nstat_udp_new_pcb() is called prior to the new item
1770 // being placed on any lists where it might be found.
1771 // By locking the udpinfo.ipi_lock prior to marking the state as a watcher,
1772 // it should be impossible for a new socket to be added twice.
1773 // On the other hand, there is still a timing issue where a new socket
1774 // results in a call to nstat_udp_new_pcb() before this watcher
1775 // is instantiated and yet the socket doesn't make it into ipi_listhead
1776 // prior to the scan. <rdar://problem/30361716>
39236c6e 1777
5ba3f43e 1778 errno_t result;
39037602 1779
39236c6e 1780 lck_rw_lock_shared(udbinfo.ipi_lock);
5ba3f43e
A
1781 result = nstat_set_provider_filter(state, req);
1782
1783 if (result == 0) {
1784 struct inpcb *inp;
1785 struct nstat_tucookie *cookie;
1786
1787 OSIncrementAtomic(&nstat_udp_watchers);
1788
1789 // Add all current UDP inpcbs.
1790 LIST_FOREACH(inp, udbinfo.ipi_listhead, inp_list)
6d2010ae 1791 {
5ba3f43e 1792 cookie = nstat_tucookie_alloc_ref(inp);
0a7de745 1793 if (cookie == NULL) {
5ba3f43e 1794 continue;
0a7de745 1795 }
5ba3f43e 1796 if (nstat_control_source_add(0, state, &nstat_udp_provider,
0a7de745 1797 cookie) != 0) {
5ba3f43e
A
1798 nstat_tucookie_release(cookie);
1799 break;
1800 }
6d2010ae
A
1801 }
1802 }
39037602 1803
39236c6e 1804 lck_rw_done(udbinfo.ipi_lock);
39037602 1805
5ba3f43e 1806 return result;
6d2010ae
A
1807}
1808
1809static void
1810nstat_udp_remove_watcher(
0a7de745 1811 __unused nstat_control_state *state)
6d2010ae
A
1812{
1813 OSDecrementAtomic(&nstat_udp_watchers);
1814}
1815
1816__private_extern__ void
1817nstat_udp_new_pcb(
0a7de745 1818 struct inpcb *inp)
6d2010ae 1819{
fe8ab488 1820 struct nstat_tucookie *cookie;
39236c6e 1821
5ba3f43e
A
1822 inp->inp_start_timestamp = mach_continuous_time();
1823
0a7de745 1824 if (nstat_udp_watchers == 0) {
6d2010ae 1825 return;
0a7de745 1826 }
39037602 1827
fe8ab488 1828 socket_lock(inp->inp_socket, 0);
6d2010ae 1829 lck_mtx_lock(&nstat_mtx);
0a7de745
A
1830 nstat_control_state *state;
1831 for (state = nstat_controls; state; state = state->ncs_next) {
1832 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_UDP_KERNEL)) != 0) {
6d2010ae
A
1833 // this client is watching tcp
1834 // acquire a reference for it
fe8ab488 1835 cookie = nstat_tucookie_alloc_ref_locked(inp);
0a7de745 1836 if (cookie == NULL) {
39236c6e 1837 continue;
0a7de745 1838 }
6d2010ae 1839 // add the source, if that fails, release the reference
39037602 1840 if (nstat_control_source_add(0, state,
0a7de745 1841 &nstat_udp_provider, cookie) != 0) {
fe8ab488 1842 nstat_tucookie_release_locked(cookie);
6d2010ae
A
1843 break;
1844 }
1845 }
1846 }
1847 lck_mtx_unlock(&nstat_mtx);
fe8ab488 1848 socket_unlock(inp->inp_socket, 0);
6d2010ae
A
1849}
1850
1851static errno_t
1852nstat_udp_copy_descriptor(
0a7de745 1853 nstat_provider_cookie_t cookie,
cb323159
A
1854 void *data,
1855 u_int32_t len)
6d2010ae 1856{
0a7de745 1857 if (len < sizeof(nstat_udp_descriptor)) {
6d2010ae
A
1858 return EINVAL;
1859 }
39037602 1860
0a7de745 1861 if (nstat_udp_gone(cookie)) {
316670eb 1862 return EINVAL;
0a7de745 1863 }
316670eb 1864
0a7de745 1865 struct nstat_tucookie *tucookie =
fe8ab488 1866 (struct nstat_tucookie *)cookie;
cb323159
A
1867 nstat_udp_descriptor *desc = (nstat_udp_descriptor*)data;
1868 struct inpcb *inp = tucookie->inp;
39236c6e 1869
6d2010ae 1870 bzero(desc, sizeof(*desc));
39037602 1871
fe8ab488 1872 if (tucookie->cached == false) {
0a7de745 1873 if (inp->inp_vflag & INP_IPV6) {
5ba3f43e 1874 in6_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
0a7de745 1875 &desc->local.v6, sizeof(desc->local.v6));
5ba3f43e 1876 in6_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
0a7de745
A
1877 &desc->remote.v6, sizeof(desc->remote.v6));
1878 } else if (inp->inp_vflag & INP_IPV4) {
fe8ab488 1879 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
0a7de745 1880 &desc->local.v4, sizeof(desc->local.v4));
fe8ab488 1881 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
0a7de745 1882 &desc->remote.v4, sizeof(desc->remote.v4));
fe8ab488 1883 }
3e170ce0 1884 desc->ifnet_properties = nstat_inpcb_to_flags(inp);
0a7de745
A
1885 } else {
1886 if (inp->inp_vflag & INP_IPV6) {
39037602
A
1887 memcpy(&desc->local.v6, &tucookie->local.v6,
1888 sizeof(desc->local.v6));
1889 memcpy(&desc->remote.v6, &tucookie->remote.v6,
1890 sizeof(desc->remote.v6));
0a7de745 1891 } else if (inp->inp_vflag & INP_IPV4) {
39037602
A
1892 memcpy(&desc->local.v4, &tucookie->local.v4,
1893 sizeof(desc->local.v4));
1894 memcpy(&desc->remote.v4, &tucookie->remote.v4,
1895 sizeof(desc->remote.v4));
1896 }
1897 desc->ifnet_properties = tucookie->ifnet_properties;
1898 }
1899
0a7de745 1900 if (inp->inp_last_outifp) {
39037602 1901 desc->ifindex = inp->inp_last_outifp->if_index;
0a7de745 1902 } else {
39037602 1903 desc->ifindex = tucookie->if_index;
0a7de745 1904 }
39037602
A
1905
1906 struct socket *so = inp->inp_socket;
0a7de745 1907 if (so) {
39037602
A
1908 // TBD - take the socket lock around these to make sure
1909 // they're in sync?
1910 desc->upid = so->last_upid;
1911 desc->pid = so->last_pid;
1912 proc_name(desc->pid, desc->pname, sizeof(desc->pname));
0a7de745 1913 if (desc->pname[0] == 0) {
39037602
A
1914 strlcpy(desc->pname, tucookie->pname,
1915 sizeof(desc->pname));
0a7de745 1916 } else {
39037602
A
1917 desc->pname[sizeof(desc->pname) - 1] = 0;
1918 strlcpy(tucookie->pname, desc->pname,
1919 sizeof(tucookie->pname));
1920 }
1921 memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
1922 memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
1923 if (so->so_flags & SOF_DELEGATED) {
1924 desc->eupid = so->e_upid;
1925 desc->epid = so->e_pid;
1926 memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
1927 } else {
1928 desc->eupid = desc->upid;
1929 desc->epid = desc->pid;
1930 memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
1931 }
1932 desc->rcvbufsize = so->so_rcv.sb_hiwat;
1933 desc->rcvbufused = so->so_rcv.sb_cc;
1934 desc->traffic_class = so->so_traffic_class;
5ba3f43e
A
1935 inp_get_activity_bitmap(inp, &desc->activity_bitmap);
1936 desc->start_timestamp = inp->inp_start_timestamp;
1937 desc->timestamp = mach_continuous_time();
39037602
A
1938 }
1939
1940 return 0;
1941}
1942
1943static bool
1944nstat_udp_reporting_allowed(nstat_provider_cookie_t cookie, nstat_provider_filter *filter)
1945{
1946 return nstat_tcpudp_reporting_allowed(cookie, filter, TRUE);
1947}
1948
1949
1950static void
1951nstat_init_udp_provider(void)
1952{
1953 bzero(&nstat_udp_provider, sizeof(nstat_udp_provider));
1954 nstat_udp_provider.nstat_provider_id = NSTAT_PROVIDER_UDP_KERNEL;
1955 nstat_udp_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor);
1956 nstat_udp_provider.nstat_lookup = nstat_udp_lookup;
1957 nstat_udp_provider.nstat_gone = nstat_udp_gone;
5ba3f43e
A
1958 nstat_udp_provider.nstat_counts = nstat_udp_counts;
1959 nstat_udp_provider.nstat_watcher_add = nstat_udp_add_watcher;
1960 nstat_udp_provider.nstat_watcher_remove = nstat_udp_remove_watcher;
1961 nstat_udp_provider.nstat_copy_descriptor = nstat_udp_copy_descriptor;
1962 nstat_udp_provider.nstat_release = nstat_udp_release;
1963 nstat_udp_provider.nstat_reporting_allowed = nstat_udp_reporting_allowed;
1964 nstat_udp_provider.next = nstat_providers;
1965 nstat_providers = &nstat_udp_provider;
6d2010ae
A
1966}
1967
39037602
A
1968
1969
39236c6e
A
1970#pragma mark -- ifnet Provider --
1971
0a7de745 1972static nstat_provider nstat_ifnet_provider;
39236c6e
A
1973
1974/*
1975 * We store a pointer to the ifnet and the original threshold
1976 * requested by the client.
1977 */
0a7de745
A
1978struct nstat_ifnet_cookie {
1979 struct ifnet *ifp;
1980 uint64_t threshold;
39236c6e
A
1981};
1982
1983static errno_t
1984nstat_ifnet_lookup(
0a7de745
A
1985 const void *data,
1986 u_int32_t length,
1987 nstat_provider_cookie_t *out_cookie)
39236c6e 1988{
3e170ce0 1989 const nstat_ifnet_add_param *param = (const nstat_ifnet_add_param *)data;
39236c6e
A
1990 struct ifnet *ifp;
1991 boolean_t changed = FALSE;
1992 nstat_control_state *state;
1993 nstat_src *src;
1994 struct nstat_ifnet_cookie *cookie;
1995
0a7de745 1996 if (length < sizeof(*param) || param->threshold < 1024 * 1024) {
39236c6e 1997 return EINVAL;
0a7de745 1998 }
39236c6e 1999 if (nstat_privcheck != 0) {
39037602 2000 errno_t result = priv_check_cred(kauth_cred_get(),
39236c6e 2001 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
0a7de745 2002 if (result != 0) {
39236c6e 2003 return result;
0a7de745 2004 }
39236c6e
A
2005 }
2006 cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag);
0a7de745 2007 if (cookie == NULL) {
39236c6e 2008 return ENOMEM;
0a7de745 2009 }
39236c6e
A
2010 bzero(cookie, sizeof(*cookie));
2011
2012 ifnet_head_lock_shared();
2013 TAILQ_FOREACH(ifp, &ifnet_head, if_link)
2014 {
2015 ifnet_lock_exclusive(ifp);
0a7de745 2016 if (ifp->if_index == param->ifindex) {
39236c6e
A
2017 cookie->ifp = ifp;
2018 cookie->threshold = param->threshold;
2019 *out_cookie = cookie;
2020 if (!ifp->if_data_threshold ||
0a7de745 2021 ifp->if_data_threshold > param->threshold) {
39236c6e
A
2022 changed = TRUE;
2023 ifp->if_data_threshold = param->threshold;
2024 }
2025 ifnet_lock_done(ifp);
2026 ifnet_reference(ifp);
2027 break;
2028 }
2029 ifnet_lock_done(ifp);
2030 }
2031 ifnet_head_done();
2032
2033 /*
2034 * When we change the threshold to something smaller, we notify
2035 * all of our clients with a description message.
2036 * We won't send a message to the client we are currently serving
2037 * because it has no `ifnet source' yet.
2038 */
0a7de745 2039 if (changed) {
39236c6e 2040 lck_mtx_lock(&nstat_mtx);
0a7de745 2041 for (state = nstat_controls; state; state = state->ncs_next) {
5ba3f43e
A
2042 lck_mtx_lock(&state->ncs_mtx);
2043 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
39236c6e 2044 {
0a7de745 2045 if (src->provider != &nstat_ifnet_provider) {
39236c6e 2046 continue;
0a7de745 2047 }
3e170ce0 2048 nstat_control_send_description(state, src, 0, 0);
39236c6e 2049 }
5ba3f43e 2050 lck_mtx_unlock(&state->ncs_mtx);
39236c6e
A
2051 }
2052 lck_mtx_unlock(&nstat_mtx);
2053 }
0a7de745 2054 if (cookie->ifp == NULL) {
39236c6e 2055 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
0a7de745 2056 }
39236c6e
A
2057
2058 return ifp ? 0 : EINVAL;
2059}
2060
2061static int
2062nstat_ifnet_gone(
0a7de745 2063 nstat_provider_cookie_t cookie)
39236c6e
A
2064{
2065 struct ifnet *ifp;
2066 struct nstat_ifnet_cookie *ifcookie =
2067 (struct nstat_ifnet_cookie *)cookie;
2068
2069 ifnet_head_lock_shared();
2070 TAILQ_FOREACH(ifp, &ifnet_head, if_link)
2071 {
0a7de745 2072 if (ifp == ifcookie->ifp) {
39236c6e 2073 break;
0a7de745 2074 }
39236c6e
A
2075 }
2076 ifnet_head_done();
2077
2078 return ifp ? 0 : 1;
2079}
2080
2081static errno_t
2082nstat_ifnet_counts(
0a7de745
A
2083 nstat_provider_cookie_t cookie,
2084 struct nstat_counts *out_counts,
2085 int *out_gone)
39236c6e
A
2086{
2087 struct nstat_ifnet_cookie *ifcookie =
2088 (struct nstat_ifnet_cookie *)cookie;
2089 struct ifnet *ifp = ifcookie->ifp;
2090
0a7de745
A
2091 if (out_gone) {
2092 *out_gone = 0;
2093 }
39037602 2094
39236c6e 2095 // if the ifnet is gone, we should stop using it
0a7de745
A
2096 if (nstat_ifnet_gone(cookie)) {
2097 if (out_gone) {
2098 *out_gone = 1;
2099 }
39236c6e
A
2100 return EINVAL;
2101 }
2102
2103 bzero(out_counts, sizeof(*out_counts));
2104 out_counts->nstat_rxpackets = ifp->if_ipackets;
2105 out_counts->nstat_rxbytes = ifp->if_ibytes;
2106 out_counts->nstat_txpackets = ifp->if_opackets;
2107 out_counts->nstat_txbytes = ifp->if_obytes;
2108 out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
39236c6e
A
2109 return 0;
2110}
2111
2112static void
2113nstat_ifnet_release(
0a7de745
A
2114 nstat_provider_cookie_t cookie,
2115 __unused int locked)
39236c6e
A
2116{
2117 struct nstat_ifnet_cookie *ifcookie;
2118 struct ifnet *ifp;
2119 nstat_control_state *state;
2120 nstat_src *src;
2121 uint64_t minthreshold = UINT64_MAX;
2122
2123 /*
2124 * Find all the clients that requested a threshold
2125 * for this ifnet and re-calculate if_data_threshold.
2126 */
2127 lck_mtx_lock(&nstat_mtx);
0a7de745 2128 for (state = nstat_controls; state; state = state->ncs_next) {
5ba3f43e
A
2129 lck_mtx_lock(&state->ncs_mtx);
2130 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
39236c6e
A
2131 {
2132 /* Skip the provider we are about to detach. */
2133 if (src->provider != &nstat_ifnet_provider ||
0a7de745 2134 src->cookie == cookie) {
39236c6e 2135 continue;
0a7de745
A
2136 }
2137 ifcookie = (struct nstat_ifnet_cookie *)src->cookie;
2138 if (ifcookie->threshold < minthreshold) {
39037602 2139 minthreshold = ifcookie->threshold;
0a7de745 2140 }
39236c6e 2141 }
5ba3f43e 2142 lck_mtx_unlock(&state->ncs_mtx);
39236c6e
A
2143 }
2144 lck_mtx_unlock(&nstat_mtx);
2145 /*
2146 * Reset if_data_threshold or disable it.
2147 */
2148 ifcookie = (struct nstat_ifnet_cookie *)cookie;
2149 ifp = ifcookie->ifp;
2150 if (ifnet_is_attached(ifp, 1)) {
2151 ifnet_lock_exclusive(ifp);
0a7de745 2152 if (minthreshold == UINT64_MAX) {
39236c6e 2153 ifp->if_data_threshold = 0;
0a7de745 2154 } else {
39236c6e 2155 ifp->if_data_threshold = minthreshold;
0a7de745 2156 }
39236c6e 2157 ifnet_lock_done(ifp);
39037602 2158 ifnet_decr_iorefcnt(ifp);
39236c6e
A
2159 }
2160 ifnet_release(ifp);
2161 OSFree(ifcookie, sizeof(*ifcookie), nstat_malloc_tag);
2162}
2163
3e170ce0
A
2164static void
2165nstat_ifnet_copy_link_status(
0a7de745
A
2166 struct ifnet *ifp,
2167 struct nstat_ifnet_descriptor *desc)
3e170ce0
A
2168{
2169 struct if_link_status *ifsr = ifp->if_link_status;
2170 nstat_ifnet_desc_link_status *link_status = &desc->link_status;
2171
2172 link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_NONE;
0a7de745 2173 if (ifsr == NULL) {
3e170ce0 2174 return;
0a7de745 2175 }
3e170ce0
A
2176
2177 lck_rw_lock_shared(&ifp->if_link_status_lock);
2178
2179 if (ifp->if_type == IFT_CELLULAR) {
3e170ce0
A
2180 nstat_ifnet_desc_cellular_status *cell_status = &link_status->u.cellular;
2181 struct if_cellular_status_v1 *if_cell_sr =
0a7de745 2182 &ifsr->ifsr_u.ifsr_cell.if_cell_u.if_status_v1;
3e170ce0 2183
0a7de745 2184 if (ifsr->ifsr_version != IF_CELLULAR_STATUS_REPORT_VERSION_1) {
3e170ce0 2185 goto done;
0a7de745 2186 }
3e170ce0
A
2187
2188 link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR;
2189
2190 if (if_cell_sr->valid_bitmask & IF_CELL_LINK_QUALITY_METRIC_VALID) {
2191 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_LINK_QUALITY_METRIC_VALID;
2192 cell_status->link_quality_metric = if_cell_sr->link_quality_metric;
2193 }
2194 if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_BANDWIDTH_VALID) {
2195 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_BANDWIDTH_VALID;
2196 cell_status->ul_effective_bandwidth = if_cell_sr->ul_effective_bandwidth;
2197 }
2198 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_BANDWIDTH_VALID) {
2199 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_BANDWIDTH_VALID;
2200 cell_status->ul_max_bandwidth = if_cell_sr->ul_max_bandwidth;
2201 }
2202 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_LATENCY_VALID) {
2203 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_LATENCY_VALID;
2204 cell_status->ul_min_latency = if_cell_sr->ul_min_latency;
2205 }
2206 if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_LATENCY_VALID) {
2207 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_LATENCY_VALID;
2208 cell_status->ul_effective_latency = if_cell_sr->ul_effective_latency;
2209 }
2210 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_LATENCY_VALID) {
2211 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_LATENCY_VALID;
2212 cell_status->ul_max_latency = if_cell_sr->ul_max_latency;
2213 }
2214 if (if_cell_sr->valid_bitmask & IF_CELL_UL_RETXT_LEVEL_VALID) {
2215 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
0a7de745 2216 if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_NONE) {
3e170ce0 2217 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_NONE;
0a7de745 2218 } else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_LOW) {
3e170ce0 2219 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_LOW;
0a7de745 2220 } else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_MEDIUM) {
3e170ce0 2221 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_MEDIUM;
0a7de745 2222 } else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_HIGH) {
3e170ce0 2223 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_HIGH;
0a7de745 2224 } else {
3e170ce0 2225 cell_status->valid_bitmask &= ~NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
0a7de745 2226 }
3e170ce0
A
2227 }
2228 if (if_cell_sr->valid_bitmask & IF_CELL_UL_BYTES_LOST_VALID) {
2229 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_BYTES_LOST_VALID;
2230 cell_status->ul_bytes_lost = if_cell_sr->ul_bytes_lost;
2231 }
2232 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_QUEUE_SIZE_VALID) {
2233 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_QUEUE_SIZE_VALID;
2234 cell_status->ul_min_queue_size = if_cell_sr->ul_min_queue_size;
2235 }
2236 if (if_cell_sr->valid_bitmask & IF_CELL_UL_AVG_QUEUE_SIZE_VALID) {
2237 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_AVG_QUEUE_SIZE_VALID;
2238 cell_status->ul_avg_queue_size = if_cell_sr->ul_avg_queue_size;
2239 }
2240 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_QUEUE_SIZE_VALID) {
2241 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_QUEUE_SIZE_VALID;
2242 cell_status->ul_max_queue_size = if_cell_sr->ul_max_queue_size;
2243 }
2244 if (if_cell_sr->valid_bitmask & IF_CELL_DL_EFFECTIVE_BANDWIDTH_VALID) {
2245 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_EFFECTIVE_BANDWIDTH_VALID;
2246 cell_status->dl_effective_bandwidth = if_cell_sr->dl_effective_bandwidth;
2247 }
2248 if (if_cell_sr->valid_bitmask & IF_CELL_DL_MAX_BANDWIDTH_VALID) {
2249 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_MAX_BANDWIDTH_VALID;
2250 cell_status->dl_max_bandwidth = if_cell_sr->dl_max_bandwidth;
2251 }
2252 if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_INACTIVITY_TIME_VALID) {
2253 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_INACTIVITY_TIME_VALID;
2254 cell_status->config_inactivity_time = if_cell_sr->config_inactivity_time;
2255 }
2256 if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_BACKOFF_TIME_VALID) {
2257 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_BACKOFF_TIME_VALID;
2258 cell_status->config_backoff_time = if_cell_sr->config_backoff_time;
2259 }
39037602
A
2260 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MSS_RECOMMENDED_VALID) {
2261 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_MSS_RECOMMENDED_VALID;
2262 cell_status->mss_recommended = if_cell_sr->mss_recommended;
2263 }
cb323159 2264 } else if (IFNET_IS_WIFI(ifp)) {
3e170ce0
A
2265 nstat_ifnet_desc_wifi_status *wifi_status = &link_status->u.wifi;
2266 struct if_wifi_status_v1 *if_wifi_sr =
0a7de745 2267 &ifsr->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
3e170ce0 2268
0a7de745 2269 if (ifsr->ifsr_version != IF_WIFI_STATUS_REPORT_VERSION_1) {
3e170ce0 2270 goto done;
0a7de745 2271 }
3e170ce0
A
2272
2273 link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI;
2274
2275 if (if_wifi_sr->valid_bitmask & IF_WIFI_LINK_QUALITY_METRIC_VALID) {
2276 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_LINK_QUALITY_METRIC_VALID;
2277 wifi_status->link_quality_metric = if_wifi_sr->link_quality_metric;
2278 }
2279 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID) {
2280 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID;
2281 wifi_status->ul_effective_bandwidth = if_wifi_sr->ul_effective_bandwidth;
2282 }
2283 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_BANDWIDTH_VALID) {
2284 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_BANDWIDTH_VALID;
2285 wifi_status->ul_max_bandwidth = if_wifi_sr->ul_max_bandwidth;
2286 }
2287 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MIN_LATENCY_VALID) {
2288 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MIN_LATENCY_VALID;
2289 wifi_status->ul_min_latency = if_wifi_sr->ul_min_latency;
2290 }
2291 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_LATENCY_VALID) {
2292 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_LATENCY_VALID;
2293 wifi_status->ul_effective_latency = if_wifi_sr->ul_effective_latency;
2294 }
2295 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_LATENCY_VALID) {
2296 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_LATENCY_VALID;
2297 wifi_status->ul_max_latency = if_wifi_sr->ul_max_latency;
2298 }
2299 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_RETXT_LEVEL_VALID) {
2300 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
0a7de745 2301 if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_NONE) {
3e170ce0 2302 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_NONE;
0a7de745 2303 } else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_LOW) {
3e170ce0 2304 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_LOW;
0a7de745 2305 } else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_MEDIUM) {
3e170ce0 2306 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_MEDIUM;
0a7de745 2307 } else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_HIGH) {
3e170ce0 2308 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_HIGH;
0a7de745 2309 } else {
3e170ce0 2310 wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
0a7de745 2311 }
3e170ce0
A
2312 }
2313 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_BYTES_LOST_VALID) {
2314 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_BYTES_LOST_VALID;
2315 wifi_status->ul_bytes_lost = if_wifi_sr->ul_bytes_lost;
2316 }
2317 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_ERROR_RATE_VALID) {
2318 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_ERROR_RATE_VALID;
2319 wifi_status->ul_error_rate = if_wifi_sr->ul_error_rate;
2320 }
2321 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID) {
2322 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID;
2323 wifi_status->dl_effective_bandwidth = if_wifi_sr->dl_effective_bandwidth;
2324 }
2325 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_BANDWIDTH_VALID) {
2326 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_BANDWIDTH_VALID;
2327 wifi_status->dl_max_bandwidth = if_wifi_sr->dl_max_bandwidth;
2328 }
2329 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MIN_LATENCY_VALID) {
2330 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MIN_LATENCY_VALID;
2331 wifi_status->dl_min_latency = if_wifi_sr->dl_min_latency;
2332 }
2333 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_LATENCY_VALID) {
2334 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_LATENCY_VALID;
2335 wifi_status->dl_effective_latency = if_wifi_sr->dl_effective_latency;
2336 }
2337 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_LATENCY_VALID) {
2338 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_LATENCY_VALID;
2339 wifi_status->dl_max_latency = if_wifi_sr->dl_max_latency;
2340 }
2341 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_ERROR_RATE_VALID) {
2342 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_ERROR_RATE_VALID;
2343 wifi_status->dl_error_rate = if_wifi_sr->dl_error_rate;
2344 }
2345 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_FREQUENCY_VALID) {
2346 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
0a7de745 2347 if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_2_4_GHZ) {
3e170ce0 2348 wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_2_4_GHZ;
0a7de745 2349 } else if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_5_0_GHZ) {
3e170ce0 2350 wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_5_0_GHZ;
0a7de745 2351 } else {
3e170ce0 2352 wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
0a7de745 2353 }
3e170ce0
A
2354 }
2355 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_MULTICAST_RATE_VALID) {
2356 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_MULTICAST_RATE_VALID;
2357 wifi_status->config_multicast_rate = if_wifi_sr->config_multicast_rate;
2358 }
2359 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_COUNT_VALID) {
2360 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_COUNT_VALID;
2361 wifi_status->scan_count = if_wifi_sr->scan_count;
2362 }
2363 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_DURATION_VALID) {
2364 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_DURATION_VALID;
2365 wifi_status->scan_duration = if_wifi_sr->scan_duration;
2366 }
2367 }
2368
2369done:
2370 lck_rw_done(&ifp->if_link_status_lock);
2371}
2372
4bd07ac2
A
2373static u_int64_t nstat_ifnet_last_report_time = 0;
2374extern int tcp_report_stats_interval;
2375
490019cf
A
2376static void
2377nstat_ifnet_compute_percentages(struct if_tcp_ecn_perf_stat *ifst)
2378{
2379 /* Retransmit percentage */
2380 if (ifst->total_rxmitpkts > 0 && ifst->total_txpkts > 0) {
2381 /* shift by 10 for precision */
2382 ifst->rxmit_percent =
2383 ((ifst->total_rxmitpkts << 10) * 100) / ifst->total_txpkts;
2384 } else {
2385 ifst->rxmit_percent = 0;
2386 }
2387
2388 /* Out-of-order percentage */
2389 if (ifst->total_oopkts > 0 && ifst->total_rxpkts > 0) {
2390 /* shift by 10 for precision */
2391 ifst->oo_percent =
2392 ((ifst->total_oopkts << 10) * 100) / ifst->total_rxpkts;
2393 } else {
2394 ifst->oo_percent = 0;
2395 }
2396
2397 /* Reorder percentage */
2398 if (ifst->total_reorderpkts > 0 &&
2399 (ifst->total_txpkts + ifst->total_rxpkts) > 0) {
2400 /* shift by 10 for precision */
2401 ifst->reorder_percent =
2402 ((ifst->total_reorderpkts << 10) * 100) /
2403 (ifst->total_txpkts + ifst->total_rxpkts);
2404 } else {
2405 ifst->reorder_percent = 0;
2406 }
2407}
2408
2409static void
2410nstat_ifnet_normalize_counter(struct if_tcp_ecn_stat *if_st)
2411{
2412 u_int64_t ecn_on_conn, ecn_off_conn;
2413
0a7de745 2414 if (if_st == NULL) {
490019cf 2415 return;
0a7de745 2416 }
490019cf
A
2417 ecn_on_conn = if_st->ecn_client_success +
2418 if_st->ecn_server_success;
2419 ecn_off_conn = if_st->ecn_off_conn +
2420 (if_st->ecn_client_setup - if_st->ecn_client_success) +
2421 (if_st->ecn_server_setup - if_st->ecn_server_success);
2422
2423 /*
2424 * report sack episodes, rst_drop and rxmit_drop
2425 * as a ratio per connection, shift by 10 for precision
2426 */
2427 if (ecn_on_conn > 0) {
2428 if_st->ecn_on.sack_episodes =
2429 (if_st->ecn_on.sack_episodes << 10) / ecn_on_conn;
2430 if_st->ecn_on.rst_drop =
2431 (if_st->ecn_on.rst_drop << 10) * 100 / ecn_on_conn;
2432 if_st->ecn_on.rxmit_drop =
2433 (if_st->ecn_on.rxmit_drop << 10) * 100 / ecn_on_conn;
2434 } else {
2435 /* set to zero, just in case */
2436 if_st->ecn_on.sack_episodes = 0;
2437 if_st->ecn_on.rst_drop = 0;
2438 if_st->ecn_on.rxmit_drop = 0;
2439 }
2440
2441 if (ecn_off_conn > 0) {
2442 if_st->ecn_off.sack_episodes =
2443 (if_st->ecn_off.sack_episodes << 10) / ecn_off_conn;
2444 if_st->ecn_off.rst_drop =
2445 (if_st->ecn_off.rst_drop << 10) * 100 / ecn_off_conn;
2446 if_st->ecn_off.rxmit_drop =
2447 (if_st->ecn_off.rxmit_drop << 10) * 100 / ecn_off_conn;
2448 } else {
2449 if_st->ecn_off.sack_episodes = 0;
2450 if_st->ecn_off.rst_drop = 0;
2451 if_st->ecn_off.rxmit_drop = 0;
2452 }
2453 if_st->ecn_total_conn = ecn_off_conn + ecn_on_conn;
2454}
2455
39037602 2456static void
4bd07ac2
A
2457nstat_ifnet_report_ecn_stats(void)
2458{
2459 u_int64_t uptime, last_report_time;
2460 struct nstat_sysinfo_data data;
2461 struct nstat_sysinfo_ifnet_ecn_stats *st;
2462 struct ifnet *ifp;
2463
2464 uptime = net_uptime();
2465
2466 if ((int)(uptime - nstat_ifnet_last_report_time) <
0a7de745 2467 tcp_report_stats_interval) {
4bd07ac2 2468 return;
0a7de745 2469 }
4bd07ac2
A
2470
2471 last_report_time = nstat_ifnet_last_report_time;
2472 nstat_ifnet_last_report_time = uptime;
2473 data.flags = NSTAT_SYSINFO_IFNET_ECN_STATS;
2474 st = &data.u.ifnet_ecn_stats;
2475
2476 ifnet_head_lock_shared();
2477 TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
0a7de745 2478 if (ifp->if_ipv4_stat == NULL || ifp->if_ipv6_stat == NULL) {
4bd07ac2 2479 continue;
0a7de745 2480 }
4bd07ac2 2481
0a7de745 2482 if (!IF_FULLY_ATTACHED(ifp)) {
4bd07ac2 2483 continue;
0a7de745 2484 }
4bd07ac2
A
2485
2486 /* Limit reporting to Wifi, Ethernet and cellular. */
0a7de745 2487 if (!(IFNET_IS_ETHERNET(ifp) || IFNET_IS_CELLULAR(ifp))) {
4bd07ac2 2488 continue;
0a7de745 2489 }
4bd07ac2
A
2490
2491 bzero(st, sizeof(*st));
2492 if (IFNET_IS_CELLULAR(ifp)) {
2493 st->ifnet_type = NSTAT_IFNET_ECN_TYPE_CELLULAR;
2494 } else if (IFNET_IS_WIFI(ifp)) {
2495 st->ifnet_type = NSTAT_IFNET_ECN_TYPE_WIFI;
2496 } else {
2497 st->ifnet_type = NSTAT_IFNET_ECN_TYPE_ETHERNET;
2498 }
39037602 2499 data.unsent_data_cnt = ifp->if_unsent_data_cnt;
4bd07ac2
A
2500 /* skip if there was no update since last report */
2501 if (ifp->if_ipv4_stat->timestamp <= 0 ||
0a7de745 2502 ifp->if_ipv4_stat->timestamp < last_report_time) {
4bd07ac2 2503 goto v6;
0a7de745 2504 }
4bd07ac2 2505 st->ifnet_proto = NSTAT_IFNET_ECN_PROTO_IPV4;
490019cf
A
2506 /* compute percentages using packet counts */
2507 nstat_ifnet_compute_percentages(&ifp->if_ipv4_stat->ecn_on);
2508 nstat_ifnet_compute_percentages(&ifp->if_ipv4_stat->ecn_off);
2509 nstat_ifnet_normalize_counter(ifp->if_ipv4_stat);
4bd07ac2
A
2510 bcopy(ifp->if_ipv4_stat, &st->ecn_stat,
2511 sizeof(st->ecn_stat));
2512 nstat_sysinfo_send_data(&data);
2513 bzero(ifp->if_ipv4_stat, sizeof(*ifp->if_ipv4_stat));
2514
2515v6:
2516 /* skip if there was no update since last report */
2517 if (ifp->if_ipv6_stat->timestamp <= 0 ||
0a7de745 2518 ifp->if_ipv6_stat->timestamp < last_report_time) {
4bd07ac2 2519 continue;
0a7de745 2520 }
4bd07ac2 2521 st->ifnet_proto = NSTAT_IFNET_ECN_PROTO_IPV6;
490019cf
A
2522
2523 /* compute percentages using packet counts */
2524 nstat_ifnet_compute_percentages(&ifp->if_ipv6_stat->ecn_on);
2525 nstat_ifnet_compute_percentages(&ifp->if_ipv6_stat->ecn_off);
2526 nstat_ifnet_normalize_counter(ifp->if_ipv6_stat);
4bd07ac2
A
2527 bcopy(ifp->if_ipv6_stat, &st->ecn_stat,
2528 sizeof(st->ecn_stat));
2529 nstat_sysinfo_send_data(&data);
2530
2531 /* Zero the stats in ifp */
2532 bzero(ifp->if_ipv6_stat, sizeof(*ifp->if_ipv6_stat));
2533 }
2534 ifnet_head_done();
4bd07ac2
A
2535}
2536
5ba3f43e 2537/* Some thresholds to determine Low Iternet mode */
0a7de745
A
2538#define NSTAT_LIM_DL_MAX_BANDWIDTH_THRESHOLD 1000000 /* 1 Mbps */
2539#define NSTAT_LIM_UL_MAX_BANDWIDTH_THRESHOLD 500000 /* 500 Kbps */
2540#define NSTAT_LIM_UL_MIN_RTT_THRESHOLD 1000 /* 1 second */
2541#define NSTAT_LIM_CONN_TIMEOUT_PERCENT_THRESHOLD (10 << 10) /* 10 percent connection timeouts */
2542#define NSTAT_LIM_PACKET_LOSS_PERCENT_THRESHOLD (2 << 10) /* 2 percent packet loss rate */
5ba3f43e
A
2543
2544static boolean_t
2545nstat_lim_activity_check(struct if_lim_perf_stat *st)
2546{
2547 /* check that the current activity is enough to report stats */
2548 if (st->lim_total_txpkts < nstat_lim_min_tx_pkts ||
2549 st->lim_total_rxpkts < nstat_lim_min_rx_pkts ||
0a7de745
A
2550 st->lim_conn_attempts == 0) {
2551 return FALSE;
2552 }
5ba3f43e
A
2553
2554 /*
2555 * Compute percentages if there was enough activity. Use
2556 * shift-left by 10 to preserve precision.
2557 */
2558 st->lim_packet_loss_percent = ((st->lim_total_retxpkts << 10) /
2559 st->lim_total_txpkts) * 100;
2560
2561 st->lim_packet_ooo_percent = ((st->lim_total_oopkts << 10) /
2562 st->lim_total_rxpkts) * 100;
2563
2564 st->lim_conn_timeout_percent = ((st->lim_conn_timeouts << 10) /
2565 st->lim_conn_attempts) * 100;
2566
2567 /*
2568 * Is Low Internet detected? First order metrics are bandwidth
2569 * and RTT. If these metrics are below the minimum thresholds
2570 * defined then the network attachment can be classified as
2571 * having Low Internet capacity.
2572 *
2573 * High connection timeout rate also indicates Low Internet
2574 * capacity.
2575 */
2576 if (st->lim_dl_max_bandwidth > 0 &&
0a7de745 2577 st->lim_dl_max_bandwidth <= NSTAT_LIM_DL_MAX_BANDWIDTH_THRESHOLD) {
5ba3f43e 2578 st->lim_dl_detected = 1;
0a7de745 2579 }
5ba3f43e
A
2580
2581 if ((st->lim_ul_max_bandwidth > 0 &&
2582 st->lim_ul_max_bandwidth <= NSTAT_LIM_UL_MAX_BANDWIDTH_THRESHOLD) ||
0a7de745 2583 st->lim_rtt_min >= NSTAT_LIM_UL_MIN_RTT_THRESHOLD) {
5ba3f43e 2584 st->lim_ul_detected = 1;
0a7de745 2585 }
5ba3f43e
A
2586
2587 if (st->lim_conn_attempts > 20 &&
2588 st->lim_conn_timeout_percent >=
0a7de745 2589 NSTAT_LIM_CONN_TIMEOUT_PERCENT_THRESHOLD) {
5ba3f43e 2590 st->lim_ul_detected = 1;
0a7de745 2591 }
5ba3f43e
A
2592 /*
2593 * Second order metrics: If there was high packet loss even after
2594 * using delay based algorithms then we classify it as Low Internet
2595 * again
2596 */
2597 if (st->lim_bk_txpkts >= nstat_lim_min_tx_pkts &&
2598 st->lim_packet_loss_percent >=
0a7de745 2599 NSTAT_LIM_PACKET_LOSS_PERCENT_THRESHOLD) {
5ba3f43e 2600 st->lim_ul_detected = 1;
0a7de745
A
2601 }
2602 return TRUE;
5ba3f43e
A
2603}
2604
2605static u_int64_t nstat_lim_last_report_time = 0;
2606static void
2607nstat_ifnet_report_lim_stats(void)
2608{
2609 u_int64_t uptime;
2610 struct nstat_sysinfo_data data;
2611 struct nstat_sysinfo_lim_stats *st;
2612 struct ifnet *ifp;
2613 int err;
2614
2615 uptime = net_uptime();
2616
2617 if ((u_int32_t)(uptime - nstat_lim_last_report_time) <
0a7de745 2618 nstat_lim_interval) {
5ba3f43e 2619 return;
0a7de745 2620 }
5ba3f43e
A
2621
2622 nstat_lim_last_report_time = uptime;
2623 data.flags = NSTAT_SYSINFO_LIM_STATS;
2624 st = &data.u.lim_stats;
2625 data.unsent_data_cnt = 0;
2626
2627 ifnet_head_lock_shared();
2628 TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
0a7de745 2629 if (!IF_FULLY_ATTACHED(ifp)) {
5ba3f43e 2630 continue;
0a7de745 2631 }
5ba3f43e
A
2632
2633 /* Limit reporting to Wifi, Ethernet and cellular */
0a7de745 2634 if (!(IFNET_IS_ETHERNET(ifp) || IFNET_IS_CELLULAR(ifp))) {
5ba3f43e 2635 continue;
0a7de745 2636 }
5ba3f43e 2637
0a7de745 2638 if (!nstat_lim_activity_check(&ifp->if_lim_stat)) {
5ba3f43e 2639 continue;
0a7de745 2640 }
5ba3f43e
A
2641
2642 bzero(st, sizeof(*st));
0a7de745 2643 st->ifnet_siglen = sizeof(st->ifnet_signature);
5ba3f43e
A
2644 err = ifnet_get_netsignature(ifp, AF_INET,
2645 (u_int8_t *)&st->ifnet_siglen, NULL,
2646 st->ifnet_signature);
2647 if (err != 0) {
2648 err = ifnet_get_netsignature(ifp, AF_INET6,
2649 (u_int8_t *)&st->ifnet_siglen, NULL,
2650 st->ifnet_signature);
0a7de745 2651 if (err != 0) {
5ba3f43e 2652 continue;
0a7de745 2653 }
5ba3f43e
A
2654 }
2655 ifnet_lock_shared(ifp);
2656 if (IFNET_IS_CELLULAR(ifp)) {
2657 st->ifnet_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR;
2658 } else if (IFNET_IS_WIFI(ifp)) {
2659 st->ifnet_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI;
2660 } else {
2661 st->ifnet_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_ETHERNET;
2662 }
2663 bcopy(&ifp->if_lim_stat, &st->lim_stat,
2664 sizeof(st->lim_stat));
2665
2666 /* Zero the stats in ifp */
2667 bzero(&ifp->if_lim_stat, sizeof(ifp->if_lim_stat));
2668 ifnet_lock_done(ifp);
2669 nstat_sysinfo_send_data(&data);
2670 }
2671 ifnet_head_done();
2672}
2673
39236c6e
A
2674static errno_t
2675nstat_ifnet_copy_descriptor(
0a7de745
A
2676 nstat_provider_cookie_t cookie,
2677 void *data,
2678 u_int32_t len)
39037602 2679{
39236c6e
A
2680 nstat_ifnet_descriptor *desc = (nstat_ifnet_descriptor *)data;
2681 struct nstat_ifnet_cookie *ifcookie =
2682 (struct nstat_ifnet_cookie *)cookie;
2683 struct ifnet *ifp = ifcookie->ifp;
2684
0a7de745 2685 if (len < sizeof(nstat_ifnet_descriptor)) {
39236c6e 2686 return EINVAL;
0a7de745 2687 }
39037602 2688
0a7de745 2689 if (nstat_ifnet_gone(cookie)) {
39236c6e 2690 return EINVAL;
0a7de745 2691 }
39236c6e
A
2692
2693 bzero(desc, sizeof(*desc));
2694 ifnet_lock_shared(ifp);
2695 strlcpy(desc->name, ifp->if_xname, sizeof(desc->name));
2696 desc->ifindex = ifp->if_index;
2697 desc->threshold = ifp->if_data_threshold;
2698 desc->type = ifp->if_type;
0a7de745 2699 if (ifp->if_desc.ifd_len < sizeof(desc->description)) {
39236c6e 2700 memcpy(desc->description, ifp->if_desc.ifd_desc,
0a7de745
A
2701 sizeof(desc->description));
2702 }
3e170ce0 2703 nstat_ifnet_copy_link_status(ifp, desc);
39236c6e 2704 ifnet_lock_done(ifp);
39236c6e
A
2705 return 0;
2706}
2707
2708static void
2709nstat_init_ifnet_provider(void)
2710{
2711 bzero(&nstat_ifnet_provider, sizeof(nstat_ifnet_provider));
2712 nstat_ifnet_provider.nstat_provider_id = NSTAT_PROVIDER_IFNET;
2713 nstat_ifnet_provider.nstat_descriptor_length = sizeof(nstat_ifnet_descriptor);
2714 nstat_ifnet_provider.nstat_lookup = nstat_ifnet_lookup;
2715 nstat_ifnet_provider.nstat_gone = nstat_ifnet_gone;
2716 nstat_ifnet_provider.nstat_counts = nstat_ifnet_counts;
2717 nstat_ifnet_provider.nstat_watcher_add = NULL;
2718 nstat_ifnet_provider.nstat_watcher_remove = NULL;
2719 nstat_ifnet_provider.nstat_copy_descriptor = nstat_ifnet_copy_descriptor;
2720 nstat_ifnet_provider.nstat_release = nstat_ifnet_release;
2721 nstat_ifnet_provider.next = nstat_providers;
2722 nstat_providers = &nstat_ifnet_provider;
2723}
2724
2725__private_extern__ void
2726nstat_ifnet_threshold_reached(unsigned int ifindex)
2727{
2728 nstat_control_state *state;
2729 nstat_src *src;
2730 struct ifnet *ifp;
2731 struct nstat_ifnet_cookie *ifcookie;
2732
2733 lck_mtx_lock(&nstat_mtx);
0a7de745 2734 for (state = nstat_controls; state; state = state->ncs_next) {
5ba3f43e
A
2735 lck_mtx_lock(&state->ncs_mtx);
2736 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
39236c6e 2737 {
0a7de745 2738 if (src->provider != &nstat_ifnet_provider) {
39236c6e 2739 continue;
0a7de745 2740 }
39236c6e
A
2741 ifcookie = (struct nstat_ifnet_cookie *)src->cookie;
2742 ifp = ifcookie->ifp;
0a7de745 2743 if (ifp->if_index != ifindex) {
39236c6e 2744 continue;
0a7de745 2745 }
3e170ce0 2746 nstat_control_send_counts(state, src, 0, 0, NULL);
39236c6e 2747 }
5ba3f43e 2748 lck_mtx_unlock(&state->ncs_mtx);
39236c6e
A
2749 }
2750 lck_mtx_unlock(&nstat_mtx);
2751}
2752
3e170ce0 2753#pragma mark -- Sysinfo --
fe8ab488 2754static void
3e170ce0 2755nstat_set_keyval_scalar(nstat_sysinfo_keyval *kv, int key, u_int32_t val)
fe8ab488 2756{
3e170ce0
A
2757 kv->nstat_sysinfo_key = key;
2758 kv->nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
2759 kv->u.nstat_sysinfo_scalar = val;
5ba3f43e
A
2760 kv->nstat_sysinfo_valsize = sizeof(kv->u.nstat_sysinfo_scalar);
2761}
2762
2763static void
2764nstat_set_keyval_string(nstat_sysinfo_keyval *kv, int key, u_int8_t *buf,
2765 u_int32_t len)
2766{
2767 kv->nstat_sysinfo_key = key;
2768 kv->nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_STRING;
2769 kv->nstat_sysinfo_valsize = min(len,
2770 NSTAT_SYSINFO_KEYVAL_STRING_MAXSIZE);
2771 bcopy(buf, kv->u.nstat_sysinfo_string, kv->nstat_sysinfo_valsize);
fe8ab488
A
2772}
2773
2774static void
2775nstat_sysinfo_send_data_internal(
2776 nstat_control_state *control,
fe8ab488
A
2777 nstat_sysinfo_data *data)
2778{
2779 nstat_msg_sysinfo_counts *syscnt = NULL;
490019cf 2780 size_t allocsize = 0, countsize = 0, nkeyvals = 0, finalsize = 0;
fe8ab488
A
2781 nstat_sysinfo_keyval *kv;
2782 errno_t result = 0;
3e170ce0 2783 size_t i = 0;
39037602 2784
fe8ab488
A
2785 allocsize = offsetof(nstat_msg_sysinfo_counts, counts);
2786 countsize = offsetof(nstat_sysinfo_counts, nstat_sysinfo_keyvals);
490019cf 2787 finalsize = allocsize;
fe8ab488
A
2788
2789 /* get number of key-vals for each kind of stat */
0a7de745
A
2790 switch (data->flags) {
2791 case NSTAT_SYSINFO_MBUF_STATS:
2792 nkeyvals = sizeof(struct nstat_sysinfo_mbuf_stats) /
2793 sizeof(u_int32_t);
2794 break;
2795 case NSTAT_SYSINFO_TCP_STATS:
2796 nkeyvals = NSTAT_SYSINFO_TCP_STATS_COUNT;
2797 break;
2798 case NSTAT_SYSINFO_IFNET_ECN_STATS:
2799 nkeyvals = (sizeof(struct if_tcp_ecn_stat) /
2800 sizeof(u_int64_t));
490019cf 2801
0a7de745
A
2802 /* Two more keys for ifnet type and proto */
2803 nkeyvals += 2;
39037602 2804
0a7de745
A
2805 /* One key for unsent data. */
2806 nkeyvals++;
2807 break;
2808 case NSTAT_SYSINFO_LIM_STATS:
2809 nkeyvals = NSTAT_LIM_STAT_KEYVAL_COUNT;
2810 break;
2811 case NSTAT_SYSINFO_NET_API_STATS:
2812 nkeyvals = NSTAT_NET_API_STAT_KEYVAL_COUNT;
2813 break;
2814 default:
2815 return;
fe8ab488
A
2816 }
2817 countsize += sizeof(nstat_sysinfo_keyval) * nkeyvals;
2818 allocsize += countsize;
2819
2820 syscnt = OSMalloc(allocsize, nstat_malloc_tag);
0a7de745 2821 if (syscnt == NULL) {
fe8ab488 2822 return;
0a7de745 2823 }
fe8ab488 2824 bzero(syscnt, allocsize);
39037602 2825
fe8ab488 2826 kv = (nstat_sysinfo_keyval *) &syscnt->counts.nstat_sysinfo_keyvals;
0a7de745
A
2827 switch (data->flags) {
2828 case NSTAT_SYSINFO_MBUF_STATS:
2829 {
2830 nstat_set_keyval_scalar(&kv[i++],
2831 NSTAT_SYSINFO_KEY_MBUF_256B_TOTAL,
2832 data->u.mb_stats.total_256b);
2833 nstat_set_keyval_scalar(&kv[i++],
2834 NSTAT_SYSINFO_KEY_MBUF_2KB_TOTAL,
2835 data->u.mb_stats.total_2kb);
2836 nstat_set_keyval_scalar(&kv[i++],
2837 NSTAT_SYSINFO_KEY_MBUF_4KB_TOTAL,
2838 data->u.mb_stats.total_4kb);
2839 nstat_set_keyval_scalar(&kv[i++],
2840 NSTAT_SYSINFO_MBUF_16KB_TOTAL,
2841 data->u.mb_stats.total_16kb);
2842 nstat_set_keyval_scalar(&kv[i++],
2843 NSTAT_SYSINFO_KEY_SOCK_MBCNT,
2844 data->u.mb_stats.sbmb_total);
2845 nstat_set_keyval_scalar(&kv[i++],
2846 NSTAT_SYSINFO_KEY_SOCK_ATMBLIMIT,
2847 data->u.mb_stats.sb_atmbuflimit);
2848 nstat_set_keyval_scalar(&kv[i++],
2849 NSTAT_SYSINFO_MBUF_DRAIN_CNT,
2850 data->u.mb_stats.draincnt);
2851 nstat_set_keyval_scalar(&kv[i++],
2852 NSTAT_SYSINFO_MBUF_MEM_RELEASED,
2853 data->u.mb_stats.memreleased);
2854 nstat_set_keyval_scalar(&kv[i++],
2855 NSTAT_SYSINFO_KEY_SOCK_MBFLOOR,
2856 data->u.mb_stats.sbmb_floor);
2857 VERIFY(i == nkeyvals);
2858 break;
2859 }
2860 case NSTAT_SYSINFO_TCP_STATS:
2861 {
2862 nstat_set_keyval_scalar(&kv[i++],
2863 NSTAT_SYSINFO_KEY_IPV4_AVGRTT,
2864 data->u.tcp_stats.ipv4_avgrtt);
2865 nstat_set_keyval_scalar(&kv[i++],
2866 NSTAT_SYSINFO_KEY_IPV6_AVGRTT,
2867 data->u.tcp_stats.ipv6_avgrtt);
2868 nstat_set_keyval_scalar(&kv[i++],
2869 NSTAT_SYSINFO_KEY_SEND_PLR,
2870 data->u.tcp_stats.send_plr);
2871 nstat_set_keyval_scalar(&kv[i++],
2872 NSTAT_SYSINFO_KEY_RECV_PLR,
2873 data->u.tcp_stats.recv_plr);
2874 nstat_set_keyval_scalar(&kv[i++],
2875 NSTAT_SYSINFO_KEY_SEND_TLRTO,
2876 data->u.tcp_stats.send_tlrto_rate);
2877 nstat_set_keyval_scalar(&kv[i++],
2878 NSTAT_SYSINFO_KEY_SEND_REORDERRATE,
2879 data->u.tcp_stats.send_reorder_rate);
2880 nstat_set_keyval_scalar(&kv[i++],
2881 NSTAT_SYSINFO_CONNECTION_ATTEMPTS,
2882 data->u.tcp_stats.connection_attempts);
2883 nstat_set_keyval_scalar(&kv[i++],
2884 NSTAT_SYSINFO_CONNECTION_ACCEPTS,
2885 data->u.tcp_stats.connection_accepts);
2886 nstat_set_keyval_scalar(&kv[i++],
2887 NSTAT_SYSINFO_ECN_CLIENT_ENABLED,
2888 data->u.tcp_stats.ecn_client_enabled);
2889 nstat_set_keyval_scalar(&kv[i++],
2890 NSTAT_SYSINFO_ECN_SERVER_ENABLED,
2891 data->u.tcp_stats.ecn_server_enabled);
2892 nstat_set_keyval_scalar(&kv[i++],
2893 NSTAT_SYSINFO_ECN_CLIENT_SETUP,
2894 data->u.tcp_stats.ecn_client_setup);
2895 nstat_set_keyval_scalar(&kv[i++],
2896 NSTAT_SYSINFO_ECN_SERVER_SETUP,
2897 data->u.tcp_stats.ecn_server_setup);
2898 nstat_set_keyval_scalar(&kv[i++],
2899 NSTAT_SYSINFO_ECN_CLIENT_SUCCESS,
2900 data->u.tcp_stats.ecn_client_success);
2901 nstat_set_keyval_scalar(&kv[i++],
2902 NSTAT_SYSINFO_ECN_SERVER_SUCCESS,
2903 data->u.tcp_stats.ecn_server_success);
2904 nstat_set_keyval_scalar(&kv[i++],
2905 NSTAT_SYSINFO_ECN_NOT_SUPPORTED,
2906 data->u.tcp_stats.ecn_not_supported);
2907 nstat_set_keyval_scalar(&kv[i++],
2908 NSTAT_SYSINFO_ECN_LOST_SYN,
2909 data->u.tcp_stats.ecn_lost_syn);
2910 nstat_set_keyval_scalar(&kv[i++],
2911 NSTAT_SYSINFO_ECN_LOST_SYNACK,
2912 data->u.tcp_stats.ecn_lost_synack);
2913 nstat_set_keyval_scalar(&kv[i++],
2914 NSTAT_SYSINFO_ECN_RECV_CE,
2915 data->u.tcp_stats.ecn_recv_ce);
2916 nstat_set_keyval_scalar(&kv[i++],
2917 NSTAT_SYSINFO_ECN_RECV_ECE,
2918 data->u.tcp_stats.ecn_recv_ece);
2919 nstat_set_keyval_scalar(&kv[i++],
2920 NSTAT_SYSINFO_ECN_SENT_ECE,
2921 data->u.tcp_stats.ecn_sent_ece);
2922 nstat_set_keyval_scalar(&kv[i++],
2923 NSTAT_SYSINFO_ECN_CONN_RECV_CE,
2924 data->u.tcp_stats.ecn_conn_recv_ce);
2925 nstat_set_keyval_scalar(&kv[i++],
2926 NSTAT_SYSINFO_ECN_CONN_RECV_ECE,
2927 data->u.tcp_stats.ecn_conn_recv_ece);
2928 nstat_set_keyval_scalar(&kv[i++],
2929 NSTAT_SYSINFO_ECN_CONN_PLNOCE,
2930 data->u.tcp_stats.ecn_conn_plnoce);
2931 nstat_set_keyval_scalar(&kv[i++],
2932 NSTAT_SYSINFO_ECN_CONN_PL_CE,
2933 data->u.tcp_stats.ecn_conn_pl_ce);
2934 nstat_set_keyval_scalar(&kv[i++],
2935 NSTAT_SYSINFO_ECN_CONN_NOPL_CE,
2936 data->u.tcp_stats.ecn_conn_nopl_ce);
2937 nstat_set_keyval_scalar(&kv[i++],
2938 NSTAT_SYSINFO_ECN_FALLBACK_SYNLOSS,
2939 data->u.tcp_stats.ecn_fallback_synloss);
2940 nstat_set_keyval_scalar(&kv[i++],
2941 NSTAT_SYSINFO_ECN_FALLBACK_REORDER,
2942 data->u.tcp_stats.ecn_fallback_reorder);
2943 nstat_set_keyval_scalar(&kv[i++],
2944 NSTAT_SYSINFO_ECN_FALLBACK_CE,
2945 data->u.tcp_stats.ecn_fallback_ce);
2946 nstat_set_keyval_scalar(&kv[i++],
2947 NSTAT_SYSINFO_TFO_SYN_DATA_RCV,
2948 data->u.tcp_stats.tfo_syn_data_rcv);
2949 nstat_set_keyval_scalar(&kv[i++],
2950 NSTAT_SYSINFO_TFO_COOKIE_REQ_RCV,
2951 data->u.tcp_stats.tfo_cookie_req_rcv);
2952 nstat_set_keyval_scalar(&kv[i++],
2953 NSTAT_SYSINFO_TFO_COOKIE_SENT,
2954 data->u.tcp_stats.tfo_cookie_sent);
2955 nstat_set_keyval_scalar(&kv[i++],
2956 NSTAT_SYSINFO_TFO_COOKIE_INVALID,
2957 data->u.tcp_stats.tfo_cookie_invalid);
2958 nstat_set_keyval_scalar(&kv[i++],
2959 NSTAT_SYSINFO_TFO_COOKIE_REQ,
2960 data->u.tcp_stats.tfo_cookie_req);
2961 nstat_set_keyval_scalar(&kv[i++],
2962 NSTAT_SYSINFO_TFO_COOKIE_RCV,
2963 data->u.tcp_stats.tfo_cookie_rcv);
2964 nstat_set_keyval_scalar(&kv[i++],
2965 NSTAT_SYSINFO_TFO_SYN_DATA_SENT,
2966 data->u.tcp_stats.tfo_syn_data_sent);
2967 nstat_set_keyval_scalar(&kv[i++],
2968 NSTAT_SYSINFO_TFO_SYN_DATA_ACKED,
2969 data->u.tcp_stats.tfo_syn_data_acked);
2970 nstat_set_keyval_scalar(&kv[i++],
2971 NSTAT_SYSINFO_TFO_SYN_LOSS,
2972 data->u.tcp_stats.tfo_syn_loss);
2973 nstat_set_keyval_scalar(&kv[i++],
2974 NSTAT_SYSINFO_TFO_BLACKHOLE,
2975 data->u.tcp_stats.tfo_blackhole);
2976 nstat_set_keyval_scalar(&kv[i++],
2977 NSTAT_SYSINFO_TFO_COOKIE_WRONG,
2978 data->u.tcp_stats.tfo_cookie_wrong);
2979 nstat_set_keyval_scalar(&kv[i++],
2980 NSTAT_SYSINFO_TFO_NO_COOKIE_RCV,
2981 data->u.tcp_stats.tfo_no_cookie_rcv);
2982 nstat_set_keyval_scalar(&kv[i++],
2983 NSTAT_SYSINFO_TFO_HEURISTICS_DISABLE,
2984 data->u.tcp_stats.tfo_heuristics_disable);
2985 nstat_set_keyval_scalar(&kv[i++],
2986 NSTAT_SYSINFO_TFO_SEND_BLACKHOLE,
2987 data->u.tcp_stats.tfo_sndblackhole);
2988 nstat_set_keyval_scalar(&kv[i++],
2989 NSTAT_SYSINFO_MPTCP_HANDOVER_ATTEMPT,
2990 data->u.tcp_stats.mptcp_handover_attempt);
2991 nstat_set_keyval_scalar(&kv[i++],
2992 NSTAT_SYSINFO_MPTCP_INTERACTIVE_ATTEMPT,
2993 data->u.tcp_stats.mptcp_interactive_attempt);
2994 nstat_set_keyval_scalar(&kv[i++],
2995 NSTAT_SYSINFO_MPTCP_AGGREGATE_ATTEMPT,
2996 data->u.tcp_stats.mptcp_aggregate_attempt);
2997 nstat_set_keyval_scalar(&kv[i++],
2998 NSTAT_SYSINFO_MPTCP_FP_HANDOVER_ATTEMPT,
2999 data->u.tcp_stats.mptcp_fp_handover_attempt);
3000 nstat_set_keyval_scalar(&kv[i++],
3001 NSTAT_SYSINFO_MPTCP_FP_INTERACTIVE_ATTEMPT,
3002 data->u.tcp_stats.mptcp_fp_interactive_attempt);
3003 nstat_set_keyval_scalar(&kv[i++],
3004 NSTAT_SYSINFO_MPTCP_FP_AGGREGATE_ATTEMPT,
3005 data->u.tcp_stats.mptcp_fp_aggregate_attempt);
3006 nstat_set_keyval_scalar(&kv[i++],
3007 NSTAT_SYSINFO_MPTCP_HEURISTIC_FALLBACK,
3008 data->u.tcp_stats.mptcp_heuristic_fallback);
3009 nstat_set_keyval_scalar(&kv[i++],
3010 NSTAT_SYSINFO_MPTCP_FP_HEURISTIC_FALLBACK,
3011 data->u.tcp_stats.mptcp_fp_heuristic_fallback);
3012 nstat_set_keyval_scalar(&kv[i++],
3013 NSTAT_SYSINFO_MPTCP_HANDOVER_SUCCESS_WIFI,
3014 data->u.tcp_stats.mptcp_handover_success_wifi);
3015 nstat_set_keyval_scalar(&kv[i++],
3016 NSTAT_SYSINFO_MPTCP_HANDOVER_SUCCESS_CELL,
3017 data->u.tcp_stats.mptcp_handover_success_cell);
3018 nstat_set_keyval_scalar(&kv[i++],
3019 NSTAT_SYSINFO_MPTCP_INTERACTIVE_SUCCESS,
3020 data->u.tcp_stats.mptcp_interactive_success);
3021 nstat_set_keyval_scalar(&kv[i++],
3022 NSTAT_SYSINFO_MPTCP_AGGREGATE_SUCCESS,
3023 data->u.tcp_stats.mptcp_aggregate_success);
3024 nstat_set_keyval_scalar(&kv[i++],
3025 NSTAT_SYSINFO_MPTCP_FP_HANDOVER_SUCCESS_WIFI,
3026 data->u.tcp_stats.mptcp_fp_handover_success_wifi);
3027 nstat_set_keyval_scalar(&kv[i++],
3028 NSTAT_SYSINFO_MPTCP_FP_HANDOVER_SUCCESS_CELL,
3029 data->u.tcp_stats.mptcp_fp_handover_success_cell);
3030 nstat_set_keyval_scalar(&kv[i++],
3031 NSTAT_SYSINFO_MPTCP_FP_INTERACTIVE_SUCCESS,
3032 data->u.tcp_stats.mptcp_fp_interactive_success);
3033 nstat_set_keyval_scalar(&kv[i++],
3034 NSTAT_SYSINFO_MPTCP_FP_AGGREGATE_SUCCESS,
3035 data->u.tcp_stats.mptcp_fp_aggregate_success);
3036 nstat_set_keyval_scalar(&kv[i++],
3037 NSTAT_SYSINFO_MPTCP_HANDOVER_CELL_FROM_WIFI,
3038 data->u.tcp_stats.mptcp_handover_cell_from_wifi);
3039 nstat_set_keyval_scalar(&kv[i++],
3040 NSTAT_SYSINFO_MPTCP_HANDOVER_WIFI_FROM_CELL,
3041 data->u.tcp_stats.mptcp_handover_wifi_from_cell);
3042 nstat_set_keyval_scalar(&kv[i++],
3043 NSTAT_SYSINFO_MPTCP_INTERACTIVE_CELL_FROM_WIFI,
3044 data->u.tcp_stats.mptcp_interactive_cell_from_wifi);
3045 nstat_set_keyval_scalar(&kv[i++],
3046 NSTAT_SYSINFO_MPTCP_HANDOVER_CELL_BYTES,
3047 data->u.tcp_stats.mptcp_handover_cell_bytes);
3048 nstat_set_keyval_scalar(&kv[i++],
3049 NSTAT_SYSINFO_MPTCP_INTERACTIVE_CELL_BYTES,
3050 data->u.tcp_stats.mptcp_interactive_cell_bytes);
3051 nstat_set_keyval_scalar(&kv[i++],
3052 NSTAT_SYSINFO_MPTCP_AGGREGATE_CELL_BYTES,
3053 data->u.tcp_stats.mptcp_aggregate_cell_bytes);
3054 nstat_set_keyval_scalar(&kv[i++],
3055 NSTAT_SYSINFO_MPTCP_HANDOVER_ALL_BYTES,
3056 data->u.tcp_stats.mptcp_handover_all_bytes);
3057 nstat_set_keyval_scalar(&kv[i++],
3058 NSTAT_SYSINFO_MPTCP_INTERACTIVE_ALL_BYTES,
3059 data->u.tcp_stats.mptcp_interactive_all_bytes);
3060 nstat_set_keyval_scalar(&kv[i++],
3061 NSTAT_SYSINFO_MPTCP_AGGREGATE_ALL_BYTES,
3062 data->u.tcp_stats.mptcp_aggregate_all_bytes);
3063 nstat_set_keyval_scalar(&kv[i++],
3064 NSTAT_SYSINFO_MPTCP_BACK_TO_WIFI,
3065 data->u.tcp_stats.mptcp_back_to_wifi);
3066 nstat_set_keyval_scalar(&kv[i++],
3067 NSTAT_SYSINFO_MPTCP_WIFI_PROXY,
3068 data->u.tcp_stats.mptcp_wifi_proxy);
3069 nstat_set_keyval_scalar(&kv[i++],
3070 NSTAT_SYSINFO_MPTCP_CELL_PROXY,
3071 data->u.tcp_stats.mptcp_cell_proxy);
3072 nstat_set_keyval_scalar(&kv[i++],
3073 NSTAT_SYSINFO_MPTCP_TRIGGERED_CELL,
3074 data->u.tcp_stats.mptcp_triggered_cell);
3075 VERIFY(i == nkeyvals);
3076 break;
3077 }
3078 case NSTAT_SYSINFO_IFNET_ECN_STATS:
3079 {
3080 nstat_set_keyval_scalar(&kv[i++],
3081 NSTAT_SYSINFO_ECN_IFNET_TYPE,
3082 data->u.ifnet_ecn_stats.ifnet_type);
3083 nstat_set_keyval_scalar(&kv[i++],
3084 NSTAT_SYSINFO_ECN_IFNET_PROTO,
3085 data->u.ifnet_ecn_stats.ifnet_proto);
3086 nstat_set_keyval_scalar(&kv[i++],
3087 NSTAT_SYSINFO_ECN_IFNET_CLIENT_SETUP,
3088 data->u.ifnet_ecn_stats.ecn_stat.ecn_client_setup);
3089 nstat_set_keyval_scalar(&kv[i++],
3090 NSTAT_SYSINFO_ECN_IFNET_SERVER_SETUP,
3091 data->u.ifnet_ecn_stats.ecn_stat.ecn_server_setup);
3092 nstat_set_keyval_scalar(&kv[i++],
3093 NSTAT_SYSINFO_ECN_IFNET_CLIENT_SUCCESS,
3094 data->u.ifnet_ecn_stats.ecn_stat.ecn_client_success);
3095 nstat_set_keyval_scalar(&kv[i++],
3096 NSTAT_SYSINFO_ECN_IFNET_SERVER_SUCCESS,
3097 data->u.ifnet_ecn_stats.ecn_stat.ecn_server_success);
3098 nstat_set_keyval_scalar(&kv[i++],
3099 NSTAT_SYSINFO_ECN_IFNET_PEER_NOSUPPORT,
3100 data->u.ifnet_ecn_stats.ecn_stat.ecn_peer_nosupport);
3101 nstat_set_keyval_scalar(&kv[i++],
3102 NSTAT_SYSINFO_ECN_IFNET_SYN_LOST,
3103 data->u.ifnet_ecn_stats.ecn_stat.ecn_syn_lost);
3104 nstat_set_keyval_scalar(&kv[i++],
3105 NSTAT_SYSINFO_ECN_IFNET_SYNACK_LOST,
3106 data->u.ifnet_ecn_stats.ecn_stat.ecn_synack_lost);
3107 nstat_set_keyval_scalar(&kv[i++],
3108 NSTAT_SYSINFO_ECN_IFNET_RECV_CE,
3109 data->u.ifnet_ecn_stats.ecn_stat.ecn_recv_ce);
3110 nstat_set_keyval_scalar(&kv[i++],
3111 NSTAT_SYSINFO_ECN_IFNET_RECV_ECE,
3112 data->u.ifnet_ecn_stats.ecn_stat.ecn_recv_ece);
3113 nstat_set_keyval_scalar(&kv[i++],
3114 NSTAT_SYSINFO_ECN_IFNET_CONN_RECV_CE,
3115 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_recv_ce);
3116 nstat_set_keyval_scalar(&kv[i++],
3117 NSTAT_SYSINFO_ECN_IFNET_CONN_RECV_ECE,
3118 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_recv_ece);
3119 nstat_set_keyval_scalar(&kv[i++],
3120 NSTAT_SYSINFO_ECN_IFNET_CONN_PLNOCE,
3121 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_plnoce);
3122 nstat_set_keyval_scalar(&kv[i++],
3123 NSTAT_SYSINFO_ECN_IFNET_CONN_PLCE,
3124 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_plce);
3125 nstat_set_keyval_scalar(&kv[i++],
3126 NSTAT_SYSINFO_ECN_IFNET_CONN_NOPLCE,
3127 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_noplce);
3128 nstat_set_keyval_scalar(&kv[i++],
3129 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_SYNLOSS,
3130 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_synloss);
3131 nstat_set_keyval_scalar(&kv[i++],
3132 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_REORDER,
3133 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_reorder);
3134 nstat_set_keyval_scalar(&kv[i++],
3135 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_CE,
3136 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_ce);
3137 nstat_set_keyval_scalar(&kv[i++],
3138 NSTAT_SYSINFO_ECN_IFNET_ON_RTT_AVG,
3139 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rtt_avg);
3140 nstat_set_keyval_scalar(&kv[i++],
3141 NSTAT_SYSINFO_ECN_IFNET_ON_RTT_VAR,
3142 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rtt_var);
3143 nstat_set_keyval_scalar(&kv[i++],
3144 NSTAT_SYSINFO_ECN_IFNET_ON_OOPERCENT,
3145 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.oo_percent);
3146 nstat_set_keyval_scalar(&kv[i++],
3147 NSTAT_SYSINFO_ECN_IFNET_ON_SACK_EPISODE,
3148 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.sack_episodes);
3149 nstat_set_keyval_scalar(&kv[i++],
3150 NSTAT_SYSINFO_ECN_IFNET_ON_REORDER_PERCENT,
3151 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.reorder_percent);
3152 nstat_set_keyval_scalar(&kv[i++],
3153 NSTAT_SYSINFO_ECN_IFNET_ON_RXMIT_PERCENT,
3154 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rxmit_percent);
3155 nstat_set_keyval_scalar(&kv[i++],
3156 NSTAT_SYSINFO_ECN_IFNET_ON_RXMIT_DROP,
3157 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rxmit_drop);
3158 nstat_set_keyval_scalar(&kv[i++],
3159 NSTAT_SYSINFO_ECN_IFNET_OFF_RTT_AVG,
3160 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rtt_avg);
3161 nstat_set_keyval_scalar(&kv[i++],
3162 NSTAT_SYSINFO_ECN_IFNET_OFF_RTT_VAR,
3163 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rtt_var);
3164 nstat_set_keyval_scalar(&kv[i++],
3165 NSTAT_SYSINFO_ECN_IFNET_OFF_OOPERCENT,
3166 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.oo_percent);
3167 nstat_set_keyval_scalar(&kv[i++],
3168 NSTAT_SYSINFO_ECN_IFNET_OFF_SACK_EPISODE,
3169 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.sack_episodes);
3170 nstat_set_keyval_scalar(&kv[i++],
3171 NSTAT_SYSINFO_ECN_IFNET_OFF_REORDER_PERCENT,
3172 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.reorder_percent);
3173 nstat_set_keyval_scalar(&kv[i++],
3174 NSTAT_SYSINFO_ECN_IFNET_OFF_RXMIT_PERCENT,
3175 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rxmit_percent);
3176 nstat_set_keyval_scalar(&kv[i++],
3177 NSTAT_SYSINFO_ECN_IFNET_OFF_RXMIT_DROP,
3178 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rxmit_drop);
3179 nstat_set_keyval_scalar(&kv[i++],
3180 NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_TXPKTS,
3181 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_txpkts);
3182 nstat_set_keyval_scalar(&kv[i++],
3183 NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_RXMTPKTS,
3184 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_rxmitpkts);
3185 nstat_set_keyval_scalar(&kv[i++],
3186 NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_RXPKTS,
3187 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_rxpkts);
3188 nstat_set_keyval_scalar(&kv[i++],
3189 NSTAT_SYSINFO_ECN_IFNET_ON_TOTAL_OOPKTS,
3190 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.total_oopkts);
3191 nstat_set_keyval_scalar(&kv[i++],
3192 NSTAT_SYSINFO_ECN_IFNET_ON_DROP_RST,
3193 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rst_drop);
3194 nstat_set_keyval_scalar(&kv[i++],
3195 NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_TXPKTS,
3196 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_txpkts);
3197 nstat_set_keyval_scalar(&kv[i++],
3198 NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_RXMTPKTS,
3199 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_rxmitpkts);
3200 nstat_set_keyval_scalar(&kv[i++],
3201 NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_RXPKTS,
3202 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_rxpkts);
3203 nstat_set_keyval_scalar(&kv[i++],
3204 NSTAT_SYSINFO_ECN_IFNET_OFF_TOTAL_OOPKTS,
3205 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.total_oopkts);
3206 nstat_set_keyval_scalar(&kv[i++],
3207 NSTAT_SYSINFO_ECN_IFNET_OFF_DROP_RST,
3208 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rst_drop);
3209 nstat_set_keyval_scalar(&kv[i++],
3210 NSTAT_SYSINFO_ECN_IFNET_TOTAL_CONN,
3211 data->u.ifnet_ecn_stats.ecn_stat.ecn_total_conn);
3212 nstat_set_keyval_scalar(&kv[i++],
3213 NSTAT_SYSINFO_IFNET_UNSENT_DATA,
3214 data->unsent_data_cnt);
3215 nstat_set_keyval_scalar(&kv[i++],
3216 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_DROPRST,
3217 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_droprst);
3218 nstat_set_keyval_scalar(&kv[i++],
3219 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_DROPRXMT,
3220 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_droprxmt);
3221 nstat_set_keyval_scalar(&kv[i++],
3222 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_SYNRST,
3223 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_synrst);
3224 break;
3225 }
3226 case NSTAT_SYSINFO_LIM_STATS:
3227 {
3228 nstat_set_keyval_string(&kv[i++],
3229 NSTAT_SYSINFO_LIM_IFNET_SIGNATURE,
3230 data->u.lim_stats.ifnet_signature,
3231 data->u.lim_stats.ifnet_siglen);
3232 nstat_set_keyval_scalar(&kv[i++],
3233 NSTAT_SYSINFO_LIM_IFNET_DL_MAX_BANDWIDTH,
3234 data->u.lim_stats.lim_stat.lim_dl_max_bandwidth);
3235 nstat_set_keyval_scalar(&kv[i++],
3236 NSTAT_SYSINFO_LIM_IFNET_UL_MAX_BANDWIDTH,
3237 data->u.lim_stats.lim_stat.lim_ul_max_bandwidth);
3238 nstat_set_keyval_scalar(&kv[i++],
3239 NSTAT_SYSINFO_LIM_IFNET_PACKET_LOSS_PERCENT,
3240 data->u.lim_stats.lim_stat.lim_packet_loss_percent);
3241 nstat_set_keyval_scalar(&kv[i++],
3242 NSTAT_SYSINFO_LIM_IFNET_PACKET_OOO_PERCENT,
3243 data->u.lim_stats.lim_stat.lim_packet_ooo_percent);
3244 nstat_set_keyval_scalar(&kv[i++],
3245 NSTAT_SYSINFO_LIM_IFNET_RTT_VARIANCE,
3246 data->u.lim_stats.lim_stat.lim_rtt_variance);
3247 nstat_set_keyval_scalar(&kv[i++],
3248 NSTAT_SYSINFO_LIM_IFNET_RTT_MIN,
3249 data->u.lim_stats.lim_stat.lim_rtt_min);
3250 nstat_set_keyval_scalar(&kv[i++],
3251 NSTAT_SYSINFO_LIM_IFNET_RTT_AVG,
3252 data->u.lim_stats.lim_stat.lim_rtt_average);
3253 nstat_set_keyval_scalar(&kv[i++],
3254 NSTAT_SYSINFO_LIM_IFNET_CONN_TIMEOUT_PERCENT,
3255 data->u.lim_stats.lim_stat.lim_conn_timeout_percent);
3256 nstat_set_keyval_scalar(&kv[i++],
3257 NSTAT_SYSINFO_LIM_IFNET_DL_DETECTED,
3258 data->u.lim_stats.lim_stat.lim_dl_detected);
3259 nstat_set_keyval_scalar(&kv[i++],
3260 NSTAT_SYSINFO_LIM_IFNET_UL_DETECTED,
3261 data->u.lim_stats.lim_stat.lim_ul_detected);
3262 nstat_set_keyval_scalar(&kv[i++],
3263 NSTAT_SYSINFO_LIM_IFNET_TYPE,
3264 data->u.lim_stats.ifnet_type);
3265 break;
3266 }
3267 case NSTAT_SYSINFO_NET_API_STATS:
3268 {
3269 nstat_set_keyval_scalar(&kv[i++],
3270 NSTAT_SYSINFO_API_IF_FLTR_ATTACH,
3271 data->u.net_api_stats.net_api_stats.nas_iflt_attach_total);
3272 nstat_set_keyval_scalar(&kv[i++],
3273 NSTAT_SYSINFO_API_IF_FLTR_ATTACH_OS,
3274 data->u.net_api_stats.net_api_stats.nas_iflt_attach_os_total);
3275 nstat_set_keyval_scalar(&kv[i++],
3276 NSTAT_SYSINFO_API_IP_FLTR_ADD,
3277 data->u.net_api_stats.net_api_stats.nas_ipf_add_total);
3278 nstat_set_keyval_scalar(&kv[i++],
3279 NSTAT_SYSINFO_API_IP_FLTR_ADD_OS,
3280 data->u.net_api_stats.net_api_stats.nas_ipf_add_os_total);
3281 nstat_set_keyval_scalar(&kv[i++],
3282 NSTAT_SYSINFO_API_SOCK_FLTR_ATTACH,
3283 data->u.net_api_stats.net_api_stats.nas_sfltr_register_total);
3284 nstat_set_keyval_scalar(&kv[i++],
3285 NSTAT_SYSINFO_API_SOCK_FLTR_ATTACH_OS,
3286 data->u.net_api_stats.net_api_stats.nas_sfltr_register_os_total);
3287
3288
3289 nstat_set_keyval_scalar(&kv[i++],
3290 NSTAT_SYSINFO_API_SOCK_ALLOC_TOTAL,
3291 data->u.net_api_stats.net_api_stats.nas_socket_alloc_total);
3292 nstat_set_keyval_scalar(&kv[i++],
3293 NSTAT_SYSINFO_API_SOCK_ALLOC_KERNEL,
3294 data->u.net_api_stats.net_api_stats.nas_socket_in_kernel_total);
3295 nstat_set_keyval_scalar(&kv[i++],
3296 NSTAT_SYSINFO_API_SOCK_ALLOC_KERNEL_OS,
3297 data->u.net_api_stats.net_api_stats.nas_socket_in_kernel_os_total);
3298 nstat_set_keyval_scalar(&kv[i++],
3299 NSTAT_SYSINFO_API_SOCK_NECP_CLIENTUUID,
3300 data->u.net_api_stats.net_api_stats.nas_socket_necp_clientuuid_total);
3301
3302 nstat_set_keyval_scalar(&kv[i++],
3303 NSTAT_SYSINFO_API_SOCK_DOMAIN_LOCAL,
3304 data->u.net_api_stats.net_api_stats.nas_socket_domain_local_total);
3305 nstat_set_keyval_scalar(&kv[i++],
3306 NSTAT_SYSINFO_API_SOCK_DOMAIN_ROUTE,
3307 data->u.net_api_stats.net_api_stats.nas_socket_domain_route_total);
3308 nstat_set_keyval_scalar(&kv[i++],
3309 NSTAT_SYSINFO_API_SOCK_DOMAIN_INET,
3310 data->u.net_api_stats.net_api_stats.nas_socket_domain_inet_total);
3311 nstat_set_keyval_scalar(&kv[i++],
3312 NSTAT_SYSINFO_API_SOCK_DOMAIN_INET6,
3313 data->u.net_api_stats.net_api_stats.nas_socket_domain_inet6_total);
3314 nstat_set_keyval_scalar(&kv[i++],
3315 NSTAT_SYSINFO_API_SOCK_DOMAIN_SYSTEM,
3316 data->u.net_api_stats.net_api_stats.nas_socket_domain_system_total);
3317 nstat_set_keyval_scalar(&kv[i++],
3318 NSTAT_SYSINFO_API_SOCK_DOMAIN_MULTIPATH,
3319 data->u.net_api_stats.net_api_stats.nas_socket_domain_multipath_total);
3320 nstat_set_keyval_scalar(&kv[i++],
3321 NSTAT_SYSINFO_API_SOCK_DOMAIN_KEY,
3322 data->u.net_api_stats.net_api_stats.nas_socket_domain_key_total);
3323 nstat_set_keyval_scalar(&kv[i++],
3324 NSTAT_SYSINFO_API_SOCK_DOMAIN_NDRV,
3325 data->u.net_api_stats.net_api_stats.nas_socket_domain_ndrv_total);
3326 nstat_set_keyval_scalar(&kv[i++],
3327 NSTAT_SYSINFO_API_SOCK_DOMAIN_OTHER,
3328 data->u.net_api_stats.net_api_stats.nas_socket_domain_other_total);
3329
3330 nstat_set_keyval_scalar(&kv[i++],
3331 NSTAT_SYSINFO_API_SOCK_INET_STREAM,
3332 data->u.net_api_stats.net_api_stats.nas_socket_inet_stream_total);
3333 nstat_set_keyval_scalar(&kv[i++],
3334 NSTAT_SYSINFO_API_SOCK_INET_DGRAM,
3335 data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_total);
3336 nstat_set_keyval_scalar(&kv[i++],
3337 NSTAT_SYSINFO_API_SOCK_INET_DGRAM_CONNECTED,
3338 data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_connected);
3339 nstat_set_keyval_scalar(&kv[i++],
3340 NSTAT_SYSINFO_API_SOCK_INET_DGRAM_DNS,
3341 data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_dns);
3342 nstat_set_keyval_scalar(&kv[i++],
3343 NSTAT_SYSINFO_API_SOCK_INET_DGRAM_NO_DATA,
3344 data->u.net_api_stats.net_api_stats.nas_socket_inet_dgram_no_data);
3345
3346 nstat_set_keyval_scalar(&kv[i++],
3347 NSTAT_SYSINFO_API_SOCK_INET6_STREAM,
3348 data->u.net_api_stats.net_api_stats.nas_socket_inet6_stream_total);
3349 nstat_set_keyval_scalar(&kv[i++],
3350 NSTAT_SYSINFO_API_SOCK_INET6_DGRAM,
3351 data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_total);
3352 nstat_set_keyval_scalar(&kv[i++],
3353 NSTAT_SYSINFO_API_SOCK_INET6_DGRAM_CONNECTED,
3354 data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_connected);
3355 nstat_set_keyval_scalar(&kv[i++],
3356 NSTAT_SYSINFO_API_SOCK_INET6_DGRAM_DNS,
3357 data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_dns);
3358 nstat_set_keyval_scalar(&kv[i++],
3359 NSTAT_SYSINFO_API_SOCK_INET6_DGRAM_NO_DATA,
3360 data->u.net_api_stats.net_api_stats.nas_socket_inet6_dgram_no_data);
3361
3362 nstat_set_keyval_scalar(&kv[i++],
3363 NSTAT_SYSINFO_API_SOCK_INET_MCAST_JOIN,
3364 data->u.net_api_stats.net_api_stats.nas_socket_mcast_join_total);
3365 nstat_set_keyval_scalar(&kv[i++],
3366 NSTAT_SYSINFO_API_SOCK_INET_MCAST_JOIN_OS,
3367 data->u.net_api_stats.net_api_stats.nas_socket_mcast_join_os_total);
3368
3369 nstat_set_keyval_scalar(&kv[i++],
3370 NSTAT_SYSINFO_API_NEXUS_FLOW_INET_STREAM,
3371 data->u.net_api_stats.net_api_stats.nas_nx_flow_inet_stream_total);
3372 nstat_set_keyval_scalar(&kv[i++],
3373 NSTAT_SYSINFO_API_NEXUS_FLOW_INET_DATAGRAM,
3374 data->u.net_api_stats.net_api_stats.nas_nx_flow_inet_dgram_total);
3375
3376 nstat_set_keyval_scalar(&kv[i++],
3377 NSTAT_SYSINFO_API_NEXUS_FLOW_INET6_STREAM,
3378 data->u.net_api_stats.net_api_stats.nas_nx_flow_inet6_stream_total);
3379 nstat_set_keyval_scalar(&kv[i++],
3380 NSTAT_SYSINFO_API_NEXUS_FLOW_INET6_DATAGRAM,
3381 data->u.net_api_stats.net_api_stats.nas_nx_flow_inet6_dgram_total);
3382
3383 nstat_set_keyval_scalar(&kv[i++],
3384 NSTAT_SYSINFO_API_IFNET_ALLOC,
3385 data->u.net_api_stats.net_api_stats.nas_ifnet_alloc_total);
3386 nstat_set_keyval_scalar(&kv[i++],
3387 NSTAT_SYSINFO_API_IFNET_ALLOC_OS,
3388 data->u.net_api_stats.net_api_stats.nas_ifnet_alloc_os_total);
3389
3390 nstat_set_keyval_scalar(&kv[i++],
3391 NSTAT_SYSINFO_API_PF_ADDRULE,
3392 data->u.net_api_stats.net_api_stats.nas_pf_addrule_total);
3393 nstat_set_keyval_scalar(&kv[i++],
3394 NSTAT_SYSINFO_API_PF_ADDRULE_OS,
3395 data->u.net_api_stats.net_api_stats.nas_pf_addrule_os);
3396
3397 nstat_set_keyval_scalar(&kv[i++],
3398 NSTAT_SYSINFO_API_VMNET_START,
3399 data->u.net_api_stats.net_api_stats.nas_vmnet_total);
3400
3401
3402 nstat_set_keyval_scalar(&kv[i++],
3403 NSTAT_SYSINFO_API_REPORT_INTERVAL,
3404 data->u.net_api_stats.report_interval);
5ba3f43e 3405
0a7de745 3406 break;
fe8ab488 3407 }
0a7de745
A
3408 }
3409 if (syscnt != NULL) {
490019cf
A
3410 VERIFY(i > 0 && i <= nkeyvals);
3411 countsize = offsetof(nstat_sysinfo_counts,
3412 nstat_sysinfo_keyvals) +
3413 sizeof(nstat_sysinfo_keyval) * i;
3414 finalsize += countsize;
3415 syscnt->hdr.type = NSTAT_MSG_TYPE_SYSINFO_COUNTS;
3416 syscnt->hdr.length = finalsize;
3417 syscnt->counts.nstat_sysinfo_len = countsize;
3418
fe8ab488 3419 result = ctl_enqueuedata(control->ncs_kctl,
490019cf 3420 control->ncs_unit, syscnt, finalsize, CTL_DATA_EOR);
0a7de745 3421 if (result != 0) {
3e170ce0
A
3422 nstat_stats.nstat_sysinfofailures += 1;
3423 }
fe8ab488
A
3424 OSFree(syscnt, allocsize, nstat_malloc_tag);
3425 }
3426 return;
3427}
3428
3429__private_extern__ void
3430nstat_sysinfo_send_data(
3431 nstat_sysinfo_data *data)
3432{
3433 nstat_control_state *control;
3434
3435 lck_mtx_lock(&nstat_mtx);
5ba3f43e
A
3436 for (control = nstat_controls; control; control = control->ncs_next) {
3437 lck_mtx_lock(&control->ncs_mtx);
3438 if ((control->ncs_flags & NSTAT_FLAG_SYSINFO_SUBSCRIBED) != 0) {
3e170ce0
A
3439 nstat_sysinfo_send_data_internal(control, data);
3440 }
5ba3f43e 3441 lck_mtx_unlock(&control->ncs_mtx);
fe8ab488
A
3442 }
3443 lck_mtx_unlock(&nstat_mtx);
fe8ab488
A
3444}
3445
3446static void
3447nstat_sysinfo_generate_report(void)
3448{
3449 mbuf_report_peak_usage();
3450 tcp_report_stats();
4bd07ac2 3451 nstat_ifnet_report_ecn_stats();
5ba3f43e
A
3452 nstat_ifnet_report_lim_stats();
3453 nstat_net_api_report_stats();
3454}
3455
3456#pragma mark -- net_api --
3457
3458static struct net_api_stats net_api_stats_before;
3459static u_int64_t net_api_stats_last_report_time;
3460
3461static void
3462nstat_net_api_report_stats(void)
3463{
3464 struct nstat_sysinfo_data data;
3465 struct nstat_sysinfo_net_api_stats *st = &data.u.net_api_stats;
3466 u_int64_t uptime;
3467
3468 uptime = net_uptime();
3469
3470 if ((u_int32_t)(uptime - net_api_stats_last_report_time) <
0a7de745 3471 net_api_stats_report_interval) {
5ba3f43e 3472 return;
0a7de745 3473 }
5ba3f43e
A
3474
3475 st->report_interval = uptime - net_api_stats_last_report_time;
3476 net_api_stats_last_report_time = uptime;
3477
3478 data.flags = NSTAT_SYSINFO_NET_API_STATS;
3479 data.unsent_data_cnt = 0;
3480
3481 /*
3482 * Some of the fields in the report are the current value and
3483 * other fields are the delta from the last report:
3484 * - Report difference for the per flow counters as they increase
3485 * with time
3486 * - Report current value for other counters as they tend not to change
3487 * much with time
3488 */
0a7de745 3489#define STATCOPY(f) \
5ba3f43e 3490 (st->net_api_stats.f = net_api_stats.f)
0a7de745 3491#define STATDIFF(f) \
5ba3f43e
A
3492 (st->net_api_stats.f = net_api_stats.f - net_api_stats_before.f)
3493
3494 STATCOPY(nas_iflt_attach_count);
3495 STATCOPY(nas_iflt_attach_total);
3496 STATCOPY(nas_iflt_attach_os_total);
3497
3498 STATCOPY(nas_ipf_add_count);
3499 STATCOPY(nas_ipf_add_total);
3500 STATCOPY(nas_ipf_add_os_total);
3501
3502 STATCOPY(nas_sfltr_register_count);
3503 STATCOPY(nas_sfltr_register_total);
3504 STATCOPY(nas_sfltr_register_os_total);
3505
3506 STATDIFF(nas_socket_alloc_total);
3507 STATDIFF(nas_socket_in_kernel_total);
3508 STATDIFF(nas_socket_in_kernel_os_total);
3509 STATDIFF(nas_socket_necp_clientuuid_total);
3510
3511 STATDIFF(nas_socket_domain_local_total);
3512 STATDIFF(nas_socket_domain_route_total);
3513 STATDIFF(nas_socket_domain_inet_total);
3514 STATDIFF(nas_socket_domain_inet6_total);
3515 STATDIFF(nas_socket_domain_system_total);
3516 STATDIFF(nas_socket_domain_multipath_total);
3517 STATDIFF(nas_socket_domain_key_total);
3518 STATDIFF(nas_socket_domain_ndrv_total);
3519 STATDIFF(nas_socket_domain_other_total);
3520
3521 STATDIFF(nas_socket_inet_stream_total);
3522 STATDIFF(nas_socket_inet_dgram_total);
3523 STATDIFF(nas_socket_inet_dgram_connected);
3524 STATDIFF(nas_socket_inet_dgram_dns);
3525 STATDIFF(nas_socket_inet_dgram_no_data);
3526
3527 STATDIFF(nas_socket_inet6_stream_total);
3528 STATDIFF(nas_socket_inet6_dgram_total);
3529 STATDIFF(nas_socket_inet6_dgram_connected);
3530 STATDIFF(nas_socket_inet6_dgram_dns);
3531 STATDIFF(nas_socket_inet6_dgram_no_data);
3532
3533 STATDIFF(nas_socket_mcast_join_total);
3534 STATDIFF(nas_socket_mcast_join_os_total);
3535
3536 STATDIFF(nas_sock_inet6_stream_exthdr_in);
3537 STATDIFF(nas_sock_inet6_stream_exthdr_out);
3538 STATDIFF(nas_sock_inet6_dgram_exthdr_in);
3539 STATDIFF(nas_sock_inet6_dgram_exthdr_out);
3540
3541 STATDIFF(nas_nx_flow_inet_stream_total);
3542 STATDIFF(nas_nx_flow_inet_dgram_total);
3543
3544 STATDIFF(nas_nx_flow_inet6_stream_total);
3545 STATDIFF(nas_nx_flow_inet6_dgram_total);
3546
3547 STATCOPY(nas_ifnet_alloc_count);
3548 STATCOPY(nas_ifnet_alloc_total);
3549 STATCOPY(nas_ifnet_alloc_os_count);
3550 STATCOPY(nas_ifnet_alloc_os_total);
3551
3552 STATCOPY(nas_pf_addrule_total);
3553 STATCOPY(nas_pf_addrule_os);
3554
3555 STATCOPY(nas_vmnet_total);
3556
3557#undef STATCOPY
3558#undef STATDIFF
3559
3560 nstat_sysinfo_send_data(&data);
3561
3562 /*
3563 * Save a copy of the current fields so we can diff them the next time
3564 */
3565 memcpy(&net_api_stats_before, &net_api_stats,
3566 sizeof(struct net_api_stats));
0a7de745 3567 _CASSERT(sizeof(net_api_stats_before) == sizeof(net_api_stats));
fe8ab488
A
3568}
3569
5ba3f43e 3570
6d2010ae
A
3571#pragma mark -- Kernel Control Socket --
3572
0a7de745
A
3573static kern_ctl_ref nstat_ctlref = NULL;
3574static lck_grp_t *nstat_lck_grp = NULL;
6d2010ae 3575
0a7de745
A
3576static errno_t nstat_control_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo);
3577static errno_t nstat_control_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo);
3578static errno_t nstat_control_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags);
6d2010ae 3579
3e170ce0
A
3580static errno_t
3581nstat_enqueue_success(
0a7de745
A
3582 uint64_t context,
3583 nstat_control_state *state,
3584 u_int16_t flags)
3e170ce0
A
3585{
3586 nstat_msg_hdr success;
3587 errno_t result;
6d2010ae 3588
3e170ce0
A
3589 bzero(&success, sizeof(success));
3590 success.context = context;
3591 success.type = NSTAT_MSG_TYPE_SUCCESS;
3592 success.length = sizeof(success);
3593 success.flags = flags;
3594 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success,
3595 sizeof(success), CTL_DATA_EOR | CTL_DATA_CRIT);
3596 if (result != 0) {
0a7de745 3597 if (nstat_debug != 0) {
3e170ce0
A
3598 printf("%s: could not enqueue success message %d\n",
3599 __func__, result);
0a7de745 3600 }
3e170ce0
A
3601 nstat_stats.nstat_successmsgfailures += 1;
3602 }
3603 return result;
3604}
3605
cb323159
A
3606static errno_t
3607nstat_control_send_event(
3608 nstat_control_state *state,
3609 nstat_src *src,
3610 u_int64_t event)
3611{
3612 errno_t result = 0;
3613 int failed = 0;
3614
3615 if (nstat_control_reporting_allowed(state, src)) {
3616 if ((state->ncs_flags & NSTAT_FLAG_SUPPORTS_UPDATES) != 0) {
3617 result = nstat_control_send_update(state, src, 0, event, 0, NULL);
3618 if (result != 0) {
3619 failed = 1;
3620 if (nstat_debug != 0) {
3621 printf("%s - nstat_control_send_event() %d\n", __func__, result);
3622 }
3623 }
3624 } else {
3625 if (nstat_debug != 0) {
3626 printf("%s - nstat_control_send_event() used when updates not supported\n", __func__);
3627 }
3628 }
3629 }
3630 return result;
3631}
3632
3e170ce0
A
3633static errno_t
3634nstat_control_send_goodbye(
0a7de745
A
3635 nstat_control_state *state,
3636 nstat_src *src)
6d2010ae 3637{
3e170ce0
A
3638 errno_t result = 0;
3639 int failed = 0;
3640
0a7de745
A
3641 if (nstat_control_reporting_allowed(state, src)) {
3642 if ((state->ncs_flags & NSTAT_FLAG_SUPPORTS_UPDATES) != 0) {
cb323159 3643 result = nstat_control_send_update(state, src, 0, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
0a7de745 3644 if (result != 0) {
3e170ce0 3645 failed = 1;
0a7de745 3646 if (nstat_debug != 0) {
3e170ce0 3647 printf("%s - nstat_control_send_update() %d\n", __func__, result);
0a7de745 3648 }
6d2010ae 3649 }
0a7de745 3650 } else {
3e170ce0
A
3651 // send one last counts notification
3652 result = nstat_control_send_counts(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
0a7de745 3653 if (result != 0) {
3e170ce0 3654 failed = 1;
0a7de745 3655 if (nstat_debug != 0) {
3e170ce0 3656 printf("%s - nstat_control_send_counts() %d\n", __func__, result);
0a7de745 3657 }
3e170ce0
A
3658 }
3659
3660 // send a last description
3661 result = nstat_control_send_description(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING);
0a7de745 3662 if (result != 0) {
3e170ce0 3663 failed = 1;
0a7de745 3664 if (nstat_debug != 0) {
3e170ce0 3665 printf("%s - nstat_control_send_description() %d\n", __func__, result);
0a7de745 3666 }
3e170ce0
A
3667 }
3668 }
3669 }
3670
3671 // send the source removed notification
3672 result = nstat_control_send_removed(state, src);
0a7de745 3673 if (result != 0 && nstat_debug) {
3e170ce0 3674 failed = 1;
0a7de745 3675 if (nstat_debug != 0) {
3e170ce0 3676 printf("%s - nstat_control_send_removed() %d\n", __func__, result);
0a7de745 3677 }
3e170ce0
A
3678 }
3679
0a7de745 3680 if (failed != 0) {
3e170ce0 3681 nstat_stats.nstat_control_send_goodbye_failures++;
0a7de745 3682 }
3e170ce0 3683
39037602 3684
3e170ce0
A
3685 return result;
3686}
3687
3688static errno_t
3689nstat_flush_accumulated_msgs(
0a7de745 3690 nstat_control_state *state)
3e170ce0
A
3691{
3692 errno_t result = 0;
0a7de745 3693 if (state->ncs_accumulated != NULL && mbuf_len(state->ncs_accumulated) > 0) {
3e170ce0
A
3694 mbuf_pkthdr_setlen(state->ncs_accumulated, mbuf_len(state->ncs_accumulated));
3695 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, state->ncs_accumulated, CTL_DATA_EOR);
0a7de745 3696 if (result != 0) {
3e170ce0 3697 nstat_stats.nstat_flush_accumulated_msgs_failures++;
0a7de745 3698 if (nstat_debug != 0) {
3e170ce0 3699 printf("%s - ctl_enqueuembuf failed: %d\n", __func__, result);
0a7de745 3700 }
3e170ce0
A
3701 mbuf_freem(state->ncs_accumulated);
3702 }
3703 state->ncs_accumulated = NULL;
3704 }
3705 return result;
3706}
3707
3708static errno_t
3709nstat_accumulate_msg(
0a7de745
A
3710 nstat_control_state *state,
3711 nstat_msg_hdr *hdr,
3712 size_t length)
3e170ce0 3713{
0a7de745 3714 if (state->ncs_accumulated && mbuf_trailingspace(state->ncs_accumulated) < length) {
3e170ce0
A
3715 // Will send the current mbuf
3716 nstat_flush_accumulated_msgs(state);
3717 }
39037602 3718
3e170ce0 3719 errno_t result = 0;
39037602 3720
0a7de745 3721 if (state->ncs_accumulated == NULL) {
3e170ce0 3722 unsigned int one = 1;
0a7de745
A
3723 if (mbuf_allocpacket(MBUF_DONTWAIT, NSTAT_MAX_MSG_SIZE, &one, &state->ncs_accumulated) != 0) {
3724 if (nstat_debug != 0) {
3e170ce0 3725 printf("%s - mbuf_allocpacket failed\n", __func__);
0a7de745 3726 }
3e170ce0 3727 result = ENOMEM;
0a7de745 3728 } else {
3e170ce0
A
3729 mbuf_setlen(state->ncs_accumulated, 0);
3730 }
3731 }
39037602 3732
0a7de745 3733 if (result == 0) {
3e170ce0
A
3734 hdr->length = length;
3735 result = mbuf_copyback(state->ncs_accumulated, mbuf_len(state->ncs_accumulated),
0a7de745 3736 length, hdr, MBUF_DONTWAIT);
3e170ce0 3737 }
39037602 3738
0a7de745 3739 if (result != 0) {
3e170ce0 3740 nstat_flush_accumulated_msgs(state);
0a7de745 3741 if (nstat_debug != 0) {
3e170ce0 3742 printf("%s - resorting to ctl_enqueuedata\n", __func__);
0a7de745 3743 }
3e170ce0
A
3744 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, hdr, length, CTL_DATA_EOR);
3745 }
39037602 3746
0a7de745 3747 if (result != 0) {
3e170ce0 3748 nstat_stats.nstat_accumulate_msg_failures++;
0a7de745 3749 }
3e170ce0
A
3750
3751 return result;
3752}
3753
3754static void*
3755nstat_idle_check(
3756 __unused thread_call_param_t p0,
3757 __unused thread_call_param_t p1)
3758{
5ba3f43e 3759 nstat_control_state *control;
cb323159 3760 nstat_src *src, *tmpsrc;
5ba3f43e
A
3761 tailq_head_nstat_src dead_list;
3762 TAILQ_INIT(&dead_list);
3763
3e170ce0 3764 lck_mtx_lock(&nstat_mtx);
39037602 3765
3e170ce0 3766 nstat_idle_time = 0;
39037602 3767
0a7de745 3768 for (control = nstat_controls; control; control = control->ncs_next) {
5ba3f43e 3769 lck_mtx_lock(&control->ncs_mtx);
0a7de745 3770 if (!(control->ncs_flags & NSTAT_FLAG_REQCOUNTS)) {
5ba3f43e 3771 TAILQ_FOREACH_SAFE(src, &control->ncs_src_queue, ns_control_link, tmpsrc)
3e170ce0 3772 {
0a7de745 3773 if (src->provider->nstat_gone(src->cookie)) {
3e170ce0 3774 errno_t result;
39037602 3775
3e170ce0 3776 // Pull it off the list
5ba3f43e 3777 TAILQ_REMOVE(&control->ncs_src_queue, src, ns_control_link);
39037602 3778
5ba3f43e 3779 result = nstat_control_send_goodbye(control, src);
39037602 3780
3e170ce0 3781 // Put this on the list to release later
5ba3f43e 3782 TAILQ_INSERT_TAIL(&dead_list, src, ns_control_link);
3e170ce0
A
3783 }
3784 }
3785 }
3786 control->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
5ba3f43e 3787 lck_mtx_unlock(&control->ncs_mtx);
3e170ce0
A
3788 }
3789
0a7de745 3790 if (nstat_controls) {
3e170ce0
A
3791 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
3792 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
6d2010ae 3793 }
39037602 3794
6d2010ae 3795 lck_mtx_unlock(&nstat_mtx);
39037602 3796
fe8ab488
A
3797 /* Generate any system level reports, if needed */
3798 nstat_sysinfo_generate_report();
39037602 3799
6d2010ae 3800 // Release the sources now that we aren't holding lots of locks
0a7de745 3801 while ((src = TAILQ_FIRST(&dead_list))) {
5ba3f43e
A
3802 TAILQ_REMOVE(&dead_list, src, ns_control_link);
3803 nstat_control_cleanup_source(NULL, src, FALSE);
6d2010ae 3804 }
39037602 3805
5ba3f43e 3806
6d2010ae
A
3807 return NULL;
3808}
3809
3810static void
3811nstat_control_register(void)
3812{
3813 // Create our lock group first
0a7de745 3814 lck_grp_attr_t *grp_attr = lck_grp_attr_alloc_init();
6d2010ae
A
3815 lck_grp_attr_setdefault(grp_attr);
3816 nstat_lck_grp = lck_grp_alloc_init("network statistics kctl", grp_attr);
3817 lck_grp_attr_free(grp_attr);
39037602 3818
6d2010ae 3819 lck_mtx_init(&nstat_mtx, nstat_lck_grp, NULL);
39037602 3820
6d2010ae 3821 // Register the control
0a7de745 3822 struct kern_ctl_reg nstat_control;
39037602 3823 bzero(&nstat_control, sizeof(nstat_control));
6d2010ae 3824 strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name));
fe8ab488
A
3825 nstat_control.ctl_flags = CTL_FLAG_REG_EXTENDED | CTL_FLAG_REG_CRIT;
3826 nstat_control.ctl_sendsize = nstat_sendspace;
3827 nstat_control.ctl_recvsize = nstat_recvspace;
6d2010ae
A
3828 nstat_control.ctl_connect = nstat_control_connect;
3829 nstat_control.ctl_disconnect = nstat_control_disconnect;
3830 nstat_control.ctl_send = nstat_control_send;
39037602 3831
316670eb 3832 ctl_register(&nstat_control, &nstat_ctlref);
6d2010ae
A
3833}
3834
3835static void
3836nstat_control_cleanup_source(
0a7de745
A
3837 nstat_control_state *state,
3838 struct nstat_src *src,
3839 boolean_t locked)
6d2010ae 3840{
fe8ab488 3841 errno_t result;
39037602 3842
0a7de745 3843 if (state) {
fe8ab488 3844 result = nstat_control_send_removed(state, src);
0a7de745 3845 if (result != 0) {
3e170ce0 3846 nstat_stats.nstat_control_cleanup_source_failures++;
0a7de745 3847 if (nstat_debug != 0) {
3e170ce0
A
3848 printf("%s - nstat_control_send_removed() %d\n",
3849 __func__, result);
0a7de745 3850 }
3e170ce0 3851 }
fe8ab488 3852 }
6d2010ae 3853 // Cleanup the source if we found it.
316670eb 3854 src->provider->nstat_release(src->cookie, locked);
6d2010ae
A
3855 OSFree(src, sizeof(*src), nstat_malloc_tag);
3856}
3857
3e170ce0
A
3858
3859static bool
3860nstat_control_reporting_allowed(
3861 nstat_control_state *state,
3862 nstat_src *src)
3863{
0a7de745 3864 if (src->provider->nstat_reporting_allowed == NULL) {
3e170ce0 3865 return TRUE;
0a7de745 3866 }
3e170ce0 3867
cb323159
A
3868 return src->provider->nstat_reporting_allowed(src->cookie,
3869 &state->ncs_provider_filters[src->provider->nstat_provider_id]);
3e170ce0
A
3870}
3871
3872
6d2010ae
A
3873static errno_t
3874nstat_control_connect(
cb323159
A
3875 kern_ctl_ref kctl,
3876 struct sockaddr_ctl *sac,
3877 void **uinfo)
6d2010ae 3878{
cb323159 3879 nstat_control_state *state = OSMalloc(sizeof(*state), nstat_malloc_tag);
0a7de745
A
3880 if (state == NULL) {
3881 return ENOMEM;
3882 }
39037602 3883
6d2010ae 3884 bzero(state, sizeof(*state));
5ba3f43e 3885 lck_mtx_init(&state->ncs_mtx, nstat_lck_grp, NULL);
316670eb
A
3886 state->ncs_kctl = kctl;
3887 state->ncs_unit = sac->sc_unit;
3888 state->ncs_flags = NSTAT_FLAG_REQCOUNTS;
6d2010ae 3889 *uinfo = state;
39037602 3890
6d2010ae 3891 lck_mtx_lock(&nstat_mtx);
316670eb 3892 state->ncs_next = nstat_controls;
6d2010ae 3893 nstat_controls = state;
39037602 3894
0a7de745 3895 if (nstat_idle_time == 0) {
6d2010ae
A
3896 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
3897 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
3898 }
39037602 3899
6d2010ae 3900 lck_mtx_unlock(&nstat_mtx);
39037602 3901
6d2010ae
A
3902 return 0;
3903}
3904
3905static errno_t
3906nstat_control_disconnect(
0a7de745 3907 __unused kern_ctl_ref kctl,
cb323159
A
3908 __unused u_int32_t unit,
3909 void *uinfo)
6d2010ae 3910{
cb323159
A
3911 u_int32_t watching;
3912 nstat_control_state *state = (nstat_control_state*)uinfo;
5ba3f43e
A
3913 tailq_head_nstat_src cleanup_list;
3914 nstat_src *src;
3915
3916 TAILQ_INIT(&cleanup_list);
39037602 3917
6d2010ae
A
3918 // pull it out of the global list of states
3919 lck_mtx_lock(&nstat_mtx);
0a7de745
A
3920 nstat_control_state **statepp;
3921 for (statepp = &nstat_controls; *statepp; statepp = &(*statepp)->ncs_next) {
3922 if (*statepp == state) {
316670eb 3923 *statepp = state->ncs_next;
6d2010ae
A
3924 break;
3925 }
3926 }
3927 lck_mtx_unlock(&nstat_mtx);
39037602 3928
5ba3f43e 3929 lck_mtx_lock(&state->ncs_mtx);
6d2010ae 3930 // Stop watching for sources
0a7de745 3931 nstat_provider *provider;
316670eb
A
3932 watching = state->ncs_watching;
3933 state->ncs_watching = 0;
0a7de745
A
3934 for (provider = nstat_providers; provider && watching; provider = provider->next) {
3935 if ((watching & (1 << provider->nstat_provider_id)) != 0) {
6d2010ae
A
3936 watching &= ~(1 << provider->nstat_provider_id);
3937 provider->nstat_watcher_remove(state);
3938 }
3939 }
39037602 3940
6d2010ae 3941 // set cleanup flags
316670eb 3942 state->ncs_flags |= NSTAT_FLAG_CLEANUP;
39037602 3943
0a7de745 3944 if (state->ncs_accumulated) {
3e170ce0
A
3945 mbuf_freem(state->ncs_accumulated);
3946 state->ncs_accumulated = NULL;
3947 }
39037602 3948
6d2010ae 3949 // Copy out the list of sources
5ba3f43e
A
3950 TAILQ_CONCAT(&cleanup_list, &state->ncs_src_queue, ns_control_link);
3951 lck_mtx_unlock(&state->ncs_mtx);
39037602 3952
0a7de745 3953 while ((src = TAILQ_FIRST(&cleanup_list))) {
5ba3f43e 3954 TAILQ_REMOVE(&cleanup_list, src, ns_control_link);
316670eb 3955 nstat_control_cleanup_source(NULL, src, FALSE);
6d2010ae 3956 }
5ba3f43e
A
3957
3958 lck_mtx_destroy(&state->ncs_mtx, nstat_lck_grp);
6d2010ae 3959 OSFree(state, sizeof(*state), nstat_malloc_tag);
39037602 3960
6d2010ae
A
3961 return 0;
3962}
3963
3964static nstat_src_ref_t
3965nstat_control_next_src_ref(
0a7de745 3966 nstat_control_state *state)
6d2010ae 3967{
39037602 3968 return ++state->ncs_next_srcref;
6d2010ae
A
3969}
3970
316670eb
A
3971static errno_t
3972nstat_control_send_counts(
cb323159
A
3973 nstat_control_state *state,
3974 nstat_src *src,
3975 unsigned long long context,
3976 u_int16_t hdr_flags,
3977 int *gone)
3e170ce0 3978{
316670eb 3979 nstat_msg_src_counts counts;
316670eb
A
3980 errno_t result = 0;
3981
fe8ab488 3982 /* Some providers may not have any counts to send */
0a7de745
A
3983 if (src->provider->nstat_counts == NULL) {
3984 return 0;
3985 }
fe8ab488
A
3986
3987 bzero(&counts, sizeof(counts));
316670eb 3988 counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
3e170ce0
A
3989 counts.hdr.length = sizeof(counts);
3990 counts.hdr.flags = hdr_flags;
316670eb
A
3991 counts.hdr.context = context;
3992 counts.srcref = src->srcref;
39037602
A
3993 counts.event_flags = 0;
3994
0a7de745 3995 if (src->provider->nstat_counts(src->cookie, &counts.counts, gone) == 0) {
39236c6e 3996 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) &&
39037602 3997 counts.counts.nstat_rxbytes == 0 &&
0a7de745 3998 counts.counts.nstat_txbytes == 0) {
39236c6e 3999 result = EAGAIN;
0a7de745 4000 } else {
39236c6e
A
4001 result = ctl_enqueuedata(state->ncs_kctl,
4002 state->ncs_unit, &counts, sizeof(counts),
4003 CTL_DATA_EOR);
0a7de745 4004 if (result != 0) {
3e170ce0 4005 nstat_stats.nstat_sendcountfailures += 1;
0a7de745 4006 }
fe8ab488 4007 }
316670eb 4008 }
316670eb
A
4009 return result;
4010}
4011
3e170ce0
A
4012static errno_t
4013nstat_control_append_counts(
cb323159
A
4014 nstat_control_state *state,
4015 nstat_src *src,
4016 int *gone)
3e170ce0
A
4017{
4018 /* Some providers may not have any counts to send */
0a7de745
A
4019 if (!src->provider->nstat_counts) {
4020 return 0;
4021 }
39037602 4022
3e170ce0
A
4023 nstat_msg_src_counts counts;
4024 bzero(&counts, sizeof(counts));
4025 counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
4026 counts.hdr.length = sizeof(counts);
4027 counts.srcref = src->srcref;
39037602
A
4028 counts.event_flags = 0;
4029
0a7de745 4030 errno_t result = 0;
3e170ce0 4031 result = src->provider->nstat_counts(src->cookie, &counts.counts, gone);
0a7de745 4032 if (result != 0) {
3e170ce0
A
4033 return result;
4034 }
39037602 4035
3e170ce0 4036 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
0a7de745 4037 counts.counts.nstat_rxbytes == 0 && counts.counts.nstat_txbytes == 0) {
3e170ce0
A
4038 return EAGAIN;
4039 }
39037602 4040
3e170ce0
A
4041 return nstat_accumulate_msg(state, &counts.hdr, counts.hdr.length);
4042}
4043
6d2010ae
A
4044static int
4045nstat_control_send_description(
cb323159
A
4046 nstat_control_state *state,
4047 nstat_src *src,
4048 u_int64_t context,
4049 u_int16_t hdr_flags)
6d2010ae
A
4050{
4051 // Provider doesn't support getting the descriptor? Done.
4052 if (src->provider->nstat_descriptor_length == 0 ||
0a7de745 4053 src->provider->nstat_copy_descriptor == NULL) {
6d2010ae
A
4054 return EOPNOTSUPP;
4055 }
fe8ab488 4056
6d2010ae 4057 // Allocate storage for the descriptor message
cb323159 4058 mbuf_t msg;
0a7de745 4059 unsigned int one = 1;
cb323159 4060 u_int32_t size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
0a7de745 4061 if (mbuf_allocpacket(MBUF_DONTWAIT, size, &one, &msg) != 0) {
6d2010ae
A
4062 return ENOMEM;
4063 }
fe8ab488 4064
cb323159 4065 nstat_msg_src_description *desc = (nstat_msg_src_description*)mbuf_data(msg);
fe8ab488 4066 bzero(desc, size);
6d2010ae
A
4067 mbuf_setlen(msg, size);
4068 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
fe8ab488 4069
6d2010ae 4070 // Query the provider for the provider specific bits
0a7de745 4071 errno_t result = src->provider->nstat_copy_descriptor(src->cookie, desc->data, src->provider->nstat_descriptor_length);
fe8ab488 4072
0a7de745 4073 if (result != 0) {
6d2010ae 4074 mbuf_freem(msg);
6d2010ae
A
4075 return result;
4076 }
fe8ab488 4077
6d2010ae
A
4078 desc->hdr.context = context;
4079 desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
3e170ce0
A
4080 desc->hdr.length = size;
4081 desc->hdr.flags = hdr_flags;
6d2010ae 4082 desc->srcref = src->srcref;
39037602 4083 desc->event_flags = 0;
6d2010ae 4084 desc->provider = src->provider->nstat_provider_id;
fe8ab488 4085
316670eb 4086 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
0a7de745 4087 if (result != 0) {
3e170ce0 4088 nstat_stats.nstat_descriptionfailures += 1;
6d2010ae
A
4089 mbuf_freem(msg);
4090 }
fe8ab488 4091
6d2010ae
A
4092 return result;
4093}
4094
3e170ce0
A
4095static errno_t
4096nstat_control_append_description(
cb323159
A
4097 nstat_control_state *state,
4098 nstat_src *src)
3e170ce0 4099{
0a7de745 4100 size_t size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
3e170ce0 4101 if (size > 512 || src->provider->nstat_descriptor_length == 0 ||
0a7de745 4102 src->provider->nstat_copy_descriptor == NULL) {
3e170ce0
A
4103 return EOPNOTSUPP;
4104 }
39037602 4105
3e170ce0 4106 // Fill out a buffer on the stack, we will copy to the mbuf later
0a7de745 4107 u_int64_t buffer[size / sizeof(u_int64_t) + 1]; // u_int64_t to ensure alignment
3e170ce0 4108 bzero(buffer, size);
39037602 4109
cb323159 4110 nstat_msg_src_description *desc = (nstat_msg_src_description*)buffer;
3e170ce0
A
4111 desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
4112 desc->hdr.length = size;
4113 desc->srcref = src->srcref;
39037602 4114 desc->event_flags = 0;
3e170ce0 4115 desc->provider = src->provider->nstat_provider_id;
39037602 4116
0a7de745 4117 errno_t result = 0;
3e170ce0
A
4118 // Fill in the description
4119 // Query the provider for the provider specific bits
4120 result = src->provider->nstat_copy_descriptor(src->cookie, desc->data,
0a7de745
A
4121 src->provider->nstat_descriptor_length);
4122 if (result != 0) {
3e170ce0
A
4123 return result;
4124 }
39037602 4125
3e170ce0
A
4126 return nstat_accumulate_msg(state, &desc->hdr, size);
4127}
4128
4129static int
4130nstat_control_send_update(
cb323159
A
4131 nstat_control_state *state,
4132 nstat_src *src,
4133 u_int64_t context,
4134 u_int64_t event,
4135 u_int16_t hdr_flags,
4136 int *gone)
3e170ce0
A
4137{
4138 // Provider doesn't support getting the descriptor or counts? Done.
4139 if ((src->provider->nstat_descriptor_length == 0 ||
0a7de745
A
4140 src->provider->nstat_copy_descriptor == NULL) &&
4141 src->provider->nstat_counts == NULL) {
3e170ce0
A
4142 return EOPNOTSUPP;
4143 }
39037602 4144
3e170ce0 4145 // Allocate storage for the descriptor message
cb323159 4146 mbuf_t msg;
0a7de745 4147 unsigned int one = 1;
cb323159 4148 u_int32_t size = offsetof(nstat_msg_src_update, data) +
0a7de745
A
4149 src->provider->nstat_descriptor_length;
4150 if (mbuf_allocpacket(MBUF_DONTWAIT, size, &one, &msg) != 0) {
3e170ce0
A
4151 return ENOMEM;
4152 }
39037602 4153
cb323159 4154 nstat_msg_src_update *desc = (nstat_msg_src_update*)mbuf_data(msg);
3e170ce0
A
4155 bzero(desc, size);
4156 desc->hdr.context = context;
4157 desc->hdr.type = NSTAT_MSG_TYPE_SRC_UPDATE;
4158 desc->hdr.length = size;
4159 desc->hdr.flags = hdr_flags;
4160 desc->srcref = src->srcref;
cb323159 4161 desc->event_flags = event;
3e170ce0 4162 desc->provider = src->provider->nstat_provider_id;
39037602 4163
3e170ce0
A
4164 mbuf_setlen(msg, size);
4165 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
39037602 4166
0a7de745
A
4167 errno_t result = 0;
4168 if (src->provider->nstat_descriptor_length != 0 && src->provider->nstat_copy_descriptor) {
3e170ce0
A
4169 // Query the provider for the provider specific bits
4170 result = src->provider->nstat_copy_descriptor(src->cookie, desc->data,
0a7de745
A
4171 src->provider->nstat_descriptor_length);
4172 if (result != 0) {
3e170ce0
A
4173 mbuf_freem(msg);
4174 return result;
4175 }
4176 }
39037602 4177
0a7de745 4178 if (src->provider->nstat_counts) {
3e170ce0 4179 result = src->provider->nstat_counts(src->cookie, &desc->counts, gone);
0a7de745 4180 if (result == 0) {
3e170ce0 4181 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
0a7de745 4182 desc->counts.nstat_rxbytes == 0 && desc->counts.nstat_txbytes == 0) {
3e170ce0 4183 result = EAGAIN;
0a7de745 4184 } else {
3e170ce0
A
4185 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
4186 }
4187 }
4188 }
39037602 4189
0a7de745 4190 if (result != 0) {
3e170ce0
A
4191 nstat_stats.nstat_srcupatefailures += 1;
4192 mbuf_freem(msg);
4193 }
4194
4195 return result;
4196}
4197
4198static errno_t
4199nstat_control_append_update(
cb323159
A
4200 nstat_control_state *state,
4201 nstat_src *src,
4202 int *gone)
3e170ce0 4203{
0a7de745 4204 size_t size = offsetof(nstat_msg_src_update, data) + src->provider->nstat_descriptor_length;
3e170ce0 4205 if (size > 512 || ((src->provider->nstat_descriptor_length == 0 ||
0a7de745
A
4206 src->provider->nstat_copy_descriptor == NULL) &&
4207 src->provider->nstat_counts == NULL)) {
3e170ce0
A
4208 return EOPNOTSUPP;
4209 }
39037602 4210
3e170ce0 4211 // Fill out a buffer on the stack, we will copy to the mbuf later
0a7de745 4212 u_int64_t buffer[size / sizeof(u_int64_t) + 1]; // u_int64_t to ensure alignment
3e170ce0 4213 bzero(buffer, size);
39037602 4214
0a7de745 4215 nstat_msg_src_update *desc = (nstat_msg_src_update*)buffer;
3e170ce0
A
4216 desc->hdr.type = NSTAT_MSG_TYPE_SRC_UPDATE;
4217 desc->hdr.length = size;
4218 desc->srcref = src->srcref;
39037602 4219 desc->event_flags = 0;
3e170ce0 4220 desc->provider = src->provider->nstat_provider_id;
39037602 4221
0a7de745 4222 errno_t result = 0;
3e170ce0 4223 // Fill in the description
0a7de745 4224 if (src->provider->nstat_descriptor_length != 0 && src->provider->nstat_copy_descriptor) {
3e170ce0
A
4225 // Query the provider for the provider specific bits
4226 result = src->provider->nstat_copy_descriptor(src->cookie, desc->data,
0a7de745
A
4227 src->provider->nstat_descriptor_length);
4228 if (result != 0) {
3e170ce0 4229 nstat_stats.nstat_copy_descriptor_failures++;
0a7de745 4230 if (nstat_debug != 0) {
3e170ce0 4231 printf("%s: src->provider->nstat_copy_descriptor: %d\n", __func__, result);
0a7de745 4232 }
3e170ce0
A
4233 return result;
4234 }
4235 }
39037602 4236
0a7de745 4237 if (src->provider->nstat_counts) {
3e170ce0 4238 result = src->provider->nstat_counts(src->cookie, &desc->counts, gone);
0a7de745 4239 if (result != 0) {
3e170ce0 4240 nstat_stats.nstat_provider_counts_failures++;
0a7de745 4241 if (nstat_debug != 0) {
3e170ce0 4242 printf("%s: src->provider->nstat_counts: %d\n", __func__, result);
0a7de745 4243 }
3e170ce0
A
4244 return result;
4245 }
39037602 4246
3e170ce0 4247 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
0a7de745 4248 desc->counts.nstat_rxbytes == 0 && desc->counts.nstat_txbytes == 0) {
3e170ce0
A
4249 return EAGAIN;
4250 }
4251 }
39037602 4252
3e170ce0
A
4253 return nstat_accumulate_msg(state, &desc->hdr, size);
4254}
4255
316670eb
A
4256static errno_t
4257nstat_control_send_removed(
cb323159
A
4258 nstat_control_state *state,
4259 nstat_src *src)
316670eb
A
4260{
4261 nstat_msg_src_removed removed;
4262 errno_t result;
4263
fe8ab488 4264 bzero(&removed, sizeof(removed));
316670eb 4265 removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED;
3e170ce0 4266 removed.hdr.length = sizeof(removed);
316670eb
A
4267 removed.hdr.context = 0;
4268 removed.srcref = src->srcref;
4269 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &removed,
fe8ab488 4270 sizeof(removed), CTL_DATA_EOR | CTL_DATA_CRIT);
0a7de745 4271 if (result != 0) {
3e170ce0 4272 nstat_stats.nstat_msgremovedfailures += 1;
0a7de745 4273 }
316670eb
A
4274
4275 return result;
4276}
4277
6d2010ae
A
4278static errno_t
4279nstat_control_handle_add_request(
cb323159
A
4280 nstat_control_state *state,
4281 mbuf_t m)
6d2010ae 4282{
0a7de745 4283 errno_t result;
39236c6e 4284
6d2010ae 4285 // Verify the header fits in the first mbuf
0a7de745 4286 if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param)) {
6d2010ae
A
4287 return EINVAL;
4288 }
39037602 4289
6d2010ae 4290 // Calculate the length of the parameter field
0a7de745
A
4291 int32_t paramlength = mbuf_pkthdr_len(m) - offsetof(nstat_msg_add_src_req, param);
4292 if (paramlength < 0 || paramlength > 2 * 1024) {
6d2010ae
A
4293 return EINVAL;
4294 }
39037602 4295
cb323159 4296 nstat_provider *provider = NULL;
0a7de745
A
4297 nstat_provider_cookie_t cookie = NULL;
4298 nstat_msg_add_src_req *req = mbuf_data(m);
4299 if (mbuf_pkthdr_len(m) > mbuf_len(m)) {
6d2010ae 4300 // parameter is too large, we need to make a contiguous copy
0a7de745 4301 void *data = OSMalloc(paramlength, nstat_malloc_tag);
39037602 4302
0a7de745
A
4303 if (!data) {
4304 return ENOMEM;
4305 }
6d2010ae 4306 result = mbuf_copydata(m, offsetof(nstat_msg_add_src_req, param), paramlength, data);
0a7de745 4307 if (result == 0) {
6d2010ae 4308 result = nstat_lookup_entry(req->provider, data, paramlength, &provider, &cookie);
0a7de745 4309 }
6d2010ae 4310 OSFree(data, paramlength, nstat_malloc_tag);
0a7de745 4311 } else {
6d2010ae
A
4312 result = nstat_lookup_entry(req->provider, (void*)&req->param, paramlength, &provider, &cookie);
4313 }
39037602 4314
0a7de745 4315 if (result != 0) {
6d2010ae
A
4316 return result;
4317 }
39037602 4318
6d2010ae 4319 result = nstat_control_source_add(req->hdr.context, state, provider, cookie);
0a7de745 4320 if (result != 0) {
316670eb 4321 provider->nstat_release(cookie, 0);
0a7de745 4322 }
39037602 4323
6d2010ae
A
4324 return result;
4325}
4326
5ba3f43e
A
4327static errno_t
4328nstat_set_provider_filter(
0a7de745 4329 nstat_control_state *state,
cb323159 4330 nstat_msg_add_all_srcs *req)
5ba3f43e
A
4331{
4332 nstat_provider_id_t provider_id = req->provider;
4333
4334 u_int32_t prev_ncs_watching = atomic_or_32_ov(&state->ncs_watching, (1 << provider_id));
4335
0a7de745 4336 if ((prev_ncs_watching & (1 << provider_id)) != 0) {
5ba3f43e 4337 return EALREADY;
0a7de745 4338 }
5ba3f43e
A
4339
4340 state->ncs_watching |= (1 << provider_id);
4341 state->ncs_provider_filters[provider_id].npf_flags = req->filter;
4342 state->ncs_provider_filters[provider_id].npf_events = req->events;
4343 state->ncs_provider_filters[provider_id].npf_pid = req->target_pid;
4344 uuid_copy(state->ncs_provider_filters[provider_id].npf_uuid, req->target_uuid);
4345 return 0;
4346}
4347
6d2010ae
A
4348static errno_t
4349nstat_control_handle_add_all(
0a7de745 4350 nstat_control_state *state,
cb323159 4351 mbuf_t m)
6d2010ae 4352{
0a7de745 4353 errno_t result = 0;
39037602 4354
6d2010ae 4355 // Verify the header fits in the first mbuf
0a7de745 4356 if (mbuf_len(m) < sizeof(nstat_msg_add_all_srcs)) {
6d2010ae
A
4357 return EINVAL;
4358 }
39037602 4359
0a7de745
A
4360 nstat_msg_add_all_srcs *req = mbuf_data(m);
4361 if (req->provider > NSTAT_PROVIDER_LAST) {
4362 return ENOENT;
4363 }
3e170ce0 4364
cb323159 4365 nstat_provider *provider = nstat_find_provider_by_id(req->provider);
3e170ce0 4366
0a7de745
A
4367 if (!provider) {
4368 return ENOENT;
4369 }
4370 if (provider->nstat_watcher_add == NULL) {
4371 return ENOTSUP;
4372 }
39037602 4373
39236c6e 4374 if (nstat_privcheck != 0) {
39037602 4375 result = priv_check_cred(kauth_cred_get(),
39236c6e 4376 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
0a7de745 4377 if (result != 0) {
39236c6e 4378 return result;
0a7de745 4379 }
39236c6e
A
4380 }
4381
5ba3f43e 4382 lck_mtx_lock(&state->ncs_mtx);
0a7de745 4383 if (req->filter & NSTAT_FILTER_SUPPRESS_SRC_ADDED) {
5ba3f43e
A
4384 // Suppression of source messages implicitly requires the use of update messages
4385 state->ncs_flags |= NSTAT_FLAG_SUPPORTS_UPDATES;
4386 }
4387 lck_mtx_unlock(&state->ncs_mtx);
39236c6e 4388
5ba3f43e
A
4389 // rdar://problem/30301300 Different providers require different synchronization
4390 // to ensure that a new entry does not get double counted due to being added prior
4391 // to all current provider entries being added. Hence pass the provider the details
4392 // in the original request for this to be applied atomically
3e170ce0 4393
5ba3f43e 4394 result = provider->nstat_watcher_add(state, req);
39037602 4395
0a7de745 4396 if (result == 0) {
3e170ce0 4397 nstat_enqueue_success(req->hdr.context, state, 0);
0a7de745 4398 }
39037602 4399
6d2010ae
A
4400 return result;
4401}
4402
4403static errno_t
4404nstat_control_source_add(
cb323159
A
4405 u_int64_t context,
4406 nstat_control_state *state,
4407 nstat_provider *provider,
4408 nstat_provider_cookie_t cookie)
6d2010ae 4409{
3e170ce0 4410 // Fill out source added message if appropriate
0a7de745
A
4411 mbuf_t msg = NULL;
4412 nstat_src_ref_t *srcrefp = NULL;
3e170ce0 4413
0a7de745 4414 u_int64_t provider_filter_flagss =
39037602 4415 state->ncs_provider_filters[provider->nstat_provider_id].npf_flags;
0a7de745 4416 boolean_t tell_user =
39037602 4417 ((provider_filter_flagss & NSTAT_FILTER_SUPPRESS_SRC_ADDED) == 0);
0a7de745 4418 u_int32_t src_filter =
39037602 4419 (provider_filter_flagss & NSTAT_FILTER_PROVIDER_NOZEROBYTES)
0a7de745 4420 ? NSTAT_FILTER_NOZEROBYTES : 0;
3e170ce0 4421
0a7de745 4422 if (provider_filter_flagss & NSTAT_FILTER_TCP_NO_EARLY_CLOSE) {
5ba3f43e
A
4423 src_filter |= NSTAT_FILTER_TCP_NO_EARLY_CLOSE;
4424 }
4425
0a7de745 4426 if (tell_user) {
3e170ce0 4427 unsigned int one = 1;
39037602 4428
3e170ce0 4429 if (mbuf_allocpacket(MBUF_DONTWAIT, sizeof(nstat_msg_src_added),
0a7de745 4430 &one, &msg) != 0) {
3e170ce0 4431 return ENOMEM;
0a7de745 4432 }
39037602 4433
3e170ce0
A
4434 mbuf_setlen(msg, sizeof(nstat_msg_src_added));
4435 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
0a7de745 4436 nstat_msg_src_added *add = mbuf_data(msg);
3e170ce0
A
4437 bzero(add, sizeof(*add));
4438 add->hdr.type = NSTAT_MSG_TYPE_SRC_ADDED;
4439 add->hdr.length = mbuf_len(msg);
4440 add->hdr.context = context;
4441 add->provider = provider->nstat_provider_id;
4442 srcrefp = &add->srcref;
4443 }
39037602 4444
6d2010ae 4445 // Allocate storage for the source
0a7de745
A
4446 nstat_src *src = OSMalloc(sizeof(*src), nstat_malloc_tag);
4447 if (src == NULL) {
4448 if (msg) {
4449 mbuf_freem(msg);
4450 }
6d2010ae
A
4451 return ENOMEM;
4452 }
39037602 4453
6d2010ae 4454 // Fill in the source, including picking an unused source ref
5ba3f43e 4455 lck_mtx_lock(&state->ncs_mtx);
3e170ce0
A
4456
4457 src->srcref = nstat_control_next_src_ref(state);
0a7de745 4458 if (srcrefp) {
3e170ce0 4459 *srcrefp = src->srcref;
0a7de745 4460 }
3e170ce0 4461
0a7de745 4462 if (state->ncs_flags & NSTAT_FLAG_CLEANUP || src->srcref == NSTAT_SRC_REF_INVALID) {
5ba3f43e 4463 lck_mtx_unlock(&state->ncs_mtx);
6d2010ae 4464 OSFree(src, sizeof(*src), nstat_malloc_tag);
0a7de745
A
4465 if (msg) {
4466 mbuf_freem(msg);
4467 }
6d2010ae
A
4468 return EINVAL;
4469 }
4470 src->provider = provider;
4471 src->cookie = cookie;
3e170ce0 4472 src->filter = src_filter;
d190cdc3 4473 src->seq = 0;
3e170ce0 4474
0a7de745 4475 if (msg) {
3e170ce0
A
4476 // send the source added message if appropriate
4477 errno_t result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg,
0a7de745
A
4478 CTL_DATA_EOR);
4479 if (result != 0) {
3e170ce0 4480 nstat_stats.nstat_srcaddedfailures += 1;
5ba3f43e 4481 lck_mtx_unlock(&state->ncs_mtx);
3e170ce0
A
4482 OSFree(src, sizeof(*src), nstat_malloc_tag);
4483 mbuf_freem(msg);
4484 return result;
4485 }
6d2010ae 4486 }
3e170ce0 4487 // Put the source in the list
5ba3f43e
A
4488 TAILQ_INSERT_HEAD(&state->ncs_src_queue, src, ns_control_link);
4489 src->ns_control = state;
39037602 4490
5ba3f43e 4491 lck_mtx_unlock(&state->ncs_mtx);
39037602 4492
6d2010ae
A
4493 return 0;
4494}
4495
4496static errno_t
4497nstat_control_handle_remove_request(
cb323159
A
4498 nstat_control_state *state,
4499 mbuf_t m)
6d2010ae 4500{
0a7de745 4501 nstat_src_ref_t srcref = NSTAT_SRC_REF_INVALID;
5ba3f43e 4502 nstat_src *src;
39037602 4503
0a7de745 4504 if (mbuf_copydata(m, offsetof(nstat_msg_rem_src_req, srcref), sizeof(srcref), &srcref) != 0) {
6d2010ae
A
4505 return EINVAL;
4506 }
39037602 4507
5ba3f43e 4508 lck_mtx_lock(&state->ncs_mtx);
39037602 4509
6d2010ae 4510 // Remove this source as we look for it
5ba3f43e 4511 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
6d2010ae 4512 {
0a7de745 4513 if (src->srcref == srcref) {
6d2010ae
A
4514 break;
4515 }
4516 }
0a7de745 4517 if (src) {
5ba3f43e
A
4518 TAILQ_REMOVE(&state->ncs_src_queue, src, ns_control_link);
4519 }
39037602 4520
5ba3f43e 4521 lck_mtx_unlock(&state->ncs_mtx);
39037602 4522
0a7de745
A
4523 if (src) {
4524 nstat_control_cleanup_source(state, src, FALSE);
4525 }
39037602 4526
6d2010ae
A
4527 return src ? 0 : ENOENT;
4528}
4529
4530static errno_t
4531nstat_control_handle_query_request(
cb323159
A
4532 nstat_control_state *state,
4533 mbuf_t m)
6d2010ae
A
4534{
4535 // TBD: handle this from another thread so we can enqueue a lot of data
39037602 4536 // As written, if a client requests query all, this function will be
6d2010ae
A
4537 // called from their send of the request message. We will attempt to write
4538 // responses and succeed until the buffer fills up. Since the clients thread
4539 // is blocked on send, it won't be reading unless the client has two threads
4540 // using this socket, one for read and one for write. Two threads probably
4541 // won't work with this code anyhow since we don't have proper locking in
4542 // place yet.
0a7de745
A
4543 tailq_head_nstat_src dead_list;
4544 errno_t result = ENOENT;
4545 nstat_msg_query_src_req req;
fe8ab488 4546
0a7de745 4547 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) {
6d2010ae
A
4548 return EINVAL;
4549 }
3e170ce0
A
4550
4551 const boolean_t all_srcs = (req.srcref == NSTAT_SRC_REF_ALL);
5ba3f43e 4552 TAILQ_INIT(&dead_list);
39037602 4553
5ba3f43e 4554 lck_mtx_lock(&state->ncs_mtx);
3e170ce0 4555
0a7de745 4556 if (all_srcs) {
316670eb 4557 state->ncs_flags |= NSTAT_FLAG_REQCOUNTS;
3e170ce0 4558 }
0a7de745
A
4559 nstat_src *src, *tmpsrc;
4560 u_int64_t src_count = 0;
4561 boolean_t partial = FALSE;
3e170ce0
A
4562
4563 /*
4564 * Error handling policy and sequence number generation is folded into
4565 * nstat_control_begin_query.
4566 */
4567 partial = nstat_control_begin_query(state, &req.hdr);
fe8ab488 4568
5ba3f43e
A
4569
4570 TAILQ_FOREACH_SAFE(src, &state->ncs_src_queue, ns_control_link, tmpsrc)
3e170ce0 4571 {
0a7de745 4572 int gone = 0;
39037602 4573
39236c6e 4574 // XXX ignore IFACE types?
0a7de745 4575 if (all_srcs || src->srcref == req.srcref) {
3e170ce0 4576 if (nstat_control_reporting_allowed(state, src)
0a7de745 4577 && (!partial || !all_srcs || src->seq != state->ncs_seq)) {
3e170ce0 4578 if (all_srcs &&
0a7de745 4579 (req.hdr.flags & NSTAT_MSG_HDR_FLAG_SUPPORTS_AGGREGATE) != 0) {
3e170ce0 4580 result = nstat_control_append_counts(state, src, &gone);
0a7de745 4581 } else {
3e170ce0
A
4582 result = nstat_control_send_counts(state, src, req.hdr.context, 0, &gone);
4583 }
4584
0a7de745 4585 if (ENOMEM == result || ENOBUFS == result) {
3e170ce0
A
4586 /*
4587 * If the counts message failed to
4588 * enqueue then we should clear our flag so
4589 * that a client doesn't miss anything on
4590 * idle cleanup. We skip the "gone"
4591 * processing in the hope that we may
4592 * catch it another time.
4593 */
fe8ab488
A
4594 state->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
4595 break;
3e170ce0 4596 }
0a7de745 4597 if (partial) {
3e170ce0
A
4598 /*
4599 * We skip over hard errors and
4600 * filtered sources.
4601 */
4602 src->seq = state->ncs_seq;
4603 src_count++;
4604 }
6d2010ae 4605 }
6d2010ae 4606 }
39037602 4607
0a7de745 4608 if (gone) {
3e170ce0
A
4609 // send one last descriptor message so client may see last state
4610 // If we can't send the notification now, it
4611 // will be sent in the idle cleanup.
5ba3f43e 4612 result = nstat_control_send_description(state, src, 0, 0);
0a7de745 4613 if (result != 0) {
3e170ce0 4614 nstat_stats.nstat_control_send_description_failures++;
0a7de745 4615 if (nstat_debug != 0) {
3e170ce0 4616 printf("%s - nstat_control_send_description() %d\n", __func__, result);
0a7de745 4617 }
3e170ce0
A
4618 state->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
4619 break;
4620 }
39037602 4621
3e170ce0 4622 // pull src out of the list
5ba3f43e
A
4623 TAILQ_REMOVE(&state->ncs_src_queue, src, ns_control_link);
4624 TAILQ_INSERT_TAIL(&dead_list, src, ns_control_link);
3e170ce0 4625 }
5ba3f43e 4626
0a7de745
A
4627 if (all_srcs) {
4628 if (src_count >= QUERY_CONTINUATION_SRC_COUNT) {
5ba3f43e
A
4629 break;
4630 }
0a7de745 4631 } else if (req.srcref == src->srcref) {
3e170ce0
A
4632 break;
4633 }
6d2010ae 4634 }
5ba3f43e 4635
3e170ce0
A
4636 nstat_flush_accumulated_msgs(state);
4637
4638 u_int16_t flags = 0;
0a7de745 4639 if (req.srcref == NSTAT_SRC_REF_ALL) {
5ba3f43e 4640 flags = nstat_control_end_query(state, src, partial);
0a7de745 4641 }
3e170ce0 4642
5ba3f43e 4643 lck_mtx_unlock(&state->ncs_mtx);
3e170ce0
A
4644
4645 /*
4646 * If an error occurred enqueueing data, then allow the error to
4647 * propagate to nstat_control_send. This way, the error is sent to
4648 * user-level.
4649 */
0a7de745 4650 if (all_srcs && ENOMEM != result && ENOBUFS != result) {
3e170ce0 4651 nstat_enqueue_success(req.hdr.context, state, flags);
fe8ab488
A
4652 result = 0;
4653 }
39037602 4654
0a7de745 4655 while ((src = TAILQ_FIRST(&dead_list))) {
5ba3f43e 4656 TAILQ_REMOVE(&dead_list, src, ns_control_link);
316670eb 4657 nstat_control_cleanup_source(state, src, FALSE);
6d2010ae 4658 }
39037602 4659
6d2010ae
A
4660 return result;
4661}
4662
4663static errno_t
4664nstat_control_handle_get_src_description(
cb323159
A
4665 nstat_control_state *state,
4666 mbuf_t m)
6d2010ae 4667{
0a7de745 4668 nstat_msg_get_src_description req;
3e170ce0 4669 errno_t result = ENOENT;
39236c6e
A
4670 nstat_src *src;
4671
0a7de745 4672 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) {
6d2010ae
A
4673 return EINVAL;
4674 }
3e170ce0 4675
5ba3f43e 4676 lck_mtx_lock(&state->ncs_mtx);
3e170ce0
A
4677 u_int64_t src_count = 0;
4678 boolean_t partial = FALSE;
4679 const boolean_t all_srcs = (req.srcref == NSTAT_SRC_REF_ALL);
4680
4681 /*
4682 * Error handling policy and sequence number generation is folded into
4683 * nstat_control_begin_query.
4684 */
4685 partial = nstat_control_begin_query(state, &req.hdr);
4686
5ba3f43e 4687 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
3e170ce0 4688 {
0a7de745 4689 if (all_srcs || src->srcref == req.srcref) {
3e170ce0 4690 if (nstat_control_reporting_allowed(state, src)
0a7de745
A
4691 && (!all_srcs || !partial || src->seq != state->ncs_seq)) {
4692 if ((req.hdr.flags & NSTAT_MSG_HDR_FLAG_SUPPORTS_AGGREGATE) != 0 && all_srcs) {
3e170ce0 4693 result = nstat_control_append_description(state, src);
0a7de745 4694 } else {
3e170ce0
A
4695 result = nstat_control_send_description(state, src, req.hdr.context, 0);
4696 }
4697
0a7de745 4698 if (ENOMEM == result || ENOBUFS == result) {
3e170ce0
A
4699 /*
4700 * If the description message failed to
4701 * enqueue then we give up for now.
4702 */
4703 break;
4704 }
0a7de745 4705 if (partial) {
3e170ce0
A
4706 /*
4707 * Note, we skip over hard errors and
4708 * filtered sources.
4709 */
4710 src->seq = state->ncs_seq;
4711 src_count++;
0a7de745 4712 if (src_count >= QUERY_CONTINUATION_SRC_COUNT) {
5ba3f43e
A
4713 break;
4714 }
3e170ce0
A
4715 }
4716 }
39037602 4717
0a7de745 4718 if (!all_srcs) {
39236c6e 4719 break;
3e170ce0 4720 }
39236c6e 4721 }
3e170ce0
A
4722 }
4723 nstat_flush_accumulated_msgs(state);
4724
4725 u_int16_t flags = 0;
0a7de745 4726 if (req.srcref == NSTAT_SRC_REF_ALL) {
3e170ce0 4727 flags = nstat_control_end_query(state, src, partial);
0a7de745 4728 }
3e170ce0 4729
5ba3f43e 4730 lck_mtx_unlock(&state->ncs_mtx);
3e170ce0
A
4731 /*
4732 * If an error occurred enqueueing data, then allow the error to
4733 * propagate to nstat_control_send. This way, the error is sent to
4734 * user-level.
4735 */
0a7de745 4736 if (all_srcs && ENOMEM != result && ENOBUFS != result) {
3e170ce0 4737 nstat_enqueue_success(req.hdr.context, state, flags);
39236c6e 4738 result = 0;
6d2010ae 4739 }
39037602 4740
6d2010ae
A
4741 return result;
4742}
4743
39236c6e
A
4744static errno_t
4745nstat_control_handle_set_filter(
cb323159
A
4746 nstat_control_state *state,
4747 mbuf_t m)
39236c6e
A
4748{
4749 nstat_msg_set_filter req;
4750 nstat_src *src;
4751
0a7de745 4752 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) {
39236c6e 4753 return EINVAL;
0a7de745 4754 }
39236c6e 4755 if (req.srcref == NSTAT_SRC_REF_ALL ||
0a7de745 4756 req.srcref == NSTAT_SRC_REF_INVALID) {
39236c6e 4757 return EINVAL;
0a7de745 4758 }
39236c6e 4759
5ba3f43e
A
4760 lck_mtx_lock(&state->ncs_mtx);
4761 TAILQ_FOREACH(src, &state->ncs_src_queue, ns_control_link)
4762 {
0a7de745 4763 if (req.srcref == src->srcref) {
39236c6e
A
4764 src->filter = req.filter;
4765 break;
4766 }
5ba3f43e
A
4767 }
4768 lck_mtx_unlock(&state->ncs_mtx);
0a7de745 4769 if (src == NULL) {
39236c6e 4770 return ENOENT;
0a7de745 4771 }
39236c6e
A
4772
4773 return 0;
3e170ce0
A
4774}
4775
4776static void
4777nstat_send_error(
0a7de745
A
4778 nstat_control_state *state,
4779 u_int64_t context,
4780 u_int32_t error)
3e170ce0
A
4781{
4782 errno_t result;
0a7de745 4783 struct nstat_msg_error err;
3e170ce0
A
4784
4785 bzero(&err, sizeof(err));
4786 err.hdr.type = NSTAT_MSG_TYPE_ERROR;
4787 err.hdr.length = sizeof(err);
4788 err.hdr.context = context;
4789 err.error = error;
4790
4791 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &err,
0a7de745
A
4792 sizeof(err), CTL_DATA_EOR | CTL_DATA_CRIT);
4793 if (result != 0) {
3e170ce0 4794 nstat_stats.nstat_msgerrorfailures++;
0a7de745 4795 }
3e170ce0
A
4796}
4797
4798static boolean_t
4799nstat_control_begin_query(
0a7de745
A
4800 nstat_control_state *state,
4801 const nstat_msg_hdr *hdrp)
3e170ce0
A
4802{
4803 boolean_t partial = FALSE;
4804
0a7de745 4805 if (hdrp->flags & NSTAT_MSG_HDR_FLAG_CONTINUATION) {
3e170ce0
A
4806 /* A partial query all has been requested. */
4807 partial = TRUE;
4808
0a7de745
A
4809 if (state->ncs_context != hdrp->context) {
4810 if (state->ncs_context != 0) {
3e170ce0 4811 nstat_send_error(state, state->ncs_context, EAGAIN);
0a7de745 4812 }
3e170ce0
A
4813
4814 /* Initialize state for a partial query all. */
4815 state->ncs_context = hdrp->context;
4816 state->ncs_seq++;
4817 }
4818 }
3e170ce0
A
4819
4820 return partial;
4821}
4822
4823static u_int16_t
4824nstat_control_end_query(
0a7de745
A
4825 nstat_control_state *state,
4826 nstat_src *last_src,
4827 boolean_t partial)
3e170ce0
A
4828{
4829 u_int16_t flags = 0;
4830
0a7de745 4831 if (last_src == NULL || !partial) {
3e170ce0
A
4832 /*
4833 * We iterated through the entire srcs list or exited early
4834 * from the loop when a partial update was not requested (an
4835 * error occurred), so clear context to indicate internally
4836 * that the query is finished.
4837 */
4838 state->ncs_context = 0;
0a7de745 4839 } else {
3e170ce0
A
4840 /*
4841 * Indicate to userlevel to make another partial request as
4842 * there are still sources left to be reported.
4843 */
4844 flags |= NSTAT_MSG_HDR_FLAG_CONTINUATION;
4845 }
4846
4847 return flags;
4848}
4849
4850static errno_t
4851nstat_control_handle_get_update(
0a7de745
A
4852 nstat_control_state *state,
4853 mbuf_t m)
3e170ce0 4854{
0a7de745 4855 nstat_msg_query_src_req req;
3e170ce0 4856
0a7de745 4857 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) {
3e170ce0
A
4858 return EINVAL;
4859 }
4860
5ba3f43e 4861 lck_mtx_lock(&state->ncs_mtx);
39037602 4862
3e170ce0
A
4863 state->ncs_flags |= NSTAT_FLAG_SUPPORTS_UPDATES;
4864
0a7de745
A
4865 errno_t result = ENOENT;
4866 nstat_src *src, *tmpsrc;
5ba3f43e 4867 tailq_head_nstat_src dead_list;
3e170ce0
A
4868 u_int64_t src_count = 0;
4869 boolean_t partial = FALSE;
5ba3f43e 4870 TAILQ_INIT(&dead_list);
3e170ce0
A
4871
4872 /*
4873 * Error handling policy and sequence number generation is folded into
4874 * nstat_control_begin_query.
4875 */
4876 partial = nstat_control_begin_query(state, &req.hdr);
4877
5ba3f43e 4878 TAILQ_FOREACH_SAFE(src, &state->ncs_src_queue, ns_control_link, tmpsrc)
3e170ce0 4879 {
cb323159 4880 int gone;
39037602 4881
3e170ce0 4882 gone = 0;
0a7de745 4883 if (nstat_control_reporting_allowed(state, src)) {
3e170ce0
A
4884 /* skip this source if it has the current state
4885 * sequence number as it's already been reported in
4886 * this query-all partial sequence. */
4887 if (req.srcref == NSTAT_SRC_REF_ALL
0a7de745 4888 && (FALSE == partial || src->seq != state->ncs_seq)) {
3e170ce0 4889 result = nstat_control_append_update(state, src, &gone);
0a7de745 4890 if (ENOMEM == result || ENOBUFS == result) {
3e170ce0
A
4891 /*
4892 * If the update message failed to
4893 * enqueue then give up.
4894 */
4895 break;
4896 }
0a7de745 4897 if (partial) {
3e170ce0
A
4898 /*
4899 * We skip over hard errors and
4900 * filtered sources.
4901 */
4902 src->seq = state->ncs_seq;
4903 src_count++;
4904 }
0a7de745 4905 } else if (src->srcref == req.srcref) {
cb323159 4906 result = nstat_control_send_update(state, src, req.hdr.context, 0, 0, &gone);
3e170ce0
A
4907 }
4908 }
39037602 4909
0a7de745 4910 if (gone) {
3e170ce0 4911 // pull src out of the list
5ba3f43e
A
4912 TAILQ_REMOVE(&state->ncs_src_queue, src, ns_control_link);
4913 TAILQ_INSERT_TAIL(&dead_list, src, ns_control_link);
3e170ce0 4914 }
39037602 4915
0a7de745 4916 if (req.srcref != NSTAT_SRC_REF_ALL && req.srcref == src->srcref) {
3e170ce0
A
4917 break;
4918 }
0a7de745 4919 if (src_count >= QUERY_CONTINUATION_SRC_COUNT) {
5ba3f43e
A
4920 break;
4921 }
3e170ce0
A
4922 }
4923
4924 nstat_flush_accumulated_msgs(state);
4925
4926
4927 u_int16_t flags = 0;
0a7de745 4928 if (req.srcref == NSTAT_SRC_REF_ALL) {
5ba3f43e 4929 flags = nstat_control_end_query(state, src, partial);
0a7de745 4930 }
3e170ce0 4931
5ba3f43e 4932 lck_mtx_unlock(&state->ncs_mtx);
3e170ce0
A
4933 /*
4934 * If an error occurred enqueueing data, then allow the error to
4935 * propagate to nstat_control_send. This way, the error is sent to
4936 * user-level.
4937 */
0a7de745 4938 if (req.srcref == NSTAT_SRC_REF_ALL && ENOMEM != result && ENOBUFS != result) {
3e170ce0
A
4939 nstat_enqueue_success(req.hdr.context, state, flags);
4940 result = 0;
4941 }
4942
0a7de745 4943 while ((src = TAILQ_FIRST(&dead_list))) {
5ba3f43e 4944 TAILQ_REMOVE(&dead_list, src, ns_control_link);
3e170ce0
A
4945 // release src and send notification
4946 nstat_control_cleanup_source(state, src, FALSE);
4947 }
39037602 4948
3e170ce0
A
4949 return result;
4950}
39236c6e 4951
3e170ce0
A
4952static errno_t
4953nstat_control_handle_subscribe_sysinfo(
0a7de745 4954 nstat_control_state *state)
3e170ce0
A
4955{
4956 errno_t result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
39037602 4957
0a7de745 4958 if (result != 0) {
3e170ce0
A
4959 return result;
4960 }
39037602 4961
5ba3f43e 4962 lck_mtx_lock(&state->ncs_mtx);
3e170ce0 4963 state->ncs_flags |= NSTAT_FLAG_SYSINFO_SUBSCRIBED;
5ba3f43e 4964 lck_mtx_unlock(&state->ncs_mtx);
39037602 4965
3e170ce0 4966 return 0;
39236c6e
A
4967}
4968
6d2010ae
A
4969static errno_t
4970nstat_control_send(
0a7de745 4971 kern_ctl_ref kctl,
cb323159
A
4972 u_int32_t unit,
4973 void *uinfo,
4974 mbuf_t m,
0a7de745 4975 __unused int flags)
6d2010ae 4976{
0a7de745
A
4977 nstat_control_state *state = (nstat_control_state*)uinfo;
4978 struct nstat_msg_hdr *hdr;
4979 struct nstat_msg_hdr storage;
4980 errno_t result = 0;
39037602 4981
0a7de745 4982 if (mbuf_pkthdr_len(m) < sizeof(*hdr)) {
6d2010ae 4983 // Is this the right thing to do?
6d2010ae
A
4984 mbuf_freem(m);
4985 return EINVAL;
4986 }
39037602 4987
0a7de745 4988 if (mbuf_len(m) >= sizeof(*hdr)) {
6d2010ae 4989 hdr = mbuf_data(m);
0a7de745 4990 } else {
6d2010ae
A
4991 mbuf_copydata(m, 0, sizeof(storage), &storage);
4992 hdr = &storage;
4993 }
39037602 4994
3e170ce0
A
4995 // Legacy clients may not set the length
4996 // Those clients are likely not setting the flags either
4997 // Fix everything up so old clients continue to work
0a7de745 4998 if (hdr->length != mbuf_pkthdr_len(m)) {
3e170ce0
A
4999 hdr->flags = 0;
5000 hdr->length = mbuf_pkthdr_len(m);
0a7de745 5001 if (hdr == &storage) {
3e170ce0
A
5002 mbuf_copyback(m, 0, sizeof(*hdr), hdr, MBUF_DONTWAIT);
5003 }
5004 }
39037602 5005
0a7de745
A
5006 switch (hdr->type) {
5007 case NSTAT_MSG_TYPE_ADD_SRC:
5008 result = nstat_control_handle_add_request(state, m);
5009 break;
39037602 5010
0a7de745
A
5011 case NSTAT_MSG_TYPE_ADD_ALL_SRCS:
5012 result = nstat_control_handle_add_all(state, m);
5013 break;
39037602 5014
0a7de745
A
5015 case NSTAT_MSG_TYPE_REM_SRC:
5016 result = nstat_control_handle_remove_request(state, m);
5017 break;
39037602 5018
0a7de745
A
5019 case NSTAT_MSG_TYPE_QUERY_SRC:
5020 result = nstat_control_handle_query_request(state, m);
5021 break;
39037602 5022
0a7de745
A
5023 case NSTAT_MSG_TYPE_GET_SRC_DESC:
5024 result = nstat_control_handle_get_src_description(state, m);
5025 break;
39037602 5026
0a7de745
A
5027 case NSTAT_MSG_TYPE_SET_FILTER:
5028 result = nstat_control_handle_set_filter(state, m);
5029 break;
39037602 5030
0a7de745
A
5031 case NSTAT_MSG_TYPE_GET_UPDATE:
5032 result = nstat_control_handle_get_update(state, m);
5033 break;
39037602 5034
0a7de745
A
5035 case NSTAT_MSG_TYPE_SUBSCRIBE_SYSINFO:
5036 result = nstat_control_handle_subscribe_sysinfo(state);
5037 break;
39037602 5038
0a7de745
A
5039 default:
5040 result = EINVAL;
5041 break;
6d2010ae 5042 }
39037602 5043
0a7de745
A
5044 if (result != 0) {
5045 struct nstat_msg_error err;
39037602 5046
fe8ab488 5047 bzero(&err, sizeof(err));
6d2010ae 5048 err.hdr.type = NSTAT_MSG_TYPE_ERROR;
3e170ce0 5049 err.hdr.length = sizeof(err) + mbuf_pkthdr_len(m);
6d2010ae
A
5050 err.hdr.context = hdr->context;
5051 err.error = result;
39037602 5052
3e170ce0 5053 if (mbuf_prepend(&m, sizeof(err), MBUF_DONTWAIT) == 0 &&
0a7de745 5054 mbuf_copyback(m, 0, sizeof(err), &err, MBUF_DONTWAIT) == 0) {
3e170ce0 5055 result = ctl_enqueuembuf(kctl, unit, m, CTL_DATA_EOR | CTL_DATA_CRIT);
0a7de745 5056 if (result != 0) {
3e170ce0
A
5057 mbuf_freem(m);
5058 }
5059 m = NULL;
5060 }
39037602 5061
0a7de745 5062 if (result != 0) {
3e170ce0
A
5063 // Unable to prepend the error to the request - just send the error
5064 err.hdr.length = sizeof(err);
5065 result = ctl_enqueuedata(kctl, unit, &err, sizeof(err),
0a7de745
A
5066 CTL_DATA_EOR | CTL_DATA_CRIT);
5067 if (result != 0) {
3e170ce0 5068 nstat_stats.nstat_msgerrorfailures += 1;
0a7de745 5069 }
3e170ce0
A
5070 }
5071 nstat_stats.nstat_handle_msg_failures += 1;
6d2010ae 5072 }
39037602 5073
0a7de745
A
5074 if (m) {
5075 mbuf_freem(m);
5076 }
39037602 5077
6d2010ae
A
5078 return result;
5079}
5ba3f43e
A
5080
5081
d9a64523 5082static int
cb323159 5083tcp_progress_indicators_for_interface(unsigned int ifindex, uint64_t recentflow_maxduration, uint16_t filter_flags, struct xtcpprogress_indicators *indicators)
d9a64523
A
5084{
5085 int error = 0;
5086 struct inpcb *inp;
5087 uint64_t min_recent_start_time;
5088
5089 min_recent_start_time = mach_continuous_time() - recentflow_maxduration;
5090 bzero(indicators, sizeof(*indicators));
5091
5092 lck_rw_lock_shared(tcbinfo.ipi_lock);
5093 /*
5094 * For progress indicators we don't need to special case TCP to collect time wait connections
5095 */
5096 LIST_FOREACH(inp, tcbinfo.ipi_listhead, inp_list)
5097 {
5098 struct tcpcb *tp = intotcpcb(inp);
5099 if (tp && inp->inp_last_outifp &&
0a7de745
A
5100 inp->inp_last_outifp->if_index == ifindex &&
5101 inp->inp_state != INPCB_STATE_DEAD &&
cb323159
A
5102 ((filter_flags == 0) ||
5103 ((filter_flags & NSTAT_IFNET_IS_NON_LOCAL) && !(tp->t_flags & TF_LOCAL)) ||
5104 ((filter_flags & NSTAT_IFNET_IS_LOCAL) && (tp->t_flags & TF_LOCAL)))) {
d9a64523
A
5105 struct tcp_conn_status connstatus;
5106 indicators->xp_numflows++;
5107 tcp_get_connectivity_status(tp, &connstatus);
0a7de745 5108 if (connstatus.write_probe_failed) {
d9a64523 5109 indicators->xp_write_probe_fails++;
0a7de745
A
5110 }
5111 if (connstatus.read_probe_failed) {
d9a64523 5112 indicators->xp_read_probe_fails++;
0a7de745
A
5113 }
5114 if (connstatus.conn_probe_failed) {
d9a64523 5115 indicators->xp_conn_probe_fails++;
0a7de745
A
5116 }
5117 if (inp->inp_start_timestamp > min_recent_start_time) {
d9a64523
A
5118 uint64_t flow_count;
5119
5120 indicators->xp_recentflows++;
5121 atomic_get_64(flow_count, &inp->inp_stat->rxbytes);
5122 indicators->xp_recentflows_rxbytes += flow_count;
5123 atomic_get_64(flow_count, &inp->inp_stat->txbytes);
5124 indicators->xp_recentflows_txbytes += flow_count;
5125
5126 indicators->xp_recentflows_rxooo += tp->t_stat.rxoutoforderbytes;
5127 indicators->xp_recentflows_rxdup += tp->t_stat.rxduplicatebytes;
5128 indicators->xp_recentflows_retx += tp->t_stat.txretransmitbytes;
0a7de745 5129 if (tp->snd_max - tp->snd_una) {
d9a64523
A
5130 indicators->xp_recentflows_unacked++;
5131 }
5132 }
5133 }
5134 }
5135 lck_rw_done(tcbinfo.ipi_lock);
5136
0a7de745 5137 return error;
d9a64523
A
5138}
5139
5140
5141__private_extern__ int
5142ntstat_tcp_progress_indicators(struct sysctl_req *req)
5143{
5144 struct xtcpprogress_indicators indicators = {};
5145 int error = 0;
5146 struct tcpprogressreq requested;
5147
0a7de745 5148 if (priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0) != 0) {
d9a64523
A
5149 return EACCES;
5150 }
0a7de745 5151 if (req->newptr == USER_ADDR_NULL) {
d9a64523
A
5152 return EINVAL;
5153 }
0a7de745 5154 if (req->newlen < sizeof(req)) {
d9a64523
A
5155 return EINVAL;
5156 }
5157 error = SYSCTL_IN(req, &requested, sizeof(requested));
0a7de745 5158 if (error != 0) {
d9a64523
A
5159 return error;
5160 }
cb323159 5161 error = tcp_progress_indicators_for_interface(requested.ifindex, requested.recentflow_maxduration, (uint16_t)requested.filter_flags, &indicators);
0a7de745 5162 if (error != 0) {
d9a64523
A
5163 return error;
5164 }
5165 error = SYSCTL_OUT(req, &indicators, sizeof(indicators));
5166
0a7de745 5167 return error;
d9a64523
A
5168}
5169
5170
5171
5172