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