]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/ntstat.c
xnu-2050.22.13.tar.gz
[apple/xnu.git] / bsd / net / ntstat.c
CommitLineData
6d2010ae 1/*
316670eb 2 * Copyright (c) 2010-2012 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>
37
38#include <kern/clock.h>
39#include <kern/debug.h>
40
41#include <libkern/libkern.h>
42#include <libkern/OSMalloc.h>
43#include <libkern/OSAtomic.h>
44#include <libkern/locks.h>
45
46#include <net/if.h>
47#include <net/route.h>
48#include <net/ntstat.h>
49
50#include <netinet/ip_var.h>
51#include <netinet/in_pcb.h>
52#include <netinet/in_var.h>
53#include <netinet/tcp.h>
54#include <netinet/tcp_var.h>
55#include <netinet/tcp_fsm.h>
56#include <netinet/udp.h>
57#include <netinet/udp_var.h>
58#include <netinet6/in6_pcb.h>
59#include <netinet6/in6_var.h>
60
61__private_extern__ int nstat_collect = 1;
62SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
63 &nstat_collect, 0, "Collect detailed statistics");
64
316670eb
A
65enum
66{
67 NSTAT_FLAG_CLEANUP = (0x1 << 0),
68 NSTAT_FLAG_REQCOUNTS = (0x1 << 1)
69};
70
6d2010ae
A
71typedef struct nstat_control_state
72{
316670eb
A
73 struct nstat_control_state *ncs_next;
74 u_int32_t ncs_watching;
6d2010ae 75 decl_lck_mtx_data(, mtx);
316670eb
A
76 kern_ctl_ref ncs_kctl;
77 u_int32_t ncs_unit;
78 nstat_src_ref_t ncs_next_srcref;
79 struct nstat_src *ncs_srcs;
80 u_int32_t ncs_flags;
6d2010ae
A
81} nstat_control_state;
82
316670eb
A
83typedef struct nstat_provider
84{
85 struct nstat_provider *next;
86 nstat_provider_id_t nstat_provider_id;
87 size_t nstat_descriptor_length;
88 errno_t (*nstat_lookup)(const void *data, u_int32_t length, nstat_provider_cookie_t *out_cookie);
89 int (*nstat_gone)(nstat_provider_cookie_t cookie);
90 errno_t (*nstat_counts)(nstat_provider_cookie_t cookie, struct nstat_counts *out_counts, int *out_gone);
91 errno_t (*nstat_watcher_add)(nstat_control_state *state);
92 void (*nstat_watcher_remove)(nstat_control_state *state);
93 errno_t (*nstat_copy_descriptor)(nstat_provider_cookie_t cookie, void *data, u_int32_t len);
94 void (*nstat_release)(nstat_provider_cookie_t cookie, boolean_t locked);
95} nstat_provider;
96
97
98typedef struct nstat_src
99{
100 struct nstat_src *next;
101 nstat_src_ref_t srcref;
102 nstat_provider *provider;
103 nstat_provider_cookie_t cookie;
104} nstat_src;
105
106static errno_t nstat_control_send_counts(nstat_control_state *,
107 nstat_src *, unsigned long long, int *);
108static int nstat_control_send_description(nstat_control_state *state, nstat_src *src, u_int64_t context);
109static errno_t nstat_control_send_removed(nstat_control_state *, nstat_src *);
110static void nstat_control_cleanup_source(nstat_control_state *state, nstat_src *src,
111 boolean_t);
112
113static u_int32_t nstat_udp_watchers = 0;
114static u_int32_t nstat_tcp_watchers = 0;
115
6d2010ae
A
116static void nstat_control_register(void);
117
118static volatile OSMallocTag nstat_malloc_tag = NULL;
119static nstat_control_state *nstat_controls = NULL;
316670eb 120static uint64_t nstat_idle_time = 0;
6d2010ae
A
121static decl_lck_mtx_data(, nstat_mtx);
122
123static void
124nstat_copy_sa_out(
125 const struct sockaddr *src,
126 struct sockaddr *dst,
127 int maxlen)
128{
129 if (src->sa_len > maxlen) return;
130
131 bcopy(src, dst, src->sa_len);
132 if (src->sa_family == AF_INET6 &&
133 src->sa_len >= sizeof(struct sockaddr_in6))
134 {
316670eb 135 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(void *)dst;
6d2010ae
A
136 if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
137 {
138 if (sin6->sin6_scope_id == 0)
139 sin6->sin6_scope_id = ntohs(sin6->sin6_addr.__u6_addr.__u6_addr16[1]);
140 sin6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
141 }
142 }
143}
144
145static void
146nstat_ip_to_sockaddr(
147 const struct in_addr *ip,
148 u_int16_t port,
149 struct sockaddr_in *sin,
150 u_int32_t maxlen)
151{
152 if (maxlen < sizeof(struct sockaddr_in))
153 return;
154
155 sin->sin_family = AF_INET;
156 sin->sin_len = sizeof(*sin);
157 sin->sin_port = port;
158 sin->sin_addr = *ip;
159}
160
161static void
162nstat_ip6_to_sockaddr(
163 const struct in6_addr *ip6,
164 u_int16_t port,
165 struct sockaddr_in6 *sin6,
166 u_int32_t maxlen)
167{
168 if (maxlen < sizeof(struct sockaddr_in6))
169 return;
170
171 sin6->sin6_family = AF_INET6;
172 sin6->sin6_len = sizeof(*sin6);
173 sin6->sin6_port = port;
174 sin6->sin6_addr = *ip6;
175 if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
176 {
177 sin6->sin6_scope_id = ntohs(sin6->sin6_addr.__u6_addr.__u6_addr16[1]);
178 sin6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
179 }
180}
181
182#pragma mark -- Network Statistic Providers --
183
6d2010ae
A
184static errno_t nstat_control_source_add(u_int64_t context, nstat_control_state *state, nstat_provider *provider, nstat_provider_cookie_t cookie);
185struct nstat_provider *nstat_providers = NULL;
186
187static struct nstat_provider*
188nstat_find_provider_by_id(
189 nstat_provider_id_t id)
190{
191 struct nstat_provider *provider;
192
193 for (provider = nstat_providers; provider != NULL; provider = provider->next)
194 {
195 if (provider->nstat_provider_id == id)
196 break;
197 }
198
199 return provider;
200}
201
202static errno_t
203nstat_lookup_entry(
204 nstat_provider_id_t id,
205 const void *data,
206 u_int32_t length,
207 nstat_provider **out_provider,
208 nstat_provider_cookie_t *out_cookie)
209{
210 *out_provider = nstat_find_provider_by_id(id);
211 if (*out_provider == NULL)
212 {
6d2010ae
A
213 return ENOENT;
214 }
215
216 return (*out_provider)->nstat_lookup(data, length, out_cookie);
217}
218
219static void nstat_init_route_provider(void);
220static void nstat_init_tcp_provider(void);
221static void nstat_init_udp_provider(void);
222
316670eb 223__private_extern__ void
6d2010ae
A
224nstat_init(void)
225{
226 if (nstat_malloc_tag != NULL) return;
227
228 OSMallocTag tag = OSMalloc_Tagalloc(NET_STAT_CONTROL_NAME, OSMT_DEFAULT);
229 if (!OSCompareAndSwapPtr(NULL, tag, &nstat_malloc_tag))
230 {
231 OSMalloc_Tagfree(tag);
232 tag = nstat_malloc_tag;
233 }
234 else
235 {
236 // we need to initialize other things, we do it here as this code path will only be hit once;
237 nstat_init_route_provider();
238 nstat_init_tcp_provider();
239 nstat_init_udp_provider();
240 nstat_control_register();
241 }
242}
243
244#pragma mark -- Aligned Buffer Allocation --
245
246struct align_header
247{
248 u_int32_t offset;
249 u_int32_t length;
250};
251
252static void*
253nstat_malloc_aligned(
254 u_int32_t length,
255 u_int8_t alignment,
256 OSMallocTag tag)
257{
258 struct align_header *hdr = NULL;
259 u_int32_t size = length + sizeof(*hdr) + alignment - 1;
260
261 u_int8_t *buffer = OSMalloc(size, tag);
262 if (buffer == NULL) return NULL;
263
264 u_int8_t *aligned = buffer + sizeof(*hdr);
265 aligned = (u_int8_t*)P2ROUNDUP(aligned, alignment);
266
316670eb 267 hdr = (struct align_header*)(void *)(aligned - sizeof(*hdr));
6d2010ae
A
268 hdr->offset = aligned - buffer;
269 hdr->length = size;
270
271 return aligned;
272}
273
274static void
275nstat_free_aligned(
276 void *buffer,
277 OSMallocTag tag)
278{
316670eb 279 struct align_header *hdr = (struct align_header*)(void *)((u_int8_t*)buffer - sizeof(*hdr));
6d2010ae
A
280 OSFree(((char*)buffer) - hdr->offset, hdr->length, tag);
281}
282
283#pragma mark -- Route Provider --
284
285static nstat_provider nstat_route_provider;
286
287static errno_t
288nstat_route_lookup(
289 const void *data,
290 u_int32_t length,
291 nstat_provider_cookie_t *out_cookie)
292{
293 // rt_lookup doesn't take const params but it doesn't modify the parameters for
294 // the lookup. So...we use a union to eliminate the warning.
295 union
296 {
297 struct sockaddr *sa;
298 const struct sockaddr *const_sa;
299 } dst, mask;
300
301 const nstat_route_add_param *param = (const nstat_route_add_param*)data;
302 *out_cookie = NULL;
303
304 if (length < sizeof(*param))
305 {
6d2010ae
A
306 return EINVAL;
307 }
308
309 if (param->dst.v4.sin_family == 0 ||
310 param->dst.v4.sin_family > AF_MAX ||
311 (param->mask.v4.sin_family != 0 && param->mask.v4.sin_family != param->dst.v4.sin_family))
312 {
6d2010ae
A
313 return EINVAL;
314 }
315
316 if (param->dst.v4.sin_len > sizeof(param->dst) ||
317 (param->mask.v4.sin_family && param->mask.v4.sin_len > sizeof(param->mask.v4.sin_len)))
318 {
316670eb 319 return EINVAL;
6d2010ae
A
320 }
321
322 // TBD: Need to validate length of sockaddr for different families?
323 dst.const_sa = (const struct sockaddr*)&param->dst;
324 mask.const_sa = param->mask.v4.sin_family ? (const struct sockaddr*)&param->mask : NULL;
325
326 struct radix_node_head *rnh = rt_tables[dst.sa->sa_family];
327 if (rnh == NULL) return EAFNOSUPPORT;
328
329 lck_mtx_lock(rnh_lock);
330 struct rtentry *rt = rt_lookup(TRUE, dst.sa, mask.sa, rnh, param->ifindex);
331 lck_mtx_unlock(rnh_lock);
332
333 if (rt) *out_cookie = (nstat_provider_cookie_t)rt;
334
335 return rt ? 0 : ENOENT;
336}
337
338static int
339nstat_route_gone(
340 nstat_provider_cookie_t cookie)
341{
342 struct rtentry *rt = (struct rtentry*)cookie;
343 return ((rt->rt_flags & RTF_UP) == 0) ? 1 : 0;
344}
345
346static errno_t
347nstat_route_counts(
348 nstat_provider_cookie_t cookie,
349 struct nstat_counts *out_counts,
350 int *out_gone)
351{
352 struct rtentry *rt = (struct rtentry*)cookie;
353 struct nstat_counts *rt_stats = rt->rt_stats;
354
355 *out_gone = 0;
356
357 if ((rt->rt_flags & RTF_UP) == 0) *out_gone = 1;
358
359 if (rt_stats)
360 {
361 atomic_get_64(out_counts->nstat_rxpackets, &rt_stats->nstat_rxpackets);
362 atomic_get_64(out_counts->nstat_rxbytes, &rt_stats->nstat_rxbytes);
363 atomic_get_64(out_counts->nstat_txpackets, &rt_stats->nstat_txpackets);
364 atomic_get_64(out_counts->nstat_txbytes, &rt_stats->nstat_txbytes);
365 out_counts->nstat_rxduplicatebytes = rt_stats->nstat_rxduplicatebytes;
366 out_counts->nstat_rxoutoforderbytes = rt_stats->nstat_rxoutoforderbytes;
367 out_counts->nstat_txretransmit = rt_stats->nstat_txretransmit;
368 out_counts->nstat_connectattempts = rt_stats->nstat_connectattempts;
369 out_counts->nstat_connectsuccesses = rt_stats->nstat_connectsuccesses;
370 out_counts->nstat_min_rtt = rt_stats->nstat_min_rtt;
371 out_counts->nstat_avg_rtt = rt_stats->nstat_avg_rtt;
372 out_counts->nstat_var_rtt = rt_stats->nstat_var_rtt;
373 }
374 else
375 bzero(out_counts, sizeof(*out_counts));
376
377 return 0;
378}
379
380static void
381nstat_route_release(
316670eb
A
382 nstat_provider_cookie_t cookie,
383 __unused int locked)
6d2010ae
A
384{
385 rtfree((struct rtentry*)cookie);
386}
387
388static u_int32_t nstat_route_watchers = 0;
389
390static int
391nstat_route_walktree_add(
392 struct radix_node *rn,
393 void *context)
394{
395 errno_t result = 0;
396 struct rtentry *rt = (struct rtentry *)rn;
397 nstat_control_state *state = (nstat_control_state*)context;
398
399 lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED);
400
401 /* RTF_UP can't change while rnh_lock is held */
402 if ((rt->rt_flags & RTF_UP) != 0)
403 {
404 /* Clear RTPRF_OURS if the route is still usable */
405 RT_LOCK(rt);
406 if (rt_validate(rt)) {
407 RT_ADDREF_LOCKED(rt);
408 RT_UNLOCK(rt);
409 } else {
410 RT_UNLOCK(rt);
411 rt = NULL;
412 }
413
414 /* Otherwise if RTF_CONDEMNED, treat it as if it were down */
415 if (rt == NULL)
416 return (0);
417
418 result = nstat_control_source_add(0, state, &nstat_route_provider, rt);
419 if (result != 0)
420 rtfree_locked(rt);
421 }
422
423 return result;
424}
425
426static errno_t
427nstat_route_add_watcher(
428 nstat_control_state *state)
429{
430 int i;
431 errno_t result = 0;
432 OSIncrementAtomic(&nstat_route_watchers);
433
434 lck_mtx_lock(rnh_lock);
435 for (i = 1; i < AF_MAX; i++)
436 {
437 struct radix_node_head *rnh;
438 rnh = rt_tables[i];
439 if (!rnh) continue;
440
441 result = rnh->rnh_walktree(rnh, nstat_route_walktree_add, state);
442 if (result != 0)
443 {
6d2010ae
A
444 break;
445 }
446 }
447 lck_mtx_unlock(rnh_lock);
448
449 return result;
450}
451
452__private_extern__ void
453nstat_route_new_entry(
454 struct rtentry *rt)
455{
456 if (nstat_route_watchers == 0)
457 return;
458
459 lck_mtx_lock(&nstat_mtx);
460 if ((rt->rt_flags & RTF_UP) != 0)
461 {
462 nstat_control_state *state;
316670eb 463 for (state = nstat_controls; state; state = state->ncs_next)
6d2010ae 464 {
316670eb 465 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_ROUTE)) != 0)
6d2010ae
A
466 {
467 // this client is watching routes
468 // acquire a reference for the route
469 RT_ADDREF(rt);
470
471 // add the source, if that fails, release the reference
472 if (nstat_control_source_add(0, state, &nstat_route_provider, rt) != 0)
473 RT_REMREF(rt);
474 }
475 }
476 }
477 lck_mtx_unlock(&nstat_mtx);
478}
479
480static void
481nstat_route_remove_watcher(
482 __unused nstat_control_state *state)
483{
484 OSDecrementAtomic(&nstat_route_watchers);
485}
486
487static errno_t
488nstat_route_copy_descriptor(
489 nstat_provider_cookie_t cookie,
490 void *data,
491 u_int32_t len)
492{
493 nstat_route_descriptor *desc = (nstat_route_descriptor*)data;
494 if (len < sizeof(*desc))
495 {
6d2010ae
A
496 return EINVAL;
497 }
498 bzero(desc, sizeof(*desc));
499
500 struct rtentry *rt = (struct rtentry*)cookie;
501 desc->id = (uintptr_t)rt;
502 desc->parent_id = (uintptr_t)rt->rt_parent;
503 desc->gateway_id = (uintptr_t)rt->rt_gwroute;
504
505
506 // key/dest
507 struct sockaddr *sa;
508 if ((sa = rt_key(rt)))
509 nstat_copy_sa_out(sa, &desc->dst.sa, sizeof(desc->dst));
510
511 // mask
512 if ((sa = rt_mask(rt)) && sa->sa_len <= sizeof(desc->mask))
513 memcpy(&desc->mask, sa, sa->sa_len);
514
515 // gateway
516 if ((sa = rt->rt_gateway))
517 nstat_copy_sa_out(sa, &desc->gateway.sa, sizeof(desc->gateway));
518
519 if (rt->rt_ifp)
520 desc->ifindex = rt->rt_ifp->if_index;
521
522 desc->flags = rt->rt_flags;
523
524 return 0;
525}
526
527static void
528nstat_init_route_provider(void)
529{
530 bzero(&nstat_route_provider, sizeof(nstat_route_provider));
531 nstat_route_provider.nstat_descriptor_length = sizeof(nstat_route_descriptor);
532 nstat_route_provider.nstat_provider_id = NSTAT_PROVIDER_ROUTE;
533 nstat_route_provider.nstat_lookup = nstat_route_lookup;
534 nstat_route_provider.nstat_gone = nstat_route_gone;
535 nstat_route_provider.nstat_counts = nstat_route_counts;
536 nstat_route_provider.nstat_release = nstat_route_release;
537 nstat_route_provider.nstat_watcher_add = nstat_route_add_watcher;
538 nstat_route_provider.nstat_watcher_remove = nstat_route_remove_watcher;
539 nstat_route_provider.nstat_copy_descriptor = nstat_route_copy_descriptor;
540 nstat_route_provider.next = nstat_providers;
541 nstat_providers = &nstat_route_provider;
542}
543
544#pragma mark -- Route Collection --
545
546static struct nstat_counts*
547nstat_route_attach(
548 struct rtentry *rte)
549{
550 struct nstat_counts *result = rte->rt_stats;
551 if (result) return result;
552
553 if (nstat_malloc_tag == NULL) nstat_init();
554
555 result = nstat_malloc_aligned(sizeof(*result), sizeof(u_int64_t), nstat_malloc_tag);
556 if (!result) return result;
557
558 bzero(result, sizeof(*result));
559
560 if (!OSCompareAndSwapPtr(NULL, result, &rte->rt_stats))
561 {
562 nstat_free_aligned(result, nstat_malloc_tag);
563 result = rte->rt_stats;
564 }
565
566 return result;
567}
568
569__private_extern__ void
570nstat_route_detach(
571 struct rtentry *rte)
572{
573 if (rte->rt_stats)
574 {
575 nstat_free_aligned(rte->rt_stats, nstat_malloc_tag);
576 rte->rt_stats = NULL;
577 }
578}
579
580__private_extern__ void
581nstat_route_connect_attempt(
582 struct rtentry *rte)
583{
584 while (rte)
585 {
586 struct nstat_counts* stats = nstat_route_attach(rte);
587 if (stats)
588 {
589 OSIncrementAtomic(&stats->nstat_connectattempts);
590 }
591
592 rte = rte->rt_parent;
593 }
594}
595
596__private_extern__ void
597nstat_route_connect_success(
598 struct rtentry *rte)
599{
600 // This route
601 while (rte)
602 {
603 struct nstat_counts* stats = nstat_route_attach(rte);
604 if (stats)
605 {
606 OSIncrementAtomic(&stats->nstat_connectsuccesses);
607 }
608
609 rte = rte->rt_parent;
610 }
611}
612
613__private_extern__ void
614nstat_route_tx(
615 struct rtentry *rte,
616 u_int32_t packets,
617 u_int32_t bytes,
618 u_int32_t flags)
619{
620 while (rte)
621 {
622 struct nstat_counts* stats = nstat_route_attach(rte);
623 if (stats)
624 {
625 if ((flags & NSTAT_TX_FLAG_RETRANSMIT) != 0)
626 {
627 OSAddAtomic(bytes, &stats->nstat_txretransmit);
628 }
629 else
630 {
631 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_txpackets);
632 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_txbytes);
633 }
634 }
635
636 rte = rte->rt_parent;
637 }
638}
639
640__private_extern__ void
641nstat_route_rx(
642 struct rtentry *rte,
643 u_int32_t packets,
644 u_int32_t bytes,
645 u_int32_t flags)
646{
647 while (rte)
648 {
649 struct nstat_counts* stats = nstat_route_attach(rte);
650 if (stats)
651 {
652 if (flags == 0)
653 {
654 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_rxpackets);
655 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_rxbytes);
656 }
657 else
658 {
659 if (flags & NSTAT_RX_FLAG_OUT_OF_ORDER)
660 OSAddAtomic(bytes, &stats->nstat_rxoutoforderbytes);
661 if (flags & NSTAT_RX_FLAG_DUPLICATE)
662 OSAddAtomic(bytes, &stats->nstat_rxduplicatebytes);
663 }
664 }
665
666 rte = rte->rt_parent;
667 }
668}
669
670__private_extern__ void
671nstat_route_rtt(
672 struct rtentry *rte,
673 u_int32_t rtt,
674 u_int32_t rtt_var)
675{
676 const int32_t factor = 8;
677
678 while (rte)
679 {
680 struct nstat_counts* stats = nstat_route_attach(rte);
681 if (stats)
682 {
683 int32_t oldrtt;
684 int32_t newrtt;
685
686 // average
687 do
688 {
689 oldrtt = stats->nstat_avg_rtt;
690 if (oldrtt == 0)
691 {
692 newrtt = rtt;
693 }
694 else
695 {
696 newrtt = oldrtt - (oldrtt - (int32_t)rtt) / factor;
697 }
698 if (oldrtt == newrtt) break;
699 } while (!OSCompareAndSwap(oldrtt, newrtt, &stats->nstat_avg_rtt));
700
701 // minimum
702 do
703 {
704 oldrtt = stats->nstat_min_rtt;
705 if (oldrtt != 0 && oldrtt < (int32_t)rtt)
706 {
707 break;
708 }
709 } while (!OSCompareAndSwap(oldrtt, rtt, &stats->nstat_min_rtt));
710
711 // variance
712 do
713 {
714 oldrtt = stats->nstat_var_rtt;
715 if (oldrtt == 0)
716 {
717 newrtt = rtt_var;
718 }
719 else
720 {
721 newrtt = oldrtt - (oldrtt - (int32_t)rtt_var) / factor;
722 }
723 if (oldrtt == newrtt) break;
724 } while (!OSCompareAndSwap(oldrtt, newrtt, &stats->nstat_var_rtt));
725 }
726
727 rte = rte->rt_parent;
728 }
729}
730
316670eb 731
6d2010ae
A
732#pragma mark -- TCP Provider --
733
734static nstat_provider nstat_tcp_provider;
735
736static errno_t
737nstat_tcpudp_lookup(
738 struct inpcbinfo *inpinfo,
739 const void *data,
740 u_int32_t length,
741 nstat_provider_cookie_t *out_cookie)
742{
743 // parameter validation
744 const nstat_tcp_add_param *param = (const nstat_tcp_add_param*)data;
745 if (length < sizeof(*param))
746 {
6d2010ae
A
747 return EINVAL;
748 }
749
750 // src and dst must match
751 if (param->remote.v4.sin_family != 0 &&
752 param->remote.v4.sin_family != param->local.v4.sin_family)
753 {
6d2010ae
A
754 return EINVAL;
755 }
756
757 struct inpcb *inp = NULL;
758
759 switch (param->local.v4.sin_family)
760 {
761 case AF_INET:
762 {
763 if (param->local.v4.sin_len != sizeof(param->local.v4) ||
764 (param->remote.v4.sin_family != 0 &&
765 param->remote.v4.sin_len != sizeof(param->remote.v4)))
766 {
6d2010ae
A
767 return EINVAL;
768 }
769
770 inp = in_pcblookup_hash(inpinfo, param->remote.v4.sin_addr, param->remote.v4.sin_port,
771 param->local.v4.sin_addr, param->local.v4.sin_port, 1, NULL);
772 }
773 break;
774
775#if INET6
776 case AF_INET6:
777 {
778 union
779 {
780 const struct in6_addr *in6c;
781 struct in6_addr *in6;
782 } local, remote;
783
784 if (param->local.v6.sin6_len != sizeof(param->local.v6) ||
785 (param->remote.v6.sin6_family != 0 &&
786 param->remote.v6.sin6_len != sizeof(param->remote.v6)))
787 {
6d2010ae
A
788 return EINVAL;
789 }
790
791 local.in6c = &param->local.v6.sin6_addr;
792 remote.in6c = &param->remote.v6.sin6_addr;
793
794 inp = in6_pcblookup_hash(inpinfo, remote.in6, param->remote.v6.sin6_port,
795 local.in6, param->local.v6.sin6_port, 1, NULL);
796 }
797 break;
798#endif
799
800 default:
6d2010ae
A
801 return EINVAL;
802 }
803
804 if (inp == NULL) return ENOENT;
805
806 // At this point we have a ref to the inpcb
807 *out_cookie = inp;
808 return 0;
809}
810
811static errno_t
812nstat_tcp_lookup(
813 const void *data,
814 u_int32_t length,
815 nstat_provider_cookie_t *out_cookie)
816{
817 return nstat_tcpudp_lookup(&tcbinfo, data, length, out_cookie);
818}
819
820static int
821nstat_tcp_gone(
822 nstat_provider_cookie_t cookie)
823{
824 struct inpcb *inp = (struct inpcb*)cookie;
825 struct tcpcb *tp = intotcpcb(inp);
826 return (inp->inp_state == INPCB_STATE_DEAD || tp->t_state == TCPS_TIME_WAIT) ? 1 : 0;
827}
828
829static errno_t
830nstat_tcp_counts(
831 nstat_provider_cookie_t cookie,
832 struct nstat_counts *out_counts,
833 int *out_gone)
834{
835 struct inpcb *inp = (struct inpcb*)cookie;
836 struct tcpcb *tp = intotcpcb(inp);
837
838 bzero(out_counts, sizeof(*out_counts));
839
840 *out_gone = 0;
841
842 // if the pcb is in the dead state, we should stop using it
843 if (inp->inp_state == INPCB_STATE_DEAD || tp->t_state == TCPS_TIME_WAIT)
844 {
845 *out_gone = 1;
846 }
847
316670eb
A
848 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
849 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
850 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
851 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
852 out_counts->nstat_rxduplicatebytes = tp->t_stat.rxduplicatebytes;
853 out_counts->nstat_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes;
854 out_counts->nstat_txretransmit = tp->t_stat.txretransmitbytes;
855 out_counts->nstat_connectattempts = tp->t_state >= TCPS_SYN_SENT ? 1 : 0;
856 out_counts->nstat_connectsuccesses = tp->t_state >= TCPS_ESTABLISHED ? 1 : 0;
857 out_counts->nstat_avg_rtt = tp->t_srtt;
858 out_counts->nstat_min_rtt = tp->t_rttbest;
859 out_counts->nstat_var_rtt = tp->t_rttvar;
860 if (out_counts->nstat_avg_rtt < out_counts->nstat_min_rtt)
861 out_counts->nstat_min_rtt = out_counts->nstat_avg_rtt;
6d2010ae
A
862
863 return 0;
864}
865
866static void
867nstat_tcp_release(
316670eb
A
868 nstat_provider_cookie_t cookie,
869 int locked)
6d2010ae
A
870{
871 struct inpcb *inp = (struct inpcb*)cookie;
316670eb 872 in_pcb_checkstate(inp, WNT_RELEASE, locked);
6d2010ae
A
873}
874
6d2010ae
A
875static errno_t
876nstat_tcp_add_watcher(
877 nstat_control_state *state)
878{
879 OSIncrementAtomic(&nstat_tcp_watchers);
880
881 lck_rw_lock_shared(tcbinfo.mtx);
882
883 // Add all current tcp inpcbs. Ignore those in timewait
884 struct inpcb *inp;
885 for (inp = LIST_FIRST(tcbinfo.listhead); inp; inp = LIST_NEXT(inp, inp_list))
886 {
887 if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
888 continue;
889
890 if (nstat_control_source_add(0, state, &nstat_tcp_provider, inp) != 0)
891 {
892 in_pcb_checkstate(inp, WNT_RELEASE, 0);
893 break;
894 }
895 }
896
897 lck_rw_done(tcbinfo.mtx);
898
899 return 0;
900}
901
902static void
903nstat_tcp_remove_watcher(
904 __unused nstat_control_state *state)
905{
906 OSDecrementAtomic(&nstat_tcp_watchers);
907}
908
909__private_extern__ void
910nstat_tcp_new_pcb(
911 struct inpcb *inp)
912{
913 if (nstat_tcp_watchers == 0)
914 return;
915
916 lck_mtx_lock(&nstat_mtx);
917 nstat_control_state *state;
316670eb 918 for (state = nstat_controls; state; state = state->ncs_next)
6d2010ae 919 {
316670eb 920 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_TCP)) != 0)
6d2010ae
A
921 {
922 // this client is watching tcp
923 // acquire a reference for it
924 if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
925 break;
926
927 // add the source, if that fails, release the reference
928 if (nstat_control_source_add(0, state, &nstat_tcp_provider, inp) != 0)
929 {
930 in_pcb_checkstate(inp, WNT_RELEASE, 0);
931 break;
932 }
933 }
934 }
935 lck_mtx_unlock(&nstat_mtx);
936}
937
316670eb
A
938__private_extern__ void
939nstat_pcb_detach(struct inpcb *inp)
940{
941 nstat_control_state *state;
942 nstat_src *src, *prevsrc;
943 nstat_src *dead_list = NULL;
944
945 if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0))
946 return;
947
948 lck_mtx_lock(&nstat_mtx);
949 for (state = nstat_controls; state; state = state->ncs_next) {
950 lck_mtx_lock(&state->mtx);
951 for (prevsrc = NULL, src = state->ncs_srcs; src;
952 prevsrc = src, src = src->next)
953 if (src->cookie == inp)
954 break;
955
956 if (src) {
957 // send one last counts notification
958 nstat_control_send_counts(state, src, 0, NULL);
959
960 // send a last description
961 nstat_control_send_description(state, src, 0);
962
963 // send the source removed notification
964 nstat_control_send_removed(state, src);
965
966 if (prevsrc)
967 prevsrc->next = src->next;
968 else
969 state->ncs_srcs = src->next;
970
971 src->next = dead_list;
972 dead_list = src;
973 }
974 lck_mtx_unlock(&state->mtx);
975 }
976 lck_mtx_unlock(&nstat_mtx);
977
978 while (dead_list) {
979 src = dead_list;
980 dead_list = src->next;
981
982 nstat_control_cleanup_source(NULL, src, TRUE);
983 }
984}
985
6d2010ae
A
986static errno_t
987nstat_tcp_copy_descriptor(
988 nstat_provider_cookie_t cookie,
989 void *data,
990 u_int32_t len)
991{
992 if (len < sizeof(nstat_tcp_descriptor))
993 {
6d2010ae
A
994 return EINVAL;
995 }
996
997 nstat_tcp_descriptor *desc = (nstat_tcp_descriptor*)data;
998 struct inpcb *inp = (struct inpcb*)cookie;
999 struct tcpcb *tp = intotcpcb(inp);
316670eb
A
1000
1001 if (inp->inp_state == INPCB_STATE_DEAD)
1002 return EINVAL;
6d2010ae
A
1003
1004 bzero(desc, sizeof(*desc));
1005
1006 if (inp->inp_vflag & INP_IPV6)
1007 {
1008 nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
1009 &desc->local.v6, sizeof(desc->local));
1010 nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
1011 &desc->remote.v6, sizeof(desc->remote));
1012 }
1013 else if (inp->inp_vflag & INP_IPV4)
1014 {
1015 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
1016 &desc->local.v4, sizeof(desc->local));
1017 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
1018 &desc->remote.v4, sizeof(desc->remote));
1019 }
1020
1021 desc->state = intotcpcb(inp)->t_state;
316670eb
A
1022 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
1023 inp->inp_last_outifp->if_index;
6d2010ae
A
1024
1025 // danger - not locked, values could be bogus
1026 desc->txunacked = tp->snd_max - tp->snd_una;
1027 desc->txwindow = tp->snd_wnd;
1028 desc->txcwindow = tp->snd_cwnd;
1029
1030 struct socket *so = inp->inp_socket;
1031 if (so)
1032 {
1033 // TBD - take the socket lock around these to make sure
1034 // they're in sync?
1035 desc->upid = so->last_upid;
1036 desc->pid = so->last_pid;
316670eb 1037 desc->traffic_class = so->so_traffic_class;
6d2010ae
A
1038
1039 proc_name(desc->pid, desc->pname, sizeof(desc->pname));
1040 desc->pname[sizeof(desc->pname) - 1] = 0;
1041
1042 desc->sndbufsize = so->so_snd.sb_hiwat;
1043 desc->sndbufused = so->so_snd.sb_cc;
1044 desc->rcvbufsize = so->so_rcv.sb_hiwat;
1045 desc->rcvbufused = so->so_rcv.sb_cc;
1046 }
1047
1048 return 0;
1049}
1050
1051static void
1052nstat_init_tcp_provider(void)
1053{
1054 bzero(&nstat_tcp_provider, sizeof(nstat_tcp_provider));
1055 nstat_tcp_provider.nstat_descriptor_length = sizeof(nstat_tcp_descriptor);
1056 nstat_tcp_provider.nstat_provider_id = NSTAT_PROVIDER_TCP;
1057 nstat_tcp_provider.nstat_lookup = nstat_tcp_lookup;
1058 nstat_tcp_provider.nstat_gone = nstat_tcp_gone;
1059 nstat_tcp_provider.nstat_counts = nstat_tcp_counts;
1060 nstat_tcp_provider.nstat_release = nstat_tcp_release;
1061 nstat_tcp_provider.nstat_watcher_add = nstat_tcp_add_watcher;
1062 nstat_tcp_provider.nstat_watcher_remove = nstat_tcp_remove_watcher;
1063 nstat_tcp_provider.nstat_copy_descriptor = nstat_tcp_copy_descriptor;
1064 nstat_tcp_provider.next = nstat_providers;
1065 nstat_providers = &nstat_tcp_provider;
1066}
1067
1068#pragma mark -- UDP Provider --
1069
1070static nstat_provider nstat_udp_provider;
1071
1072static errno_t
1073nstat_udp_lookup(
1074 const void *data,
1075 u_int32_t length,
1076 nstat_provider_cookie_t *out_cookie)
1077{
1078 return nstat_tcpudp_lookup(&udbinfo, data, length, out_cookie);
1079}
1080
1081static int
1082nstat_udp_gone(
1083 nstat_provider_cookie_t cookie)
1084{
1085 struct inpcb *inp = (struct inpcb*)cookie;
1086 return (inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
1087}
1088
1089static errno_t
1090nstat_udp_counts(
1091 nstat_provider_cookie_t cookie,
1092 struct nstat_counts *out_counts,
1093 int *out_gone)
1094{
1095 struct inpcb *inp = (struct inpcb*)cookie;
1096
1097 *out_gone = 0;
1098
1099 // if the pcb is in the dead state, we should stop using it
1100 if (inp->inp_state == INPCB_STATE_DEAD)
1101 {
1102 *out_gone = 1;
1103 }
1104
1105 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
1106 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
1107 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
1108 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
1109
1110 return 0;
1111}
1112
1113static void
1114nstat_udp_release(
316670eb
A
1115 nstat_provider_cookie_t cookie,
1116 int locked)
6d2010ae
A
1117{
1118 struct inpcb *inp = (struct inpcb*)cookie;
316670eb 1119 in_pcb_checkstate(inp, WNT_RELEASE, locked);
6d2010ae
A
1120}
1121
6d2010ae
A
1122static errno_t
1123nstat_udp_add_watcher(
1124 nstat_control_state *state)
1125{
1126 OSIncrementAtomic(&nstat_udp_watchers);
1127
1128 lck_rw_lock_shared(tcbinfo.mtx);
1129
1130 // Add all current tcp inpcbs. Ignore those in timewait
1131 struct inpcb *inp;
1132 for (inp = LIST_FIRST(udbinfo.listhead); inp; inp = LIST_NEXT(inp, inp_list))
1133 {
1134 if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
1135 continue;
1136
1137 if (nstat_control_source_add(0, state, &nstat_udp_provider, inp) != 0)
1138 {
1139 in_pcb_checkstate(inp, WNT_RELEASE, 0);
1140 break;
1141 }
1142 }
1143
1144 lck_rw_done(tcbinfo.mtx);
1145
1146 return 0;
1147}
1148
1149static void
1150nstat_udp_remove_watcher(
1151 __unused nstat_control_state *state)
1152{
1153 OSDecrementAtomic(&nstat_udp_watchers);
1154}
1155
1156__private_extern__ void
1157nstat_udp_new_pcb(
1158 struct inpcb *inp)
1159{
1160 if (nstat_udp_watchers == 0)
1161 return;
1162
1163 lck_mtx_lock(&nstat_mtx);
1164 nstat_control_state *state;
316670eb 1165 for (state = nstat_controls; state; state = state->ncs_next)
6d2010ae 1166 {
316670eb 1167 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_UDP)) != 0)
6d2010ae
A
1168 {
1169 // this client is watching tcp
1170 // acquire a reference for it
1171 if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
1172 break;
1173
1174 // add the source, if that fails, release the reference
1175 if (nstat_control_source_add(0, state, &nstat_udp_provider, inp) != 0)
1176 {
1177 in_pcb_checkstate(inp, WNT_RELEASE, 0);
1178 break;
1179 }
1180 }
1181 }
1182 lck_mtx_unlock(&nstat_mtx);
1183}
1184
1185static errno_t
1186nstat_udp_copy_descriptor(
1187 nstat_provider_cookie_t cookie,
1188 void *data,
1189 u_int32_t len)
1190{
1191 if (len < sizeof(nstat_udp_descriptor))
1192 {
6d2010ae
A
1193 return EINVAL;
1194 }
1195
1196 nstat_udp_descriptor *desc = (nstat_udp_descriptor*)data;
1197 struct inpcb *inp = (struct inpcb*)cookie;
1198
316670eb
A
1199 if (inp->inp_state == INPCB_STATE_DEAD)
1200 return EINVAL;
1201
6d2010ae
A
1202 bzero(desc, sizeof(*desc));
1203
1204 if (inp->inp_vflag & INP_IPV6)
1205 {
1206 nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
1207 &desc->local.v6, sizeof(desc->local));
1208 nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
1209 &desc->remote.v6, sizeof(desc->remote));
1210 }
1211 else if (inp->inp_vflag & INP_IPV4)
1212 {
1213 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
1214 &desc->local.v4, sizeof(desc->local));
1215 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
1216 &desc->remote.v4, sizeof(desc->remote));
1217 }
1218
316670eb
A
1219 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
1220 inp->inp_last_outifp->if_index;
1221
6d2010ae
A
1222 struct socket *so = inp->inp_socket;
1223 if (so)
1224 {
1225 // TBD - take the socket lock around these to make sure
1226 // they're in sync?
1227 desc->upid = so->last_upid;
1228 desc->pid = so->last_pid;
1229
1230 desc->rcvbufsize = so->so_rcv.sb_hiwat;
1231 desc->rcvbufused = so->so_rcv.sb_cc;
316670eb 1232 desc->traffic_class = so->so_traffic_class;
6d2010ae
A
1233
1234 proc_name(desc->pid, desc->pname, sizeof(desc->pname));
1235 desc->pname[sizeof(desc->pname) - 1] = 0;
1236 }
1237
1238 return 0;
1239}
1240
1241static void
1242nstat_init_udp_provider(void)
1243{
1244 bzero(&nstat_udp_provider, sizeof(nstat_udp_provider));
1245 nstat_udp_provider.nstat_provider_id = NSTAT_PROVIDER_UDP;
1246 nstat_udp_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor);
1247 nstat_udp_provider.nstat_lookup = nstat_udp_lookup;
1248 nstat_udp_provider.nstat_gone = nstat_udp_gone;
1249 nstat_udp_provider.nstat_counts = nstat_udp_counts;
1250 nstat_udp_provider.nstat_watcher_add = nstat_udp_add_watcher;
1251 nstat_udp_provider.nstat_watcher_remove = nstat_udp_remove_watcher;
1252 nstat_udp_provider.nstat_copy_descriptor = nstat_udp_copy_descriptor;
1253 nstat_udp_provider.nstat_release = nstat_udp_release;
1254 nstat_udp_provider.next = nstat_providers;
1255 nstat_providers = &nstat_udp_provider;
1256}
1257
1258#pragma mark -- Kernel Control Socket --
1259
6d2010ae
A
1260static kern_ctl_ref nstat_ctlref = NULL;
1261static lck_grp_t *nstat_lck_grp = NULL;
1262
1263static errno_t nstat_control_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo);
1264static errno_t nstat_control_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo);
1265static errno_t nstat_control_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags);
6d2010ae
A
1266
1267
1268static void*
1269nstat_idle_check(
1270 __unused thread_call_param_t p0,
1271 __unused thread_call_param_t p1)
1272{
1273 lck_mtx_lock(&nstat_mtx);
1274
316670eb 1275 nstat_idle_time = 0;
6d2010ae
A
1276
1277 nstat_control_state *control;
1278 nstat_src *dead = NULL;
1279 nstat_src *dead_list = NULL;
316670eb 1280 for (control = nstat_controls; control; control = control->ncs_next)
6d2010ae
A
1281 {
1282 lck_mtx_lock(&control->mtx);
316670eb 1283 nstat_src **srcpp = &control->ncs_srcs;
6d2010ae 1284
316670eb 1285 if (!(control->ncs_flags & NSTAT_FLAG_REQCOUNTS))
6d2010ae 1286 {
316670eb 1287 while(*srcpp != NULL)
6d2010ae 1288 {
316670eb
A
1289 if ((*srcpp)->provider->nstat_gone((*srcpp)->cookie))
1290 {
1291 // Pull it off the list
1292 dead = *srcpp;
1293 *srcpp = (*srcpp)->next;
1294
1295 // send one last counts notification
1296 nstat_control_send_counts(control, dead,
1297 0, NULL);
1298
1299 // send a last description
1300 nstat_control_send_description(control, dead, 0);
1301
1302 // send the source removed notification
1303 nstat_control_send_removed(control, dead);
1304
1305 // Put this on the list to release later
1306 dead->next = dead_list;
1307 dead_list = dead;
1308 }
1309 else
1310 {
1311 srcpp = &(*srcpp)->next;
1312 }
6d2010ae
A
1313 }
1314 }
316670eb 1315 control->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
6d2010ae
A
1316 lck_mtx_unlock(&control->mtx);
1317 }
1318
1319 if (nstat_controls)
1320 {
1321 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
1322 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
1323 }
1324
1325 lck_mtx_unlock(&nstat_mtx);
1326
1327 // Release the sources now that we aren't holding lots of locks
1328 while (dead_list)
1329 {
1330 dead = dead_list;
1331 dead_list = dead->next;
1332
316670eb 1333 nstat_control_cleanup_source(NULL, dead, FALSE);
6d2010ae
A
1334 }
1335
1336 return NULL;
1337}
1338
1339static void
1340nstat_control_register(void)
1341{
1342 // Create our lock group first
1343 lck_grp_attr_t *grp_attr = lck_grp_attr_alloc_init();
1344 lck_grp_attr_setdefault(grp_attr);
1345 nstat_lck_grp = lck_grp_alloc_init("network statistics kctl", grp_attr);
1346 lck_grp_attr_free(grp_attr);
1347
1348 lck_mtx_init(&nstat_mtx, nstat_lck_grp, NULL);
1349
1350 // Register the control
1351 struct kern_ctl_reg nstat_control;
1352 bzero(&nstat_control, sizeof(nstat_control));
1353 strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name));
1354 nstat_control.ctl_connect = nstat_control_connect;
1355 nstat_control.ctl_disconnect = nstat_control_disconnect;
1356 nstat_control.ctl_send = nstat_control_send;
1357
316670eb 1358 ctl_register(&nstat_control, &nstat_ctlref);
6d2010ae
A
1359}
1360
1361static void
1362nstat_control_cleanup_source(
1363 nstat_control_state *state,
316670eb
A
1364 struct nstat_src *src,
1365 boolean_t locked)
6d2010ae
A
1366{
1367 if (state)
316670eb 1368 nstat_control_send_removed(state, src);
6d2010ae
A
1369
1370 // Cleanup the source if we found it.
316670eb 1371 src->provider->nstat_release(src->cookie, locked);
6d2010ae
A
1372 OSFree(src, sizeof(*src), nstat_malloc_tag);
1373}
1374
1375static errno_t
1376nstat_control_connect(
1377 kern_ctl_ref kctl,
1378 struct sockaddr_ctl *sac,
1379 void **uinfo)
1380{
1381 nstat_control_state *state = OSMalloc(sizeof(*state), nstat_malloc_tag);
1382 if (state == NULL) return ENOMEM;
1383
1384 bzero(state, sizeof(*state));
1385 lck_mtx_init(&state->mtx, nstat_lck_grp, NULL);
316670eb
A
1386 state->ncs_kctl = kctl;
1387 state->ncs_unit = sac->sc_unit;
1388 state->ncs_flags = NSTAT_FLAG_REQCOUNTS;
6d2010ae
A
1389 *uinfo = state;
1390
6d2010ae 1391 lck_mtx_lock(&nstat_mtx);
316670eb 1392 state->ncs_next = nstat_controls;
6d2010ae
A
1393 nstat_controls = state;
1394
316670eb 1395 if (nstat_idle_time == 0)
6d2010ae
A
1396 {
1397 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
1398 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
1399 }
1400
1401 lck_mtx_unlock(&nstat_mtx);
1402
1403 return 0;
1404}
1405
1406static errno_t
1407nstat_control_disconnect(
1408 __unused kern_ctl_ref kctl,
1409 __unused u_int32_t unit,
1410 __unused void *uinfo)
1411{
1412 u_int32_t watching;
1413 nstat_control_state *state = (nstat_control_state*)uinfo;
1414
1415 // pull it out of the global list of states
1416 lck_mtx_lock(&nstat_mtx);
1417 nstat_control_state **statepp;
316670eb 1418 for (statepp = &nstat_controls; *statepp; statepp = &(*statepp)->ncs_next)
6d2010ae
A
1419 {
1420 if (*statepp == state)
1421 {
316670eb 1422 *statepp = state->ncs_next;
6d2010ae
A
1423 break;
1424 }
1425 }
1426 lck_mtx_unlock(&nstat_mtx);
1427
1428 lck_mtx_lock(&state->mtx);
1429 // Stop watching for sources
1430 nstat_provider *provider;
316670eb
A
1431 watching = state->ncs_watching;
1432 state->ncs_watching = 0;
6d2010ae
A
1433 for (provider = nstat_providers; provider && watching; provider = provider->next)
1434 {
1435 if ((watching & (1 << provider->nstat_provider_id)) != 0)
1436 {
1437 watching &= ~(1 << provider->nstat_provider_id);
1438 provider->nstat_watcher_remove(state);
1439 }
1440 }
1441
1442 // set cleanup flags
316670eb 1443 state->ncs_flags |= NSTAT_FLAG_CLEANUP;
6d2010ae
A
1444
1445 // Copy out the list of sources
316670eb
A
1446 nstat_src *srcs = state->ncs_srcs;
1447 state->ncs_srcs = NULL;
6d2010ae
A
1448 lck_mtx_unlock(&state->mtx);
1449
1450 while (srcs)
1451 {
1452 nstat_src *src;
1453
1454 // pull it out of the list
1455 src = srcs;
1456 srcs = src->next;
1457
1458 // clean it up
316670eb 1459 nstat_control_cleanup_source(NULL, src, FALSE);
6d2010ae
A
1460 }
1461
1462 OSFree(state, sizeof(*state), nstat_malloc_tag);
1463
1464 return 0;
1465}
1466
1467static nstat_src_ref_t
1468nstat_control_next_src_ref(
1469 nstat_control_state *state)
1470{
1471 int i = 0;
1472 nstat_src_ref_t toReturn = NSTAT_SRC_REF_INVALID;
1473
1474 for (i = 0; i < 1000 && toReturn == NSTAT_SRC_REF_INVALID; i++)
1475 {
316670eb
A
1476 if (state->ncs_next_srcref == NSTAT_SRC_REF_INVALID ||
1477 state->ncs_next_srcref == NSTAT_SRC_REF_ALL)
6d2010ae 1478 {
316670eb 1479 state->ncs_next_srcref = 1;
6d2010ae
A
1480 }
1481
1482 nstat_src *src;
316670eb 1483 for (src = state->ncs_srcs; src; src = src->next)
6d2010ae 1484 {
316670eb 1485 if (src->srcref == state->ncs_next_srcref)
6d2010ae
A
1486 break;
1487 }
1488
316670eb
A
1489 if (src == NULL) toReturn = state->ncs_next_srcref;
1490 state->ncs_next_srcref++;
6d2010ae
A
1491 }
1492
1493 return toReturn;
1494}
1495
316670eb
A
1496static errno_t
1497nstat_control_send_counts(
1498 nstat_control_state *state,
1499 nstat_src *src,
1500 unsigned long long context,
1501 int *gone)
1502{
1503 nstat_msg_src_counts counts;
1504 int localgone = 0;
1505 errno_t result = 0;
1506
1507 counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
1508 counts.hdr.context = context;
1509 counts.srcref = src->srcref;
1510 bzero(&counts.counts, sizeof(counts.counts));
1511 if (src->provider->nstat_counts(src->cookie, &counts.counts,
1512 &localgone) == 0) {
1513 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &counts,
1514 sizeof(counts), CTL_DATA_EOR);
1515 }
1516 if (gone)
1517 *gone = localgone;
1518 return result;
1519}
1520
6d2010ae
A
1521static int
1522nstat_control_send_description(
1523 nstat_control_state *state,
1524 nstat_src *src,
1525 u_int64_t context)
1526{
1527 // Provider doesn't support getting the descriptor? Done.
1528 if (src->provider->nstat_descriptor_length == 0 ||
1529 src->provider->nstat_copy_descriptor == NULL)
1530 {
6d2010ae
A
1531 return EOPNOTSUPP;
1532 }
1533
1534 // Allocate storage for the descriptor message
1535 mbuf_t msg;
1536 unsigned int one = 1;
1537 u_int32_t size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
1538 if (mbuf_allocpacket(MBUF_WAITOK, size, &one, &msg) != 0)
1539 {
6d2010ae
A
1540 return ENOMEM;
1541 }
1542
1543 nstat_msg_src_description *desc = (nstat_msg_src_description*)mbuf_data(msg);
1544 mbuf_setlen(msg, size);
1545 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
1546
1547 // Query the provider for the provider specific bits
1548 errno_t result = src->provider->nstat_copy_descriptor(src->cookie, desc->data, src->provider->nstat_descriptor_length);
1549
1550 if (result != 0)
1551 {
1552 mbuf_freem(msg);
6d2010ae
A
1553 return result;
1554 }
1555
1556 desc->hdr.context = context;
1557 desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
1558 desc->srcref = src->srcref;
1559 desc->provider = src->provider->nstat_provider_id;
1560
316670eb 1561 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
6d2010ae
A
1562 if (result != 0)
1563 {
6d2010ae
A
1564 mbuf_freem(msg);
1565 }
1566
1567 return result;
1568}
1569
316670eb
A
1570static errno_t
1571nstat_control_send_removed(
1572 nstat_control_state *state,
1573 nstat_src *src)
1574{
1575 nstat_msg_src_removed removed;
1576 errno_t result;
1577
1578 removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED;
1579 removed.hdr.context = 0;
1580 removed.srcref = src->srcref;
1581 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &removed,
1582 sizeof(removed), CTL_DATA_EOR);
1583
1584 return result;
1585}
1586
6d2010ae
A
1587static errno_t
1588nstat_control_handle_add_request(
1589 nstat_control_state *state,
1590 mbuf_t m)
1591{
1592 errno_t result;
1593
1594 // Verify the header fits in the first mbuf
1595 if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param))
1596 {
6d2010ae
A
1597 return EINVAL;
1598 }
1599
1600 // Calculate the length of the parameter field
1601 int32_t paramlength = mbuf_pkthdr_len(m) - offsetof(nstat_msg_add_src_req, param);
1602 if (paramlength < 0 || paramlength > 2 * 1024)
1603 {
6d2010ae
A
1604 return EINVAL;
1605 }
1606
1607 nstat_provider *provider;
1608 nstat_provider_cookie_t cookie;
1609 nstat_msg_add_src_req *req = mbuf_data(m);
1610 if (mbuf_pkthdr_len(m) > mbuf_len(m))
1611 {
1612 // parameter is too large, we need to make a contiguous copy
1613 void *data = OSMalloc(paramlength, nstat_malloc_tag);
1614
1615 if (!data) return ENOMEM;
1616 result = mbuf_copydata(m, offsetof(nstat_msg_add_src_req, param), paramlength, data);
1617 if (result == 0)
1618 result = nstat_lookup_entry(req->provider, data, paramlength, &provider, &cookie);
1619 OSFree(data, paramlength, nstat_malloc_tag);
1620 }
1621 else
1622 {
1623 result = nstat_lookup_entry(req->provider, (void*)&req->param, paramlength, &provider, &cookie);
1624 }
1625
1626 if (result != 0)
1627 {
6d2010ae
A
1628 return result;
1629 }
1630
1631 result = nstat_control_source_add(req->hdr.context, state, provider, cookie);
1632 if (result != 0)
316670eb 1633 provider->nstat_release(cookie, 0);
6d2010ae
A
1634
1635 return result;
1636}
1637
6d2010ae
A
1638static errno_t
1639nstat_control_handle_add_all(
1640 nstat_control_state *state,
1641 mbuf_t m)
1642{
1643 errno_t result = 0;
1644
6d2010ae
A
1645 // Verify the header fits in the first mbuf
1646 if (mbuf_len(m) < sizeof(nstat_msg_add_all_srcs))
1647 {
6d2010ae
A
1648 return EINVAL;
1649 }
1650
1651 nstat_msg_add_all_srcs *req = mbuf_data(m);
1652 nstat_provider *provider = nstat_find_provider_by_id(req->provider);
1653
1654 if (!provider) return ENOENT;
1655 if (provider->nstat_watcher_add == NULL) return ENOTSUP;
1656
1657 // Make sure we don't add the provider twice
1658 lck_mtx_lock(&state->mtx);
316670eb 1659 if ((state->ncs_watching & (1 << provider->nstat_provider_id)) != 0)
6d2010ae 1660 result = EALREADY;
316670eb 1661 state->ncs_watching |= (1 << provider->nstat_provider_id);
6d2010ae
A
1662 lck_mtx_unlock(&state->mtx);
1663 if (result != 0) return result;
1664
1665 result = provider->nstat_watcher_add(state);
1666 if (result != 0)
1667 {
1668 lck_mtx_lock(&state->mtx);
316670eb 1669 state->ncs_watching &= ~(1 << provider->nstat_provider_id);
6d2010ae
A
1670 lck_mtx_unlock(&state->mtx);
1671 }
1672
1673 if (result == 0)
1674 {
1675 // Notify the client
1676 nstat_msg_hdr success;
1677 success.context = req->hdr.context;
1678 success.type = NSTAT_MSG_TYPE_SUCCESS;
1679 success.pad = 0;
316670eb 1680 ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR);
6d2010ae
A
1681 }
1682
1683 return result;
1684}
1685
1686static errno_t
1687nstat_control_source_add(
1688 u_int64_t context,
1689 nstat_control_state *state,
1690 nstat_provider *provider,
1691 nstat_provider_cookie_t cookie)
1692{
1693 // Fill out source added message
1694 mbuf_t msg = NULL;
1695 unsigned int one = 1;
1696
1697 if (mbuf_allocpacket(MBUF_WAITOK, sizeof(nstat_msg_src_added), &one, &msg) != 0)
1698 return ENOMEM;
1699
1700 mbuf_setlen(msg, sizeof(nstat_msg_src_added));
1701 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
1702 nstat_msg_src_added *add = mbuf_data(msg);
1703 bzero(add, sizeof(*add));
1704 add->hdr.type = NSTAT_MSG_TYPE_SRC_ADDED;
1705 add->hdr.context = context;
1706 add->provider = provider->nstat_provider_id;
1707
1708 // Allocate storage for the source
1709 nstat_src *src = OSMalloc(sizeof(*src), nstat_malloc_tag);
1710 if (src == NULL)
1711 {
1712 mbuf_freem(msg);
1713 return ENOMEM;
1714 }
1715
1716 // Fill in the source, including picking an unused source ref
1717 lck_mtx_lock(&state->mtx);
1718
1719 add->srcref = src->srcref = nstat_control_next_src_ref(state);
316670eb 1720 if (state->ncs_flags & NSTAT_FLAG_CLEANUP || src->srcref == NSTAT_SRC_REF_INVALID)
6d2010ae
A
1721 {
1722 lck_mtx_unlock(&state->mtx);
1723 OSFree(src, sizeof(*src), nstat_malloc_tag);
1724 mbuf_freem(msg);
1725 return EINVAL;
1726 }
1727 src->provider = provider;
1728 src->cookie = cookie;
1729
1730 // send the source added message
316670eb 1731 errno_t result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
6d2010ae
A
1732 if (result != 0)
1733 {
1734 lck_mtx_unlock(&state->mtx);
6d2010ae
A
1735 OSFree(src, sizeof(*src), nstat_malloc_tag);
1736 mbuf_freem(msg);
1737 return result;
1738 }
1739
1740 // Put the source in the list
316670eb
A
1741 src->next = state->ncs_srcs;
1742 state->ncs_srcs = src;
6d2010ae
A
1743
1744 // send the description message
1745 // not useful as the source is often not complete
316670eb 1746// nstat_control_send_description(state, src, 0);
6d2010ae
A
1747
1748 lck_mtx_unlock(&state->mtx);
1749
1750 return 0;
1751}
1752
1753static errno_t
1754nstat_control_handle_remove_request(
1755 nstat_control_state *state,
1756 mbuf_t m)
1757{
1758 nstat_src_ref_t srcref = NSTAT_SRC_REF_INVALID;
1759
1760 if (mbuf_copydata(m, offsetof(nstat_msg_rem_src_req, srcref), sizeof(srcref), &srcref) != 0)
1761 {
6d2010ae
A
1762 return EINVAL;
1763 }
1764
1765 lck_mtx_lock(&state->mtx);
1766
1767 // Remove this source as we look for it
1768 nstat_src **nextp;
1769 nstat_src *src = NULL;
316670eb 1770 for (nextp = &state->ncs_srcs; *nextp; nextp = &(*nextp)->next)
6d2010ae
A
1771 {
1772 if ((*nextp)->srcref == srcref)
1773 {
1774 src = *nextp;
1775 *nextp = src->next;
1776 break;
1777 }
1778 }
1779
1780 lck_mtx_unlock(&state->mtx);
1781
316670eb 1782 if (src) nstat_control_cleanup_source(state, src, FALSE);
6d2010ae
A
1783
1784 return src ? 0 : ENOENT;
1785}
1786
1787static errno_t
1788nstat_control_handle_query_request(
1789 nstat_control_state *state,
1790 mbuf_t m)
1791{
1792 // TBD: handle this from another thread so we can enqueue a lot of data
1793 // As written, if a client requests query all, this function will be
1794 // called from their send of the request message. We will attempt to write
1795 // responses and succeed until the buffer fills up. Since the clients thread
1796 // is blocked on send, it won't be reading unless the client has two threads
1797 // using this socket, one for read and one for write. Two threads probably
1798 // won't work with this code anyhow since we don't have proper locking in
1799 // place yet.
1800 nstat_src *dead_srcs = NULL;
1801 errno_t result = ENOENT;
1802 nstat_msg_query_src_req req;
1803 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
1804 {
6d2010ae
A
1805 return EINVAL;
1806 }
1807
1808 lck_mtx_lock(&state->mtx);
316670eb
A
1809 if (req.srcref == NSTAT_SRC_REF_ALL)
1810 state->ncs_flags |= NSTAT_FLAG_REQCOUNTS;
1811 nstat_src **srcpp = &state->ncs_srcs;
6d2010ae
A
1812 while (*srcpp != NULL)
1813 {
1814 int gone;
1815 gone = 0;
1816
1817 if (req.srcref == NSTAT_SRC_REF_ALL ||
1818 (*srcpp)->srcref == req.srcref)
1819 {
316670eb
A
1820 result = nstat_control_send_counts(state, *srcpp,
1821 req.hdr.context, &gone);
6d2010ae 1822
316670eb
A
1823 // If the counts message failed to enqueue then we should clear our flag so
1824 // that a client doesn't miss anything on idle cleanup.
1825 if (result != 0)
1826 state->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
6d2010ae
A
1827
1828 if (gone)
1829 {
1830 // send one last descriptor message so client may see last state
316670eb
A
1831
1832 nstat_control_send_description(state, *srcpp,
1833 0);
6d2010ae
A
1834
1835 // pull src out of the list
1836 nstat_src *src = *srcpp;
1837 *srcpp = src->next;
1838
1839 src->next = dead_srcs;
1840 dead_srcs = src;
1841 }
1842
1843 if (req.srcref != NSTAT_SRC_REF_ALL)
1844 break;
1845 }
1846
1847 if (!gone)
1848 srcpp = &(*srcpp)->next;
1849 }
1850 lck_mtx_unlock(&state->mtx);
1851
1852 while (dead_srcs)
1853 {
1854 nstat_src *src;
1855
1856 src = dead_srcs;
1857 dead_srcs = src->next;
1858
1859 // release src and send notification
316670eb 1860 nstat_control_cleanup_source(state, src, FALSE);
6d2010ae
A
1861 }
1862
1863 if (req.srcref == NSTAT_SRC_REF_ALL)
1864 {
1865 nstat_msg_hdr success;
1866 success.context = req.hdr.context;
1867 success.type = NSTAT_MSG_TYPE_SUCCESS;
1868 success.pad = 0;
316670eb 1869 ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR);
6d2010ae
A
1870 result = 0;
1871 }
1872
1873 return result;
1874}
1875
1876static errno_t
1877nstat_control_handle_get_src_description(
1878 nstat_control_state *state,
1879 mbuf_t m)
1880{
1881 nstat_msg_get_src_description req;
1882 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
1883 {
6d2010ae
A
1884 return EINVAL;
1885 }
1886
1887 // Find the source
1888 lck_mtx_lock(&state->mtx);
1889 nstat_src *src;
316670eb 1890 for (src = state->ncs_srcs; src; src = src->next)
6d2010ae
A
1891 {
1892 if (src->srcref == req.srcref)
1893 break;
1894 }
1895
1896 // No source? Done.
1897 if (!src)
1898 {
1899 lck_mtx_unlock(&state->mtx);
6d2010ae
A
1900 return ENOENT;
1901 }
1902
1903 errno_t result = nstat_control_send_description(state, src, req.hdr.context);
1904 lck_mtx_unlock(&state->mtx);
1905
1906 return result;
1907}
1908
1909static errno_t
1910nstat_control_send(
1911 kern_ctl_ref kctl,
1912 u_int32_t unit,
1913 __unused void *uinfo,
1914 mbuf_t m,
1915 __unused int flags)
1916{
1917 nstat_control_state *state = (nstat_control_state*)uinfo;
1918 struct nstat_msg_hdr *hdr;
1919 struct nstat_msg_hdr storage;
1920 errno_t result = 0;
1921
1922 if (mbuf_pkthdr_len(m) < sizeof(hdr))
1923 {
1924 // Is this the right thing to do?
6d2010ae
A
1925 mbuf_freem(m);
1926 return EINVAL;
1927 }
1928
1929 if (mbuf_len(m) >= sizeof(*hdr))
1930 {
1931 hdr = mbuf_data(m);
1932 }
1933 else
1934 {
1935 mbuf_copydata(m, 0, sizeof(storage), &storage);
1936 hdr = &storage;
1937 }
1938
1939 switch (hdr->type)
1940 {
1941 case NSTAT_MSG_TYPE_ADD_SRC:
1942 result = nstat_control_handle_add_request(state, m);
1943 break;
1944
1945 case NSTAT_MSG_TYPE_ADD_ALL_SRCS:
1946 result = nstat_control_handle_add_all(state, m);
1947 break;
1948
1949 case NSTAT_MSG_TYPE_REM_SRC:
1950 result = nstat_control_handle_remove_request(state, m);
1951 break;
1952
1953 case NSTAT_MSG_TYPE_QUERY_SRC:
1954 result = nstat_control_handle_query_request(state, m);
1955 break;
1956
1957 case NSTAT_MSG_TYPE_GET_SRC_DESC:
1958 result = nstat_control_handle_get_src_description(state, m);
1959 break;
1960
1961 default:
6d2010ae
A
1962 result = EINVAL;
1963 break;
1964 }
1965
1966 if (result != 0)
1967 {
1968 struct nstat_msg_error err;
1969
1970 err.hdr.type = NSTAT_MSG_TYPE_ERROR;
1971 err.hdr.context = hdr->context;
1972 err.error = result;
1973
1974 result = ctl_enqueuedata(kctl, unit, &err, sizeof(err), CTL_DATA_EOR);
1975 }
1976
1977 mbuf_freem(m);
1978
1979 return result;
1980}