]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/ntstat.c
xnu-3248.30.4.tar.gz
[apple/xnu.git] / bsd / net / ntstat.c
CommitLineData
6d2010ae 1/*
3e170ce0 2 * Copyright (c) 2010-2015 Apple Inc. All rights reserved.
6d2010ae
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/param.h>
30#include <sys/types.h>
31#include <sys/kpi_mbuf.h>
32#include <sys/socket.h>
33#include <sys/kern_control.h>
34#include <sys/mcache.h>
35#include <sys/socketvar.h>
36#include <sys/sysctl.h>
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
A
52#include <net/route.h>
53#include <net/ntstat.h>
54
55#include <netinet/ip_var.h>
56#include <netinet/in_pcb.h>
57#include <netinet/in_var.h>
58#include <netinet/tcp.h>
59#include <netinet/tcp_var.h>
60#include <netinet/tcp_fsm.h>
fe8ab488 61#include <netinet/tcp_cc.h>
6d2010ae
A
62#include <netinet/udp.h>
63#include <netinet/udp_var.h>
64#include <netinet6/in6_pcb.h>
65#include <netinet6/in6_var.h>
66
67__private_extern__ int nstat_collect = 1;
68SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
69 &nstat_collect, 0, "Collect detailed statistics");
70
39236c6e
A
71static int nstat_privcheck = 0;
72SYSCTL_INT(_net, OID_AUTO, statistics_privcheck, CTLFLAG_RW | CTLFLAG_LOCKED,
73 &nstat_privcheck, 0, "Entitlement check");
74
fe8ab488
A
75SYSCTL_NODE(_net, OID_AUTO, stats,
76 CTLFLAG_RW|CTLFLAG_LOCKED, 0, "network statistics");
77
78static int nstat_debug = 0;
79SYSCTL_INT(_net_stats, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
80 &nstat_debug, 0, "");
81
82static int nstat_sendspace = 2048;
83SYSCTL_INT(_net_stats, OID_AUTO, sendspace, CTLFLAG_RW | CTLFLAG_LOCKED,
84 &nstat_sendspace, 0, "");
85
86static int nstat_recvspace = 8192;
87SYSCTL_INT(_net_stats, OID_AUTO, recvspace, CTLFLAG_RW | CTLFLAG_LOCKED,
88 &nstat_recvspace, 0, "");
89
3e170ce0
A
90static struct nstat_stats nstat_stats;
91SYSCTL_STRUCT(_net_stats, OID_AUTO, stats, CTLFLAG_RD | CTLFLAG_LOCKED,
92 &nstat_stats, nstat_stats, "");
fe8ab488 93
316670eb
A
94enum
95{
3e170ce0
A
96 NSTAT_FLAG_CLEANUP = (1 << 0),
97 NSTAT_FLAG_REQCOUNTS = (1 << 1),
98 NSTAT_FLAG_SUPPORTS_UPDATES = (1 << 2),
99 NSTAT_FLAG_SYSINFO_SUBSCRIBED = (1 << 3),
316670eb
A
100};
101
3e170ce0
A
102#define QUERY_CONTINUATION_SRC_COUNT 100
103
6d2010ae
A
104typedef struct nstat_control_state
105{
316670eb 106 struct nstat_control_state *ncs_next;
39236c6e 107 u_int32_t ncs_watching;
6d2010ae 108 decl_lck_mtx_data(, mtx);
39236c6e
A
109 kern_ctl_ref ncs_kctl;
110 u_int32_t ncs_unit;
111 nstat_src_ref_t ncs_next_srcref;
112 struct nstat_src *ncs_srcs;
3e170ce0 113 mbuf_t ncs_accumulated;
39236c6e 114 u_int32_t ncs_flags;
3e170ce0
A
115 u_int64_t ncs_provider_filters[NSTAT_PROVIDER_COUNT];
116 /* state maintained for partial query requests */
117 u_int64_t ncs_context;
118 u_int64_t ncs_seq;
6d2010ae
A
119} nstat_control_state;
120
316670eb
A
121typedef struct nstat_provider
122{
123 struct nstat_provider *next;
124 nstat_provider_id_t nstat_provider_id;
125 size_t nstat_descriptor_length;
126 errno_t (*nstat_lookup)(const void *data, u_int32_t length, nstat_provider_cookie_t *out_cookie);
127 int (*nstat_gone)(nstat_provider_cookie_t cookie);
128 errno_t (*nstat_counts)(nstat_provider_cookie_t cookie, struct nstat_counts *out_counts, int *out_gone);
129 errno_t (*nstat_watcher_add)(nstat_control_state *state);
130 void (*nstat_watcher_remove)(nstat_control_state *state);
131 errno_t (*nstat_copy_descriptor)(nstat_provider_cookie_t cookie, void *data, u_int32_t len);
132 void (*nstat_release)(nstat_provider_cookie_t cookie, boolean_t locked);
3e170ce0 133 bool (*nstat_reporting_allowed)(nstat_provider_cookie_t cookie, uint64_t filter);
316670eb
A
134} nstat_provider;
135
136
137typedef struct nstat_src
138{
139 struct nstat_src *next;
140 nstat_src_ref_t srcref;
141 nstat_provider *provider;
39236c6e
A
142 nstat_provider_cookie_t cookie;
143 uint32_t filter;
3e170ce0 144 uint64_t seq;
316670eb
A
145} nstat_src;
146
147static errno_t nstat_control_send_counts(nstat_control_state *,
3e170ce0
A
148 nstat_src *, unsigned long long, u_int16_t, int *);
149static int nstat_control_send_description(nstat_control_state *state, nstat_src *src, u_int64_t context, u_int16_t hdr_flags);
150static int nstat_control_send_update(nstat_control_state *state, nstat_src *src, u_int64_t context, u_int16_t hdr_flags, int *gone);
316670eb 151static errno_t nstat_control_send_removed(nstat_control_state *, nstat_src *);
3e170ce0
A
152static errno_t nstat_control_send_goodbye(nstat_control_state *state, nstat_src *src);
153static void nstat_control_cleanup_source(nstat_control_state *state, nstat_src *src, boolean_t);
154static bool nstat_control_reporting_allowed(nstat_control_state *state, nstat_src *src);
155static boolean_t nstat_control_begin_query(nstat_control_state *state, const nstat_msg_hdr *hdrp);
156static u_int16_t nstat_control_end_query(nstat_control_state *state, nstat_src *last_src, boolean_t partial);
4bd07ac2 157static void nstat_ifnet_report_ecn_stats(void);
316670eb
A
158
159static u_int32_t nstat_udp_watchers = 0;
160static u_int32_t nstat_tcp_watchers = 0;
161
6d2010ae
A
162static void nstat_control_register(void);
163
fe8ab488
A
164/*
165 * The lock order is as follows:
166 *
167 * socket_lock (inpcb)
168 * nstat_mtx
169 * state->mtx
170 */
6d2010ae
A
171static volatile OSMallocTag nstat_malloc_tag = NULL;
172static nstat_control_state *nstat_controls = NULL;
316670eb 173static uint64_t nstat_idle_time = 0;
6d2010ae
A
174static decl_lck_mtx_data(, nstat_mtx);
175
fe8ab488
A
176/* some extern definitions */
177extern void mbuf_report_peak_usage(void);
178extern void tcp_report_stats(void);
179
6d2010ae
A
180static void
181nstat_copy_sa_out(
182 const struct sockaddr *src,
183 struct sockaddr *dst,
184 int maxlen)
185{
186 if (src->sa_len > maxlen) return;
187
188 bcopy(src, dst, src->sa_len);
189 if (src->sa_family == AF_INET6 &&
190 src->sa_len >= sizeof(struct sockaddr_in6))
191 {
316670eb 192 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(void *)dst;
6d2010ae
A
193 if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
194 {
195 if (sin6->sin6_scope_id == 0)
fe8ab488
A
196 sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
197 sin6->sin6_addr.s6_addr16[1] = 0;
6d2010ae
A
198 }
199 }
200}
201
202static void
203nstat_ip_to_sockaddr(
204 const struct in_addr *ip,
205 u_int16_t port,
206 struct sockaddr_in *sin,
207 u_int32_t maxlen)
208{
209 if (maxlen < sizeof(struct sockaddr_in))
210 return;
211
212 sin->sin_family = AF_INET;
213 sin->sin_len = sizeof(*sin);
214 sin->sin_port = port;
215 sin->sin_addr = *ip;
216}
217
218static void
219nstat_ip6_to_sockaddr(
220 const struct in6_addr *ip6,
221 u_int16_t port,
222 struct sockaddr_in6 *sin6,
223 u_int32_t maxlen)
224{
225 if (maxlen < sizeof(struct sockaddr_in6))
226 return;
227
228 sin6->sin6_family = AF_INET6;
229 sin6->sin6_len = sizeof(*sin6);
230 sin6->sin6_port = port;
231 sin6->sin6_addr = *ip6;
232 if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
233 {
fe8ab488
A
234 sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
235 sin6->sin6_addr.s6_addr16[1] = 0;
6d2010ae
A
236 }
237}
238
3e170ce0
A
239static u_int16_t
240nstat_inpcb_to_flags(
241 const struct inpcb *inp)
242{
243 u_int16_t flags = 0;
244
245 if ((inp != NULL ) && (inp->inp_last_outifp != NULL))
246 {
247 struct ifnet *ifp = inp->inp_last_outifp;
248
249 u_int32_t functional_type = if_functional_type(ifp);
250
251 /* Panic if someone adds a functional type without updating ntstat. */
252 VERIFY(0 <= functional_type && functional_type <= IFRTYPE_FUNCTIONAL_LAST);
253
254 switch (functional_type)
255 {
256 case IFRTYPE_FUNCTIONAL_UNKNOWN:
257 flags |= NSTAT_IFNET_IS_UNKNOWN_TYPE;
258 break;
259 case IFRTYPE_FUNCTIONAL_LOOPBACK:
260 flags |= NSTAT_IFNET_IS_LOOPBACK;
261 break;
262 case IFRTYPE_FUNCTIONAL_WIRED:
263 flags |= NSTAT_IFNET_IS_WIRED;
264 break;
265 case IFRTYPE_FUNCTIONAL_WIFI_INFRA:
266 flags |= NSTAT_IFNET_IS_WIFI;
267 break;
268 case IFRTYPE_FUNCTIONAL_WIFI_AWDL:
269 flags |= NSTAT_IFNET_IS_WIFI;
270 flags |= NSTAT_IFNET_IS_AWDL;
271 break;
272 case IFRTYPE_FUNCTIONAL_CELLULAR:
273 flags |= NSTAT_IFNET_IS_CELLULAR;
274 break;
275 }
276
277 if (IFNET_IS_EXPENSIVE(ifp))
278 {
279 flags |= NSTAT_IFNET_IS_EXPENSIVE;
280 }
281 }
282 else
283 {
284 flags = NSTAT_IFNET_IS_UNKNOWN_TYPE;
285 }
286
287 return flags;
288}
289
6d2010ae
A
290#pragma mark -- Network Statistic Providers --
291
6d2010ae
A
292static errno_t nstat_control_source_add(u_int64_t context, nstat_control_state *state, nstat_provider *provider, nstat_provider_cookie_t cookie);
293struct nstat_provider *nstat_providers = NULL;
294
295static struct nstat_provider*
296nstat_find_provider_by_id(
297 nstat_provider_id_t id)
298{
299 struct nstat_provider *provider;
300
301 for (provider = nstat_providers; provider != NULL; provider = provider->next)
302 {
303 if (provider->nstat_provider_id == id)
304 break;
305 }
306
307 return provider;
308}
309
310static errno_t
311nstat_lookup_entry(
312 nstat_provider_id_t id,
313 const void *data,
314 u_int32_t length,
315 nstat_provider **out_provider,
316 nstat_provider_cookie_t *out_cookie)
317{
318 *out_provider = nstat_find_provider_by_id(id);
319 if (*out_provider == NULL)
320 {
6d2010ae
A
321 return ENOENT;
322 }
323
324 return (*out_provider)->nstat_lookup(data, length, out_cookie);
325}
326
327static void nstat_init_route_provider(void);
328static void nstat_init_tcp_provider(void);
329static void nstat_init_udp_provider(void);
39236c6e 330static void nstat_init_ifnet_provider(void);
6d2010ae 331
316670eb 332__private_extern__ void
6d2010ae
A
333nstat_init(void)
334{
335 if (nstat_malloc_tag != NULL) return;
336
337 OSMallocTag tag = OSMalloc_Tagalloc(NET_STAT_CONTROL_NAME, OSMT_DEFAULT);
338 if (!OSCompareAndSwapPtr(NULL, tag, &nstat_malloc_tag))
339 {
340 OSMalloc_Tagfree(tag);
341 tag = nstat_malloc_tag;
342 }
343 else
344 {
345 // we need to initialize other things, we do it here as this code path will only be hit once;
346 nstat_init_route_provider();
347 nstat_init_tcp_provider();
348 nstat_init_udp_provider();
39236c6e 349 nstat_init_ifnet_provider();
6d2010ae
A
350 nstat_control_register();
351 }
352}
353
354#pragma mark -- Aligned Buffer Allocation --
355
356struct align_header
357{
358 u_int32_t offset;
359 u_int32_t length;
360};
361
362static void*
363nstat_malloc_aligned(
364 u_int32_t length,
365 u_int8_t alignment,
366 OSMallocTag tag)
367{
368 struct align_header *hdr = NULL;
369 u_int32_t size = length + sizeof(*hdr) + alignment - 1;
370
371 u_int8_t *buffer = OSMalloc(size, tag);
372 if (buffer == NULL) return NULL;
373
374 u_int8_t *aligned = buffer + sizeof(*hdr);
375 aligned = (u_int8_t*)P2ROUNDUP(aligned, alignment);
376
316670eb 377 hdr = (struct align_header*)(void *)(aligned - sizeof(*hdr));
6d2010ae
A
378 hdr->offset = aligned - buffer;
379 hdr->length = size;
380
381 return aligned;
382}
383
384static void
385nstat_free_aligned(
386 void *buffer,
387 OSMallocTag tag)
388{
316670eb 389 struct align_header *hdr = (struct align_header*)(void *)((u_int8_t*)buffer - sizeof(*hdr));
6d2010ae
A
390 OSFree(((char*)buffer) - hdr->offset, hdr->length, tag);
391}
392
393#pragma mark -- Route Provider --
394
395static nstat_provider nstat_route_provider;
396
397static errno_t
398nstat_route_lookup(
399 const void *data,
400 u_int32_t length,
401 nstat_provider_cookie_t *out_cookie)
402{
403 // rt_lookup doesn't take const params but it doesn't modify the parameters for
404 // the lookup. So...we use a union to eliminate the warning.
405 union
406 {
407 struct sockaddr *sa;
408 const struct sockaddr *const_sa;
409 } dst, mask;
410
411 const nstat_route_add_param *param = (const nstat_route_add_param*)data;
412 *out_cookie = NULL;
413
414 if (length < sizeof(*param))
415 {
6d2010ae
A
416 return EINVAL;
417 }
418
419 if (param->dst.v4.sin_family == 0 ||
420 param->dst.v4.sin_family > AF_MAX ||
421 (param->mask.v4.sin_family != 0 && param->mask.v4.sin_family != param->dst.v4.sin_family))
422 {
6d2010ae
A
423 return EINVAL;
424 }
425
426 if (param->dst.v4.sin_len > sizeof(param->dst) ||
427 (param->mask.v4.sin_family && param->mask.v4.sin_len > sizeof(param->mask.v4.sin_len)))
428 {
316670eb 429 return EINVAL;
6d2010ae 430 }
fe8ab488
A
431 if ((param->dst.v4.sin_family == AF_INET &&
432 param->dst.v4.sin_len < sizeof(struct sockaddr_in)) ||
433 (param->dst.v6.sin6_family == AF_INET6 &&
434 param->dst.v6.sin6_len < sizeof(struct sockaddr_in6)))
435 {
436 return EINVAL;
437 }
6d2010ae 438
6d2010ae
A
439 dst.const_sa = (const struct sockaddr*)&param->dst;
440 mask.const_sa = param->mask.v4.sin_family ? (const struct sockaddr*)&param->mask : NULL;
441
442 struct radix_node_head *rnh = rt_tables[dst.sa->sa_family];
443 if (rnh == NULL) return EAFNOSUPPORT;
444
445 lck_mtx_lock(rnh_lock);
446 struct rtentry *rt = rt_lookup(TRUE, dst.sa, mask.sa, rnh, param->ifindex);
447 lck_mtx_unlock(rnh_lock);
448
449 if (rt) *out_cookie = (nstat_provider_cookie_t)rt;
450
451 return rt ? 0 : ENOENT;
452}
453
454static int
455nstat_route_gone(
456 nstat_provider_cookie_t cookie)
457{
458 struct rtentry *rt = (struct rtentry*)cookie;
459 return ((rt->rt_flags & RTF_UP) == 0) ? 1 : 0;
460}
461
462static errno_t
463nstat_route_counts(
464 nstat_provider_cookie_t cookie,
465 struct nstat_counts *out_counts,
466 int *out_gone)
467{
468 struct rtentry *rt = (struct rtentry*)cookie;
469 struct nstat_counts *rt_stats = rt->rt_stats;
470
3e170ce0 471 if (out_gone) *out_gone = 0;
6d2010ae 472
3e170ce0 473 if (out_gone && (rt->rt_flags & RTF_UP) == 0) *out_gone = 1;
6d2010ae
A
474
475 if (rt_stats)
476 {
477 atomic_get_64(out_counts->nstat_rxpackets, &rt_stats->nstat_rxpackets);
478 atomic_get_64(out_counts->nstat_rxbytes, &rt_stats->nstat_rxbytes);
479 atomic_get_64(out_counts->nstat_txpackets, &rt_stats->nstat_txpackets);
480 atomic_get_64(out_counts->nstat_txbytes, &rt_stats->nstat_txbytes);
481 out_counts->nstat_rxduplicatebytes = rt_stats->nstat_rxduplicatebytes;
482 out_counts->nstat_rxoutoforderbytes = rt_stats->nstat_rxoutoforderbytes;
483 out_counts->nstat_txretransmit = rt_stats->nstat_txretransmit;
484 out_counts->nstat_connectattempts = rt_stats->nstat_connectattempts;
485 out_counts->nstat_connectsuccesses = rt_stats->nstat_connectsuccesses;
486 out_counts->nstat_min_rtt = rt_stats->nstat_min_rtt;
487 out_counts->nstat_avg_rtt = rt_stats->nstat_avg_rtt;
488 out_counts->nstat_var_rtt = rt_stats->nstat_var_rtt;
39236c6e 489 out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
6d2010ae
A
490 }
491 else
3e170ce0 492 {
6d2010ae 493 bzero(out_counts, sizeof(*out_counts));
3e170ce0 494 }
6d2010ae
A
495
496 return 0;
497}
498
499static void
500nstat_route_release(
316670eb
A
501 nstat_provider_cookie_t cookie,
502 __unused int locked)
6d2010ae
A
503{
504 rtfree((struct rtentry*)cookie);
505}
506
507static u_int32_t nstat_route_watchers = 0;
508
509static int
510nstat_route_walktree_add(
511 struct radix_node *rn,
512 void *context)
513{
514 errno_t result = 0;
515 struct rtentry *rt = (struct rtentry *)rn;
516 nstat_control_state *state = (nstat_control_state*)context;
517
518 lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED);
519
520 /* RTF_UP can't change while rnh_lock is held */
521 if ((rt->rt_flags & RTF_UP) != 0)
522 {
523 /* Clear RTPRF_OURS if the route is still usable */
524 RT_LOCK(rt);
525 if (rt_validate(rt)) {
526 RT_ADDREF_LOCKED(rt);
527 RT_UNLOCK(rt);
528 } else {
529 RT_UNLOCK(rt);
530 rt = NULL;
531 }
532
533 /* Otherwise if RTF_CONDEMNED, treat it as if it were down */
534 if (rt == NULL)
535 return (0);
536
537 result = nstat_control_source_add(0, state, &nstat_route_provider, rt);
538 if (result != 0)
539 rtfree_locked(rt);
540 }
541
542 return result;
543}
544
545static errno_t
546nstat_route_add_watcher(
547 nstat_control_state *state)
548{
549 int i;
550 errno_t result = 0;
551 OSIncrementAtomic(&nstat_route_watchers);
552
553 lck_mtx_lock(rnh_lock);
554 for (i = 1; i < AF_MAX; i++)
555 {
556 struct radix_node_head *rnh;
557 rnh = rt_tables[i];
558 if (!rnh) continue;
559
560 result = rnh->rnh_walktree(rnh, nstat_route_walktree_add, state);
561 if (result != 0)
562 {
6d2010ae
A
563 break;
564 }
565 }
566 lck_mtx_unlock(rnh_lock);
567
568 return result;
569}
570
571__private_extern__ void
572nstat_route_new_entry(
573 struct rtentry *rt)
574{
575 if (nstat_route_watchers == 0)
576 return;
577
578 lck_mtx_lock(&nstat_mtx);
579 if ((rt->rt_flags & RTF_UP) != 0)
580 {
581 nstat_control_state *state;
316670eb 582 for (state = nstat_controls; state; state = state->ncs_next)
6d2010ae 583 {
316670eb 584 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_ROUTE)) != 0)
6d2010ae
A
585 {
586 // this client is watching routes
587 // acquire a reference for the route
588 RT_ADDREF(rt);
589
590 // add the source, if that fails, release the reference
591 if (nstat_control_source_add(0, state, &nstat_route_provider, rt) != 0)
592 RT_REMREF(rt);
593 }
594 }
595 }
596 lck_mtx_unlock(&nstat_mtx);
597}
598
599static void
600nstat_route_remove_watcher(
601 __unused nstat_control_state *state)
602{
603 OSDecrementAtomic(&nstat_route_watchers);
604}
605
606static errno_t
607nstat_route_copy_descriptor(
608 nstat_provider_cookie_t cookie,
609 void *data,
610 u_int32_t len)
611{
612 nstat_route_descriptor *desc = (nstat_route_descriptor*)data;
613 if (len < sizeof(*desc))
614 {
6d2010ae
A
615 return EINVAL;
616 }
617 bzero(desc, sizeof(*desc));
618
619 struct rtentry *rt = (struct rtentry*)cookie;
fe8ab488
A
620 desc->id = (uint64_t)VM_KERNEL_ADDRPERM(rt);
621 desc->parent_id = (uint64_t)VM_KERNEL_ADDRPERM(rt->rt_parent);
622 desc->gateway_id = (uint64_t)VM_KERNEL_ADDRPERM(rt->rt_gwroute);
6d2010ae
A
623
624
625 // key/dest
626 struct sockaddr *sa;
627 if ((sa = rt_key(rt)))
628 nstat_copy_sa_out(sa, &desc->dst.sa, sizeof(desc->dst));
629
630 // mask
631 if ((sa = rt_mask(rt)) && sa->sa_len <= sizeof(desc->mask))
632 memcpy(&desc->mask, sa, sa->sa_len);
633
634 // gateway
635 if ((sa = rt->rt_gateway))
636 nstat_copy_sa_out(sa, &desc->gateway.sa, sizeof(desc->gateway));
637
638 if (rt->rt_ifp)
639 desc->ifindex = rt->rt_ifp->if_index;
640
641 desc->flags = rt->rt_flags;
642
643 return 0;
644}
645
646static void
647nstat_init_route_provider(void)
648{
649 bzero(&nstat_route_provider, sizeof(nstat_route_provider));
650 nstat_route_provider.nstat_descriptor_length = sizeof(nstat_route_descriptor);
651 nstat_route_provider.nstat_provider_id = NSTAT_PROVIDER_ROUTE;
652 nstat_route_provider.nstat_lookup = nstat_route_lookup;
653 nstat_route_provider.nstat_gone = nstat_route_gone;
654 nstat_route_provider.nstat_counts = nstat_route_counts;
655 nstat_route_provider.nstat_release = nstat_route_release;
656 nstat_route_provider.nstat_watcher_add = nstat_route_add_watcher;
657 nstat_route_provider.nstat_watcher_remove = nstat_route_remove_watcher;
658 nstat_route_provider.nstat_copy_descriptor = nstat_route_copy_descriptor;
659 nstat_route_provider.next = nstat_providers;
660 nstat_providers = &nstat_route_provider;
661}
662
663#pragma mark -- Route Collection --
664
665static struct nstat_counts*
666nstat_route_attach(
667 struct rtentry *rte)
668{
669 struct nstat_counts *result = rte->rt_stats;
670 if (result) return result;
671
672 if (nstat_malloc_tag == NULL) nstat_init();
673
674 result = nstat_malloc_aligned(sizeof(*result), sizeof(u_int64_t), nstat_malloc_tag);
675 if (!result) return result;
676
677 bzero(result, sizeof(*result));
678
679 if (!OSCompareAndSwapPtr(NULL, result, &rte->rt_stats))
680 {
681 nstat_free_aligned(result, nstat_malloc_tag);
682 result = rte->rt_stats;
683 }
684
685 return result;
686}
687
688__private_extern__ void
689nstat_route_detach(
690 struct rtentry *rte)
691{
692 if (rte->rt_stats)
693 {
694 nstat_free_aligned(rte->rt_stats, nstat_malloc_tag);
695 rte->rt_stats = NULL;
696 }
697}
698
699__private_extern__ void
700nstat_route_connect_attempt(
701 struct rtentry *rte)
702{
703 while (rte)
704 {
705 struct nstat_counts* stats = nstat_route_attach(rte);
706 if (stats)
707 {
708 OSIncrementAtomic(&stats->nstat_connectattempts);
709 }
710
711 rte = rte->rt_parent;
712 }
713}
714
715__private_extern__ void
716nstat_route_connect_success(
717 struct rtentry *rte)
718{
719 // This route
720 while (rte)
721 {
722 struct nstat_counts* stats = nstat_route_attach(rte);
723 if (stats)
724 {
725 OSIncrementAtomic(&stats->nstat_connectsuccesses);
726 }
727
728 rte = rte->rt_parent;
729 }
730}
731
732__private_extern__ void
733nstat_route_tx(
734 struct rtentry *rte,
735 u_int32_t packets,
736 u_int32_t bytes,
737 u_int32_t flags)
738{
739 while (rte)
740 {
741 struct nstat_counts* stats = nstat_route_attach(rte);
742 if (stats)
743 {
744 if ((flags & NSTAT_TX_FLAG_RETRANSMIT) != 0)
745 {
746 OSAddAtomic(bytes, &stats->nstat_txretransmit);
747 }
748 else
749 {
750 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_txpackets);
751 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_txbytes);
752 }
753 }
754
755 rte = rte->rt_parent;
756 }
757}
758
759__private_extern__ void
760nstat_route_rx(
761 struct rtentry *rte,
762 u_int32_t packets,
763 u_int32_t bytes,
764 u_int32_t flags)
765{
766 while (rte)
767 {
768 struct nstat_counts* stats = nstat_route_attach(rte);
769 if (stats)
770 {
771 if (flags == 0)
772 {
773 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_rxpackets);
774 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_rxbytes);
775 }
776 else
777 {
778 if (flags & NSTAT_RX_FLAG_OUT_OF_ORDER)
779 OSAddAtomic(bytes, &stats->nstat_rxoutoforderbytes);
780 if (flags & NSTAT_RX_FLAG_DUPLICATE)
781 OSAddAtomic(bytes, &stats->nstat_rxduplicatebytes);
782 }
783 }
784
785 rte = rte->rt_parent;
786 }
787}
788
789__private_extern__ void
790nstat_route_rtt(
791 struct rtentry *rte,
792 u_int32_t rtt,
793 u_int32_t rtt_var)
794{
795 const int32_t factor = 8;
796
797 while (rte)
798 {
799 struct nstat_counts* stats = nstat_route_attach(rte);
800 if (stats)
801 {
802 int32_t oldrtt;
803 int32_t newrtt;
804
805 // average
806 do
807 {
808 oldrtt = stats->nstat_avg_rtt;
809 if (oldrtt == 0)
810 {
811 newrtt = rtt;
812 }
813 else
814 {
815 newrtt = oldrtt - (oldrtt - (int32_t)rtt) / factor;
816 }
817 if (oldrtt == newrtt) break;
818 } while (!OSCompareAndSwap(oldrtt, newrtt, &stats->nstat_avg_rtt));
819
820 // minimum
821 do
822 {
823 oldrtt = stats->nstat_min_rtt;
824 if (oldrtt != 0 && oldrtt < (int32_t)rtt)
825 {
826 break;
827 }
828 } while (!OSCompareAndSwap(oldrtt, rtt, &stats->nstat_min_rtt));
829
830 // variance
831 do
832 {
833 oldrtt = stats->nstat_var_rtt;
834 if (oldrtt == 0)
835 {
836 newrtt = rtt_var;
837 }
838 else
839 {
840 newrtt = oldrtt - (oldrtt - (int32_t)rtt_var) / factor;
841 }
842 if (oldrtt == newrtt) break;
843 } while (!OSCompareAndSwap(oldrtt, newrtt, &stats->nstat_var_rtt));
844 }
845
846 rte = rte->rt_parent;
847 }
848}
849
316670eb 850
6d2010ae
A
851#pragma mark -- TCP Provider --
852
39236c6e
A
853/*
854 * Due to the way the kernel deallocates a process (the process structure
855 * might be gone by the time we get the PCB detach notification),
856 * we need to cache the process name. Without this, proc_name() would
857 * return null and the process name would never be sent to userland.
fe8ab488
A
858 *
859 * For UDP sockets, we also store the cached the connection tuples along with
860 * the interface index. This is necessary because when UDP sockets are
861 * disconnected, the connection tuples are forever lost from the inpcb, thus
862 * we need to keep track of the last call to connect() in ntstat.
39236c6e 863 */
fe8ab488 864struct nstat_tucookie {
39236c6e
A
865 struct inpcb *inp;
866 char pname[MAXCOMLEN+1];
fe8ab488
A
867 bool cached;
868 union
869 {
870 struct sockaddr_in v4;
871 struct sockaddr_in6 v6;
872 } local;
873 union
874 {
875 struct sockaddr_in v4;
876 struct sockaddr_in6 v6;
877 } remote;
878 unsigned int if_index;
3e170ce0 879 uint16_t ifnet_properties;
39236c6e
A
880};
881
fe8ab488
A
882static struct nstat_tucookie *
883nstat_tucookie_alloc_internal(
39236c6e 884 struct inpcb *inp,
fe8ab488
A
885 bool ref,
886 bool locked)
39236c6e 887{
fe8ab488 888 struct nstat_tucookie *cookie;
39236c6e
A
889
890 cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag);
891 if (cookie == NULL)
892 return NULL;
fe8ab488
A
893 if (!locked)
894 lck_mtx_assert(&nstat_mtx, LCK_MTX_ASSERT_NOTOWNED);
895 if (ref && in_pcb_checkstate(inp, WNT_ACQUIRE, locked) == WNT_STOPUSING)
39236c6e
A
896 {
897 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
898 return NULL;
899 }
900 bzero(cookie, sizeof(*cookie));
901 cookie->inp = inp;
902 proc_name(inp->inp_socket->last_pid, cookie->pname,
903 sizeof(cookie->pname));
fe8ab488
A
904 /*
905 * We only increment the reference count for UDP sockets because we
906 * only cache UDP socket tuples.
907 */
908 if (SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP)
909 OSIncrementAtomic(&inp->inp_nstat_refcnt);
39236c6e
A
910
911 return cookie;
912}
913
fe8ab488
A
914static struct nstat_tucookie *
915nstat_tucookie_alloc(
916 struct inpcb *inp)
917{
918 return nstat_tucookie_alloc_internal(inp, false, false);
919}
920
921static struct nstat_tucookie *
922nstat_tucookie_alloc_ref(
923 struct inpcb *inp)
924{
925 return nstat_tucookie_alloc_internal(inp, true, false);
926}
927
928static struct nstat_tucookie *
929nstat_tucookie_alloc_ref_locked(
930 struct inpcb *inp)
931{
932 return nstat_tucookie_alloc_internal(inp, true, true);
933}
934
39236c6e 935static void
fe8ab488
A
936nstat_tucookie_release_internal(
937 struct nstat_tucookie *cookie,
39236c6e
A
938 int inplock)
939{
fe8ab488
A
940 if (SOCK_PROTO(cookie->inp->inp_socket) == IPPROTO_UDP)
941 OSDecrementAtomic(&cookie->inp->inp_nstat_refcnt);
39236c6e
A
942 in_pcb_checkstate(cookie->inp, WNT_RELEASE, inplock);
943 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
944}
945
fe8ab488
A
946static void
947nstat_tucookie_release(
948 struct nstat_tucookie *cookie)
949{
950 nstat_tucookie_release_internal(cookie, false);
951}
952
953static void
954nstat_tucookie_release_locked(
955 struct nstat_tucookie *cookie)
956{
957 nstat_tucookie_release_internal(cookie, true);
958}
959
960
6d2010ae
A
961static nstat_provider nstat_tcp_provider;
962
963static errno_t
964nstat_tcpudp_lookup(
39236c6e
A
965 struct inpcbinfo *inpinfo,
966 const void *data,
967 u_int32_t length,
6d2010ae
A
968 nstat_provider_cookie_t *out_cookie)
969{
39236c6e
A
970 struct inpcb *inp = NULL;
971
6d2010ae
A
972 // parameter validation
973 const nstat_tcp_add_param *param = (const nstat_tcp_add_param*)data;
974 if (length < sizeof(*param))
975 {
6d2010ae
A
976 return EINVAL;
977 }
978
979 // src and dst must match
980 if (param->remote.v4.sin_family != 0 &&
981 param->remote.v4.sin_family != param->local.v4.sin_family)
982 {
6d2010ae
A
983 return EINVAL;
984 }
985
6d2010ae
A
986
987 switch (param->local.v4.sin_family)
988 {
989 case AF_INET:
990 {
991 if (param->local.v4.sin_len != sizeof(param->local.v4) ||
992 (param->remote.v4.sin_family != 0 &&
993 param->remote.v4.sin_len != sizeof(param->remote.v4)))
994 {
6d2010ae
A
995 return EINVAL;
996 }
997
998 inp = in_pcblookup_hash(inpinfo, param->remote.v4.sin_addr, param->remote.v4.sin_port,
999 param->local.v4.sin_addr, param->local.v4.sin_port, 1, NULL);
1000 }
1001 break;
1002
1003#if INET6
1004 case AF_INET6:
1005 {
1006 union
1007 {
1008 const struct in6_addr *in6c;
1009 struct in6_addr *in6;
1010 } local, remote;
1011
1012 if (param->local.v6.sin6_len != sizeof(param->local.v6) ||
1013 (param->remote.v6.sin6_family != 0 &&
1014 param->remote.v6.sin6_len != sizeof(param->remote.v6)))
1015 {
6d2010ae
A
1016 return EINVAL;
1017 }
1018
1019 local.in6c = &param->local.v6.sin6_addr;
1020 remote.in6c = &param->remote.v6.sin6_addr;
1021
1022 inp = in6_pcblookup_hash(inpinfo, remote.in6, param->remote.v6.sin6_port,
1023 local.in6, param->local.v6.sin6_port, 1, NULL);
1024 }
1025 break;
1026#endif
1027
1028 default:
6d2010ae
A
1029 return EINVAL;
1030 }
1031
39236c6e
A
1032 if (inp == NULL)
1033 return ENOENT;
6d2010ae
A
1034
1035 // At this point we have a ref to the inpcb
fe8ab488 1036 *out_cookie = nstat_tucookie_alloc(inp);
39236c6e
A
1037 if (*out_cookie == NULL)
1038 in_pcb_checkstate(inp, WNT_RELEASE, 0);
1039
6d2010ae
A
1040 return 0;
1041}
1042
1043static errno_t
1044nstat_tcp_lookup(
1045 const void *data,
1046 u_int32_t length,
1047 nstat_provider_cookie_t *out_cookie)
1048{
1049 return nstat_tcpudp_lookup(&tcbinfo, data, length, out_cookie);
1050}
1051
1052static int
1053nstat_tcp_gone(
1054 nstat_provider_cookie_t cookie)
1055{
fe8ab488
A
1056 struct nstat_tucookie *tucookie =
1057 (struct nstat_tucookie *)cookie;
39236c6e
A
1058 struct inpcb *inp;
1059 struct tcpcb *tp;
1060
1061 return (!(inp = tucookie->inp) ||
fe8ab488
A
1062 !(tp = intotcpcb(inp)) ||
1063 inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
6d2010ae
A
1064}
1065
1066static errno_t
1067nstat_tcp_counts(
1068 nstat_provider_cookie_t cookie,
1069 struct nstat_counts *out_counts,
1070 int *out_gone)
1071{
fe8ab488
A
1072 struct nstat_tucookie *tucookie =
1073 (struct nstat_tucookie *)cookie;
39236c6e
A
1074 struct inpcb *inp;
1075
6d2010ae
A
1076 bzero(out_counts, sizeof(*out_counts));
1077
3e170ce0 1078 if (out_gone) *out_gone = 0;
6d2010ae
A
1079
1080 // if the pcb is in the dead state, we should stop using it
39236c6e 1081 if (nstat_tcp_gone(cookie))
6d2010ae 1082 {
3e170ce0 1083 if (out_gone) *out_gone = 1;
39236c6e
A
1084 if (!(inp = tucookie->inp) || !intotcpcb(inp))
1085 return EINVAL;
1086 }
1087 inp = tucookie->inp;
1088 struct tcpcb *tp = intotcpcb(inp);
6d2010ae 1089
316670eb
A
1090 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
1091 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
1092 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
1093 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
1094 out_counts->nstat_rxduplicatebytes = tp->t_stat.rxduplicatebytes;
1095 out_counts->nstat_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes;
1096 out_counts->nstat_txretransmit = tp->t_stat.txretransmitbytes;
1097 out_counts->nstat_connectattempts = tp->t_state >= TCPS_SYN_SENT ? 1 : 0;
1098 out_counts->nstat_connectsuccesses = tp->t_state >= TCPS_ESTABLISHED ? 1 : 0;
1099 out_counts->nstat_avg_rtt = tp->t_srtt;
1100 out_counts->nstat_min_rtt = tp->t_rttbest;
1101 out_counts->nstat_var_rtt = tp->t_rttvar;
1102 if (out_counts->nstat_avg_rtt < out_counts->nstat_min_rtt)
1103 out_counts->nstat_min_rtt = out_counts->nstat_avg_rtt;
39236c6e
A
1104 atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes);
1105 atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes);
1106 atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes);
1107 atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes);
fe8ab488
A
1108 atomic_get_64(out_counts->nstat_wired_rxbytes, &inp->inp_Wstat->rxbytes);
1109 atomic_get_64(out_counts->nstat_wired_txbytes, &inp->inp_Wstat->txbytes);
6d2010ae
A
1110
1111 return 0;
1112}
1113
1114static void
1115nstat_tcp_release(
316670eb
A
1116 nstat_provider_cookie_t cookie,
1117 int locked)
6d2010ae 1118{
fe8ab488
A
1119 struct nstat_tucookie *tucookie =
1120 (struct nstat_tucookie *)cookie;
39236c6e 1121
fe8ab488 1122 nstat_tucookie_release_internal(tucookie, locked);
6d2010ae
A
1123}
1124
6d2010ae
A
1125static errno_t
1126nstat_tcp_add_watcher(
1127 nstat_control_state *state)
1128{
1129 OSIncrementAtomic(&nstat_tcp_watchers);
1130
39236c6e 1131 lck_rw_lock_shared(tcbinfo.ipi_lock);
6d2010ae
A
1132
1133 // Add all current tcp inpcbs. Ignore those in timewait
1134 struct inpcb *inp;
fe8ab488
A
1135 struct nstat_tucookie *cookie;
1136 LIST_FOREACH(inp, tcbinfo.ipi_listhead, inp_list)
6d2010ae 1137 {
fe8ab488 1138 cookie = nstat_tucookie_alloc_ref(inp);
39236c6e 1139 if (cookie == NULL)
6d2010ae 1140 continue;
39236c6e
A
1141 if (nstat_control_source_add(0, state, &nstat_tcp_provider,
1142 cookie) != 0)
6d2010ae 1143 {
fe8ab488 1144 nstat_tucookie_release(cookie);
6d2010ae
A
1145 break;
1146 }
1147 }
1148
39236c6e 1149 lck_rw_done(tcbinfo.ipi_lock);
6d2010ae
A
1150
1151 return 0;
1152}
1153
1154static void
1155nstat_tcp_remove_watcher(
1156 __unused nstat_control_state *state)
1157{
1158 OSDecrementAtomic(&nstat_tcp_watchers);
1159}
1160
1161__private_extern__ void
1162nstat_tcp_new_pcb(
1163 struct inpcb *inp)
1164{
fe8ab488 1165 struct nstat_tucookie *cookie;
39236c6e 1166
6d2010ae
A
1167 if (nstat_tcp_watchers == 0)
1168 return;
fe8ab488
A
1169
1170 socket_lock(inp->inp_socket, 0);
6d2010ae
A
1171 lck_mtx_lock(&nstat_mtx);
1172 nstat_control_state *state;
316670eb 1173 for (state = nstat_controls; state; state = state->ncs_next)
6d2010ae 1174 {
316670eb 1175 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_TCP)) != 0)
6d2010ae
A
1176 {
1177 // this client is watching tcp
1178 // acquire a reference for it
fe8ab488 1179 cookie = nstat_tucookie_alloc_ref_locked(inp);
39236c6e
A
1180 if (cookie == NULL)
1181 continue;
6d2010ae 1182 // add the source, if that fails, release the reference
39236c6e
A
1183 if (nstat_control_source_add(0, state,
1184 &nstat_tcp_provider, cookie) != 0)
6d2010ae 1185 {
fe8ab488 1186 nstat_tucookie_release_locked(cookie);
6d2010ae
A
1187 break;
1188 }
1189 }
1190 }
1191 lck_mtx_unlock(&nstat_mtx);
fe8ab488 1192 socket_unlock(inp->inp_socket, 0);
6d2010ae
A
1193}
1194
316670eb
A
1195__private_extern__ void
1196nstat_pcb_detach(struct inpcb *inp)
1197{
1198 nstat_control_state *state;
1199 nstat_src *src, *prevsrc;
1200 nstat_src *dead_list = NULL;
fe8ab488
A
1201 struct nstat_tucookie *tucookie;
1202 errno_t result;
316670eb
A
1203
1204 if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0))
1205 return;
1206
1207 lck_mtx_lock(&nstat_mtx);
3e170ce0
A
1208 for (state = nstat_controls; state; state = state->ncs_next)
1209 {
316670eb
A
1210 lck_mtx_lock(&state->mtx);
1211 for (prevsrc = NULL, src = state->ncs_srcs; src;
39236c6e
A
1212 prevsrc = src, src = src->next)
1213 {
fe8ab488 1214 tucookie = (struct nstat_tucookie *)src->cookie;
39236c6e 1215 if (tucookie->inp == inp)
316670eb 1216 break;
39236c6e 1217 }
316670eb 1218
3e170ce0
A
1219 if (src)
1220 {
1221 result = nstat_control_send_goodbye(state, src);
1222
316670eb
A
1223 if (prevsrc)
1224 prevsrc->next = src->next;
1225 else
1226 state->ncs_srcs = src->next;
3e170ce0 1227
316670eb
A
1228 src->next = dead_list;
1229 dead_list = src;
1230 }
1231 lck_mtx_unlock(&state->mtx);
1232 }
1233 lck_mtx_unlock(&nstat_mtx);
1234
1235 while (dead_list) {
1236 src = dead_list;
1237 dead_list = src->next;
1238
1239 nstat_control_cleanup_source(NULL, src, TRUE);
1240 }
1241}
1242
fe8ab488
A
1243__private_extern__ void
1244nstat_pcb_cache(struct inpcb *inp)
1245{
1246 nstat_control_state *state;
1247 nstat_src *src;
1248 struct nstat_tucookie *tucookie;
1249
1250 if (inp == NULL || nstat_udp_watchers == 0 ||
1251 inp->inp_nstat_refcnt == 0)
1252 return;
1253 VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
1254 lck_mtx_lock(&nstat_mtx);
1255 for (state = nstat_controls; state; state = state->ncs_next) {
1256 lck_mtx_lock(&state->mtx);
1257 for (src = state->ncs_srcs; src; src = src->next)
1258 {
1259 tucookie = (struct nstat_tucookie *)src->cookie;
1260 if (tucookie->inp == inp)
1261 {
1262 if (inp->inp_vflag & INP_IPV6)
1263 {
1264 nstat_ip6_to_sockaddr(&inp->in6p_laddr,
1265 inp->inp_lport,
1266 &tucookie->local.v6,
1267 sizeof(tucookie->local));
1268 nstat_ip6_to_sockaddr(&inp->in6p_faddr,
1269 inp->inp_fport,
1270 &tucookie->remote.v6,
1271 sizeof(tucookie->remote));
1272 }
1273 else if (inp->inp_vflag & INP_IPV4)
1274 {
1275 nstat_ip_to_sockaddr(&inp->inp_laddr,
1276 inp->inp_lport,
1277 &tucookie->local.v4,
1278 sizeof(tucookie->local));
1279 nstat_ip_to_sockaddr(&inp->inp_faddr,
1280 inp->inp_fport,
1281 &tucookie->remote.v4,
1282 sizeof(tucookie->remote));
1283 }
1284 if (inp->inp_last_outifp)
1285 tucookie->if_index =
1286 inp->inp_last_outifp->if_index;
3e170ce0
A
1287
1288 tucookie->ifnet_properties = nstat_inpcb_to_flags(inp);
fe8ab488
A
1289 tucookie->cached = true;
1290 break;
1291 }
1292 }
1293 lck_mtx_unlock(&state->mtx);
1294 }
1295 lck_mtx_unlock(&nstat_mtx);
1296}
1297
1298__private_extern__ void
1299nstat_pcb_invalidate_cache(struct inpcb *inp)
1300{
1301 nstat_control_state *state;
1302 nstat_src *src;
1303 struct nstat_tucookie *tucookie;
1304
1305 if (inp == NULL || nstat_udp_watchers == 0 ||
1306 inp->inp_nstat_refcnt == 0)
1307 return;
1308 VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
1309 lck_mtx_lock(&nstat_mtx);
1310 for (state = nstat_controls; state; state = state->ncs_next) {
1311 lck_mtx_lock(&state->mtx);
1312 for (src = state->ncs_srcs; src; src = src->next)
1313 {
1314 tucookie = (struct nstat_tucookie *)src->cookie;
1315 if (tucookie->inp == inp)
1316 {
1317 tucookie->cached = false;
1318 break;
1319 }
1320 }
1321 lck_mtx_unlock(&state->mtx);
1322 }
1323 lck_mtx_unlock(&nstat_mtx);
1324}
1325
6d2010ae
A
1326static errno_t
1327nstat_tcp_copy_descriptor(
1328 nstat_provider_cookie_t cookie,
39236c6e
A
1329 void *data,
1330 u_int32_t len)
6d2010ae
A
1331{
1332 if (len < sizeof(nstat_tcp_descriptor))
1333 {
6d2010ae
A
1334 return EINVAL;
1335 }
316670eb 1336
39236c6e 1337 if (nstat_tcp_gone(cookie))
316670eb 1338 return EINVAL;
6d2010ae 1339
39236c6e 1340 nstat_tcp_descriptor *desc = (nstat_tcp_descriptor*)data;
fe8ab488
A
1341 struct nstat_tucookie *tucookie =
1342 (struct nstat_tucookie *)cookie;
39236c6e
A
1343 struct inpcb *inp = tucookie->inp;
1344 struct tcpcb *tp = intotcpcb(inp);
6d2010ae
A
1345 bzero(desc, sizeof(*desc));
1346
1347 if (inp->inp_vflag & INP_IPV6)
1348 {
1349 nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
1350 &desc->local.v6, sizeof(desc->local));
1351 nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
1352 &desc->remote.v6, sizeof(desc->remote));
1353 }
1354 else if (inp->inp_vflag & INP_IPV4)
1355 {
1356 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
1357 &desc->local.v4, sizeof(desc->local));
1358 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
1359 &desc->remote.v4, sizeof(desc->remote));
1360 }
1361
1362 desc->state = intotcpcb(inp)->t_state;
316670eb
A
1363 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
1364 inp->inp_last_outifp->if_index;
6d2010ae
A
1365
1366 // danger - not locked, values could be bogus
1367 desc->txunacked = tp->snd_max - tp->snd_una;
1368 desc->txwindow = tp->snd_wnd;
1369 desc->txcwindow = tp->snd_cwnd;
fe8ab488
A
1370
1371 if (CC_ALGO(tp)->name != NULL) {
1372 strlcpy(desc->cc_algo, CC_ALGO(tp)->name,
1373 sizeof(desc->cc_algo));
1374 }
6d2010ae
A
1375
1376 struct socket *so = inp->inp_socket;
1377 if (so)
1378 {
1379 // TBD - take the socket lock around these to make sure
1380 // they're in sync?
1381 desc->upid = so->last_upid;
1382 desc->pid = so->last_pid;
316670eb 1383 desc->traffic_class = so->so_traffic_class;
fe8ab488 1384 desc->traffic_mgt_flags = so->so_traffic_mgt_flags;
6d2010ae 1385 proc_name(desc->pid, desc->pname, sizeof(desc->pname));
3e170ce0 1386 if (desc->pname[0] == 0)
39236c6e
A
1387 {
1388 strlcpy(desc->pname, tucookie->pname,
1389 sizeof(desc->pname));
1390 }
1391 else
1392 {
1393 desc->pname[sizeof(desc->pname) - 1] = 0;
1394 strlcpy(tucookie->pname, desc->pname,
1395 sizeof(tucookie->pname));
1396 }
1397 memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
fe8ab488 1398 memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
39236c6e
A
1399 if (so->so_flags & SOF_DELEGATED) {
1400 desc->eupid = so->e_upid;
1401 desc->epid = so->e_pid;
1402 memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
1403 } else {
1404 desc->eupid = desc->upid;
1405 desc->epid = desc->pid;
1406 memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
1407 }
6d2010ae
A
1408 desc->sndbufsize = so->so_snd.sb_hiwat;
1409 desc->sndbufused = so->so_snd.sb_cc;
1410 desc->rcvbufsize = so->so_rcv.sb_hiwat;
1411 desc->rcvbufused = so->so_rcv.sb_cc;
1412 }
3e170ce0
A
1413
1414 tcp_get_connectivity_status(tp, &desc->connstatus);
1415 desc->ifnet_properties = nstat_inpcb_to_flags(inp);
6d2010ae
A
1416 return 0;
1417}
1418
3e170ce0
A
1419static bool
1420nstat_tcpudp_reporting_allowed(nstat_provider_cookie_t cookie, uint64_t filter)
1421{
1422 bool retval = true;
1423
1424 /* Only apply interface filter if at least one is allowed. */
1425 if ((filter & NSTAT_FILTER_ACCEPT_ALL) != 0)
1426 {
1427 struct nstat_tucookie *tucookie = (struct nstat_tucookie *)cookie;
1428 struct inpcb *inp = tucookie->inp;
1429
1430 uint16_t interface_properties = nstat_inpcb_to_flags(inp);
1431
1432 /* For now, just check on interface type. */
1433 retval = ((filter & interface_properties) != 0);
1434 }
1435 return retval;
1436}
1437
6d2010ae
A
1438static void
1439nstat_init_tcp_provider(void)
1440{
1441 bzero(&nstat_tcp_provider, sizeof(nstat_tcp_provider));
1442 nstat_tcp_provider.nstat_descriptor_length = sizeof(nstat_tcp_descriptor);
1443 nstat_tcp_provider.nstat_provider_id = NSTAT_PROVIDER_TCP;
1444 nstat_tcp_provider.nstat_lookup = nstat_tcp_lookup;
1445 nstat_tcp_provider.nstat_gone = nstat_tcp_gone;
1446 nstat_tcp_provider.nstat_counts = nstat_tcp_counts;
1447 nstat_tcp_provider.nstat_release = nstat_tcp_release;
1448 nstat_tcp_provider.nstat_watcher_add = nstat_tcp_add_watcher;
1449 nstat_tcp_provider.nstat_watcher_remove = nstat_tcp_remove_watcher;
1450 nstat_tcp_provider.nstat_copy_descriptor = nstat_tcp_copy_descriptor;
3e170ce0 1451 nstat_tcp_provider.nstat_reporting_allowed = nstat_tcpudp_reporting_allowed;
6d2010ae
A
1452 nstat_tcp_provider.next = nstat_providers;
1453 nstat_providers = &nstat_tcp_provider;
1454}
1455
1456#pragma mark -- UDP Provider --
1457
1458static nstat_provider nstat_udp_provider;
1459
1460static errno_t
1461nstat_udp_lookup(
1462 const void *data,
1463 u_int32_t length,
1464 nstat_provider_cookie_t *out_cookie)
1465{
1466 return nstat_tcpudp_lookup(&udbinfo, data, length, out_cookie);
1467}
1468
1469static int
1470nstat_udp_gone(
1471 nstat_provider_cookie_t cookie)
1472{
fe8ab488
A
1473 struct nstat_tucookie *tucookie =
1474 (struct nstat_tucookie *)cookie;
39236c6e
A
1475 struct inpcb *inp;
1476
1477 return (!(inp = tucookie->inp) ||
1478 inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
6d2010ae
A
1479}
1480
1481static errno_t
1482nstat_udp_counts(
1483 nstat_provider_cookie_t cookie,
39236c6e
A
1484 struct nstat_counts *out_counts,
1485 int *out_gone)
6d2010ae 1486{
fe8ab488
A
1487 struct nstat_tucookie *tucookie =
1488 (struct nstat_tucookie *)cookie;
6d2010ae 1489
3e170ce0 1490 if (out_gone) *out_gone = 0;
6d2010ae
A
1491
1492 // if the pcb is in the dead state, we should stop using it
39236c6e 1493 if (nstat_udp_gone(cookie))
6d2010ae 1494 {
3e170ce0 1495 if (out_gone) *out_gone = 1;
39236c6e
A
1496 if (!tucookie->inp)
1497 return EINVAL;
6d2010ae 1498 }
39236c6e 1499 struct inpcb *inp = tucookie->inp;
6d2010ae
A
1500
1501 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
1502 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
1503 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
1504 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
39236c6e
A
1505 atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes);
1506 atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes);
1507 atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes);
1508 atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes);
fe8ab488
A
1509 atomic_get_64(out_counts->nstat_wired_rxbytes, &inp->inp_Wstat->rxbytes);
1510 atomic_get_64(out_counts->nstat_wired_txbytes, &inp->inp_Wstat->txbytes);
6d2010ae
A
1511
1512 return 0;
1513}
1514
1515static void
1516nstat_udp_release(
316670eb
A
1517 nstat_provider_cookie_t cookie,
1518 int locked)
6d2010ae 1519{
fe8ab488
A
1520 struct nstat_tucookie *tucookie =
1521 (struct nstat_tucookie *)cookie;
39236c6e 1522
fe8ab488 1523 nstat_tucookie_release_internal(tucookie, locked);
6d2010ae
A
1524}
1525
6d2010ae
A
1526static errno_t
1527nstat_udp_add_watcher(
1528 nstat_control_state *state)
1529{
39236c6e 1530 struct inpcb *inp;
fe8ab488 1531 struct nstat_tucookie *cookie;
39236c6e 1532
6d2010ae
A
1533 OSIncrementAtomic(&nstat_udp_watchers);
1534
39236c6e
A
1535 lck_rw_lock_shared(udbinfo.ipi_lock);
1536 // Add all current UDP inpcbs.
fe8ab488 1537 LIST_FOREACH(inp, udbinfo.ipi_listhead, inp_list)
6d2010ae 1538 {
fe8ab488 1539 cookie = nstat_tucookie_alloc_ref(inp);
39236c6e 1540 if (cookie == NULL)
6d2010ae 1541 continue;
39236c6e
A
1542 if (nstat_control_source_add(0, state, &nstat_udp_provider,
1543 cookie) != 0)
6d2010ae 1544 {
fe8ab488 1545 nstat_tucookie_release(cookie);
6d2010ae
A
1546 break;
1547 }
1548 }
1549
39236c6e 1550 lck_rw_done(udbinfo.ipi_lock);
6d2010ae
A
1551
1552 return 0;
1553}
1554
1555static void
1556nstat_udp_remove_watcher(
1557 __unused nstat_control_state *state)
1558{
1559 OSDecrementAtomic(&nstat_udp_watchers);
1560}
1561
1562__private_extern__ void
1563nstat_udp_new_pcb(
1564 struct inpcb *inp)
1565{
fe8ab488 1566 struct nstat_tucookie *cookie;
39236c6e 1567
6d2010ae
A
1568 if (nstat_udp_watchers == 0)
1569 return;
1570
fe8ab488 1571 socket_lock(inp->inp_socket, 0);
6d2010ae
A
1572 lck_mtx_lock(&nstat_mtx);
1573 nstat_control_state *state;
316670eb 1574 for (state = nstat_controls; state; state = state->ncs_next)
6d2010ae 1575 {
316670eb 1576 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_UDP)) != 0)
6d2010ae
A
1577 {
1578 // this client is watching tcp
1579 // acquire a reference for it
fe8ab488 1580 cookie = nstat_tucookie_alloc_ref_locked(inp);
39236c6e
A
1581 if (cookie == NULL)
1582 continue;
6d2010ae 1583 // add the source, if that fails, release the reference
39236c6e
A
1584 if (nstat_control_source_add(0, state,
1585 &nstat_udp_provider, cookie) != 0)
6d2010ae 1586 {
fe8ab488 1587 nstat_tucookie_release_locked(cookie);
6d2010ae
A
1588 break;
1589 }
1590 }
1591 }
1592 lck_mtx_unlock(&nstat_mtx);
fe8ab488 1593 socket_unlock(inp->inp_socket, 0);
6d2010ae
A
1594}
1595
1596static errno_t
1597nstat_udp_copy_descriptor(
1598 nstat_provider_cookie_t cookie,
1599 void *data,
1600 u_int32_t len)
1601{
1602 if (len < sizeof(nstat_udp_descriptor))
1603 {
6d2010ae
A
1604 return EINVAL;
1605 }
1606
39236c6e 1607 if (nstat_udp_gone(cookie))
316670eb
A
1608 return EINVAL;
1609
fe8ab488
A
1610 struct nstat_tucookie *tucookie =
1611 (struct nstat_tucookie *)cookie;
39236c6e
A
1612 nstat_udp_descriptor *desc = (nstat_udp_descriptor*)data;
1613 struct inpcb *inp = tucookie->inp;
1614
6d2010ae
A
1615 bzero(desc, sizeof(*desc));
1616
fe8ab488
A
1617 if (tucookie->cached == false) {
1618 if (inp->inp_vflag & INP_IPV6)
1619 {
1620 nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
3e170ce0 1621 &desc->local.v6, sizeof(desc->local.v6));
fe8ab488 1622 nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
3e170ce0 1623 &desc->remote.v6, sizeof(desc->remote.v6));
fe8ab488
A
1624 }
1625 else if (inp->inp_vflag & INP_IPV4)
1626 {
1627 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
3e170ce0 1628 &desc->local.v4, sizeof(desc->local.v4));
fe8ab488 1629 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
3e170ce0 1630 &desc->remote.v4, sizeof(desc->remote.v4));
fe8ab488 1631 }
3e170ce0 1632 desc->ifnet_properties = nstat_inpcb_to_flags(inp);
6d2010ae 1633 }
fe8ab488 1634 else
6d2010ae 1635 {
fe8ab488
A
1636 if (inp->inp_vflag & INP_IPV6)
1637 {
1638 memcpy(&desc->local.v6, &tucookie->local.v6,
3e170ce0 1639 sizeof(desc->local.v6));
fe8ab488 1640 memcpy(&desc->remote.v6, &tucookie->remote.v6,
3e170ce0 1641 sizeof(desc->remote.v6));
fe8ab488
A
1642 }
1643 else if (inp->inp_vflag & INP_IPV4)
1644 {
1645 memcpy(&desc->local.v4, &tucookie->local.v4,
3e170ce0 1646 sizeof(desc->local.v4));
fe8ab488 1647 memcpy(&desc->remote.v4, &tucookie->remote.v4,
3e170ce0 1648 sizeof(desc->remote.v4));
fe8ab488 1649 }
3e170ce0 1650 desc->ifnet_properties = tucookie->ifnet_properties;
6d2010ae
A
1651 }
1652
fe8ab488
A
1653 if (inp->inp_last_outifp)
1654 desc->ifindex = inp->inp_last_outifp->if_index;
1655 else
1656 desc->ifindex = tucookie->if_index;
316670eb 1657
6d2010ae
A
1658 struct socket *so = inp->inp_socket;
1659 if (so)
1660 {
1661 // TBD - take the socket lock around these to make sure
1662 // they're in sync?
1663 desc->upid = so->last_upid;
1664 desc->pid = so->last_pid;
39236c6e 1665 proc_name(desc->pid, desc->pname, sizeof(desc->pname));
3e170ce0 1666 if (desc->pname[0] == 0)
39236c6e
A
1667 {
1668 strlcpy(desc->pname, tucookie->pname,
1669 sizeof(desc->pname));
1670 }
1671 else
1672 {
1673 desc->pname[sizeof(desc->pname) - 1] = 0;
1674 strlcpy(tucookie->pname, desc->pname,
1675 sizeof(tucookie->pname));
1676 }
1677 memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
fe8ab488 1678 memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
39236c6e
A
1679 if (so->so_flags & SOF_DELEGATED) {
1680 desc->eupid = so->e_upid;
1681 desc->epid = so->e_pid;
1682 memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
1683 } else {
1684 desc->eupid = desc->upid;
1685 desc->epid = desc->pid;
1686 memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
1687 }
6d2010ae
A
1688 desc->rcvbufsize = so->so_rcv.sb_hiwat;
1689 desc->rcvbufused = so->so_rcv.sb_cc;
316670eb 1690 desc->traffic_class = so->so_traffic_class;
6d2010ae 1691 }
3e170ce0 1692
6d2010ae
A
1693 return 0;
1694}
1695
1696static void
1697nstat_init_udp_provider(void)
1698{
1699 bzero(&nstat_udp_provider, sizeof(nstat_udp_provider));
1700 nstat_udp_provider.nstat_provider_id = NSTAT_PROVIDER_UDP;
1701 nstat_udp_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor);
1702 nstat_udp_provider.nstat_lookup = nstat_udp_lookup;
1703 nstat_udp_provider.nstat_gone = nstat_udp_gone;
1704 nstat_udp_provider.nstat_counts = nstat_udp_counts;
1705 nstat_udp_provider.nstat_watcher_add = nstat_udp_add_watcher;
1706 nstat_udp_provider.nstat_watcher_remove = nstat_udp_remove_watcher;
1707 nstat_udp_provider.nstat_copy_descriptor = nstat_udp_copy_descriptor;
1708 nstat_udp_provider.nstat_release = nstat_udp_release;
3e170ce0 1709 nstat_udp_provider.nstat_reporting_allowed = nstat_tcpudp_reporting_allowed;
6d2010ae
A
1710 nstat_udp_provider.next = nstat_providers;
1711 nstat_providers = &nstat_udp_provider;
1712}
1713
39236c6e
A
1714#pragma mark -- ifnet Provider --
1715
1716static nstat_provider nstat_ifnet_provider;
1717
1718/*
1719 * We store a pointer to the ifnet and the original threshold
1720 * requested by the client.
1721 */
1722struct nstat_ifnet_cookie
1723{
1724 struct ifnet *ifp;
1725 uint64_t threshold;
1726};
1727
1728static errno_t
1729nstat_ifnet_lookup(
1730 const void *data,
1731 u_int32_t length,
1732 nstat_provider_cookie_t *out_cookie)
1733{
3e170ce0 1734 const nstat_ifnet_add_param *param = (const nstat_ifnet_add_param *)data;
39236c6e
A
1735 struct ifnet *ifp;
1736 boolean_t changed = FALSE;
1737 nstat_control_state *state;
1738 nstat_src *src;
1739 struct nstat_ifnet_cookie *cookie;
1740
1741 if (length < sizeof(*param) || param->threshold < 1024*1024)
1742 return EINVAL;
1743 if (nstat_privcheck != 0) {
1744 errno_t result = priv_check_cred(kauth_cred_get(),
1745 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
1746 if (result != 0)
1747 return result;
1748 }
1749 cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag);
1750 if (cookie == NULL)
1751 return ENOMEM;
1752 bzero(cookie, sizeof(*cookie));
1753
1754 ifnet_head_lock_shared();
1755 TAILQ_FOREACH(ifp, &ifnet_head, if_link)
1756 {
1757 ifnet_lock_exclusive(ifp);
1758 if (ifp->if_index == param->ifindex)
1759 {
1760 cookie->ifp = ifp;
1761 cookie->threshold = param->threshold;
1762 *out_cookie = cookie;
1763 if (!ifp->if_data_threshold ||
1764 ifp->if_data_threshold > param->threshold)
1765 {
1766 changed = TRUE;
1767 ifp->if_data_threshold = param->threshold;
1768 }
1769 ifnet_lock_done(ifp);
1770 ifnet_reference(ifp);
1771 break;
1772 }
1773 ifnet_lock_done(ifp);
1774 }
1775 ifnet_head_done();
1776
1777 /*
1778 * When we change the threshold to something smaller, we notify
1779 * all of our clients with a description message.
1780 * We won't send a message to the client we are currently serving
1781 * because it has no `ifnet source' yet.
1782 */
1783 if (changed)
1784 {
1785 lck_mtx_lock(&nstat_mtx);
1786 for (state = nstat_controls; state; state = state->ncs_next)
1787 {
1788 lck_mtx_lock(&state->mtx);
1789 for (src = state->ncs_srcs; src; src = src->next)
1790 {
1791 if (src->provider != &nstat_ifnet_provider)
1792 continue;
3e170ce0 1793 nstat_control_send_description(state, src, 0, 0);
39236c6e
A
1794 }
1795 lck_mtx_unlock(&state->mtx);
1796 }
1797 lck_mtx_unlock(&nstat_mtx);
1798 }
1799 if (cookie->ifp == NULL)
1800 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
1801
1802 return ifp ? 0 : EINVAL;
1803}
1804
1805static int
1806nstat_ifnet_gone(
1807 nstat_provider_cookie_t cookie)
1808{
1809 struct ifnet *ifp;
1810 struct nstat_ifnet_cookie *ifcookie =
1811 (struct nstat_ifnet_cookie *)cookie;
1812
1813 ifnet_head_lock_shared();
1814 TAILQ_FOREACH(ifp, &ifnet_head, if_link)
1815 {
1816 if (ifp == ifcookie->ifp)
1817 break;
1818 }
1819 ifnet_head_done();
1820
1821 return ifp ? 0 : 1;
1822}
1823
1824static errno_t
1825nstat_ifnet_counts(
1826 nstat_provider_cookie_t cookie,
1827 struct nstat_counts *out_counts,
1828 int *out_gone)
1829{
1830 struct nstat_ifnet_cookie *ifcookie =
1831 (struct nstat_ifnet_cookie *)cookie;
1832 struct ifnet *ifp = ifcookie->ifp;
1833
3e170ce0 1834 if (out_gone) *out_gone = 0;
39236c6e
A
1835
1836 // if the ifnet is gone, we should stop using it
1837 if (nstat_ifnet_gone(cookie))
1838 {
3e170ce0 1839 if (out_gone) *out_gone = 1;
39236c6e
A
1840 return EINVAL;
1841 }
1842
1843 bzero(out_counts, sizeof(*out_counts));
1844 out_counts->nstat_rxpackets = ifp->if_ipackets;
1845 out_counts->nstat_rxbytes = ifp->if_ibytes;
1846 out_counts->nstat_txpackets = ifp->if_opackets;
1847 out_counts->nstat_txbytes = ifp->if_obytes;
1848 out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
39236c6e
A
1849 return 0;
1850}
1851
1852static void
1853nstat_ifnet_release(
1854 nstat_provider_cookie_t cookie,
1855 __unused int locked)
1856{
1857 struct nstat_ifnet_cookie *ifcookie;
1858 struct ifnet *ifp;
1859 nstat_control_state *state;
1860 nstat_src *src;
1861 uint64_t minthreshold = UINT64_MAX;
1862
1863 /*
1864 * Find all the clients that requested a threshold
1865 * for this ifnet and re-calculate if_data_threshold.
1866 */
1867 lck_mtx_lock(&nstat_mtx);
1868 for (state = nstat_controls; state; state = state->ncs_next)
1869 {
1870 lck_mtx_lock(&state->mtx);
1871 for (src = state->ncs_srcs; src; src = src->next)
1872 {
1873 /* Skip the provider we are about to detach. */
1874 if (src->provider != &nstat_ifnet_provider ||
1875 src->cookie == cookie)
1876 continue;
1877 ifcookie = (struct nstat_ifnet_cookie *)src->cookie;
1878 if (ifcookie->threshold < minthreshold)
1879 minthreshold = ifcookie->threshold;
1880 }
1881 lck_mtx_unlock(&state->mtx);
1882 }
1883 lck_mtx_unlock(&nstat_mtx);
1884 /*
1885 * Reset if_data_threshold or disable it.
1886 */
1887 ifcookie = (struct nstat_ifnet_cookie *)cookie;
1888 ifp = ifcookie->ifp;
1889 if (ifnet_is_attached(ifp, 1)) {
1890 ifnet_lock_exclusive(ifp);
1891 if (minthreshold == UINT64_MAX)
1892 ifp->if_data_threshold = 0;
1893 else
1894 ifp->if_data_threshold = minthreshold;
1895 ifnet_lock_done(ifp);
1896 ifnet_decr_iorefcnt(ifp);
1897 }
1898 ifnet_release(ifp);
1899 OSFree(ifcookie, sizeof(*ifcookie), nstat_malloc_tag);
1900}
1901
3e170ce0
A
1902static void
1903nstat_ifnet_copy_link_status(
1904 struct ifnet *ifp,
1905 struct nstat_ifnet_descriptor *desc)
1906{
1907 struct if_link_status *ifsr = ifp->if_link_status;
1908 nstat_ifnet_desc_link_status *link_status = &desc->link_status;
1909
1910 link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_NONE;
1911 if (ifsr == NULL)
1912 return;
1913
1914 lck_rw_lock_shared(&ifp->if_link_status_lock);
1915
1916 if (ifp->if_type == IFT_CELLULAR) {
1917
1918 nstat_ifnet_desc_cellular_status *cell_status = &link_status->u.cellular;
1919 struct if_cellular_status_v1 *if_cell_sr =
1920 &ifsr->ifsr_u.ifsr_cell.if_cell_u.if_status_v1;
1921
1922 if (ifsr->ifsr_version != IF_CELLULAR_STATUS_REPORT_VERSION_1)
1923 goto done;
1924
1925 link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR;
1926
1927 if (if_cell_sr->valid_bitmask & IF_CELL_LINK_QUALITY_METRIC_VALID) {
1928 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_LINK_QUALITY_METRIC_VALID;
1929 cell_status->link_quality_metric = if_cell_sr->link_quality_metric;
1930 }
1931 if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_BANDWIDTH_VALID) {
1932 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_BANDWIDTH_VALID;
1933 cell_status->ul_effective_bandwidth = if_cell_sr->ul_effective_bandwidth;
1934 }
1935 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_BANDWIDTH_VALID) {
1936 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_BANDWIDTH_VALID;
1937 cell_status->ul_max_bandwidth = if_cell_sr->ul_max_bandwidth;
1938 }
1939 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_LATENCY_VALID) {
1940 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_LATENCY_VALID;
1941 cell_status->ul_min_latency = if_cell_sr->ul_min_latency;
1942 }
1943 if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_LATENCY_VALID) {
1944 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_LATENCY_VALID;
1945 cell_status->ul_effective_latency = if_cell_sr->ul_effective_latency;
1946 }
1947 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_LATENCY_VALID) {
1948 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_LATENCY_VALID;
1949 cell_status->ul_max_latency = if_cell_sr->ul_max_latency;
1950 }
1951 if (if_cell_sr->valid_bitmask & IF_CELL_UL_RETXT_LEVEL_VALID) {
1952 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
1953 if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_NONE)
1954 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_NONE;
1955 else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_LOW)
1956 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_LOW;
1957 else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_MEDIUM)
1958 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_MEDIUM;
1959 else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_HIGH)
1960 cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_HIGH;
1961 else
1962 cell_status->valid_bitmask &= ~NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
1963 }
1964 if (if_cell_sr->valid_bitmask & IF_CELL_UL_BYTES_LOST_VALID) {
1965 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_BYTES_LOST_VALID;
1966 cell_status->ul_bytes_lost = if_cell_sr->ul_bytes_lost;
1967 }
1968 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_QUEUE_SIZE_VALID) {
1969 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_QUEUE_SIZE_VALID;
1970 cell_status->ul_min_queue_size = if_cell_sr->ul_min_queue_size;
1971 }
1972 if (if_cell_sr->valid_bitmask & IF_CELL_UL_AVG_QUEUE_SIZE_VALID) {
1973 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_AVG_QUEUE_SIZE_VALID;
1974 cell_status->ul_avg_queue_size = if_cell_sr->ul_avg_queue_size;
1975 }
1976 if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_QUEUE_SIZE_VALID) {
1977 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_QUEUE_SIZE_VALID;
1978 cell_status->ul_max_queue_size = if_cell_sr->ul_max_queue_size;
1979 }
1980 if (if_cell_sr->valid_bitmask & IF_CELL_DL_EFFECTIVE_BANDWIDTH_VALID) {
1981 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_EFFECTIVE_BANDWIDTH_VALID;
1982 cell_status->dl_effective_bandwidth = if_cell_sr->dl_effective_bandwidth;
1983 }
1984 if (if_cell_sr->valid_bitmask & IF_CELL_DL_MAX_BANDWIDTH_VALID) {
1985 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_MAX_BANDWIDTH_VALID;
1986 cell_status->dl_max_bandwidth = if_cell_sr->dl_max_bandwidth;
1987 }
1988 if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_INACTIVITY_TIME_VALID) {
1989 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_INACTIVITY_TIME_VALID;
1990 cell_status->config_inactivity_time = if_cell_sr->config_inactivity_time;
1991 }
1992 if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_BACKOFF_TIME_VALID) {
1993 cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_BACKOFF_TIME_VALID;
1994 cell_status->config_backoff_time = if_cell_sr->config_backoff_time;
1995 }
1996
1997 } else if (ifp->if_subfamily == IFNET_SUBFAMILY_WIFI) {
1998
1999 nstat_ifnet_desc_wifi_status *wifi_status = &link_status->u.wifi;
2000 struct if_wifi_status_v1 *if_wifi_sr =
2001 &ifsr->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
2002
2003 if (ifsr->ifsr_version != IF_WIFI_STATUS_REPORT_VERSION_1)
2004 goto done;
2005
2006 link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI;
2007
2008 if (if_wifi_sr->valid_bitmask & IF_WIFI_LINK_QUALITY_METRIC_VALID) {
2009 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_LINK_QUALITY_METRIC_VALID;
2010 wifi_status->link_quality_metric = if_wifi_sr->link_quality_metric;
2011 }
2012 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID) {
2013 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID;
2014 wifi_status->ul_effective_bandwidth = if_wifi_sr->ul_effective_bandwidth;
2015 }
2016 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_BANDWIDTH_VALID) {
2017 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_BANDWIDTH_VALID;
2018 wifi_status->ul_max_bandwidth = if_wifi_sr->ul_max_bandwidth;
2019 }
2020 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MIN_LATENCY_VALID) {
2021 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MIN_LATENCY_VALID;
2022 wifi_status->ul_min_latency = if_wifi_sr->ul_min_latency;
2023 }
2024 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_LATENCY_VALID) {
2025 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_LATENCY_VALID;
2026 wifi_status->ul_effective_latency = if_wifi_sr->ul_effective_latency;
2027 }
2028 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_LATENCY_VALID) {
2029 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_LATENCY_VALID;
2030 wifi_status->ul_max_latency = if_wifi_sr->ul_max_latency;
2031 }
2032 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_RETXT_LEVEL_VALID) {
2033 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
2034 if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_NONE)
2035 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_NONE;
2036 else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_LOW)
2037 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_LOW;
2038 else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_MEDIUM)
2039 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_MEDIUM;
2040 else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_HIGH)
2041 wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_HIGH;
2042 else
2043 wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
2044 }
2045 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_BYTES_LOST_VALID) {
2046 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_BYTES_LOST_VALID;
2047 wifi_status->ul_bytes_lost = if_wifi_sr->ul_bytes_lost;
2048 }
2049 if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_ERROR_RATE_VALID) {
2050 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_ERROR_RATE_VALID;
2051 wifi_status->ul_error_rate = if_wifi_sr->ul_error_rate;
2052 }
2053 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID) {
2054 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID;
2055 wifi_status->dl_effective_bandwidth = if_wifi_sr->dl_effective_bandwidth;
2056 }
2057 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_BANDWIDTH_VALID) {
2058 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_BANDWIDTH_VALID;
2059 wifi_status->dl_max_bandwidth = if_wifi_sr->dl_max_bandwidth;
2060 }
2061 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MIN_LATENCY_VALID) {
2062 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MIN_LATENCY_VALID;
2063 wifi_status->dl_min_latency = if_wifi_sr->dl_min_latency;
2064 }
2065 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_LATENCY_VALID) {
2066 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_LATENCY_VALID;
2067 wifi_status->dl_effective_latency = if_wifi_sr->dl_effective_latency;
2068 }
2069 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_LATENCY_VALID) {
2070 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_LATENCY_VALID;
2071 wifi_status->dl_max_latency = if_wifi_sr->dl_max_latency;
2072 }
2073 if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_ERROR_RATE_VALID) {
2074 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_ERROR_RATE_VALID;
2075 wifi_status->dl_error_rate = if_wifi_sr->dl_error_rate;
2076 }
2077 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_FREQUENCY_VALID) {
2078 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
2079 if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_2_4_GHZ)
2080 wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_2_4_GHZ;
2081 else if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_5_0_GHZ)
2082 wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_5_0_GHZ;
2083 else
2084 wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
2085 }
2086 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_MULTICAST_RATE_VALID) {
2087 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_MULTICAST_RATE_VALID;
2088 wifi_status->config_multicast_rate = if_wifi_sr->config_multicast_rate;
2089 }
2090 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_COUNT_VALID) {
2091 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_COUNT_VALID;
2092 wifi_status->scan_count = if_wifi_sr->scan_count;
2093 }
2094 if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_DURATION_VALID) {
2095 wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_DURATION_VALID;
2096 wifi_status->scan_duration = if_wifi_sr->scan_duration;
2097 }
2098 }
2099
2100done:
2101 lck_rw_done(&ifp->if_link_status_lock);
2102}
2103
4bd07ac2
A
2104static u_int64_t nstat_ifnet_last_report_time = 0;
2105extern int tcp_report_stats_interval;
2106
2107void
2108nstat_ifnet_report_ecn_stats(void)
2109{
2110 u_int64_t uptime, last_report_time;
2111 struct nstat_sysinfo_data data;
2112 struct nstat_sysinfo_ifnet_ecn_stats *st;
2113 struct ifnet *ifp;
2114
2115 uptime = net_uptime();
2116
2117 if ((int)(uptime - nstat_ifnet_last_report_time) <
2118 tcp_report_stats_interval)
2119 return;
2120
2121 last_report_time = nstat_ifnet_last_report_time;
2122 nstat_ifnet_last_report_time = uptime;
2123 data.flags = NSTAT_SYSINFO_IFNET_ECN_STATS;
2124 st = &data.u.ifnet_ecn_stats;
2125
2126 ifnet_head_lock_shared();
2127 TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
2128 if (ifp->if_ipv4_stat == NULL || ifp->if_ipv6_stat == NULL)
2129 continue;
2130
2131 if ((ifp->if_refflags & (IFRF_ATTACHED | IFRF_DETACHING)) !=
2132 IFRF_ATTACHED)
2133 continue;
2134
2135 /* Limit reporting to Wifi, Ethernet and cellular. */
2136 if (!(IFNET_IS_ETHERNET(ifp) || IFNET_IS_CELLULAR(ifp)))
2137 continue;
2138
2139 bzero(st, sizeof(*st));
2140 if (IFNET_IS_CELLULAR(ifp)) {
2141 st->ifnet_type = NSTAT_IFNET_ECN_TYPE_CELLULAR;
2142 } else if (IFNET_IS_WIFI(ifp)) {
2143 st->ifnet_type = NSTAT_IFNET_ECN_TYPE_WIFI;
2144 } else {
2145 st->ifnet_type = NSTAT_IFNET_ECN_TYPE_ETHERNET;
2146 }
2147
2148 /* skip if there was no update since last report */
2149 if (ifp->if_ipv4_stat->timestamp <= 0 ||
2150 ifp->if_ipv4_stat->timestamp < last_report_time)
2151 goto v6;
2152 st->ifnet_proto = NSTAT_IFNET_ECN_PROTO_IPV4;
2153 bcopy(ifp->if_ipv4_stat, &st->ecn_stat,
2154 sizeof(st->ecn_stat));
2155 nstat_sysinfo_send_data(&data);
2156 bzero(ifp->if_ipv4_stat, sizeof(*ifp->if_ipv4_stat));
2157
2158v6:
2159 /* skip if there was no update since last report */
2160 if (ifp->if_ipv6_stat->timestamp <= 0 ||
2161 ifp->if_ipv6_stat->timestamp < last_report_time)
2162 continue;
2163 st->ifnet_proto = NSTAT_IFNET_ECN_PROTO_IPV6;
2164 bcopy(ifp->if_ipv6_stat, &st->ecn_stat,
2165 sizeof(st->ecn_stat));
2166 nstat_sysinfo_send_data(&data);
2167
2168 /* Zero the stats in ifp */
2169 bzero(ifp->if_ipv6_stat, sizeof(*ifp->if_ipv6_stat));
2170 }
2171 ifnet_head_done();
2172
2173}
2174
39236c6e
A
2175static errno_t
2176nstat_ifnet_copy_descriptor(
2177 nstat_provider_cookie_t cookie,
2178 void *data,
2179 u_int32_t len)
2180{
2181 nstat_ifnet_descriptor *desc = (nstat_ifnet_descriptor *)data;
2182 struct nstat_ifnet_cookie *ifcookie =
2183 (struct nstat_ifnet_cookie *)cookie;
2184 struct ifnet *ifp = ifcookie->ifp;
2185
2186 if (len < sizeof(nstat_ifnet_descriptor))
2187 return EINVAL;
2188
2189 if (nstat_ifnet_gone(cookie))
2190 return EINVAL;
2191
2192 bzero(desc, sizeof(*desc));
2193 ifnet_lock_shared(ifp);
2194 strlcpy(desc->name, ifp->if_xname, sizeof(desc->name));
2195 desc->ifindex = ifp->if_index;
2196 desc->threshold = ifp->if_data_threshold;
2197 desc->type = ifp->if_type;
2198 if (ifp->if_desc.ifd_len < sizeof(desc->description))
2199 memcpy(desc->description, ifp->if_desc.ifd_desc,
2200 sizeof(desc->description));
3e170ce0 2201 nstat_ifnet_copy_link_status(ifp, desc);
39236c6e 2202 ifnet_lock_done(ifp);
39236c6e
A
2203 return 0;
2204}
2205
2206static void
2207nstat_init_ifnet_provider(void)
2208{
2209 bzero(&nstat_ifnet_provider, sizeof(nstat_ifnet_provider));
2210 nstat_ifnet_provider.nstat_provider_id = NSTAT_PROVIDER_IFNET;
2211 nstat_ifnet_provider.nstat_descriptor_length = sizeof(nstat_ifnet_descriptor);
2212 nstat_ifnet_provider.nstat_lookup = nstat_ifnet_lookup;
2213 nstat_ifnet_provider.nstat_gone = nstat_ifnet_gone;
2214 nstat_ifnet_provider.nstat_counts = nstat_ifnet_counts;
2215 nstat_ifnet_provider.nstat_watcher_add = NULL;
2216 nstat_ifnet_provider.nstat_watcher_remove = NULL;
2217 nstat_ifnet_provider.nstat_copy_descriptor = nstat_ifnet_copy_descriptor;
2218 nstat_ifnet_provider.nstat_release = nstat_ifnet_release;
2219 nstat_ifnet_provider.next = nstat_providers;
2220 nstat_providers = &nstat_ifnet_provider;
2221}
2222
2223__private_extern__ void
2224nstat_ifnet_threshold_reached(unsigned int ifindex)
2225{
2226 nstat_control_state *state;
2227 nstat_src *src;
2228 struct ifnet *ifp;
2229 struct nstat_ifnet_cookie *ifcookie;
2230
2231 lck_mtx_lock(&nstat_mtx);
2232 for (state = nstat_controls; state; state = state->ncs_next)
2233 {
2234 lck_mtx_lock(&state->mtx);
2235 for (src = state->ncs_srcs; src; src = src->next)
2236 {
2237 if (src->provider != &nstat_ifnet_provider)
2238 continue;
2239 ifcookie = (struct nstat_ifnet_cookie *)src->cookie;
2240 ifp = ifcookie->ifp;
2241 if (ifp->if_index != ifindex)
2242 continue;
3e170ce0 2243 nstat_control_send_counts(state, src, 0, 0, NULL);
39236c6e
A
2244 }
2245 lck_mtx_unlock(&state->mtx);
2246 }
2247 lck_mtx_unlock(&nstat_mtx);
2248}
2249
3e170ce0 2250#pragma mark -- Sysinfo --
fe8ab488 2251static void
3e170ce0 2252nstat_set_keyval_scalar(nstat_sysinfo_keyval *kv, int key, u_int32_t val)
fe8ab488 2253{
3e170ce0
A
2254 kv->nstat_sysinfo_key = key;
2255 kv->nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
2256 kv->u.nstat_sysinfo_scalar = val;
fe8ab488
A
2257}
2258
2259static void
2260nstat_sysinfo_send_data_internal(
2261 nstat_control_state *control,
fe8ab488
A
2262 nstat_sysinfo_data *data)
2263{
2264 nstat_msg_sysinfo_counts *syscnt = NULL;
2265 size_t allocsize = 0, countsize = 0, nkeyvals = 0;
2266 nstat_sysinfo_keyval *kv;
2267 errno_t result = 0;
3e170ce0 2268 size_t i = 0;
fe8ab488
A
2269
2270 allocsize = offsetof(nstat_msg_sysinfo_counts, counts);
2271 countsize = offsetof(nstat_sysinfo_counts, nstat_sysinfo_keyvals);
2272
2273 /* get number of key-vals for each kind of stat */
2274 switch (data->flags)
2275 {
2276 case NSTAT_SYSINFO_MBUF_STATS:
3e170ce0
A
2277 nkeyvals = sizeof(struct nstat_sysinfo_mbuf_stats) /
2278 sizeof(u_int32_t);
fe8ab488
A
2279 break;
2280 case NSTAT_SYSINFO_TCP_STATS:
3e170ce0
A
2281 nkeyvals = sizeof(struct nstat_sysinfo_tcp_stats) /
2282 sizeof(u_int32_t);
fe8ab488 2283 break;
4bd07ac2
A
2284 case NSTAT_SYSINFO_IFNET_ECN_STATS:
2285 nkeyvals = (sizeof(struct if_tcp_ecn_stat) /
2286 sizeof(u_int64_t));
2287 /* One less because we are not going to send timestamp */
2288 nkeyvals -= 1;
2289 /* Two more keys for ifnet type and proto */
2290 nkeyvals += 2;
2291 break;
fe8ab488
A
2292 default:
2293 return;
2294 }
2295 countsize += sizeof(nstat_sysinfo_keyval) * nkeyvals;
2296 allocsize += countsize;
2297
2298 syscnt = OSMalloc(allocsize, nstat_malloc_tag);
2299 if (syscnt == NULL)
2300 return;
2301 bzero(syscnt, allocsize);
2302
2303 syscnt->hdr.type = NSTAT_MSG_TYPE_SYSINFO_COUNTS;
3e170ce0 2304 syscnt->hdr.length = allocsize;
fe8ab488 2305 syscnt->counts.nstat_sysinfo_len = countsize;
3e170ce0 2306
fe8ab488
A
2307 kv = (nstat_sysinfo_keyval *) &syscnt->counts.nstat_sysinfo_keyvals;
2308 switch (data->flags)
2309 {
2310 case NSTAT_SYSINFO_MBUF_STATS:
2311 {
3e170ce0
A
2312 nstat_set_keyval_scalar(&kv[i++],
2313 NSTAT_SYSINFO_KEY_MBUF_256B_TOTAL,
2314 data->u.mb_stats.total_256b);
2315 nstat_set_keyval_scalar(&kv[i++],
2316 NSTAT_SYSINFO_KEY_MBUF_2KB_TOTAL,
2317 data->u.mb_stats.total_2kb);
2318 nstat_set_keyval_scalar(&kv[i++],
2319 NSTAT_SYSINFO_KEY_MBUF_4KB_TOTAL,
2320 data->u.mb_stats.total_4kb);
2321 nstat_set_keyval_scalar(&kv[i++],
2322 NSTAT_SYSINFO_MBUF_16KB_TOTAL,
2323 data->u.mb_stats.total_16kb);
2324 nstat_set_keyval_scalar(&kv[i++],
2325 NSTAT_SYSINFO_KEY_SOCK_MBCNT,
2326 data->u.mb_stats.sbmb_total);
2327 nstat_set_keyval_scalar(&kv[i++],
2328 NSTAT_SYSINFO_KEY_SOCK_ATMBLIMIT,
2329 data->u.mb_stats.sb_atmbuflimit);
2330 nstat_set_keyval_scalar(&kv[i++],
2331 NSTAT_SYSINFO_MBUF_DRAIN_CNT,
2332 data->u.mb_stats.draincnt);
2333 nstat_set_keyval_scalar(&kv[i++],
2334 NSTAT_SYSINFO_MBUF_MEM_RELEASED,
2335 data->u.mb_stats.memreleased);
2336 VERIFY(i == nkeyvals);
fe8ab488
A
2337 break;
2338 }
2339 case NSTAT_SYSINFO_TCP_STATS:
2340 {
3e170ce0
A
2341 nstat_set_keyval_scalar(&kv[i++],
2342 NSTAT_SYSINFO_KEY_IPV4_AVGRTT,
2343 data->u.tcp_stats.ipv4_avgrtt);
2344 nstat_set_keyval_scalar(&kv[i++],
2345 NSTAT_SYSINFO_KEY_IPV6_AVGRTT,
2346 data->u.tcp_stats.ipv6_avgrtt);
2347 nstat_set_keyval_scalar(&kv[i++],
2348 NSTAT_SYSINFO_KEY_SEND_PLR,
2349 data->u.tcp_stats.send_plr);
2350 nstat_set_keyval_scalar(&kv[i++],
2351 NSTAT_SYSINFO_KEY_RECV_PLR,
2352 data->u.tcp_stats.recv_plr);
2353 nstat_set_keyval_scalar(&kv[i++],
2354 NSTAT_SYSINFO_KEY_SEND_TLRTO,
2355 data->u.tcp_stats.send_tlrto_rate);
2356 nstat_set_keyval_scalar(&kv[i++],
2357 NSTAT_SYSINFO_KEY_SEND_REORDERRATE,
2358 data->u.tcp_stats.send_reorder_rate);
2359 nstat_set_keyval_scalar(&kv[i++],
2360 NSTAT_SYSINFO_CONNECTION_ATTEMPTS,
2361 data->u.tcp_stats.connection_attempts);
2362 nstat_set_keyval_scalar(&kv[i++],
2363 NSTAT_SYSINFO_CONNECTION_ACCEPTS,
2364 data->u.tcp_stats.connection_accepts);
2365 nstat_set_keyval_scalar(&kv[i++],
2366 NSTAT_SYSINFO_ECN_CLIENT_ENABLED,
2367 data->u.tcp_stats.ecn_client_enabled);
2368 nstat_set_keyval_scalar(&kv[i++],
2369 NSTAT_SYSINFO_ECN_SERVER_ENABLED,
2370 data->u.tcp_stats.ecn_server_enabled);
2371 nstat_set_keyval_scalar(&kv[i++],
2372 NSTAT_SYSINFO_ECN_CLIENT_SETUP,
2373 data->u.tcp_stats.ecn_client_setup);
2374 nstat_set_keyval_scalar(&kv[i++],
2375 NSTAT_SYSINFO_ECN_SERVER_SETUP,
2376 data->u.tcp_stats.ecn_server_setup);
2377 nstat_set_keyval_scalar(&kv[i++],
2378 NSTAT_SYSINFO_ECN_CLIENT_SUCCESS,
2379 data->u.tcp_stats.ecn_client_success);
2380 nstat_set_keyval_scalar(&kv[i++],
2381 NSTAT_SYSINFO_ECN_SERVER_SUCCESS,
2382 data->u.tcp_stats.ecn_server_success);
2383 nstat_set_keyval_scalar(&kv[i++],
2384 NSTAT_SYSINFO_ECN_NOT_SUPPORTED,
2385 data->u.tcp_stats.ecn_not_supported);
2386 nstat_set_keyval_scalar(&kv[i++],
2387 NSTAT_SYSINFO_ECN_LOST_SYN,
2388 data->u.tcp_stats.ecn_lost_syn);
2389 nstat_set_keyval_scalar(&kv[i++],
2390 NSTAT_SYSINFO_ECN_LOST_SYNACK,
2391 data->u.tcp_stats.ecn_lost_synack);
2392 nstat_set_keyval_scalar(&kv[i++],
2393 NSTAT_SYSINFO_ECN_RECV_CE,
2394 data->u.tcp_stats.ecn_recv_ce);
2395 nstat_set_keyval_scalar(&kv[i++],
2396 NSTAT_SYSINFO_ECN_RECV_ECE,
2397 data->u.tcp_stats.ecn_recv_ece);
2398 nstat_set_keyval_scalar(&kv[i++],
2399 NSTAT_SYSINFO_ECN_SENT_ECE,
2400 data->u.tcp_stats.ecn_sent_ece);
2401 nstat_set_keyval_scalar(&kv[i++],
2402 NSTAT_SYSINFO_ECN_CONN_RECV_CE,
2403 data->u.tcp_stats.ecn_conn_recv_ce);
2404 nstat_set_keyval_scalar(&kv[i++],
2405 NSTAT_SYSINFO_ECN_CONN_RECV_ECE,
2406 data->u.tcp_stats.ecn_conn_recv_ece);
2407 nstat_set_keyval_scalar(&kv[i++],
2408 NSTAT_SYSINFO_ECN_CONN_PLNOCE,
2409 data->u.tcp_stats.ecn_conn_plnoce);
2410 nstat_set_keyval_scalar(&kv[i++],
2411 NSTAT_SYSINFO_ECN_CONN_PL_CE,
2412 data->u.tcp_stats.ecn_conn_pl_ce);
2413 nstat_set_keyval_scalar(&kv[i++],
2414 NSTAT_SYSINFO_ECN_CONN_NOPL_CE,
2415 data->u.tcp_stats.ecn_conn_nopl_ce);
4bd07ac2
A
2416 nstat_set_keyval_scalar(&kv[i++],
2417 NSTAT_SYSINFO_ECN_FALLBACK_SYNLOSS,
2418 data->u.tcp_stats.ecn_fallback_synloss);
2419 nstat_set_keyval_scalar(&kv[i++],
2420 NSTAT_SYSINFO_ECN_FALLBACK_REORDER,
2421 data->u.tcp_stats.ecn_fallback_reorder);
2422 nstat_set_keyval_scalar(&kv[i++],
2423 NSTAT_SYSINFO_ECN_FALLBACK_CE,
2424 data->u.tcp_stats.ecn_fallback_ce);
3e170ce0
A
2425 nstat_set_keyval_scalar(&kv[i++],
2426 NSTAT_SYSINFO_TFO_SYN_DATA_RCV,
2427 data->u.tcp_stats.tfo_syn_data_rcv);
2428 nstat_set_keyval_scalar(&kv[i++],
2429 NSTAT_SYSINFO_TFO_COOKIE_REQ_RCV,
2430 data->u.tcp_stats.tfo_cookie_req_rcv);
2431 nstat_set_keyval_scalar(&kv[i++],
2432 NSTAT_SYSINFO_TFO_COOKIE_SENT,
2433 data->u.tcp_stats.tfo_cookie_sent);
2434 nstat_set_keyval_scalar(&kv[i++],
2435 NSTAT_SYSINFO_TFO_COOKIE_INVALID,
2436 data->u.tcp_stats.tfo_cookie_invalid);
2437 nstat_set_keyval_scalar(&kv[i++],
2438 NSTAT_SYSINFO_TFO_COOKIE_REQ,
2439 data->u.tcp_stats.tfo_cookie_req);
2440 nstat_set_keyval_scalar(&kv[i++],
2441 NSTAT_SYSINFO_TFO_COOKIE_RCV,
2442 data->u.tcp_stats.tfo_cookie_rcv);
2443 nstat_set_keyval_scalar(&kv[i++],
2444 NSTAT_SYSINFO_TFO_SYN_DATA_SENT,
2445 data->u.tcp_stats.tfo_syn_data_sent);
2446 nstat_set_keyval_scalar(&kv[i++],
2447 NSTAT_SYSINFO_TFO_SYN_DATA_ACKED,
2448 data->u.tcp_stats.tfo_syn_data_acked);
2449 nstat_set_keyval_scalar(&kv[i++],
2450 NSTAT_SYSINFO_TFO_SYN_LOSS,
2451 data->u.tcp_stats.tfo_syn_loss);
2452 nstat_set_keyval_scalar(&kv[i++],
2453 NSTAT_SYSINFO_TFO_BLACKHOLE,
2454 data->u.tcp_stats.tfo_blackhole);
2455
2456 VERIFY(i == nkeyvals);
fe8ab488
A
2457 break;
2458 }
4bd07ac2
A
2459 case NSTAT_SYSINFO_IFNET_ECN_STATS:
2460 {
2461 nstat_set_keyval_scalar(&kv[i++],
2462 NSTAT_SYSINFO_ECN_IFNET_TYPE,
2463 data->u.ifnet_ecn_stats.ifnet_type);
2464 nstat_set_keyval_scalar(&kv[i++],
2465 NSTAT_SYSINFO_ECN_IFNET_PROTO,
2466 data->u.ifnet_ecn_stats.ifnet_proto);
2467 nstat_set_keyval_scalar(&kv[i++],
2468 NSTAT_SYSINFO_ECN_IFNET_CLIENT_SETUP,
2469 data->u.ifnet_ecn_stats.ecn_stat.ecn_client_setup);
2470 nstat_set_keyval_scalar(&kv[i++],
2471 NSTAT_SYSINFO_ECN_IFNET_SERVER_SETUP,
2472 data->u.ifnet_ecn_stats.ecn_stat.ecn_server_setup);
2473 nstat_set_keyval_scalar(&kv[i++],
2474 NSTAT_SYSINFO_ECN_IFNET_CLIENT_SUCCESS,
2475 data->u.ifnet_ecn_stats.ecn_stat.ecn_client_success);
2476 nstat_set_keyval_scalar(&kv[i++],
2477 NSTAT_SYSINFO_ECN_IFNET_SERVER_SUCCESS,
2478 data->u.ifnet_ecn_stats.ecn_stat.ecn_server_success);
2479 nstat_set_keyval_scalar(&kv[i++],
2480 NSTAT_SYSINFO_ECN_IFNET_PEER_NOSUPPORT,
2481 data->u.ifnet_ecn_stats.ecn_stat.ecn_peer_nosupport);
2482 nstat_set_keyval_scalar(&kv[i++],
2483 NSTAT_SYSINFO_ECN_IFNET_SYN_LOST,
2484 data->u.ifnet_ecn_stats.ecn_stat.ecn_syn_lost);
2485 nstat_set_keyval_scalar(&kv[i++],
2486 NSTAT_SYSINFO_ECN_IFNET_SYNACK_LOST,
2487 data->u.ifnet_ecn_stats.ecn_stat.ecn_synack_lost);
2488 nstat_set_keyval_scalar(&kv[i++],
2489 NSTAT_SYSINFO_ECN_IFNET_RECV_CE,
2490 data->u.ifnet_ecn_stats.ecn_stat.ecn_recv_ce);
2491 nstat_set_keyval_scalar(&kv[i++],
2492 NSTAT_SYSINFO_ECN_IFNET_RECV_ECE,
2493 data->u.ifnet_ecn_stats.ecn_stat.ecn_recv_ece);
2494 nstat_set_keyval_scalar(&kv[i++],
2495 NSTAT_SYSINFO_ECN_IFNET_CONN_RECV_CE,
2496 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_recv_ce);
2497 nstat_set_keyval_scalar(&kv[i++],
2498 NSTAT_SYSINFO_ECN_IFNET_CONN_RECV_ECE,
2499 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_recv_ece);
2500 nstat_set_keyval_scalar(&kv[i++],
2501 NSTAT_SYSINFO_ECN_IFNET_CONN_PLNOCE,
2502 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_plnoce);
2503 nstat_set_keyval_scalar(&kv[i++],
2504 NSTAT_SYSINFO_ECN_IFNET_CONN_PLCE,
2505 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_plce);
2506 nstat_set_keyval_scalar(&kv[i++],
2507 NSTAT_SYSINFO_ECN_IFNET_CONN_NOPLCE,
2508 data->u.ifnet_ecn_stats.ecn_stat.ecn_conn_noplce);
2509 nstat_set_keyval_scalar(&kv[i++],
2510 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_SYNLOSS,
2511 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_synloss);
2512 nstat_set_keyval_scalar(&kv[i++],
2513 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_REORDER,
2514 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_reorder);
2515 nstat_set_keyval_scalar(&kv[i++],
2516 NSTAT_SYSINFO_ECN_IFNET_FALLBACK_CE,
2517 data->u.ifnet_ecn_stats.ecn_stat.ecn_fallback_ce);
2518 nstat_set_keyval_scalar(&kv[i++],
2519 NSTAT_SYSINFO_ECN_IFNET_ON_RTT_AVG,
2520 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rtt_avg);
2521 nstat_set_keyval_scalar(&kv[i++],
2522 NSTAT_SYSINFO_ECN_IFNET_ON_RTT_VAR,
2523 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rtt_var);
2524 nstat_set_keyval_scalar(&kv[i++],
2525 NSTAT_SYSINFO_ECN_IFNET_ON_OOPERCENT,
2526 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.oo_percent);
2527 nstat_set_keyval_scalar(&kv[i++],
2528 NSTAT_SYSINFO_ECN_IFNET_ON_SACK_EPISODE,
2529 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.sack_episodes);
2530 nstat_set_keyval_scalar(&kv[i++],
2531 NSTAT_SYSINFO_ECN_IFNET_ON_REORDER_PERCENT,
2532 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.reorder_percent);
2533 nstat_set_keyval_scalar(&kv[i++],
2534 NSTAT_SYSINFO_ECN_IFNET_ON_RXMIT_PERCENT,
2535 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rxmit_percent);
2536 nstat_set_keyval_scalar(&kv[i++],
2537 NSTAT_SYSINFO_ECN_IFNET_ON_RXMIT_DROP,
2538 data->u.ifnet_ecn_stats.ecn_stat.ecn_on.rxmit_drop);
2539 nstat_set_keyval_scalar(&kv[i++],
2540 NSTAT_SYSINFO_ECN_IFNET_OFF_RTT_AVG,
2541 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rtt_avg);
2542 nstat_set_keyval_scalar(&kv[i++],
2543 NSTAT_SYSINFO_ECN_IFNET_OFF_RTT_VAR,
2544 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rtt_var);
2545 nstat_set_keyval_scalar(&kv[i++],
2546 NSTAT_SYSINFO_ECN_IFNET_OFF_OOPERCENT,
2547 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.oo_percent);
2548 nstat_set_keyval_scalar(&kv[i++],
2549 NSTAT_SYSINFO_ECN_IFNET_OFF_SACK_EPISODE,
2550 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.sack_episodes);
2551 nstat_set_keyval_scalar(&kv[i++],
2552 NSTAT_SYSINFO_ECN_IFNET_OFF_REORDER_PERCENT,
2553 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.reorder_percent);
2554 nstat_set_keyval_scalar(&kv[i++],
2555 NSTAT_SYSINFO_ECN_IFNET_OFF_RXMIT_PERCENT,
2556 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rxmit_percent);
2557 nstat_set_keyval_scalar(&kv[i++],
2558 NSTAT_SYSINFO_ECN_IFNET_OFF_RXMIT_DROP,
2559 data->u.ifnet_ecn_stats.ecn_stat.ecn_off.rxmit_drop);
2560 VERIFY(i == nkeyvals);
2561 break;
2562 }
fe8ab488
A
2563 }
2564
2565 if (syscnt != NULL)
2566 {
2567 result = ctl_enqueuedata(control->ncs_kctl,
2568 control->ncs_unit, syscnt, allocsize, CTL_DATA_EOR);
2569 if (result != 0)
3e170ce0
A
2570 {
2571 nstat_stats.nstat_sysinfofailures += 1;
2572 }
fe8ab488
A
2573 OSFree(syscnt, allocsize, nstat_malloc_tag);
2574 }
2575 return;
2576}
2577
2578__private_extern__ void
2579nstat_sysinfo_send_data(
2580 nstat_sysinfo_data *data)
2581{
2582 nstat_control_state *control;
2583
2584 lck_mtx_lock(&nstat_mtx);
2585 for (control = nstat_controls; control; control = control->ncs_next)
2586 {
2587 lck_mtx_lock(&control->mtx);
3e170ce0 2588 if ((control->ncs_flags & NSTAT_FLAG_SYSINFO_SUBSCRIBED) != 0)
fe8ab488 2589 {
3e170ce0
A
2590 nstat_sysinfo_send_data_internal(control, data);
2591 }
fe8ab488
A
2592 lck_mtx_unlock(&control->mtx);
2593 }
2594 lck_mtx_unlock(&nstat_mtx);
fe8ab488
A
2595}
2596
2597static void
2598nstat_sysinfo_generate_report(void)
2599{
2600 mbuf_report_peak_usage();
2601 tcp_report_stats();
4bd07ac2 2602 nstat_ifnet_report_ecn_stats();
fe8ab488
A
2603}
2604
6d2010ae
A
2605#pragma mark -- Kernel Control Socket --
2606
6d2010ae
A
2607static kern_ctl_ref nstat_ctlref = NULL;
2608static lck_grp_t *nstat_lck_grp = NULL;
2609
2610static errno_t nstat_control_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo);
2611static errno_t nstat_control_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo);
2612static errno_t nstat_control_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags);
6d2010ae 2613
3e170ce0
A
2614static errno_t
2615nstat_enqueue_success(
2616 uint64_t context,
2617 nstat_control_state *state,
2618 u_int16_t flags)
2619{
2620 nstat_msg_hdr success;
2621 errno_t result;
6d2010ae 2622
3e170ce0
A
2623 bzero(&success, sizeof(success));
2624 success.context = context;
2625 success.type = NSTAT_MSG_TYPE_SUCCESS;
2626 success.length = sizeof(success);
2627 success.flags = flags;
2628 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success,
2629 sizeof(success), CTL_DATA_EOR | CTL_DATA_CRIT);
2630 if (result != 0) {
2631 if (nstat_debug != 0)
2632 printf("%s: could not enqueue success message %d\n",
2633 __func__, result);
2634 nstat_stats.nstat_successmsgfailures += 1;
2635 }
2636 return result;
2637}
2638
2639static errno_t
2640nstat_control_send_goodbye(
2641 nstat_control_state *state,
2642 nstat_src *src)
6d2010ae 2643{
3e170ce0
A
2644 errno_t result = 0;
2645 int failed = 0;
2646
2647 if (nstat_control_reporting_allowed(state, src))
6d2010ae 2648 {
3e170ce0 2649 if ((state->ncs_flags & NSTAT_FLAG_SUPPORTS_UPDATES) != 0)
6d2010ae 2650 {
3e170ce0
A
2651 result = nstat_control_send_update(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
2652 if (result != 0)
6d2010ae 2653 {
3e170ce0
A
2654 failed = 1;
2655 if (nstat_debug != 0)
2656 printf("%s - nstat_control_send_update() %d\n", __func__, result);
6d2010ae
A
2657 }
2658 }
3e170ce0
A
2659 else
2660 {
2661 // send one last counts notification
2662 result = nstat_control_send_counts(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
2663 if (result != 0)
2664 {
2665 failed = 1;
2666 if (nstat_debug != 0)
2667 printf("%s - nstat_control_send_counts() %d\n", __func__, result);
2668 }
2669
2670 // send a last description
2671 result = nstat_control_send_description(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING);
2672 if (result != 0)
2673 {
2674 failed = 1;
2675 if (nstat_debug != 0)
2676 printf("%s - nstat_control_send_description() %d\n", __func__, result);
2677 }
2678 }
2679 }
2680
2681 // send the source removed notification
2682 result = nstat_control_send_removed(state, src);
2683 if (result != 0 && nstat_debug)
2684 {
2685 failed = 1;
2686 if (nstat_debug != 0)
2687 printf("%s - nstat_control_send_removed() %d\n", __func__, result);
2688 }
2689
2690 if (failed != 0)
2691 nstat_stats.nstat_control_send_goodbye_failures++;
2692
2693
2694 return result;
2695}
2696
2697static errno_t
2698nstat_flush_accumulated_msgs(
2699 nstat_control_state *state)
2700{
2701 errno_t result = 0;
2702 if (state->ncs_accumulated && mbuf_len(state->ncs_accumulated))
2703 {
2704 mbuf_pkthdr_setlen(state->ncs_accumulated, mbuf_len(state->ncs_accumulated));
2705 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, state->ncs_accumulated, CTL_DATA_EOR);
2706 if (result != 0 && nstat_debug)
2707 {
2708 nstat_stats.nstat_flush_accumulated_msgs_failures++;
2709 if (nstat_debug != 0)
2710 printf("%s - ctl_enqueuembuf failed: %d\n", __func__, result);
2711 mbuf_freem(state->ncs_accumulated);
2712 }
2713 state->ncs_accumulated = NULL;
2714 }
2715 return result;
2716}
2717
2718static errno_t
2719nstat_accumulate_msg(
2720 nstat_control_state *state,
2721 nstat_msg_hdr *hdr,
2722 size_t length)
2723{
2724 if (state->ncs_accumulated && mbuf_trailingspace(state->ncs_accumulated) < length)
2725 {
2726 // Will send the current mbuf
2727 nstat_flush_accumulated_msgs(state);
2728 }
2729
2730 errno_t result = 0;
2731
2732 if (state->ncs_accumulated == NULL)
2733 {
2734 unsigned int one = 1;
2735 if (mbuf_allocpacket(MBUF_DONTWAIT, NSTAT_MAX_MSG_SIZE, &one, &state->ncs_accumulated) != 0)
2736 {
2737 if (nstat_debug != 0)
2738 printf("%s - mbuf_allocpacket failed\n", __func__);
2739 result = ENOMEM;
2740 }
2741 else
2742 {
2743 mbuf_setlen(state->ncs_accumulated, 0);
2744 }
2745 }
2746
2747 if (result == 0)
2748 {
2749 hdr->length = length;
2750 result = mbuf_copyback(state->ncs_accumulated, mbuf_len(state->ncs_accumulated),
2751 length, hdr, MBUF_DONTWAIT);
2752 }
2753
2754 if (result != 0)
2755 {
2756 nstat_flush_accumulated_msgs(state);
2757 if (nstat_debug != 0)
2758 printf("%s - resorting to ctl_enqueuedata\n", __func__);
2759 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, hdr, length, CTL_DATA_EOR);
2760 }
2761
2762 if (result != 0)
2763 nstat_stats.nstat_accumulate_msg_failures++;
2764
2765 return result;
2766}
2767
2768static void*
2769nstat_idle_check(
2770 __unused thread_call_param_t p0,
2771 __unused thread_call_param_t p1)
2772{
2773 lck_mtx_lock(&nstat_mtx);
2774
2775 nstat_idle_time = 0;
2776
2777 nstat_control_state *control;
2778 nstat_src *dead = NULL;
2779 nstat_src *dead_list = NULL;
2780 for (control = nstat_controls; control; control = control->ncs_next)
2781 {
2782 lck_mtx_lock(&control->mtx);
2783 nstat_src **srcpp = &control->ncs_srcs;
2784
2785 if (!(control->ncs_flags & NSTAT_FLAG_REQCOUNTS))
2786 {
2787 while(*srcpp != NULL)
2788 {
2789 if ((*srcpp)->provider->nstat_gone((*srcpp)->cookie))
2790 {
2791 errno_t result;
2792
2793 // Pull it off the list
2794 dead = *srcpp;
2795 *srcpp = (*srcpp)->next;
2796
2797 result = nstat_control_send_goodbye(control, dead);
2798
2799 // Put this on the list to release later
2800 dead->next = dead_list;
2801 dead_list = dead;
2802 }
2803 else
2804 {
2805 srcpp = &(*srcpp)->next;
2806 }
2807 }
2808 }
2809 control->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
2810 lck_mtx_unlock(&control->mtx);
2811 }
2812
2813 if (nstat_controls)
2814 {
2815 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
2816 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
6d2010ae
A
2817 }
2818
2819 lck_mtx_unlock(&nstat_mtx);
2820
fe8ab488
A
2821 /* Generate any system level reports, if needed */
2822 nstat_sysinfo_generate_report();
2823
6d2010ae
A
2824 // Release the sources now that we aren't holding lots of locks
2825 while (dead_list)
2826 {
2827 dead = dead_list;
2828 dead_list = dead->next;
2829
316670eb 2830 nstat_control_cleanup_source(NULL, dead, FALSE);
6d2010ae
A
2831 }
2832
2833 return NULL;
2834}
2835
2836static void
2837nstat_control_register(void)
2838{
2839 // Create our lock group first
2840 lck_grp_attr_t *grp_attr = lck_grp_attr_alloc_init();
2841 lck_grp_attr_setdefault(grp_attr);
2842 nstat_lck_grp = lck_grp_alloc_init("network statistics kctl", grp_attr);
2843 lck_grp_attr_free(grp_attr);
2844
2845 lck_mtx_init(&nstat_mtx, nstat_lck_grp, NULL);
2846
2847 // Register the control
2848 struct kern_ctl_reg nstat_control;
2849 bzero(&nstat_control, sizeof(nstat_control));
2850 strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name));
fe8ab488
A
2851 nstat_control.ctl_flags = CTL_FLAG_REG_EXTENDED | CTL_FLAG_REG_CRIT;
2852 nstat_control.ctl_sendsize = nstat_sendspace;
2853 nstat_control.ctl_recvsize = nstat_recvspace;
6d2010ae
A
2854 nstat_control.ctl_connect = nstat_control_connect;
2855 nstat_control.ctl_disconnect = nstat_control_disconnect;
2856 nstat_control.ctl_send = nstat_control_send;
2857
316670eb 2858 ctl_register(&nstat_control, &nstat_ctlref);
6d2010ae
A
2859}
2860
2861static void
2862nstat_control_cleanup_source(
2863 nstat_control_state *state,
316670eb
A
2864 struct nstat_src *src,
2865 boolean_t locked)
6d2010ae 2866{
fe8ab488 2867 errno_t result;
6d2010ae 2868
3e170ce0
A
2869 if (state)
2870 {
fe8ab488 2871 result = nstat_control_send_removed(state, src);
3e170ce0
A
2872 if (result != 0)
2873 {
2874 nstat_stats.nstat_control_cleanup_source_failures++;
2875 if (nstat_debug != 0)
2876 printf("%s - nstat_control_send_removed() %d\n",
2877 __func__, result);
2878 }
fe8ab488 2879 }
6d2010ae 2880 // Cleanup the source if we found it.
316670eb 2881 src->provider->nstat_release(src->cookie, locked);
6d2010ae
A
2882 OSFree(src, sizeof(*src), nstat_malloc_tag);
2883}
2884
3e170ce0
A
2885
2886static bool
2887nstat_control_reporting_allowed(
2888 nstat_control_state *state,
2889 nstat_src *src)
2890{
2891 if (src->provider->nstat_reporting_allowed == NULL)
2892 return TRUE;
2893
2894 return (
2895 src->provider->nstat_reporting_allowed( src->cookie,
2896 state->ncs_provider_filters[src->provider->nstat_provider_id])
2897 );
2898}
2899
2900
6d2010ae
A
2901static errno_t
2902nstat_control_connect(
2903 kern_ctl_ref kctl,
2904 struct sockaddr_ctl *sac,
2905 void **uinfo)
2906{
2907 nstat_control_state *state = OSMalloc(sizeof(*state), nstat_malloc_tag);
2908 if (state == NULL) return ENOMEM;
2909
2910 bzero(state, sizeof(*state));
2911 lck_mtx_init(&state->mtx, nstat_lck_grp, NULL);
316670eb
A
2912 state->ncs_kctl = kctl;
2913 state->ncs_unit = sac->sc_unit;
2914 state->ncs_flags = NSTAT_FLAG_REQCOUNTS;
6d2010ae
A
2915 *uinfo = state;
2916
6d2010ae 2917 lck_mtx_lock(&nstat_mtx);
316670eb 2918 state->ncs_next = nstat_controls;
6d2010ae
A
2919 nstat_controls = state;
2920
316670eb 2921 if (nstat_idle_time == 0)
6d2010ae
A
2922 {
2923 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
2924 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
2925 }
2926
2927 lck_mtx_unlock(&nstat_mtx);
2928
2929 return 0;
2930}
2931
2932static errno_t
2933nstat_control_disconnect(
2934 __unused kern_ctl_ref kctl,
2935 __unused u_int32_t unit,
3e170ce0 2936 void *uinfo)
6d2010ae
A
2937{
2938 u_int32_t watching;
2939 nstat_control_state *state = (nstat_control_state*)uinfo;
2940
2941 // pull it out of the global list of states
2942 lck_mtx_lock(&nstat_mtx);
2943 nstat_control_state **statepp;
316670eb 2944 for (statepp = &nstat_controls; *statepp; statepp = &(*statepp)->ncs_next)
6d2010ae
A
2945 {
2946 if (*statepp == state)
2947 {
316670eb 2948 *statepp = state->ncs_next;
6d2010ae
A
2949 break;
2950 }
2951 }
2952 lck_mtx_unlock(&nstat_mtx);
2953
2954 lck_mtx_lock(&state->mtx);
2955 // Stop watching for sources
2956 nstat_provider *provider;
316670eb
A
2957 watching = state->ncs_watching;
2958 state->ncs_watching = 0;
6d2010ae
A
2959 for (provider = nstat_providers; provider && watching; provider = provider->next)
2960 {
2961 if ((watching & (1 << provider->nstat_provider_id)) != 0)
2962 {
2963 watching &= ~(1 << provider->nstat_provider_id);
2964 provider->nstat_watcher_remove(state);
2965 }
2966 }
2967
2968 // set cleanup flags
316670eb 2969 state->ncs_flags |= NSTAT_FLAG_CLEANUP;
6d2010ae 2970
3e170ce0
A
2971 if (state->ncs_accumulated)
2972 {
2973 mbuf_freem(state->ncs_accumulated);
2974 state->ncs_accumulated = NULL;
2975 }
2976
6d2010ae 2977 // Copy out the list of sources
316670eb
A
2978 nstat_src *srcs = state->ncs_srcs;
2979 state->ncs_srcs = NULL;
6d2010ae
A
2980 lck_mtx_unlock(&state->mtx);
2981
2982 while (srcs)
2983 {
2984 nstat_src *src;
2985
2986 // pull it out of the list
2987 src = srcs;
2988 srcs = src->next;
2989
2990 // clean it up
316670eb 2991 nstat_control_cleanup_source(NULL, src, FALSE);
6d2010ae 2992 }
39236c6e 2993 lck_mtx_destroy(&state->mtx, nstat_lck_grp);
6d2010ae
A
2994 OSFree(state, sizeof(*state), nstat_malloc_tag);
2995
2996 return 0;
2997}
2998
2999static nstat_src_ref_t
3000nstat_control_next_src_ref(
3001 nstat_control_state *state)
3002{
3003 int i = 0;
3004 nstat_src_ref_t toReturn = NSTAT_SRC_REF_INVALID;
3005
3006 for (i = 0; i < 1000 && toReturn == NSTAT_SRC_REF_INVALID; i++)
3007 {
316670eb
A
3008 if (state->ncs_next_srcref == NSTAT_SRC_REF_INVALID ||
3009 state->ncs_next_srcref == NSTAT_SRC_REF_ALL)
6d2010ae 3010 {
316670eb 3011 state->ncs_next_srcref = 1;
6d2010ae
A
3012 }
3013
3014 nstat_src *src;
316670eb 3015 for (src = state->ncs_srcs; src; src = src->next)
6d2010ae 3016 {
316670eb 3017 if (src->srcref == state->ncs_next_srcref)
6d2010ae
A
3018 break;
3019 }
3020
316670eb
A
3021 if (src == NULL) toReturn = state->ncs_next_srcref;
3022 state->ncs_next_srcref++;
6d2010ae
A
3023 }
3024
3025 return toReturn;
3026}
3027
316670eb
A
3028static errno_t
3029nstat_control_send_counts(
3030 nstat_control_state *state,
3031 nstat_src *src,
3032 unsigned long long context,
3e170ce0 3033 u_int16_t hdr_flags,
316670eb 3034 int *gone)
3e170ce0 3035{
316670eb 3036 nstat_msg_src_counts counts;
316670eb
A
3037 errno_t result = 0;
3038
fe8ab488
A
3039 /* Some providers may not have any counts to send */
3040 if (src->provider->nstat_counts == NULL)
3041 return (0);
3042
3043 bzero(&counts, sizeof(counts));
316670eb 3044 counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
3e170ce0
A
3045 counts.hdr.length = sizeof(counts);
3046 counts.hdr.flags = hdr_flags;
316670eb
A
3047 counts.hdr.context = context;
3048 counts.srcref = src->srcref;
fe8ab488 3049
3e170ce0
A
3050 if (src->provider->nstat_counts(src->cookie, &counts.counts, gone) == 0)
3051 {
39236c6e
A
3052 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) &&
3053 counts.counts.nstat_rxbytes == 0 &&
3e170ce0
A
3054 counts.counts.nstat_txbytes == 0)
3055 {
39236c6e 3056 result = EAGAIN;
3e170ce0
A
3057 }
3058 else
3059 {
39236c6e
A
3060 result = ctl_enqueuedata(state->ncs_kctl,
3061 state->ncs_unit, &counts, sizeof(counts),
3062 CTL_DATA_EOR);
fe8ab488 3063 if (result != 0)
3e170ce0 3064 nstat_stats.nstat_sendcountfailures += 1;
fe8ab488 3065 }
316670eb 3066 }
316670eb
A
3067 return result;
3068}
3069
3e170ce0
A
3070static errno_t
3071nstat_control_append_counts(
3072 nstat_control_state *state,
3073 nstat_src *src,
3074 int *gone)
3075{
3076 /* Some providers may not have any counts to send */
3077 if (!src->provider->nstat_counts) return 0;
3078
3079 nstat_msg_src_counts counts;
3080 bzero(&counts, sizeof(counts));
3081 counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
3082 counts.hdr.length = sizeof(counts);
3083 counts.srcref = src->srcref;
3084
3085 errno_t result = 0;
3086 result = src->provider->nstat_counts(src->cookie, &counts.counts, gone);
3087 if (result != 0)
3088 {
3089 return result;
3090 }
3091
3092 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
3093 counts.counts.nstat_rxbytes == 0 && counts.counts.nstat_txbytes == 0)
3094 {
3095 return EAGAIN;
3096 }
3097
3098 return nstat_accumulate_msg(state, &counts.hdr, counts.hdr.length);
3099}
3100
6d2010ae
A
3101static int
3102nstat_control_send_description(
3103 nstat_control_state *state,
3104 nstat_src *src,
3e170ce0
A
3105 u_int64_t context,
3106 u_int16_t hdr_flags)
6d2010ae
A
3107{
3108 // Provider doesn't support getting the descriptor? Done.
3109 if (src->provider->nstat_descriptor_length == 0 ||
3110 src->provider->nstat_copy_descriptor == NULL)
3111 {
6d2010ae
A
3112 return EOPNOTSUPP;
3113 }
fe8ab488 3114
6d2010ae
A
3115 // Allocate storage for the descriptor message
3116 mbuf_t msg;
3117 unsigned int one = 1;
3118 u_int32_t size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
fe8ab488 3119 if (mbuf_allocpacket(MBUF_DONTWAIT, size, &one, &msg) != 0)
6d2010ae 3120 {
6d2010ae
A
3121 return ENOMEM;
3122 }
fe8ab488 3123
6d2010ae 3124 nstat_msg_src_description *desc = (nstat_msg_src_description*)mbuf_data(msg);
fe8ab488 3125 bzero(desc, size);
6d2010ae
A
3126 mbuf_setlen(msg, size);
3127 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
fe8ab488 3128
6d2010ae
A
3129 // Query the provider for the provider specific bits
3130 errno_t result = src->provider->nstat_copy_descriptor(src->cookie, desc->data, src->provider->nstat_descriptor_length);
fe8ab488 3131
6d2010ae
A
3132 if (result != 0)
3133 {
3134 mbuf_freem(msg);
6d2010ae
A
3135 return result;
3136 }
fe8ab488 3137
6d2010ae
A
3138 desc->hdr.context = context;
3139 desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
3e170ce0
A
3140 desc->hdr.length = size;
3141 desc->hdr.flags = hdr_flags;
6d2010ae
A
3142 desc->srcref = src->srcref;
3143 desc->provider = src->provider->nstat_provider_id;
fe8ab488 3144
316670eb 3145 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
6d2010ae
A
3146 if (result != 0)
3147 {
3e170ce0 3148 nstat_stats.nstat_descriptionfailures += 1;
6d2010ae
A
3149 mbuf_freem(msg);
3150 }
fe8ab488 3151
6d2010ae
A
3152 return result;
3153}
3154
3e170ce0
A
3155static errno_t
3156nstat_control_append_description(
3157 nstat_control_state *state,
3158 nstat_src *src)
3159{
3160 size_t size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
3161 if (size > 512 || src->provider->nstat_descriptor_length == 0 ||
3162 src->provider->nstat_copy_descriptor == NULL)
3163 {
3164 return EOPNOTSUPP;
3165 }
3166
3167 // Fill out a buffer on the stack, we will copy to the mbuf later
3168 u_int64_t buffer[size/sizeof(u_int64_t) + 1]; // u_int64_t to ensure alignment
3169 bzero(buffer, size);
3170
3171 nstat_msg_src_description *desc = (nstat_msg_src_description*)buffer;
3172 desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
3173 desc->hdr.length = size;
3174 desc->srcref = src->srcref;
3175 desc->provider = src->provider->nstat_provider_id;
3176
3177 errno_t result = 0;
3178 // Fill in the description
3179 // Query the provider for the provider specific bits
3180 result = src->provider->nstat_copy_descriptor(src->cookie, desc->data,
3181 src->provider->nstat_descriptor_length);
3182 if (result != 0)
3183 {
3184 return result;
3185 }
3186
3187 return nstat_accumulate_msg(state, &desc->hdr, size);
3188}
3189
3190static int
3191nstat_control_send_update(
3192 nstat_control_state *state,
3193 nstat_src *src,
3194 u_int64_t context,
3195 u_int16_t hdr_flags,
3196 int *gone)
3197{
3198 // Provider doesn't support getting the descriptor or counts? Done.
3199 if ((src->provider->nstat_descriptor_length == 0 ||
3200 src->provider->nstat_copy_descriptor == NULL) &&
3201 src->provider->nstat_counts == NULL)
3202 {
3203 return EOPNOTSUPP;
3204 }
3205
3206 // Allocate storage for the descriptor message
3207 mbuf_t msg;
3208 unsigned int one = 1;
3209 u_int32_t size = offsetof(nstat_msg_src_update, data) +
3210 src->provider->nstat_descriptor_length;
3211 if (mbuf_allocpacket(MBUF_DONTWAIT, size, &one, &msg) != 0)
3212 {
3213 return ENOMEM;
3214 }
3215
3216 nstat_msg_src_update *desc = (nstat_msg_src_update*)mbuf_data(msg);
3217 bzero(desc, size);
3218 desc->hdr.context = context;
3219 desc->hdr.type = NSTAT_MSG_TYPE_SRC_UPDATE;
3220 desc->hdr.length = size;
3221 desc->hdr.flags = hdr_flags;
3222 desc->srcref = src->srcref;
3223 desc->provider = src->provider->nstat_provider_id;
3224
3225 mbuf_setlen(msg, size);
3226 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
3227
3228 errno_t result = 0;
3229 if (src->provider->nstat_descriptor_length != 0 && src->provider->nstat_copy_descriptor)
3230 {
3231 // Query the provider for the provider specific bits
3232 result = src->provider->nstat_copy_descriptor(src->cookie, desc->data,
3233 src->provider->nstat_descriptor_length);
3234 if (result != 0)
3235 {
3236 mbuf_freem(msg);
3237 return result;
3238 }
3239 }
3240
3241 if (src->provider->nstat_counts)
3242 {
3243 result = src->provider->nstat_counts(src->cookie, &desc->counts, gone);
3244 if (result == 0)
3245 {
3246 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
3247 desc->counts.nstat_rxbytes == 0 && desc->counts.nstat_txbytes == 0)
3248 {
3249 result = EAGAIN;
3250 }
3251 else
3252 {
3253 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
3254 }
3255 }
3256 }
3257
3258 if (result != 0)
3259 {
3260 nstat_stats.nstat_srcupatefailures += 1;
3261 mbuf_freem(msg);
3262 }
3263
3264 return result;
3265}
3266
3267static errno_t
3268nstat_control_append_update(
3269 nstat_control_state *state,
3270 nstat_src *src,
3271 int *gone)
3272{
3273 size_t size = offsetof(nstat_msg_src_update, data) + src->provider->nstat_descriptor_length;
3274 if (size > 512 || ((src->provider->nstat_descriptor_length == 0 ||
3275 src->provider->nstat_copy_descriptor == NULL) &&
3276 src->provider->nstat_counts == NULL))
3277 {
3278 return EOPNOTSUPP;
3279 }
3280
3281 // Fill out a buffer on the stack, we will copy to the mbuf later
3282 u_int64_t buffer[size/sizeof(u_int64_t) + 1]; // u_int64_t to ensure alignment
3283 bzero(buffer, size);
3284
3285 nstat_msg_src_update *desc = (nstat_msg_src_update*)buffer;
3286 desc->hdr.type = NSTAT_MSG_TYPE_SRC_UPDATE;
3287 desc->hdr.length = size;
3288 desc->srcref = src->srcref;
3289 desc->provider = src->provider->nstat_provider_id;
3290
3291 errno_t result = 0;
3292 // Fill in the description
3293 if (src->provider->nstat_descriptor_length != 0 && src->provider->nstat_copy_descriptor)
3294 {
3295 // Query the provider for the provider specific bits
3296 result = src->provider->nstat_copy_descriptor(src->cookie, desc->data,
3297 src->provider->nstat_descriptor_length);
3298 if (result != 0)
3299 {
3300 nstat_stats.nstat_copy_descriptor_failures++;
3301 if (nstat_debug != 0)
3302 printf("%s: src->provider->nstat_copy_descriptor: %d\n", __func__, result);
3303 return result;
3304 }
3305 }
3306
3307 if (src->provider->nstat_counts)
3308 {
3309 result = src->provider->nstat_counts(src->cookie, &desc->counts, gone);
3310 if (result != 0)
3311 {
3312 nstat_stats.nstat_provider_counts_failures++;
3313 if (nstat_debug != 0)
3314 printf("%s: src->provider->nstat_counts: %d\n", __func__, result);
3315 return result;
3316 }
3317
3318 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) == NSTAT_FILTER_NOZEROBYTES &&
3319 desc->counts.nstat_rxbytes == 0 && desc->counts.nstat_txbytes == 0)
3320 {
3321 return EAGAIN;
3322 }
3323 }
3324
3325 return nstat_accumulate_msg(state, &desc->hdr, size);
3326}
3327
316670eb
A
3328static errno_t
3329nstat_control_send_removed(
3330 nstat_control_state *state,
3331 nstat_src *src)
3332{
3333 nstat_msg_src_removed removed;
3334 errno_t result;
3335
fe8ab488 3336 bzero(&removed, sizeof(removed));
316670eb 3337 removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED;
3e170ce0 3338 removed.hdr.length = sizeof(removed);
316670eb
A
3339 removed.hdr.context = 0;
3340 removed.srcref = src->srcref;
3341 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &removed,
fe8ab488
A
3342 sizeof(removed), CTL_DATA_EOR | CTL_DATA_CRIT);
3343 if (result != 0)
3e170ce0 3344 nstat_stats.nstat_msgremovedfailures += 1;
316670eb
A
3345
3346 return result;
3347}
3348
6d2010ae
A
3349static errno_t
3350nstat_control_handle_add_request(
3351 nstat_control_state *state,
3352 mbuf_t m)
3353{
3354 errno_t result;
39236c6e 3355
6d2010ae
A
3356 // Verify the header fits in the first mbuf
3357 if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param))
3358 {
6d2010ae
A
3359 return EINVAL;
3360 }
3361
3362 // Calculate the length of the parameter field
3363 int32_t paramlength = mbuf_pkthdr_len(m) - offsetof(nstat_msg_add_src_req, param);
3364 if (paramlength < 0 || paramlength > 2 * 1024)
3365 {
6d2010ae
A
3366 return EINVAL;
3367 }
3368
3369 nstat_provider *provider;
3370 nstat_provider_cookie_t cookie;
3371 nstat_msg_add_src_req *req = mbuf_data(m);
3372 if (mbuf_pkthdr_len(m) > mbuf_len(m))
3373 {
3374 // parameter is too large, we need to make a contiguous copy
3375 void *data = OSMalloc(paramlength, nstat_malloc_tag);
3376
3377 if (!data) return ENOMEM;
3378 result = mbuf_copydata(m, offsetof(nstat_msg_add_src_req, param), paramlength, data);
3379 if (result == 0)
3380 result = nstat_lookup_entry(req->provider, data, paramlength, &provider, &cookie);
3381 OSFree(data, paramlength, nstat_malloc_tag);
3382 }
3383 else
3384 {
3385 result = nstat_lookup_entry(req->provider, (void*)&req->param, paramlength, &provider, &cookie);
3386 }
3387
3388 if (result != 0)
3389 {
6d2010ae
A
3390 return result;
3391 }
3392
3393 result = nstat_control_source_add(req->hdr.context, state, provider, cookie);
3394 if (result != 0)
316670eb 3395 provider->nstat_release(cookie, 0);
6d2010ae
A
3396
3397 return result;
3398}
3399
6d2010ae
A
3400static errno_t
3401nstat_control_handle_add_all(
3402 nstat_control_state *state,
3403 mbuf_t m)
3404{
3405 errno_t result = 0;
3406
6d2010ae
A
3407 // Verify the header fits in the first mbuf
3408 if (mbuf_len(m) < sizeof(nstat_msg_add_all_srcs))
3409 {
6d2010ae
A
3410 return EINVAL;
3411 }
3412
3e170ce0 3413
6d2010ae 3414 nstat_msg_add_all_srcs *req = mbuf_data(m);
3e170ce0
A
3415 if (req->provider > NSTAT_PROVIDER_LAST) return ENOENT;
3416
6d2010ae 3417 nstat_provider *provider = nstat_find_provider_by_id(req->provider);
3e170ce0
A
3418 u_int64_t filter = req->filter;
3419
6d2010ae
A
3420 if (!provider) return ENOENT;
3421 if (provider->nstat_watcher_add == NULL) return ENOTSUP;
3422
39236c6e
A
3423 if (nstat_privcheck != 0) {
3424 result = priv_check_cred(kauth_cred_get(),
3425 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
3426 if (result != 0)
3427 return result;
3428 }
3429
6d2010ae
A
3430 // Make sure we don't add the provider twice
3431 lck_mtx_lock(&state->mtx);
316670eb 3432 if ((state->ncs_watching & (1 << provider->nstat_provider_id)) != 0)
6d2010ae 3433 result = EALREADY;
316670eb 3434 state->ncs_watching |= (1 << provider->nstat_provider_id);
6d2010ae
A
3435 lck_mtx_unlock(&state->mtx);
3436 if (result != 0) return result;
39236c6e 3437
3e170ce0
A
3438 state->ncs_provider_filters[req->provider] = filter;
3439
6d2010ae
A
3440 result = provider->nstat_watcher_add(state);
3441 if (result != 0)
3442 {
3e170ce0 3443 state->ncs_provider_filters[req->provider] = 0;
6d2010ae 3444 lck_mtx_lock(&state->mtx);
316670eb 3445 state->ncs_watching &= ~(1 << provider->nstat_provider_id);
6d2010ae
A
3446 lck_mtx_unlock(&state->mtx);
3447 }
6d2010ae 3448 if (result == 0)
3e170ce0 3449 nstat_enqueue_success(req->hdr.context, state, 0);
6d2010ae
A
3450
3451 return result;
3452}
3453
3454static errno_t
3455nstat_control_source_add(
3e170ce0 3456 u_int64_t context,
6d2010ae
A
3457 nstat_control_state *state,
3458 nstat_provider *provider,
3e170ce0 3459 nstat_provider_cookie_t cookie)
6d2010ae 3460{
3e170ce0
A
3461 // Fill out source added message if appropriate
3462 mbuf_t msg = NULL;
3463 nstat_src_ref_t *srcrefp = NULL;
3464
3465 u_int64_t provider_filters =
3466 state->ncs_provider_filters[provider->nstat_provider_id];
3467 boolean_t tell_user =
3468 ((provider_filters & NSTAT_FILTER_SUPPRESS_SRC_ADDED) == 0);
3469 u_int32_t src_filter =
3470 (provider_filters & NSTAT_FILTER_PROVIDER_NOZEROBYTES)
3471 ? NSTAT_FILTER_NOZEROBYTES : 0;
3472
3473 if (tell_user)
3474 {
3475 unsigned int one = 1;
6d2010ae 3476
3e170ce0
A
3477 if (mbuf_allocpacket(MBUF_DONTWAIT, sizeof(nstat_msg_src_added),
3478 &one, &msg) != 0)
3479 return ENOMEM;
6d2010ae 3480
3e170ce0
A
3481 mbuf_setlen(msg, sizeof(nstat_msg_src_added));
3482 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
3483 nstat_msg_src_added *add = mbuf_data(msg);
3484 bzero(add, sizeof(*add));
3485 add->hdr.type = NSTAT_MSG_TYPE_SRC_ADDED;
3486 add->hdr.length = mbuf_len(msg);
3487 add->hdr.context = context;
3488 add->provider = provider->nstat_provider_id;
3489 srcrefp = &add->srcref;
3490 }
6d2010ae
A
3491
3492 // Allocate storage for the source
3493 nstat_src *src = OSMalloc(sizeof(*src), nstat_malloc_tag);
3494 if (src == NULL)
3495 {
3e170ce0 3496 if (msg) mbuf_freem(msg);
6d2010ae
A
3497 return ENOMEM;
3498 }
3499
3500 // Fill in the source, including picking an unused source ref
3501 lck_mtx_lock(&state->mtx);
3e170ce0
A
3502
3503 src->srcref = nstat_control_next_src_ref(state);
3504 if (srcrefp)
3505 *srcrefp = src->srcref;
3506
316670eb 3507 if (state->ncs_flags & NSTAT_FLAG_CLEANUP || src->srcref == NSTAT_SRC_REF_INVALID)
6d2010ae
A
3508 {
3509 lck_mtx_unlock(&state->mtx);
3510 OSFree(src, sizeof(*src), nstat_malloc_tag);
3e170ce0 3511 if (msg) mbuf_freem(msg);
6d2010ae
A
3512 return EINVAL;
3513 }
3514 src->provider = provider;
3515 src->cookie = cookie;
3e170ce0
A
3516 src->filter = src_filter;
3517
3518 if (msg)
6d2010ae 3519 {
3e170ce0
A
3520 // send the source added message if appropriate
3521 errno_t result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg,
3522 CTL_DATA_EOR);
3523 if (result != 0)
3524 {
3525 nstat_stats.nstat_srcaddedfailures += 1;
3526 lck_mtx_unlock(&state->mtx);
3527 OSFree(src, sizeof(*src), nstat_malloc_tag);
3528 mbuf_freem(msg);
3529 return result;
3530 }
6d2010ae 3531 }
3e170ce0 3532 // Put the source in the list
316670eb
A
3533 src->next = state->ncs_srcs;
3534 state->ncs_srcs = src;
6d2010ae 3535
6d2010ae
A
3536 lck_mtx_unlock(&state->mtx);
3537
3538 return 0;
3539}
3540
3541static errno_t
3542nstat_control_handle_remove_request(
3543 nstat_control_state *state,
3544 mbuf_t m)
3545{
3546 nstat_src_ref_t srcref = NSTAT_SRC_REF_INVALID;
3547
3548 if (mbuf_copydata(m, offsetof(nstat_msg_rem_src_req, srcref), sizeof(srcref), &srcref) != 0)
3549 {
6d2010ae
A
3550 return EINVAL;
3551 }
3552
3553 lck_mtx_lock(&state->mtx);
3554
3555 // Remove this source as we look for it
3556 nstat_src **nextp;
3557 nstat_src *src = NULL;
316670eb 3558 for (nextp = &state->ncs_srcs; *nextp; nextp = &(*nextp)->next)
6d2010ae
A
3559 {
3560 if ((*nextp)->srcref == srcref)
3561 {
3562 src = *nextp;
3563 *nextp = src->next;
3564 break;
3565 }
3566 }
3567
3568 lck_mtx_unlock(&state->mtx);
3569
316670eb 3570 if (src) nstat_control_cleanup_source(state, src, FALSE);
6d2010ae
A
3571
3572 return src ? 0 : ENOENT;
3573}
3574
3575static errno_t
3576nstat_control_handle_query_request(
3577 nstat_control_state *state,
3578 mbuf_t m)
3579{
3580 // TBD: handle this from another thread so we can enqueue a lot of data
3581 // As written, if a client requests query all, this function will be
3582 // called from their send of the request message. We will attempt to write
3583 // responses and succeed until the buffer fills up. Since the clients thread
3584 // is blocked on send, it won't be reading unless the client has two threads
3585 // using this socket, one for read and one for write. Two threads probably
3586 // won't work with this code anyhow since we don't have proper locking in
3587 // place yet.
3588 nstat_src *dead_srcs = NULL;
3589 errno_t result = ENOENT;
3590 nstat_msg_query_src_req req;
fe8ab488 3591
6d2010ae
A
3592 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
3593 {
6d2010ae
A
3594 return EINVAL;
3595 }
3e170ce0
A
3596
3597 const boolean_t all_srcs = (req.srcref == NSTAT_SRC_REF_ALL);
6d2010ae
A
3598
3599 lck_mtx_lock(&state->mtx);
3e170ce0
A
3600
3601 if (all_srcs)
3602 {
316670eb 3603 state->ncs_flags |= NSTAT_FLAG_REQCOUNTS;
3e170ce0 3604 }
316670eb 3605 nstat_src **srcpp = &state->ncs_srcs;
3e170ce0
A
3606 u_int64_t src_count = 0;
3607 boolean_t partial = FALSE;
3608
3609 /*
3610 * Error handling policy and sequence number generation is folded into
3611 * nstat_control_begin_query.
3612 */
3613 partial = nstat_control_begin_query(state, &req.hdr);
fe8ab488 3614
3e170ce0
A
3615 while (*srcpp != NULL
3616 && (!partial || src_count < QUERY_CONTINUATION_SRC_COUNT))
3617 {
3618 nstat_src *src = NULL;
3619 int gone;
3620
3621 src = *srcpp;
6d2010ae 3622 gone = 0;
39236c6e 3623 // XXX ignore IFACE types?
3e170ce0 3624 if (all_srcs || src->srcref == req.srcref)
6d2010ae 3625 {
3e170ce0
A
3626 if (nstat_control_reporting_allowed(state, src)
3627 && (!partial || !all_srcs || src->seq != state->ncs_seq))
6d2010ae 3628 {
3e170ce0
A
3629 if (all_srcs &&
3630 (req.hdr.flags & NSTAT_MSG_HDR_FLAG_SUPPORTS_AGGREGATE) != 0)
3631 {
3632 result = nstat_control_append_counts(state, src, &gone);
3633 }
3634 else
3635 {
3636 result = nstat_control_send_counts(state, src, req.hdr.context, 0, &gone);
3637 }
3638
3639 if (ENOMEM == result || ENOBUFS == result)
3640 {
3641 /*
3642 * If the counts message failed to
3643 * enqueue then we should clear our flag so
3644 * that a client doesn't miss anything on
3645 * idle cleanup. We skip the "gone"
3646 * processing in the hope that we may
3647 * catch it another time.
3648 */
fe8ab488
A
3649 state->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
3650 break;
3e170ce0
A
3651 }
3652 if (partial)
3653 {
3654 /*
3655 * We skip over hard errors and
3656 * filtered sources.
3657 */
3658 src->seq = state->ncs_seq;
3659 src_count++;
3660 }
6d2010ae 3661 }
6d2010ae
A
3662 }
3663
3e170ce0
A
3664 if (gone)
3665 {
3666 // send one last descriptor message so client may see last state
3667 // If we can't send the notification now, it
3668 // will be sent in the idle cleanup.
3669 result = nstat_control_send_description(state, *srcpp, 0, 0);
3670 if (result != 0)
3671 {
3672 nstat_stats.nstat_control_send_description_failures++;
3673 if (nstat_debug != 0)
3674 printf("%s - nstat_control_send_description() %d\n", __func__, result);
3675 state->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
3676 break;
3677 }
3678
3679 // pull src out of the list
3680 *srcpp = src->next;
3681
3682 src->next = dead_srcs;
3683 dead_srcs = src;
3684 }
3685 else
3686 {
6d2010ae 3687 srcpp = &(*srcpp)->next;
3e170ce0
A
3688 }
3689
3690 if (!all_srcs && req.srcref == src->srcref)
3691 {
3692 break;
3693 }
6d2010ae 3694 }
3e170ce0
A
3695 nstat_flush_accumulated_msgs(state);
3696
3697 u_int16_t flags = 0;
fe8ab488 3698 if (req.srcref == NSTAT_SRC_REF_ALL)
3e170ce0
A
3699 flags = nstat_control_end_query(state, *srcpp, partial);
3700
3701 lck_mtx_unlock(&state->mtx);
3702
3703 /*
3704 * If an error occurred enqueueing data, then allow the error to
3705 * propagate to nstat_control_send. This way, the error is sent to
3706 * user-level.
3707 */
3708 if (all_srcs && ENOMEM != result && ENOBUFS != result)
fe8ab488 3709 {
3e170ce0 3710 nstat_enqueue_success(req.hdr.context, state, flags);
fe8ab488
A
3711 result = 0;
3712 }
3713
6d2010ae
A
3714 while (dead_srcs)
3715 {
3716 nstat_src *src;
3717
3718 src = dead_srcs;
3719 dead_srcs = src->next;
3720
3721 // release src and send notification
316670eb 3722 nstat_control_cleanup_source(state, src, FALSE);
6d2010ae
A
3723 }
3724
6d2010ae
A
3725 return result;
3726}
3727
3728static errno_t
3729nstat_control_handle_get_src_description(
3730 nstat_control_state *state,
3e170ce0 3731 mbuf_t m)
6d2010ae
A
3732{
3733 nstat_msg_get_src_description req;
3e170ce0 3734 errno_t result = ENOENT;
39236c6e
A
3735 nstat_src *src;
3736
6d2010ae
A
3737 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
3738 {
6d2010ae
A
3739 return EINVAL;
3740 }
3e170ce0 3741
6d2010ae 3742 lck_mtx_lock(&state->mtx);
3e170ce0
A
3743 u_int64_t src_count = 0;
3744 boolean_t partial = FALSE;
3745 const boolean_t all_srcs = (req.srcref == NSTAT_SRC_REF_ALL);
3746
3747 /*
3748 * Error handling policy and sequence number generation is folded into
3749 * nstat_control_begin_query.
3750 */
3751 partial = nstat_control_begin_query(state, &req.hdr);
3752
3753 for (src = state->ncs_srcs;
3754 src && (!partial || src_count < QUERY_CONTINUATION_SRC_COUNT);
3755 src = src->next)
3756 {
3757 if (all_srcs || src->srcref == req.srcref)
39236c6e 3758 {
3e170ce0
A
3759 if (nstat_control_reporting_allowed(state, src)
3760 && (!all_srcs || !partial || src->seq != state->ncs_seq))
3761 {
3762 if ((req.hdr.flags & NSTAT_MSG_HDR_FLAG_SUPPORTS_AGGREGATE) != 0 && all_srcs)
3763 {
3764 result = nstat_control_append_description(state, src);
3765 }
3766 else
3767 {
3768 result = nstat_control_send_description(state, src, req.hdr.context, 0);
3769 }
3770
3771 if (ENOMEM == result || ENOBUFS == result)
3772 {
3773 /*
3774 * If the description message failed to
3775 * enqueue then we give up for now.
3776 */
3777 break;
3778 }
3779 if (partial)
3780 {
3781 /*
3782 * Note, we skip over hard errors and
3783 * filtered sources.
3784 */
3785 src->seq = state->ncs_seq;
3786 src_count++;
3787 }
3788 }
3789
3790 if (!all_srcs)
3791 {
39236c6e 3792 break;
3e170ce0 3793 }
39236c6e 3794 }
3e170ce0
A
3795 }
3796 nstat_flush_accumulated_msgs(state);
3797
3798 u_int16_t flags = 0;
3799 if (req.srcref == NSTAT_SRC_REF_ALL)
3800 flags = nstat_control_end_query(state, src, partial);
3801
39236c6e 3802 lck_mtx_unlock(&state->mtx);
3e170ce0
A
3803 /*
3804 * If an error occurred enqueueing data, then allow the error to
3805 * propagate to nstat_control_send. This way, the error is sent to
3806 * user-level.
3807 */
3808 if (all_srcs && ENOMEM != result && ENOBUFS != result)
6d2010ae 3809 {
3e170ce0 3810 nstat_enqueue_success(req.hdr.context, state, flags);
39236c6e 3811 result = 0;
6d2010ae
A
3812 }
3813
6d2010ae
A
3814 return result;
3815}
3816
39236c6e
A
3817static errno_t
3818nstat_control_handle_set_filter(
3819 nstat_control_state *state,
3820 mbuf_t m)
3821{
3822 nstat_msg_set_filter req;
3823 nstat_src *src;
3824
3825 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
3826 return EINVAL;
3827 if (req.srcref == NSTAT_SRC_REF_ALL ||
3828 req.srcref == NSTAT_SRC_REF_INVALID)
3829 return EINVAL;
3830
3831 lck_mtx_lock(&state->mtx);
3832 for (src = state->ncs_srcs; src; src = src->next)
3833 if (req.srcref == src->srcref)
3834 {
3835 src->filter = req.filter;
3836 break;
3837 }
3838 lck_mtx_unlock(&state->mtx);
3839 if (src == NULL)
3840 return ENOENT;
3841
3842 return 0;
3e170ce0
A
3843}
3844
3845static void
3846nstat_send_error(
3847 nstat_control_state *state,
3848 u_int64_t context,
3849 u_int32_t error)
3850{
3851 errno_t result;
3852 struct nstat_msg_error err;
3853
3854 bzero(&err, sizeof(err));
3855 err.hdr.type = NSTAT_MSG_TYPE_ERROR;
3856 err.hdr.length = sizeof(err);
3857 err.hdr.context = context;
3858 err.error = error;
3859
3860 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &err,
3861 sizeof(err), CTL_DATA_EOR | CTL_DATA_CRIT);
3862 if (result != 0)
3863 nstat_stats.nstat_msgerrorfailures++;
3864}
3865
3866static boolean_t
3867nstat_control_begin_query(
3868 nstat_control_state *state,
3869 const nstat_msg_hdr *hdrp)
3870{
3871 boolean_t partial = FALSE;
3872
3873 if (hdrp->flags & NSTAT_MSG_HDR_FLAG_CONTINUATION)
3874 {
3875 /* A partial query all has been requested. */
3876 partial = TRUE;
3877
3878 if (state->ncs_context != hdrp->context)
3879 {
3880 if (state->ncs_context != 0)
3881 nstat_send_error(state, state->ncs_context, EAGAIN);
3882
3883 /* Initialize state for a partial query all. */
3884 state->ncs_context = hdrp->context;
3885 state->ncs_seq++;
3886 }
3887 }
3888 else if (state->ncs_context != 0)
3889 {
3890 /*
3891 * A continuation of a paced-query was in progress. Send that
3892 * context an error and reset the state. If the same context
3893 * has changed its mind, just send the full query results.
3894 */
3895 if (state->ncs_context != hdrp->context)
3896 nstat_send_error(state, state->ncs_context, EAGAIN);
3897 }
3898
3899 return partial;
3900}
3901
3902static u_int16_t
3903nstat_control_end_query(
3904 nstat_control_state *state,
3905 nstat_src *last_src,
3906 boolean_t partial)
3907{
3908 u_int16_t flags = 0;
3909
3910 if (last_src == NULL || !partial)
3911 {
3912 /*
3913 * We iterated through the entire srcs list or exited early
3914 * from the loop when a partial update was not requested (an
3915 * error occurred), so clear context to indicate internally
3916 * that the query is finished.
3917 */
3918 state->ncs_context = 0;
3919 }
3920 else
3921 {
3922 /*
3923 * Indicate to userlevel to make another partial request as
3924 * there are still sources left to be reported.
3925 */
3926 flags |= NSTAT_MSG_HDR_FLAG_CONTINUATION;
3927 }
3928
3929 return flags;
3930}
3931
3932static errno_t
3933nstat_control_handle_get_update(
3934 nstat_control_state *state,
3935 mbuf_t m)
3936{
3937 nstat_msg_query_src_req req;
3938
3939 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
3940 {
3941 return EINVAL;
3942 }
3943
3944 lck_mtx_lock(&state->mtx);
3945
3946 state->ncs_flags |= NSTAT_FLAG_SUPPORTS_UPDATES;
3947
3948 errno_t result = ENOENT;
3949 nstat_src *src;
3950 nstat_src *dead_srcs = NULL;
3951 nstat_src **srcpp = &state->ncs_srcs;
3952 u_int64_t src_count = 0;
3953 boolean_t partial = FALSE;
3954
3955 /*
3956 * Error handling policy and sequence number generation is folded into
3957 * nstat_control_begin_query.
3958 */
3959 partial = nstat_control_begin_query(state, &req.hdr);
3960
3961 while (*srcpp != NULL
3962 && (FALSE == partial
3963 || src_count < QUERY_CONTINUATION_SRC_COUNT))
3964 {
3965 int gone;
3966
3967 gone = 0;
3968 src = *srcpp;
3969 if (nstat_control_reporting_allowed(state, src))
3970 {
3971 /* skip this source if it has the current state
3972 * sequence number as it's already been reported in
3973 * this query-all partial sequence. */
3974 if (req.srcref == NSTAT_SRC_REF_ALL
3975 && (FALSE == partial || src->seq != state->ncs_seq))
3976 {
3977 result = nstat_control_append_update(state, src, &gone);
3978 if (ENOMEM == result || ENOBUFS == result)
3979 {
3980 /*
3981 * If the update message failed to
3982 * enqueue then give up.
3983 */
3984 break;
3985 }
3986 if (partial)
3987 {
3988 /*
3989 * We skip over hard errors and
3990 * filtered sources.
3991 */
3992 src->seq = state->ncs_seq;
3993 src_count++;
3994 }
3995 }
3996 else if (src->srcref == req.srcref)
3997 {
3998 result = nstat_control_send_update(state, src, req.hdr.context, 0, &gone);
3999 }
4000 }
4001
4002 if (gone)
4003 {
4004 // pull src out of the list
4005 *srcpp = src->next;
4006
4007 src->next = dead_srcs;
4008 dead_srcs = src;
4009 }
4010 else
4011 {
4012 srcpp = &(*srcpp)->next;
4013 }
4014
4015 if (req.srcref != NSTAT_SRC_REF_ALL && req.srcref == src->srcref)
4016 {
4017 break;
4018 }
4019 }
4020
4021 nstat_flush_accumulated_msgs(state);
4022
4023
4024 u_int16_t flags = 0;
4025 if (req.srcref == NSTAT_SRC_REF_ALL)
4026 flags = nstat_control_end_query(state, *srcpp, partial);
4027
4028 lck_mtx_unlock(&state->mtx);
4029 /*
4030 * If an error occurred enqueueing data, then allow the error to
4031 * propagate to nstat_control_send. This way, the error is sent to
4032 * user-level.
4033 */
4034 if (req.srcref == NSTAT_SRC_REF_ALL && ENOMEM != result && ENOBUFS != result)
4035 {
4036 nstat_enqueue_success(req.hdr.context, state, flags);
4037 result = 0;
4038 }
4039
4040 while (dead_srcs)
4041 {
4042 src = dead_srcs;
4043 dead_srcs = src->next;
4044
4045 // release src and send notification
4046 nstat_control_cleanup_source(state, src, FALSE);
4047 }
4048
4049 return result;
4050}
39236c6e 4051
3e170ce0
A
4052static errno_t
4053nstat_control_handle_subscribe_sysinfo(
4054 nstat_control_state *state)
4055{
4056 errno_t result = priv_check_cred(kauth_cred_get(), PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
4057
4058 if (result != 0)
4059 {
4060 return result;
4061 }
4062
4063 lck_mtx_lock(&state->mtx);
4064 state->ncs_flags |= NSTAT_FLAG_SYSINFO_SUBSCRIBED;
4065 lck_mtx_unlock(&state->mtx);
4066
4067 return 0;
39236c6e
A
4068}
4069
6d2010ae
A
4070static errno_t
4071nstat_control_send(
4072 kern_ctl_ref kctl,
4073 u_int32_t unit,
39236c6e 4074 void *uinfo,
6d2010ae
A
4075 mbuf_t m,
4076 __unused int flags)
4077{
4078 nstat_control_state *state = (nstat_control_state*)uinfo;
4079 struct nstat_msg_hdr *hdr;
4080 struct nstat_msg_hdr storage;
4081 errno_t result = 0;
4082
3e170ce0 4083 if (mbuf_pkthdr_len(m) < sizeof(*hdr))
6d2010ae
A
4084 {
4085 // Is this the right thing to do?
6d2010ae
A
4086 mbuf_freem(m);
4087 return EINVAL;
4088 }
4089
4090 if (mbuf_len(m) >= sizeof(*hdr))
4091 {
4092 hdr = mbuf_data(m);
4093 }
4094 else
4095 {
4096 mbuf_copydata(m, 0, sizeof(storage), &storage);
4097 hdr = &storage;
4098 }
4099
3e170ce0
A
4100 // Legacy clients may not set the length
4101 // Those clients are likely not setting the flags either
4102 // Fix everything up so old clients continue to work
4103 if (hdr->length != mbuf_pkthdr_len(m))
4104 {
4105 hdr->flags = 0;
4106 hdr->length = mbuf_pkthdr_len(m);
4107 if (hdr == &storage)
4108 {
4109 mbuf_copyback(m, 0, sizeof(*hdr), hdr, MBUF_DONTWAIT);
4110 }
4111 }
4112
6d2010ae
A
4113 switch (hdr->type)
4114 {
4115 case NSTAT_MSG_TYPE_ADD_SRC:
4116 result = nstat_control_handle_add_request(state, m);
4117 break;
4118
4119 case NSTAT_MSG_TYPE_ADD_ALL_SRCS:
4120 result = nstat_control_handle_add_all(state, m);
4121 break;
4122
4123 case NSTAT_MSG_TYPE_REM_SRC:
4124 result = nstat_control_handle_remove_request(state, m);
4125 break;
4126
4127 case NSTAT_MSG_TYPE_QUERY_SRC:
4128 result = nstat_control_handle_query_request(state, m);
4129 break;
4130
4131 case NSTAT_MSG_TYPE_GET_SRC_DESC:
4132 result = nstat_control_handle_get_src_description(state, m);
4133 break;
3e170ce0 4134
39236c6e
A
4135 case NSTAT_MSG_TYPE_SET_FILTER:
4136 result = nstat_control_handle_set_filter(state, m);
4137 break;
3e170ce0
A
4138
4139 case NSTAT_MSG_TYPE_GET_UPDATE:
4140 result = nstat_control_handle_get_update(state, m);
4141 break;
4142
4143 case NSTAT_MSG_TYPE_SUBSCRIBE_SYSINFO:
4144 result = nstat_control_handle_subscribe_sysinfo(state);
4145 break;
4146
6d2010ae 4147 default:
6d2010ae
A
4148 result = EINVAL;
4149 break;
4150 }
4151
4152 if (result != 0)
4153 {
4154 struct nstat_msg_error err;
4155
fe8ab488 4156 bzero(&err, sizeof(err));
6d2010ae 4157 err.hdr.type = NSTAT_MSG_TYPE_ERROR;
3e170ce0 4158 err.hdr.length = sizeof(err) + mbuf_pkthdr_len(m);
6d2010ae
A
4159 err.hdr.context = hdr->context;
4160 err.error = result;
4161
3e170ce0
A
4162 if (mbuf_prepend(&m, sizeof(err), MBUF_DONTWAIT) == 0 &&
4163 mbuf_copyback(m, 0, sizeof(err), &err, MBUF_DONTWAIT) == 0)
4164 {
4165 result = ctl_enqueuembuf(kctl, unit, m, CTL_DATA_EOR | CTL_DATA_CRIT);
4166 if (result != 0)
4167 {
4168 mbuf_freem(m);
4169 }
4170 m = NULL;
4171 }
4172
fe8ab488 4173 if (result != 0)
3e170ce0
A
4174 {
4175 // Unable to prepend the error to the request - just send the error
4176 err.hdr.length = sizeof(err);
4177 result = ctl_enqueuedata(kctl, unit, &err, sizeof(err),
4178 CTL_DATA_EOR | CTL_DATA_CRIT);
4179 if (result != 0)
4180 nstat_stats.nstat_msgerrorfailures += 1;
4181 }
4182 nstat_stats.nstat_handle_msg_failures += 1;
6d2010ae
A
4183 }
4184
3e170ce0 4185 if (m) mbuf_freem(m);
6d2010ae
A
4186
4187 return result;
4188}