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