]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/ntstat.c
eb83ac3c957f4ebdba749fd6b610854376f8f448
[apple/xnu.git] / bsd / net / ntstat.c
1 /*
2 * Copyright (c) 2010-2012 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;
62 SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
63 &nstat_collect, 0, "Collect detailed statistics");
64
65 enum
66 {
67 NSTAT_FLAG_CLEANUP = (0x1 << 0),
68 NSTAT_FLAG_REQCOUNTS = (0x1 << 1)
69 };
70
71 typedef struct nstat_control_state
72 {
73 struct nstat_control_state *ncs_next;
74 u_int32_t ncs_watching;
75 decl_lck_mtx_data(, mtx);
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;
81 } nstat_control_state;
82
83 typedef 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
98 typedef 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
106 static errno_t nstat_control_send_counts(nstat_control_state *,
107 nstat_src *, unsigned long long, int *);
108 static int nstat_control_send_description(nstat_control_state *state, nstat_src *src, u_int64_t context);
109 static errno_t nstat_control_send_removed(nstat_control_state *, nstat_src *);
110 static void nstat_control_cleanup_source(nstat_control_state *state, nstat_src *src,
111 boolean_t);
112
113 static u_int32_t nstat_udp_watchers = 0;
114 static u_int32_t nstat_tcp_watchers = 0;
115
116 static void nstat_control_register(void);
117
118 static volatile OSMallocTag nstat_malloc_tag = NULL;
119 static nstat_control_state *nstat_controls = NULL;
120 static uint64_t nstat_idle_time = 0;
121 static decl_lck_mtx_data(, nstat_mtx);
122
123 static void
124 nstat_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 {
135 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(void *)dst;
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
145 static void
146 nstat_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
161 static void
162 nstat_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
184 static errno_t nstat_control_source_add(u_int64_t context, nstat_control_state *state, nstat_provider *provider, nstat_provider_cookie_t cookie);
185 struct nstat_provider *nstat_providers = NULL;
186
187 static struct nstat_provider*
188 nstat_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
202 static errno_t
203 nstat_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 {
213 return ENOENT;
214 }
215
216 return (*out_provider)->nstat_lookup(data, length, out_cookie);
217 }
218
219 static void nstat_init_route_provider(void);
220 static void nstat_init_tcp_provider(void);
221 static void nstat_init_udp_provider(void);
222
223 __private_extern__ void
224 nstat_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
246 struct align_header
247 {
248 u_int32_t offset;
249 u_int32_t length;
250 };
251
252 static void*
253 nstat_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
267 hdr = (struct align_header*)(void *)(aligned - sizeof(*hdr));
268 hdr->offset = aligned - buffer;
269 hdr->length = size;
270
271 return aligned;
272 }
273
274 static void
275 nstat_free_aligned(
276 void *buffer,
277 OSMallocTag tag)
278 {
279 struct align_header *hdr = (struct align_header*)(void *)((u_int8_t*)buffer - sizeof(*hdr));
280 OSFree(((char*)buffer) - hdr->offset, hdr->length, tag);
281 }
282
283 #pragma mark -- Route Provider --
284
285 static nstat_provider nstat_route_provider;
286
287 static errno_t
288 nstat_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 {
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 {
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 {
319 return EINVAL;
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
338 static int
339 nstat_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
346 static errno_t
347 nstat_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
380 static void
381 nstat_route_release(
382 nstat_provider_cookie_t cookie,
383 __unused int locked)
384 {
385 rtfree((struct rtentry*)cookie);
386 }
387
388 static u_int32_t nstat_route_watchers = 0;
389
390 static int
391 nstat_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
426 static errno_t
427 nstat_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 {
444 break;
445 }
446 }
447 lck_mtx_unlock(rnh_lock);
448
449 return result;
450 }
451
452 __private_extern__ void
453 nstat_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;
463 for (state = nstat_controls; state; state = state->ncs_next)
464 {
465 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_ROUTE)) != 0)
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
480 static void
481 nstat_route_remove_watcher(
482 __unused nstat_control_state *state)
483 {
484 OSDecrementAtomic(&nstat_route_watchers);
485 }
486
487 static errno_t
488 nstat_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 {
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
527 static void
528 nstat_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
546 static struct nstat_counts*
547 nstat_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
570 nstat_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
581 nstat_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
597 nstat_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
614 nstat_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
641 nstat_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
671 nstat_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
731
732 #pragma mark -- TCP Provider --
733
734 static nstat_provider nstat_tcp_provider;
735
736 static errno_t
737 nstat_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 {
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 {
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 {
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 {
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:
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
811 static errno_t
812 nstat_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
820 static int
821 nstat_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
829 static errno_t
830 nstat_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
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;
862
863 return 0;
864 }
865
866 static void
867 nstat_tcp_release(
868 nstat_provider_cookie_t cookie,
869 int locked)
870 {
871 struct inpcb *inp = (struct inpcb*)cookie;
872 in_pcb_checkstate(inp, WNT_RELEASE, locked);
873 }
874
875 static errno_t
876 nstat_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
902 static void
903 nstat_tcp_remove_watcher(
904 __unused nstat_control_state *state)
905 {
906 OSDecrementAtomic(&nstat_tcp_watchers);
907 }
908
909 __private_extern__ void
910 nstat_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;
918 for (state = nstat_controls; state; state = state->ncs_next)
919 {
920 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_TCP)) != 0)
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
938 __private_extern__ void
939 nstat_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
986 static errno_t
987 nstat_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 {
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);
1000
1001 if (inp->inp_state == INPCB_STATE_DEAD)
1002 return EINVAL;
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;
1022 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
1023 inp->inp_last_outifp->if_index;
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;
1037 desc->traffic_class = so->so_traffic_class;
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
1051 static void
1052 nstat_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
1070 static nstat_provider nstat_udp_provider;
1071
1072 static errno_t
1073 nstat_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
1081 static int
1082 nstat_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
1089 static errno_t
1090 nstat_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
1113 static void
1114 nstat_udp_release(
1115 nstat_provider_cookie_t cookie,
1116 int locked)
1117 {
1118 struct inpcb *inp = (struct inpcb*)cookie;
1119 in_pcb_checkstate(inp, WNT_RELEASE, locked);
1120 }
1121
1122 static errno_t
1123 nstat_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
1149 static void
1150 nstat_udp_remove_watcher(
1151 __unused nstat_control_state *state)
1152 {
1153 OSDecrementAtomic(&nstat_udp_watchers);
1154 }
1155
1156 __private_extern__ void
1157 nstat_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;
1165 for (state = nstat_controls; state; state = state->ncs_next)
1166 {
1167 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_UDP)) != 0)
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
1185 static errno_t
1186 nstat_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 {
1193 return EINVAL;
1194 }
1195
1196 nstat_udp_descriptor *desc = (nstat_udp_descriptor*)data;
1197 struct inpcb *inp = (struct inpcb*)cookie;
1198
1199 if (inp->inp_state == INPCB_STATE_DEAD)
1200 return EINVAL;
1201
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
1219 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
1220 inp->inp_last_outifp->if_index;
1221
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;
1232 desc->traffic_class = so->so_traffic_class;
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
1241 static void
1242 nstat_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
1260 static kern_ctl_ref nstat_ctlref = NULL;
1261 static lck_grp_t *nstat_lck_grp = NULL;
1262
1263 static errno_t nstat_control_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo);
1264 static errno_t nstat_control_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo);
1265 static errno_t nstat_control_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags);
1266
1267
1268 static void*
1269 nstat_idle_check(
1270 __unused thread_call_param_t p0,
1271 __unused thread_call_param_t p1)
1272 {
1273 lck_mtx_lock(&nstat_mtx);
1274
1275 nstat_idle_time = 0;
1276
1277 nstat_control_state *control;
1278 nstat_src *dead = NULL;
1279 nstat_src *dead_list = NULL;
1280 for (control = nstat_controls; control; control = control->ncs_next)
1281 {
1282 lck_mtx_lock(&control->mtx);
1283 nstat_src **srcpp = &control->ncs_srcs;
1284
1285 if (!(control->ncs_flags & NSTAT_FLAG_REQCOUNTS))
1286 {
1287 while(*srcpp != NULL)
1288 {
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 }
1313 }
1314 }
1315 control->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
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
1333 nstat_control_cleanup_source(NULL, dead, FALSE);
1334 }
1335
1336 return NULL;
1337 }
1338
1339 static void
1340 nstat_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
1358 ctl_register(&nstat_control, &nstat_ctlref);
1359 }
1360
1361 static void
1362 nstat_control_cleanup_source(
1363 nstat_control_state *state,
1364 struct nstat_src *src,
1365 boolean_t locked)
1366 {
1367 if (state)
1368 nstat_control_send_removed(state, src);
1369
1370 // Cleanup the source if we found it.
1371 src->provider->nstat_release(src->cookie, locked);
1372 OSFree(src, sizeof(*src), nstat_malloc_tag);
1373 }
1374
1375 static errno_t
1376 nstat_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);
1386 state->ncs_kctl = kctl;
1387 state->ncs_unit = sac->sc_unit;
1388 state->ncs_flags = NSTAT_FLAG_REQCOUNTS;
1389 *uinfo = state;
1390
1391 lck_mtx_lock(&nstat_mtx);
1392 state->ncs_next = nstat_controls;
1393 nstat_controls = state;
1394
1395 if (nstat_idle_time == 0)
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
1406 static errno_t
1407 nstat_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;
1418 for (statepp = &nstat_controls; *statepp; statepp = &(*statepp)->ncs_next)
1419 {
1420 if (*statepp == state)
1421 {
1422 *statepp = state->ncs_next;
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;
1431 watching = state->ncs_watching;
1432 state->ncs_watching = 0;
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
1443 state->ncs_flags |= NSTAT_FLAG_CLEANUP;
1444
1445 // Copy out the list of sources
1446 nstat_src *srcs = state->ncs_srcs;
1447 state->ncs_srcs = NULL;
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
1459 nstat_control_cleanup_source(NULL, src, FALSE);
1460 }
1461
1462 OSFree(state, sizeof(*state), nstat_malloc_tag);
1463
1464 return 0;
1465 }
1466
1467 static nstat_src_ref_t
1468 nstat_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 {
1476 if (state->ncs_next_srcref == NSTAT_SRC_REF_INVALID ||
1477 state->ncs_next_srcref == NSTAT_SRC_REF_ALL)
1478 {
1479 state->ncs_next_srcref = 1;
1480 }
1481
1482 nstat_src *src;
1483 for (src = state->ncs_srcs; src; src = src->next)
1484 {
1485 if (src->srcref == state->ncs_next_srcref)
1486 break;
1487 }
1488
1489 if (src == NULL) toReturn = state->ncs_next_srcref;
1490 state->ncs_next_srcref++;
1491 }
1492
1493 return toReturn;
1494 }
1495
1496 static errno_t
1497 nstat_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
1521 static int
1522 nstat_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 {
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 {
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);
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
1561 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
1562 if (result != 0)
1563 {
1564 mbuf_freem(msg);
1565 }
1566
1567 return result;
1568 }
1569
1570 static errno_t
1571 nstat_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
1587 static errno_t
1588 nstat_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 {
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 {
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 {
1628 return result;
1629 }
1630
1631 result = nstat_control_source_add(req->hdr.context, state, provider, cookie);
1632 if (result != 0)
1633 provider->nstat_release(cookie, 0);
1634
1635 return result;
1636 }
1637
1638 static errno_t
1639 nstat_control_handle_add_all(
1640 nstat_control_state *state,
1641 mbuf_t m)
1642 {
1643 errno_t result = 0;
1644
1645 // Verify the header fits in the first mbuf
1646 if (mbuf_len(m) < sizeof(nstat_msg_add_all_srcs))
1647 {
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);
1659 if ((state->ncs_watching & (1 << provider->nstat_provider_id)) != 0)
1660 result = EALREADY;
1661 state->ncs_watching |= (1 << provider->nstat_provider_id);
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);
1669 state->ncs_watching &= ~(1 << provider->nstat_provider_id);
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;
1680 ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR);
1681 }
1682
1683 return result;
1684 }
1685
1686 static errno_t
1687 nstat_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);
1720 if (state->ncs_flags & NSTAT_FLAG_CLEANUP || src->srcref == NSTAT_SRC_REF_INVALID)
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
1731 errno_t result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
1732 if (result != 0)
1733 {
1734 lck_mtx_unlock(&state->mtx);
1735 OSFree(src, sizeof(*src), nstat_malloc_tag);
1736 mbuf_freem(msg);
1737 return result;
1738 }
1739
1740 // Put the source in the list
1741 src->next = state->ncs_srcs;
1742 state->ncs_srcs = src;
1743
1744 // send the description message
1745 // not useful as the source is often not complete
1746 // nstat_control_send_description(state, src, 0);
1747
1748 lck_mtx_unlock(&state->mtx);
1749
1750 return 0;
1751 }
1752
1753 static errno_t
1754 nstat_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 {
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;
1770 for (nextp = &state->ncs_srcs; *nextp; nextp = &(*nextp)->next)
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
1782 if (src) nstat_control_cleanup_source(state, src, FALSE);
1783
1784 return src ? 0 : ENOENT;
1785 }
1786
1787 static errno_t
1788 nstat_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 {
1805 return EINVAL;
1806 }
1807
1808 lck_mtx_lock(&state->mtx);
1809 if (req.srcref == NSTAT_SRC_REF_ALL)
1810 state->ncs_flags |= NSTAT_FLAG_REQCOUNTS;
1811 nstat_src **srcpp = &state->ncs_srcs;
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 {
1820 result = nstat_control_send_counts(state, *srcpp,
1821 req.hdr.context, &gone);
1822
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;
1827
1828 if (gone)
1829 {
1830 // send one last descriptor message so client may see last state
1831
1832 nstat_control_send_description(state, *srcpp,
1833 0);
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
1860 nstat_control_cleanup_source(state, src, FALSE);
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;
1869 ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR);
1870 result = 0;
1871 }
1872
1873 return result;
1874 }
1875
1876 static errno_t
1877 nstat_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 {
1884 return EINVAL;
1885 }
1886
1887 // Find the source
1888 lck_mtx_lock(&state->mtx);
1889 nstat_src *src;
1890 for (src = state->ncs_srcs; src; src = src->next)
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);
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
1909 static errno_t
1910 nstat_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?
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:
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 }