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