]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/ntstat.c
xnu-1699.24.8.tar.gz
[apple/xnu.git] / bsd / net / ntstat.c
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;
62 SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
63 &nstat_collect, 0, "Collect detailed statistics");
64
65 typedef 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
78 static void nstat_control_register(void);
79
80 static volatile OSMallocTag nstat_malloc_tag = NULL;
81 static nstat_control_state *nstat_controls = NULL;
82 static uint64_t nstat_idle_time = 0ULL;
83 static decl_lck_mtx_data(, nstat_mtx);
84
85 static void
86 nstat_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
107 static void
108 nstat_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
123 static void
124 nstat_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
146 typedef 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
160 static errno_t nstat_control_source_add(u_int64_t context, nstat_control_state *state, nstat_provider *provider, nstat_provider_cookie_t cookie);
161 struct nstat_provider *nstat_providers = NULL;
162
163 static struct nstat_provider*
164 nstat_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
178 static errno_t
179 nstat_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
196 static void nstat_init_route_provider(void);
197 static void nstat_init_tcp_provider(void);
198 static void nstat_init_udp_provider(void);
199
200 static void
201 nstat_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
223 struct align_header
224 {
225 u_int32_t offset;
226 u_int32_t length;
227 };
228
229 static void*
230 nstat_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
251 static void
252 nstat_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
262 static nstat_provider nstat_route_provider;
263
264 static errno_t
265 nstat_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
319 static int
320 nstat_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
327 static errno_t
328 nstat_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
361 static void
362 nstat_route_release(
363 nstat_provider_cookie_t cookie)
364 {
365 rtfree((struct rtentry*)cookie);
366 }
367
368 static u_int32_t nstat_route_watchers = 0;
369
370 static int
371 nstat_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
406 static errno_t
407 nstat_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
434 nstat_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
461 static void
462 nstat_route_remove_watcher(
463 __unused nstat_control_state *state)
464 {
465 OSDecrementAtomic(&nstat_route_watchers);
466 }
467
468 static errno_t
469 nstat_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
509 static void
510 nstat_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
528 static struct nstat_counts*
529 nstat_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
552 nstat_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
563 nstat_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
579 nstat_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
596 nstat_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
623 nstat_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
653 nstat_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
715 static nstat_provider nstat_tcp_provider;
716
717 static errno_t
718 nstat_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
802 static errno_t
803 nstat_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
811 static int
812 nstat_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
820 static errno_t
821 nstat_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
858 static void
859 nstat_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
866 static u_int32_t nstat_tcp_watchers = 0;
867
868 static errno_t
869 nstat_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
895 static void
896 nstat_tcp_remove_watcher(
897 __unused nstat_control_state *state)
898 {
899 OSDecrementAtomic(&nstat_tcp_watchers);
900 }
901
902 __private_extern__ void
903 nstat_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
931 static errno_t
932 nstat_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
993 static void
994 nstat_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
1012 static nstat_provider nstat_udp_provider;
1013
1014 static errno_t
1015 nstat_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
1023 static int
1024 nstat_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
1031 static errno_t
1032 nstat_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
1055 static void
1056 nstat_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
1063 static u_int32_t nstat_udp_watchers = 0;
1064
1065 static errno_t
1066 nstat_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
1092 static void
1093 nstat_udp_remove_watcher(
1094 __unused nstat_control_state *state)
1095 {
1096 OSDecrementAtomic(&nstat_udp_watchers);
1097 }
1098
1099 __private_extern__ void
1100 nstat_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
1128 static errno_t
1129 nstat_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
1181 static void
1182 nstat_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
1200 typedef 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
1208 static kern_ctl_ref nstat_ctlref = NULL;
1209 static lck_grp_t *nstat_lck_grp = NULL;
1210
1211 static errno_t nstat_control_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo);
1212 static errno_t nstat_control_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo);
1213 static errno_t nstat_control_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags);
1214 static int nstat_control_send_description(nstat_control_state *state, nstat_src *src, u_int64_t context);
1215 static void nstat_control_cleanup_source(nstat_control_state *state, struct nstat_src *src);
1216
1217
1218 static void*
1219 nstat_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;
1251 errno_t result = ctl_enqueuedata(control->kctl, control->unit, &removed, sizeof(removed), CTL_DATA_EOR);
1252 if (result != 0) printf("%s:%d ctl_enqueuedata failed: %d\n", __FUNCTION__, __LINE__, result);
1253
1254 // Put this on the list to release later
1255 dead->next = dead_list;
1256 dead_list = dead;
1257 }
1258 else
1259 {
1260 srcpp = &(*srcpp)->next;
1261 }
1262 }
1263 lck_mtx_unlock(&control->mtx);
1264 }
1265
1266 if (nstat_controls)
1267 {
1268 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
1269 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
1270 }
1271
1272 lck_mtx_unlock(&nstat_mtx);
1273
1274 // Release the sources now that we aren't holding lots of locks
1275 while (dead_list)
1276 {
1277 dead = dead_list;
1278 dead_list = dead->next;
1279
1280 nstat_control_cleanup_source(NULL, dead);
1281 }
1282
1283 return NULL;
1284 }
1285
1286 static void
1287 nstat_control_register(void)
1288 {
1289 // Create our lock group first
1290 lck_grp_attr_t *grp_attr = lck_grp_attr_alloc_init();
1291 lck_grp_attr_setdefault(grp_attr);
1292 nstat_lck_grp = lck_grp_alloc_init("network statistics kctl", grp_attr);
1293 lck_grp_attr_free(grp_attr);
1294
1295 lck_mtx_init(&nstat_mtx, nstat_lck_grp, NULL);
1296
1297 // Register the control
1298 struct kern_ctl_reg nstat_control;
1299 bzero(&nstat_control, sizeof(nstat_control));
1300 strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name));
1301 nstat_control.ctl_connect = nstat_control_connect;
1302 nstat_control.ctl_disconnect = nstat_control_disconnect;
1303 nstat_control.ctl_send = nstat_control_send;
1304
1305 errno_t result = ctl_register(&nstat_control, &nstat_ctlref);
1306 if (result != 0)
1307 printf("%s:%d ctl_register failed: %d", __FUNCTION__, __LINE__, result);
1308 }
1309
1310 static void
1311 nstat_control_cleanup_source(
1312 nstat_control_state *state,
1313 struct nstat_src *src)
1314 {
1315 if (state)
1316 {
1317 nstat_msg_src_removed removed;
1318 removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED;
1319 removed.hdr.context = 0;
1320 removed.srcref = src->srcref;
1321 errno_t result = ctl_enqueuedata(state->kctl, state->unit, &removed, sizeof(removed), CTL_DATA_EOR);
1322 if (result != 0) printf("%s:%d ctl_enqueuedata failed: %d\n", __FUNCTION__, __LINE__, result);
1323 }
1324
1325 // Cleanup the source if we found it.
1326 src->provider->nstat_release(src->cookie);
1327 OSFree(src, sizeof(*src), nstat_malloc_tag);
1328 }
1329
1330 static errno_t
1331 nstat_control_connect(
1332 kern_ctl_ref kctl,
1333 struct sockaddr_ctl *sac,
1334 void **uinfo)
1335 {
1336 nstat_control_state *state = OSMalloc(sizeof(*state), nstat_malloc_tag);
1337 if (state == NULL) return ENOMEM;
1338
1339 bzero(state, sizeof(*state));
1340 lck_mtx_init(&state->mtx, nstat_lck_grp, NULL);
1341 state->kctl = kctl;
1342 state->unit = sac->sc_unit;
1343 *uinfo = state;
1344
1345 // check if we're super user
1346 proc_t pself = proc_self();
1347 state->suser = proc_suser(pself) == 0;
1348 proc_rele(pself);
1349
1350 lck_mtx_lock(&nstat_mtx);
1351 state->next = nstat_controls;
1352 nstat_controls = state;
1353
1354 if (nstat_idle_time == 0ULL)
1355 {
1356 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
1357 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time);
1358 }
1359
1360 lck_mtx_unlock(&nstat_mtx);
1361
1362 return 0;
1363 }
1364
1365 static errno_t
1366 nstat_control_disconnect(
1367 __unused kern_ctl_ref kctl,
1368 __unused u_int32_t unit,
1369 __unused void *uinfo)
1370 {
1371 u_int32_t watching;
1372 nstat_control_state *state = (nstat_control_state*)uinfo;
1373
1374 // pull it out of the global list of states
1375 lck_mtx_lock(&nstat_mtx);
1376 nstat_control_state **statepp;
1377 for (statepp = &nstat_controls; *statepp; statepp = &(*statepp)->next)
1378 {
1379 if (*statepp == state)
1380 {
1381 *statepp = state->next;
1382 break;
1383 }
1384 }
1385 lck_mtx_unlock(&nstat_mtx);
1386
1387 lck_mtx_lock(&state->mtx);
1388 // Stop watching for sources
1389 nstat_provider *provider;
1390 watching = state->watching;
1391 state->watching = 0;
1392 for (provider = nstat_providers; provider && watching; provider = provider->next)
1393 {
1394 if ((watching & (1 << provider->nstat_provider_id)) != 0)
1395 {
1396 watching &= ~(1 << provider->nstat_provider_id);
1397 provider->nstat_watcher_remove(state);
1398 }
1399 }
1400
1401 // set cleanup flags
1402 state->cleanup = TRUE;
1403
1404 // Copy out the list of sources
1405 nstat_src *srcs = state->srcs;
1406 state->srcs = NULL;
1407 lck_mtx_unlock(&state->mtx);
1408
1409 while (srcs)
1410 {
1411 nstat_src *src;
1412
1413 // pull it out of the list
1414 src = srcs;
1415 srcs = src->next;
1416
1417 // clean it up
1418 nstat_control_cleanup_source(NULL, src);
1419 }
1420
1421 OSFree(state, sizeof(*state), nstat_malloc_tag);
1422
1423 return 0;
1424 }
1425
1426 static nstat_src_ref_t
1427 nstat_control_next_src_ref(
1428 nstat_control_state *state)
1429 {
1430 int i = 0;
1431 nstat_src_ref_t toReturn = NSTAT_SRC_REF_INVALID;
1432
1433 for (i = 0; i < 1000 && toReturn == NSTAT_SRC_REF_INVALID; i++)
1434 {
1435 if (state->next_srcref == NSTAT_SRC_REF_INVALID ||
1436 state->next_srcref == NSTAT_SRC_REF_ALL)
1437 {
1438 state->next_srcref = 1;
1439 }
1440
1441 nstat_src *src;
1442 for (src = state->srcs; src; src = src->next)
1443 {
1444 if (src->srcref == state->next_srcref)
1445 break;
1446 }
1447
1448 if (src == NULL) toReturn = state->next_srcref;
1449 state->next_srcref++;
1450 }
1451
1452 return toReturn;
1453 }
1454
1455 static int
1456 nstat_control_send_description(
1457 nstat_control_state *state,
1458 nstat_src *src,
1459 u_int64_t context)
1460 {
1461 // Provider doesn't support getting the descriptor? Done.
1462 if (src->provider->nstat_descriptor_length == 0 ||
1463 src->provider->nstat_copy_descriptor == NULL)
1464 {
1465 lck_mtx_unlock(&state->mtx);
1466 printf("%s:%d - provider doesn't support descriptions\n", __FUNCTION__, __LINE__);
1467 return EOPNOTSUPP;
1468 }
1469
1470 // Allocate storage for the descriptor message
1471 mbuf_t msg;
1472 unsigned int one = 1;
1473 u_int32_t size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
1474 if (mbuf_allocpacket(MBUF_WAITOK, size, &one, &msg) != 0)
1475 {
1476 lck_mtx_unlock(&state->mtx);
1477 printf("%s:%d - failed to allocate response\n", __FUNCTION__, __LINE__);
1478 return ENOMEM;
1479 }
1480
1481 nstat_msg_src_description *desc = (nstat_msg_src_description*)mbuf_data(msg);
1482 mbuf_setlen(msg, size);
1483 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
1484
1485 // Query the provider for the provider specific bits
1486 errno_t result = src->provider->nstat_copy_descriptor(src->cookie, desc->data, src->provider->nstat_descriptor_length);
1487
1488 if (result != 0)
1489 {
1490 mbuf_freem(msg);
1491 printf("%s:%d - provider failed to copy descriptor %d\n", __FUNCTION__, __LINE__, result);
1492 return result;
1493 }
1494
1495 desc->hdr.context = context;
1496 desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
1497 desc->srcref = src->srcref;
1498 desc->provider = src->provider->nstat_provider_id;
1499
1500 result = ctl_enqueuembuf(state->kctl, state->unit, msg, CTL_DATA_EOR);
1501 if (result != 0)
1502 {
1503 printf("%s:%d ctl_enqueuembuf returned error %d\n", __FUNCTION__, __LINE__, result);
1504 mbuf_freem(msg);
1505 }
1506
1507 return result;
1508 }
1509
1510 static errno_t
1511 nstat_control_handle_add_request(
1512 nstat_control_state *state,
1513 mbuf_t m)
1514 {
1515 errno_t result;
1516
1517 // Verify the header fits in the first mbuf
1518 if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param))
1519 {
1520 printf("mbuf_len(m)=%lu, offsetof(nstat_msg_add_src_req*, param)=%lu\n",
1521 mbuf_len(m), offsetof(nstat_msg_add_src_req, param));
1522 return EINVAL;
1523 }
1524
1525 // Calculate the length of the parameter field
1526 int32_t paramlength = mbuf_pkthdr_len(m) - offsetof(nstat_msg_add_src_req, param);
1527 if (paramlength < 0 || paramlength > 2 * 1024)
1528 {
1529 printf("invalid paramlength=%d\n", paramlength);
1530 return EINVAL;
1531 }
1532
1533 nstat_provider *provider;
1534 nstat_provider_cookie_t cookie;
1535 nstat_msg_add_src_req *req = mbuf_data(m);
1536 if (mbuf_pkthdr_len(m) > mbuf_len(m))
1537 {
1538 // parameter is too large, we need to make a contiguous copy
1539 void *data = OSMalloc(paramlength, nstat_malloc_tag);
1540
1541 if (!data) return ENOMEM;
1542 result = mbuf_copydata(m, offsetof(nstat_msg_add_src_req, param), paramlength, data);
1543 if (result == 0)
1544 result = nstat_lookup_entry(req->provider, data, paramlength, &provider, &cookie);
1545 OSFree(data, paramlength, nstat_malloc_tag);
1546 }
1547 else
1548 {
1549 result = nstat_lookup_entry(req->provider, (void*)&req->param, paramlength, &provider, &cookie);
1550 }
1551
1552 if (result != 0)
1553 {
1554 printf("nstat_lookup_entry failed: %d\n", result);
1555 return result;
1556 }
1557
1558 result = nstat_control_source_add(req->hdr.context, state, provider, cookie);
1559 if (result != 0)
1560 provider->nstat_release(cookie);
1561
1562 return result;
1563 }
1564
1565 static int
1566 nstat_perm_check(
1567 __unused nstat_control_state *state)
1568 {
1569 int allow = 0;
1570 #if !REQUIRE_ROOT_FOR_STATS
1571 allow = 1;
1572 #else
1573 // If the socket was created by a priv process, allow
1574 if (state->suser) return 1;
1575
1576 // If the current process is priv, allow
1577 proc_t self = proc_self();
1578 allow = proc_suser(self) == 0;
1579 proc_rele(self);
1580
1581 // TBD: check for entitlement, root check is too coarse
1582 #endif /* REQUIRE_ROOT_FOR_STATS */
1583
1584 return allow;
1585 }
1586
1587 static errno_t
1588 nstat_control_handle_add_all(
1589 nstat_control_state *state,
1590 mbuf_t m)
1591 {
1592 errno_t result = 0;
1593
1594 if (!nstat_perm_check(state))
1595 {
1596 return EPERM;
1597 }
1598
1599 // Verify the header fits in the first mbuf
1600 if (mbuf_len(m) < sizeof(nstat_msg_add_all_srcs))
1601 {
1602 printf("mbuf_len(m)=%lu, sizeof(nstat_msg_add_all_srcs)=%lu\n",
1603 mbuf_len(m), sizeof(nstat_msg_add_all_srcs));
1604 return EINVAL;
1605 }
1606
1607 nstat_msg_add_all_srcs *req = mbuf_data(m);
1608 nstat_provider *provider = nstat_find_provider_by_id(req->provider);
1609
1610 if (!provider) return ENOENT;
1611 if (provider->nstat_watcher_add == NULL) return ENOTSUP;
1612
1613 // Make sure we don't add the provider twice
1614 lck_mtx_lock(&state->mtx);
1615 if ((state->watching & (1 << provider->nstat_provider_id)) != 0)
1616 result = EALREADY;
1617 state->watching |= (1 << provider->nstat_provider_id);
1618 lck_mtx_unlock(&state->mtx);
1619 if (result != 0) return result;
1620
1621 result = provider->nstat_watcher_add(state);
1622 if (result != 0)
1623 {
1624 lck_mtx_lock(&state->mtx);
1625 state->watching &= ~(1 << provider->nstat_provider_id);
1626 lck_mtx_unlock(&state->mtx);
1627 }
1628
1629 if (result == 0)
1630 {
1631 // Notify the client
1632 nstat_msg_hdr success;
1633 success.context = req->hdr.context;
1634 success.type = NSTAT_MSG_TYPE_SUCCESS;
1635 success.pad = 0;
1636 if (ctl_enqueuedata(state->kctl, state->unit, &success, sizeof(success), CTL_DATA_EOR) != 0)
1637 printf("%s:%d - failed to enqueue success message\n", __FUNCTION__, __LINE__);
1638 }
1639
1640 return result;
1641 }
1642
1643 static errno_t
1644 nstat_control_source_add(
1645 u_int64_t context,
1646 nstat_control_state *state,
1647 nstat_provider *provider,
1648 nstat_provider_cookie_t cookie)
1649 {
1650 // Fill out source added message
1651 mbuf_t msg = NULL;
1652 unsigned int one = 1;
1653
1654 if (mbuf_allocpacket(MBUF_WAITOK, sizeof(nstat_msg_src_added), &one, &msg) != 0)
1655 return ENOMEM;
1656
1657 mbuf_setlen(msg, sizeof(nstat_msg_src_added));
1658 mbuf_pkthdr_setlen(msg, mbuf_len(msg));
1659 nstat_msg_src_added *add = mbuf_data(msg);
1660 bzero(add, sizeof(*add));
1661 add->hdr.type = NSTAT_MSG_TYPE_SRC_ADDED;
1662 add->hdr.context = context;
1663 add->provider = provider->nstat_provider_id;
1664
1665 // Allocate storage for the source
1666 nstat_src *src = OSMalloc(sizeof(*src), nstat_malloc_tag);
1667 if (src == NULL)
1668 {
1669 mbuf_freem(msg);
1670 return ENOMEM;
1671 }
1672
1673 // Fill in the source, including picking an unused source ref
1674 lck_mtx_lock(&state->mtx);
1675
1676 add->srcref = src->srcref = nstat_control_next_src_ref(state);
1677 if (state->cleanup || src->srcref == NSTAT_SRC_REF_INVALID)
1678 {
1679 lck_mtx_unlock(&state->mtx);
1680 OSFree(src, sizeof(*src), nstat_malloc_tag);
1681 mbuf_freem(msg);
1682 return EINVAL;
1683 }
1684 src->provider = provider;
1685 src->cookie = cookie;
1686
1687 // send the source added message
1688 errno_t result = ctl_enqueuembuf(state->kctl, state->unit, msg, CTL_DATA_EOR);
1689 if (result != 0)
1690 {
1691 lck_mtx_unlock(&state->mtx);
1692 printf("%s:%d ctl_enqueuembuf failed: %d\n", __FUNCTION__, __LINE__, result);
1693 OSFree(src, sizeof(*src), nstat_malloc_tag);
1694 mbuf_freem(msg);
1695 return result;
1696 }
1697
1698 // Put the source in the list
1699 src->next = state->srcs;
1700 state->srcs = src;
1701
1702 // send the description message
1703 // not useful as the source is often not complete
1704 // nstat_control_send_description(state, src, 0ULL);
1705
1706 lck_mtx_unlock(&state->mtx);
1707
1708 return 0;
1709 }
1710
1711 static errno_t
1712 nstat_control_handle_remove_request(
1713 nstat_control_state *state,
1714 mbuf_t m)
1715 {
1716 nstat_src_ref_t srcref = NSTAT_SRC_REF_INVALID;
1717
1718 if (mbuf_copydata(m, offsetof(nstat_msg_rem_src_req, srcref), sizeof(srcref), &srcref) != 0)
1719 {
1720 printf("%s:%d - invalid length %u, expected %lu\n", __FUNCTION__, __LINE__, (u_int32_t)mbuf_pkthdr_len(m), sizeof(nstat_msg_rem_src_req));
1721 return EINVAL;
1722 }
1723
1724 lck_mtx_lock(&state->mtx);
1725
1726 // Remove this source as we look for it
1727 nstat_src **nextp;
1728 nstat_src *src = NULL;
1729 for (nextp = &state->srcs; *nextp; nextp = &(*nextp)->next)
1730 {
1731 if ((*nextp)->srcref == srcref)
1732 {
1733 src = *nextp;
1734 *nextp = src->next;
1735 break;
1736 }
1737 }
1738
1739 lck_mtx_unlock(&state->mtx);
1740
1741 if (src) nstat_control_cleanup_source(state, src);
1742
1743 return src ? 0 : ENOENT;
1744 }
1745
1746 static errno_t
1747 nstat_control_handle_query_request(
1748 nstat_control_state *state,
1749 mbuf_t m)
1750 {
1751 // TBD: handle this from another thread so we can enqueue a lot of data
1752 // As written, if a client requests query all, this function will be
1753 // called from their send of the request message. We will attempt to write
1754 // responses and succeed until the buffer fills up. Since the clients thread
1755 // is blocked on send, it won't be reading unless the client has two threads
1756 // using this socket, one for read and one for write. Two threads probably
1757 // won't work with this code anyhow since we don't have proper locking in
1758 // place yet.
1759 nstat_src *dead_srcs = NULL;
1760 errno_t result = ENOENT;
1761 nstat_msg_query_src_req req;
1762 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
1763 {
1764 printf("%s:%d - invalid length %u, expected %lu\n", __FUNCTION__, __LINE__, (u_int32_t)mbuf_pkthdr_len(m), sizeof(req));
1765 return EINVAL;
1766 }
1767
1768 lck_mtx_lock(&state->mtx);
1769 nstat_src **srcpp = &state->srcs;
1770 while (*srcpp != NULL)
1771 {
1772 int gone;
1773 gone = 0;
1774
1775 if (req.srcref == NSTAT_SRC_REF_ALL ||
1776 (*srcpp)->srcref == req.srcref)
1777 {
1778 nstat_msg_src_counts counts;
1779 counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
1780 counts.hdr.context = req.hdr.context;
1781 counts.srcref = (*srcpp)->srcref;
1782 bzero(&counts.counts, sizeof(counts.counts));
1783 result = (*srcpp)->provider->nstat_counts((*srcpp)->cookie, &counts.counts, &gone);
1784
1785 if (result == 0)
1786 {
1787 result = ctl_enqueuedata(state->kctl, state->unit, &counts, sizeof(counts), CTL_DATA_EOR);
1788 if (result != 0)
1789 {
1790 printf("%s:%d ctl_enqueuedata failed: %d\n", __FUNCTION__, __LINE__, result);
1791 }
1792 }
1793 else
1794 {
1795 printf("%s:%d provider->nstat_counts failed: %d\n", __FUNCTION__, __LINE__, result);
1796 }
1797
1798 if (gone)
1799 {
1800 // send one last descriptor message so client may see last state
1801 nstat_control_send_description(state, *srcpp, 0ULL);
1802
1803 // pull src out of the list
1804 nstat_src *src = *srcpp;
1805 *srcpp = src->next;
1806
1807 src->next = dead_srcs;
1808 dead_srcs = src;
1809 }
1810
1811 if (req.srcref != NSTAT_SRC_REF_ALL)
1812 break;
1813 }
1814
1815 if (!gone)
1816 srcpp = &(*srcpp)->next;
1817 }
1818 lck_mtx_unlock(&state->mtx);
1819
1820 while (dead_srcs)
1821 {
1822 nstat_src *src;
1823
1824 src = dead_srcs;
1825 dead_srcs = src->next;
1826
1827 // release src and send notification
1828 nstat_control_cleanup_source(state, src);
1829 }
1830
1831 if (req.srcref == NSTAT_SRC_REF_ALL)
1832 {
1833 nstat_msg_hdr success;
1834 success.context = req.hdr.context;
1835 success.type = NSTAT_MSG_TYPE_SUCCESS;
1836 success.pad = 0;
1837 if (ctl_enqueuedata(state->kctl, state->unit, &success, sizeof(success), CTL_DATA_EOR) != 0)
1838 printf("%s:%d - failed to enqueue success message\n", __FUNCTION__, __LINE__);
1839 result = 0;
1840 }
1841
1842 return result;
1843 }
1844
1845 static errno_t
1846 nstat_control_handle_get_src_description(
1847 nstat_control_state *state,
1848 mbuf_t m)
1849 {
1850 nstat_msg_get_src_description req;
1851 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
1852 {
1853 printf("%s:%d - invalid length %u, expected %lu\n", __FUNCTION__, __LINE__, (u_int32_t)mbuf_pkthdr_len(m), sizeof(req));
1854 return EINVAL;
1855 }
1856
1857 // Find the source
1858 lck_mtx_lock(&state->mtx);
1859 nstat_src *src;
1860 for (src = state->srcs; src; src = src->next)
1861 {
1862 if (src->srcref == req.srcref)
1863 break;
1864 }
1865
1866 // No source? Done.
1867 if (!src)
1868 {
1869 lck_mtx_unlock(&state->mtx);
1870 printf("%s:%d - no matching source\n", __FUNCTION__, __LINE__);
1871 return ENOENT;
1872 }
1873
1874 errno_t result = nstat_control_send_description(state, src, req.hdr.context);
1875 lck_mtx_unlock(&state->mtx);
1876
1877 return result;
1878 }
1879
1880 static errno_t
1881 nstat_control_send(
1882 kern_ctl_ref kctl,
1883 u_int32_t unit,
1884 __unused void *uinfo,
1885 mbuf_t m,
1886 __unused int flags)
1887 {
1888 nstat_control_state *state = (nstat_control_state*)uinfo;
1889 struct nstat_msg_hdr *hdr;
1890 struct nstat_msg_hdr storage;
1891 errno_t result = 0;
1892
1893 if (mbuf_pkthdr_len(m) < sizeof(hdr))
1894 {
1895 // Is this the right thing to do?
1896 printf("%s:%d - message too short, was %ld expected %lu\n", __FUNCTION__, __LINE__,
1897 mbuf_pkthdr_len(m), sizeof(*hdr));
1898 mbuf_freem(m);
1899 return EINVAL;
1900 }
1901
1902 if (mbuf_len(m) >= sizeof(*hdr))
1903 {
1904 hdr = mbuf_data(m);
1905 }
1906 else
1907 {
1908 mbuf_copydata(m, 0, sizeof(storage), &storage);
1909 hdr = &storage;
1910 }
1911
1912 switch (hdr->type)
1913 {
1914 case NSTAT_MSG_TYPE_ADD_SRC:
1915 result = nstat_control_handle_add_request(state, m);
1916 break;
1917
1918 case NSTAT_MSG_TYPE_ADD_ALL_SRCS:
1919 result = nstat_control_handle_add_all(state, m);
1920 break;
1921
1922 case NSTAT_MSG_TYPE_REM_SRC:
1923 result = nstat_control_handle_remove_request(state, m);
1924 break;
1925
1926 case NSTAT_MSG_TYPE_QUERY_SRC:
1927 result = nstat_control_handle_query_request(state, m);
1928 break;
1929
1930 case NSTAT_MSG_TYPE_GET_SRC_DESC:
1931 result = nstat_control_handle_get_src_description(state, m);
1932 break;
1933
1934 default:
1935 printf("%s:%d - unknown message type %d\n", __FUNCTION__, __LINE__, hdr->type);
1936 result = EINVAL;
1937 break;
1938 }
1939
1940 if (result != 0)
1941 {
1942 struct nstat_msg_error err;
1943
1944 err.hdr.type = NSTAT_MSG_TYPE_ERROR;
1945 err.hdr.context = hdr->context;
1946 err.error = result;
1947
1948 result = ctl_enqueuedata(kctl, unit, &err, sizeof(err), CTL_DATA_EOR);
1949 }
1950
1951 mbuf_freem(m);
1952
1953 return result;
1954 }