2 * Copyright (c) 2010-2014 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
39 #include <sys/protosw.h>
41 #include <kern/clock.h>
42 #include <kern/debug.h>
44 #include <libkern/libkern.h>
45 #include <libkern/OSMalloc.h>
46 #include <libkern/OSAtomic.h>
47 #include <libkern/locks.h>
50 #include <net/if_var.h>
51 #include <net/if_types.h>
52 #include <net/route.h>
53 #include <net/ntstat.h>
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>
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");
71 static int nstat_privcheck
= 0;
72 SYSCTL_INT(_net
, OID_AUTO
, statistics_privcheck
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
73 &nstat_privcheck
, 0, "Entitlement check");
75 SYSCTL_NODE(_net
, OID_AUTO
, stats
,
76 CTLFLAG_RW
|CTLFLAG_LOCKED
, 0, "network statistics");
78 static int nstat_debug
= 0;
79 SYSCTL_INT(_net_stats
, OID_AUTO
, debug
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
82 static int nstat_sendspace
= 2048;
83 SYSCTL_INT(_net_stats
, OID_AUTO
, sendspace
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
84 &nstat_sendspace
, 0, "");
86 static int nstat_recvspace
= 8192;
87 SYSCTL_INT(_net_stats
, OID_AUTO
, recvspace
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
88 &nstat_recvspace
, 0, "");
90 static int nstat_successmsgfailures
= 0;
91 SYSCTL_INT(_net_stats
, OID_AUTO
, successmsgfailures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
92 &nstat_successmsgfailures
, 0, "");
94 static int nstat_sendountfailures
= 0;
95 SYSCTL_INT(_net_stats
, OID_AUTO
, sendountfailures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
96 &nstat_sendountfailures
, 0, "");
98 static int nstat_sysinfofailures
= 0;
99 SYSCTL_INT(_net_stats
, OID_AUTO
, sysinfofalures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
100 &nstat_sysinfofailures
, 0, "");
102 static int nstat_srccountfailures
= 0;
103 SYSCTL_INT(_net_stats
, OID_AUTO
, srccountfailures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
104 &nstat_srccountfailures
, 0, "");
106 static int nstat_descriptionfailures
= 0;
107 SYSCTL_INT(_net_stats
, OID_AUTO
, descriptionfailures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
108 &nstat_descriptionfailures
, 0, "");
110 static int nstat_msgremovedfailures
= 0;
111 SYSCTL_INT(_net_stats
, OID_AUTO
, msgremovedfailures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
112 &nstat_msgremovedfailures
, 0, "");
114 static int nstat_srcaddedfailures
= 0;
115 SYSCTL_INT(_net_stats
, OID_AUTO
, srcaddedfailures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
116 &nstat_srcaddedfailures
, 0, "");
118 static int nstat_msgerrorfailures
= 0;
119 SYSCTL_INT(_net_stats
, OID_AUTO
, msgerrorfailures
, CTLFLAG_RD
| CTLFLAG_LOCKED
,
120 &nstat_msgerrorfailures
, 0, "");
125 NSTAT_FLAG_CLEANUP
= (1 << 0),
126 NSTAT_FLAG_REQCOUNTS
= (1 << 1),
127 NSTAT_FLAG_REQDESCS
= (1 << 2)
130 typedef struct nstat_control_state
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
;
137 nstat_src_ref_t ncs_next_srcref
;
138 struct nstat_src
*ncs_srcs
;
140 } nstat_control_state
;
142 typedef struct nstat_provider
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
);
157 typedef struct nstat_src
159 struct nstat_src
*next
;
160 nstat_src_ref_t srcref
;
161 nstat_provider
*provider
;
162 nstat_provider_cookie_t cookie
;
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
,
173 static u_int32_t nstat_udp_watchers
= 0;
174 static u_int32_t nstat_tcp_watchers
= 0;
176 static void nstat_control_register(void);
179 * The lock order is as follows:
181 * socket_lock (inpcb)
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
);
190 /* some extern definitions */
191 extern void mbuf_report_peak_usage(void);
192 extern void tcp_report_stats(void);
196 const struct sockaddr
*src
,
197 struct sockaddr
*dst
,
200 if (src
->sa_len
> maxlen
) return;
202 bcopy(src
, dst
, src
->sa_len
);
203 if (src
->sa_family
== AF_INET6
&&
204 src
->sa_len
>= sizeof(struct sockaddr_in6
))
206 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)dst
;
207 if (IN6_IS_SCOPE_EMBED(&sin6
->sin6_addr
))
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;
217 nstat_ip_to_sockaddr(
218 const struct in_addr
*ip
,
220 struct sockaddr_in
*sin
,
223 if (maxlen
< sizeof(struct sockaddr_in
))
226 sin
->sin_family
= AF_INET
;
227 sin
->sin_len
= sizeof(*sin
);
228 sin
->sin_port
= port
;
233 nstat_ip6_to_sockaddr(
234 const struct in6_addr
*ip6
,
236 struct sockaddr_in6
*sin6
,
239 if (maxlen
< sizeof(struct sockaddr_in6
))
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
))
248 sin6
->sin6_scope_id
= ntohs(sin6
->sin6_addr
.s6_addr16
[1]);
249 sin6
->sin6_addr
.s6_addr16
[1] = 0;
253 #pragma mark -- Network Statistic Providers --
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
;
258 static struct nstat_provider
*
259 nstat_find_provider_by_id(
260 nstat_provider_id_t id
)
262 struct nstat_provider
*provider
;
264 for (provider
= nstat_providers
; provider
!= NULL
; provider
= provider
->next
)
266 if (provider
->nstat_provider_id
== id
)
275 nstat_provider_id_t id
,
278 nstat_provider
**out_provider
,
279 nstat_provider_cookie_t
*out_cookie
)
281 *out_provider
= nstat_find_provider_by_id(id
);
282 if (*out_provider
== NULL
)
287 return (*out_provider
)->nstat_lookup(data
, length
, out_cookie
);
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);
296 __private_extern__
void
299 if (nstat_malloc_tag
!= NULL
) return;
301 OSMallocTag tag
= OSMalloc_Tagalloc(NET_STAT_CONTROL_NAME
, OSMT_DEFAULT
);
302 if (!OSCompareAndSwapPtr(NULL
, tag
, &nstat_malloc_tag
))
304 OSMalloc_Tagfree(tag
);
305 tag
= nstat_malloc_tag
;
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();
319 #pragma mark -- Aligned Buffer Allocation --
328 nstat_malloc_aligned(
333 struct align_header
*hdr
= NULL
;
334 u_int32_t size
= length
+ sizeof(*hdr
) + alignment
- 1;
336 u_int8_t
*buffer
= OSMalloc(size
, tag
);
337 if (buffer
== NULL
) return NULL
;
339 u_int8_t
*aligned
= buffer
+ sizeof(*hdr
);
340 aligned
= (u_int8_t
*)P2ROUNDUP(aligned
, alignment
);
342 hdr
= (struct align_header
*)(void *)(aligned
- sizeof(*hdr
));
343 hdr
->offset
= aligned
- buffer
;
354 struct align_header
*hdr
= (struct align_header
*)(void *)((u_int8_t
*)buffer
- sizeof(*hdr
));
355 OSFree(((char*)buffer
) - hdr
->offset
, hdr
->length
, tag
);
358 #pragma mark -- Route Provider --
360 static nstat_provider nstat_route_provider
;
366 nstat_provider_cookie_t
*out_cookie
)
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.
373 const struct sockaddr
*const_sa
;
376 const nstat_route_add_param
*param
= (const nstat_route_add_param
*)data
;
379 if (length
< sizeof(*param
))
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
))
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
)))
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
)))
404 dst
.const_sa
= (const struct sockaddr
*)¶m
->dst
;
405 mask
.const_sa
= param
->mask
.v4
.sin_family
? (const struct sockaddr
*)¶m
->mask
: NULL
;
407 struct radix_node_head
*rnh
= rt_tables
[dst
.sa
->sa_family
];
408 if (rnh
== NULL
) return EAFNOSUPPORT
;
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
);
414 if (rt
) *out_cookie
= (nstat_provider_cookie_t
)rt
;
416 return rt
? 0 : ENOENT
;
421 nstat_provider_cookie_t cookie
)
423 struct rtentry
*rt
= (struct rtentry
*)cookie
;
424 return ((rt
->rt_flags
& RTF_UP
) == 0) ? 1 : 0;
429 nstat_provider_cookie_t cookie
,
430 struct nstat_counts
*out_counts
,
433 struct rtentry
*rt
= (struct rtentry
*)cookie
;
434 struct nstat_counts
*rt_stats
= rt
->rt_stats
;
438 if ((rt
->rt_flags
& RTF_UP
) == 0) *out_gone
= 1;
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;
457 bzero(out_counts
, sizeof(*out_counts
));
464 nstat_provider_cookie_t cookie
,
467 rtfree((struct rtentry
*)cookie
);
470 static u_int32_t nstat_route_watchers
= 0;
473 nstat_route_walktree_add(
474 struct radix_node
*rn
,
478 struct rtentry
*rt
= (struct rtentry
*)rn
;
479 nstat_control_state
*state
= (nstat_control_state
*)context
;
481 lck_mtx_assert(rnh_lock
, LCK_MTX_ASSERT_OWNED
);
483 /* RTF_UP can't change while rnh_lock is held */
484 if ((rt
->rt_flags
& RTF_UP
) != 0)
486 /* Clear RTPRF_OURS if the route is still usable */
488 if (rt_validate(rt
)) {
489 RT_ADDREF_LOCKED(rt
);
496 /* Otherwise if RTF_CONDEMNED, treat it as if it were down */
500 result
= nstat_control_source_add(0, state
, &nstat_route_provider
, rt
);
509 nstat_route_add_watcher(
510 nstat_control_state
*state
)
514 OSIncrementAtomic(&nstat_route_watchers
);
516 lck_mtx_lock(rnh_lock
);
517 for (i
= 1; i
< AF_MAX
; i
++)
519 struct radix_node_head
*rnh
;
523 result
= rnh
->rnh_walktree(rnh
, nstat_route_walktree_add
, state
);
529 lck_mtx_unlock(rnh_lock
);
534 __private_extern__
void
535 nstat_route_new_entry(
538 if (nstat_route_watchers
== 0)
541 lck_mtx_lock(&nstat_mtx
);
542 if ((rt
->rt_flags
& RTF_UP
) != 0)
544 nstat_control_state
*state
;
545 for (state
= nstat_controls
; state
; state
= state
->ncs_next
)
547 if ((state
->ncs_watching
& (1 << NSTAT_PROVIDER_ROUTE
)) != 0)
549 // this client is watching routes
550 // acquire a reference for the route
553 // add the source, if that fails, release the reference
554 if (nstat_control_source_add(0, state
, &nstat_route_provider
, rt
) != 0)
559 lck_mtx_unlock(&nstat_mtx
);
563 nstat_route_remove_watcher(
564 __unused nstat_control_state
*state
)
566 OSDecrementAtomic(&nstat_route_watchers
);
570 nstat_route_copy_descriptor(
571 nstat_provider_cookie_t cookie
,
575 nstat_route_descriptor
*desc
= (nstat_route_descriptor
*)data
;
576 if (len
< sizeof(*desc
))
580 bzero(desc
, sizeof(*desc
));
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
);
590 if ((sa
= rt_key(rt
)))
591 nstat_copy_sa_out(sa
, &desc
->dst
.sa
, sizeof(desc
->dst
));
594 if ((sa
= rt_mask(rt
)) && sa
->sa_len
<= sizeof(desc
->mask
))
595 memcpy(&desc
->mask
, sa
, sa
->sa_len
);
598 if ((sa
= rt
->rt_gateway
))
599 nstat_copy_sa_out(sa
, &desc
->gateway
.sa
, sizeof(desc
->gateway
));
602 desc
->ifindex
= rt
->rt_ifp
->if_index
;
604 desc
->flags
= rt
->rt_flags
;
610 nstat_init_route_provider(void)
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
;
626 #pragma mark -- Route Collection --
628 static struct nstat_counts
*
632 struct nstat_counts
*result
= rte
->rt_stats
;
633 if (result
) return result
;
635 if (nstat_malloc_tag
== NULL
) nstat_init();
637 result
= nstat_malloc_aligned(sizeof(*result
), sizeof(u_int64_t
), nstat_malloc_tag
);
638 if (!result
) return result
;
640 bzero(result
, sizeof(*result
));
642 if (!OSCompareAndSwapPtr(NULL
, result
, &rte
->rt_stats
))
644 nstat_free_aligned(result
, nstat_malloc_tag
);
645 result
= rte
->rt_stats
;
651 __private_extern__
void
657 nstat_free_aligned(rte
->rt_stats
, nstat_malloc_tag
);
658 rte
->rt_stats
= NULL
;
662 __private_extern__
void
663 nstat_route_connect_attempt(
668 struct nstat_counts
* stats
= nstat_route_attach(rte
);
671 OSIncrementAtomic(&stats
->nstat_connectattempts
);
674 rte
= rte
->rt_parent
;
678 __private_extern__
void
679 nstat_route_connect_success(
685 struct nstat_counts
* stats
= nstat_route_attach(rte
);
688 OSIncrementAtomic(&stats
->nstat_connectsuccesses
);
691 rte
= rte
->rt_parent
;
695 __private_extern__
void
704 struct nstat_counts
* stats
= nstat_route_attach(rte
);
707 if ((flags
& NSTAT_TX_FLAG_RETRANSMIT
) != 0)
709 OSAddAtomic(bytes
, &stats
->nstat_txretransmit
);
713 OSAddAtomic64((SInt64
)packets
, (SInt64
*)&stats
->nstat_txpackets
);
714 OSAddAtomic64((SInt64
)bytes
, (SInt64
*)&stats
->nstat_txbytes
);
718 rte
= rte
->rt_parent
;
722 __private_extern__
void
731 struct nstat_counts
* stats
= nstat_route_attach(rte
);
736 OSAddAtomic64((SInt64
)packets
, (SInt64
*)&stats
->nstat_rxpackets
);
737 OSAddAtomic64((SInt64
)bytes
, (SInt64
*)&stats
->nstat_rxbytes
);
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
);
748 rte
= rte
->rt_parent
;
752 __private_extern__
void
758 const int32_t factor
= 8;
762 struct nstat_counts
* stats
= nstat_route_attach(rte
);
771 oldrtt
= stats
->nstat_avg_rtt
;
778 newrtt
= oldrtt
- (oldrtt
- (int32_t)rtt
) / factor
;
780 if (oldrtt
== newrtt
) break;
781 } while (!OSCompareAndSwap(oldrtt
, newrtt
, &stats
->nstat_avg_rtt
));
786 oldrtt
= stats
->nstat_min_rtt
;
787 if (oldrtt
!= 0 && oldrtt
< (int32_t)rtt
)
791 } while (!OSCompareAndSwap(oldrtt
, rtt
, &stats
->nstat_min_rtt
));
796 oldrtt
= stats
->nstat_var_rtt
;
803 newrtt
= oldrtt
- (oldrtt
- (int32_t)rtt_var
) / factor
;
805 if (oldrtt
== newrtt
) break;
806 } while (!OSCompareAndSwap(oldrtt
, newrtt
, &stats
->nstat_var_rtt
));
809 rte
= rte
->rt_parent
;
814 #pragma mark -- TCP Provider --
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.
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.
827 struct nstat_tucookie
{
829 char pname
[MAXCOMLEN
+1];
833 struct sockaddr_in v4
;
834 struct sockaddr_in6 v6
;
838 struct sockaddr_in v4
;
839 struct sockaddr_in6 v6
;
841 unsigned int if_index
;
844 static struct nstat_tucookie
*
845 nstat_tucookie_alloc_internal(
850 struct nstat_tucookie
*cookie
;
852 cookie
= OSMalloc(sizeof(*cookie
), nstat_malloc_tag
);
856 lck_mtx_assert(&nstat_mtx
, LCK_MTX_ASSERT_NOTOWNED
);
857 if (ref
&& in_pcb_checkstate(inp
, WNT_ACQUIRE
, locked
) == WNT_STOPUSING
)
859 OSFree(cookie
, sizeof(*cookie
), nstat_malloc_tag
);
862 bzero(cookie
, sizeof(*cookie
));
864 proc_name(inp
->inp_socket
->last_pid
, cookie
->pname
,
865 sizeof(cookie
->pname
));
867 * We only increment the reference count for UDP sockets because we
868 * only cache UDP socket tuples.
870 if (SOCK_PROTO(inp
->inp_socket
) == IPPROTO_UDP
)
871 OSIncrementAtomic(&inp
->inp_nstat_refcnt
);
876 static struct nstat_tucookie
*
877 nstat_tucookie_alloc(
880 return nstat_tucookie_alloc_internal(inp
, false, false);
883 static struct nstat_tucookie
*
884 nstat_tucookie_alloc_ref(
887 return nstat_tucookie_alloc_internal(inp
, true, false);
890 static struct nstat_tucookie
*
891 nstat_tucookie_alloc_ref_locked(
894 return nstat_tucookie_alloc_internal(inp
, true, true);
898 nstat_tucookie_release_internal(
899 struct nstat_tucookie
*cookie
,
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
);
909 nstat_tucookie_release(
910 struct nstat_tucookie
*cookie
)
912 nstat_tucookie_release_internal(cookie
, false);
916 nstat_tucookie_release_locked(
917 struct nstat_tucookie
*cookie
)
919 nstat_tucookie_release_internal(cookie
, true);
923 static nstat_provider nstat_tcp_provider
;
927 struct inpcbinfo
*inpinfo
,
930 nstat_provider_cookie_t
*out_cookie
)
932 struct inpcb
*inp
= NULL
;
934 // parameter validation
935 const nstat_tcp_add_param
*param
= (const nstat_tcp_add_param
*)data
;
936 if (length
< sizeof(*param
))
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
)
949 switch (param
->local
.v4
.sin_family
)
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
)))
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
);
970 const struct in6_addr
*in6c
;
971 struct in6_addr
*in6
;
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
)))
981 local
.in6c
= ¶m
->local
.v6
.sin6_addr
;
982 remote
.in6c
= ¶m
->remote
.v6
.sin6_addr
;
984 inp
= in6_pcblookup_hash(inpinfo
, remote
.in6
, param
->remote
.v6
.sin6_port
,
985 local
.in6
, param
->local
.v6
.sin6_port
, 1, NULL
);
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);
1009 nstat_provider_cookie_t
*out_cookie
)
1011 return nstat_tcpudp_lookup(&tcbinfo
, data
, length
, out_cookie
);
1016 nstat_provider_cookie_t cookie
)
1018 struct nstat_tucookie
*tucookie
=
1019 (struct nstat_tucookie
*)cookie
;
1023 return (!(inp
= tucookie
->inp
) ||
1024 !(tp
= intotcpcb(inp
)) ||
1025 inp
->inp_state
== INPCB_STATE_DEAD
) ? 1 : 0;
1030 nstat_provider_cookie_t cookie
,
1031 struct nstat_counts
*out_counts
,
1034 struct nstat_tucookie
*tucookie
=
1035 (struct nstat_tucookie
*)cookie
;
1038 bzero(out_counts
, sizeof(*out_counts
));
1042 // if the pcb is in the dead state, we should stop using it
1043 if (nstat_tcp_gone(cookie
))
1046 if (!(inp
= tucookie
->inp
) || !intotcpcb(inp
))
1049 inp
= tucookie
->inp
;
1050 struct tcpcb
*tp
= intotcpcb(inp
);
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
);
1078 nstat_provider_cookie_t cookie
,
1081 struct nstat_tucookie
*tucookie
=
1082 (struct nstat_tucookie
*)cookie
;
1084 nstat_tucookie_release_internal(tucookie
, locked
);
1088 nstat_tcp_add_watcher(
1089 nstat_control_state
*state
)
1091 OSIncrementAtomic(&nstat_tcp_watchers
);
1093 lck_rw_lock_shared(tcbinfo
.ipi_lock
);
1095 // Add all current tcp inpcbs. Ignore those in timewait
1097 struct nstat_tucookie
*cookie
;
1098 LIST_FOREACH(inp
, tcbinfo
.ipi_listhead
, inp_list
)
1100 cookie
= nstat_tucookie_alloc_ref(inp
);
1103 if (nstat_control_source_add(0, state
, &nstat_tcp_provider
,
1106 nstat_tucookie_release(cookie
);
1111 lck_rw_done(tcbinfo
.ipi_lock
);
1117 nstat_tcp_remove_watcher(
1118 __unused nstat_control_state
*state
)
1120 OSDecrementAtomic(&nstat_tcp_watchers
);
1123 __private_extern__
void
1127 struct nstat_tucookie
*cookie
;
1129 if (nstat_tcp_watchers
== 0)
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
)
1137 if ((state
->ncs_watching
& (1 << NSTAT_PROVIDER_TCP
)) != 0)
1139 // this client is watching tcp
1140 // acquire a reference for it
1141 cookie
= nstat_tucookie_alloc_ref_locked(inp
);
1144 // add the source, if that fails, release the reference
1145 if (nstat_control_source_add(0, state
,
1146 &nstat_tcp_provider
, cookie
) != 0)
1148 nstat_tucookie_release_locked(cookie
);
1153 lck_mtx_unlock(&nstat_mtx
);
1154 socket_unlock(inp
->inp_socket
, 0);
1157 __private_extern__
void
1158 nstat_pcb_detach(struct inpcb
*inp
)
1160 nstat_control_state
*state
;
1161 nstat_src
*src
, *prevsrc
;
1162 nstat_src
*dead_list
= NULL
;
1163 struct nstat_tucookie
*tucookie
;
1166 if (inp
== NULL
|| (nstat_tcp_watchers
== 0 && nstat_udp_watchers
== 0))
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
)
1175 tucookie
= (struct nstat_tucookie
*)src
->cookie
;
1176 if (tucookie
->inp
== inp
)
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",
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",
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",
1200 prevsrc
->next
= src
->next
;
1202 state
->ncs_srcs
= src
->next
;
1204 src
->next
= dead_list
;
1207 lck_mtx_unlock(&state
->mtx
);
1209 lck_mtx_unlock(&nstat_mtx
);
1213 dead_list
= src
->next
;
1215 nstat_control_cleanup_source(NULL
, src
, TRUE
);
1219 __private_extern__
void
1220 nstat_pcb_cache(struct inpcb
*inp
)
1222 nstat_control_state
*state
;
1224 struct nstat_tucookie
*tucookie
;
1226 if (inp
== NULL
|| nstat_udp_watchers
== 0 ||
1227 inp
->inp_nstat_refcnt
== 0)
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
)
1235 tucookie
= (struct nstat_tucookie
*)src
->cookie
;
1236 if (tucookie
->inp
== inp
)
1238 if (inp
->inp_vflag
& INP_IPV6
)
1240 nstat_ip6_to_sockaddr(&inp
->in6p_laddr
,
1242 &tucookie
->local
.v6
,
1243 sizeof(tucookie
->local
));
1244 nstat_ip6_to_sockaddr(&inp
->in6p_faddr
,
1246 &tucookie
->remote
.v6
,
1247 sizeof(tucookie
->remote
));
1249 else if (inp
->inp_vflag
& INP_IPV4
)
1251 nstat_ip_to_sockaddr(&inp
->inp_laddr
,
1253 &tucookie
->local
.v4
,
1254 sizeof(tucookie
->local
));
1255 nstat_ip_to_sockaddr(&inp
->inp_faddr
,
1257 &tucookie
->remote
.v4
,
1258 sizeof(tucookie
->remote
));
1260 if (inp
->inp_last_outifp
)
1261 tucookie
->if_index
=
1262 inp
->inp_last_outifp
->if_index
;
1263 tucookie
->cached
= true;
1267 lck_mtx_unlock(&state
->mtx
);
1269 lck_mtx_unlock(&nstat_mtx
);
1272 __private_extern__
void
1273 nstat_pcb_invalidate_cache(struct inpcb
*inp
)
1275 nstat_control_state
*state
;
1277 struct nstat_tucookie
*tucookie
;
1279 if (inp
== NULL
|| nstat_udp_watchers
== 0 ||
1280 inp
->inp_nstat_refcnt
== 0)
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
)
1288 tucookie
= (struct nstat_tucookie
*)src
->cookie
;
1289 if (tucookie
->inp
== inp
)
1291 tucookie
->cached
= false;
1295 lck_mtx_unlock(&state
->mtx
);
1297 lck_mtx_unlock(&nstat_mtx
);
1301 nstat_tcp_copy_descriptor(
1302 nstat_provider_cookie_t cookie
,
1306 if (len
< sizeof(nstat_tcp_descriptor
))
1311 if (nstat_tcp_gone(cookie
))
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
));
1321 if (inp
->inp_vflag
& INP_IPV6
)
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
));
1328 else if (inp
->inp_vflag
& INP_IPV4
)
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
));
1336 desc
->state
= intotcpcb(inp
)->t_state
;
1337 desc
->ifindex
= (inp
->inp_last_outifp
== NULL
) ? 0 :
1338 inp
->inp_last_outifp
->if_index
;
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
;
1345 if (CC_ALGO(tp
)->name
!= NULL
) {
1346 strlcpy(desc
->cc_algo
, CC_ALGO(tp
)->name
,
1347 sizeof(desc
->cc_algo
));
1350 struct socket
*so
= inp
->inp_socket
;
1353 // TBD - take the socket lock around these to make sure
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)
1362 strlcpy(desc
->pname
, tucookie
->pname
,
1363 sizeof(desc
->pname
));
1367 desc
->pname
[sizeof(desc
->pname
) - 1] = 0;
1368 strlcpy(tucookie
->pname
, desc
->pname
,
1369 sizeof(tucookie
->pname
));
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
));
1378 desc
->eupid
= desc
->upid
;
1379 desc
->epid
= desc
->pid
;
1380 memcpy(desc
->euuid
, desc
->uuid
, sizeof(desc
->uuid
));
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
;
1392 nstat_init_tcp_provider(void)
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
;
1408 #pragma mark -- UDP Provider --
1410 static nstat_provider nstat_udp_provider
;
1416 nstat_provider_cookie_t
*out_cookie
)
1418 return nstat_tcpudp_lookup(&udbinfo
, data
, length
, out_cookie
);
1423 nstat_provider_cookie_t cookie
)
1425 struct nstat_tucookie
*tucookie
=
1426 (struct nstat_tucookie
*)cookie
;
1429 return (!(inp
= tucookie
->inp
) ||
1430 inp
->inp_state
== INPCB_STATE_DEAD
) ? 1 : 0;
1435 nstat_provider_cookie_t cookie
,
1436 struct nstat_counts
*out_counts
,
1439 struct nstat_tucookie
*tucookie
=
1440 (struct nstat_tucookie
*)cookie
;
1444 // if the pcb is in the dead state, we should stop using it
1445 if (nstat_udp_gone(cookie
))
1451 struct inpcb
*inp
= tucookie
->inp
;
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
);
1469 nstat_provider_cookie_t cookie
,
1472 struct nstat_tucookie
*tucookie
=
1473 (struct nstat_tucookie
*)cookie
;
1475 nstat_tucookie_release_internal(tucookie
, locked
);
1479 nstat_udp_add_watcher(
1480 nstat_control_state
*state
)
1483 struct nstat_tucookie
*cookie
;
1485 OSIncrementAtomic(&nstat_udp_watchers
);
1487 lck_rw_lock_shared(udbinfo
.ipi_lock
);
1488 // Add all current UDP inpcbs.
1489 LIST_FOREACH(inp
, udbinfo
.ipi_listhead
, inp_list
)
1491 cookie
= nstat_tucookie_alloc_ref(inp
);
1494 if (nstat_control_source_add(0, state
, &nstat_udp_provider
,
1497 nstat_tucookie_release(cookie
);
1502 lck_rw_done(udbinfo
.ipi_lock
);
1508 nstat_udp_remove_watcher(
1509 __unused nstat_control_state
*state
)
1511 OSDecrementAtomic(&nstat_udp_watchers
);
1514 __private_extern__
void
1518 struct nstat_tucookie
*cookie
;
1520 if (nstat_udp_watchers
== 0)
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
)
1528 if ((state
->ncs_watching
& (1 << NSTAT_PROVIDER_UDP
)) != 0)
1530 // this client is watching tcp
1531 // acquire a reference for it
1532 cookie
= nstat_tucookie_alloc_ref_locked(inp
);
1535 // add the source, if that fails, release the reference
1536 if (nstat_control_source_add(0, state
,
1537 &nstat_udp_provider
, cookie
) != 0)
1539 nstat_tucookie_release_locked(cookie
);
1544 lck_mtx_unlock(&nstat_mtx
);
1545 socket_unlock(inp
->inp_socket
, 0);
1549 nstat_udp_copy_descriptor(
1550 nstat_provider_cookie_t cookie
,
1554 if (len
< sizeof(nstat_udp_descriptor
))
1559 if (nstat_udp_gone(cookie
))
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
;
1567 bzero(desc
, sizeof(*desc
));
1569 if (tucookie
->cached
== false) {
1570 if (inp
->inp_vflag
& INP_IPV6
)
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
));
1577 else if (inp
->inp_vflag
& INP_IPV4
)
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
));
1587 if (inp
->inp_vflag
& INP_IPV6
)
1589 memcpy(&desc
->local
.v6
, &tucookie
->local
.v6
,
1590 sizeof(desc
->local
));
1591 memcpy(&desc
->remote
.v6
, &tucookie
->remote
.v6
,
1592 sizeof(desc
->remote
));
1594 else if (inp
->inp_vflag
& INP_IPV4
)
1596 memcpy(&desc
->local
.v4
, &tucookie
->local
.v4
,
1597 sizeof(desc
->local
));
1598 memcpy(&desc
->remote
.v4
, &tucookie
->remote
.v4
,
1599 sizeof(desc
->remote
));
1603 if (inp
->inp_last_outifp
)
1604 desc
->ifindex
= inp
->inp_last_outifp
->if_index
;
1606 desc
->ifindex
= tucookie
->if_index
;
1608 struct socket
*so
= inp
->inp_socket
;
1611 // TBD - take the socket lock around these to make sure
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)
1618 strlcpy(desc
->pname
, tucookie
->pname
,
1619 sizeof(desc
->pname
));
1623 desc
->pname
[sizeof(desc
->pname
) - 1] = 0;
1624 strlcpy(tucookie
->pname
, desc
->pname
,
1625 sizeof(tucookie
->pname
));
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
));
1634 desc
->eupid
= desc
->upid
;
1635 desc
->epid
= desc
->pid
;
1636 memcpy(desc
->euuid
, desc
->uuid
, sizeof(desc
->uuid
));
1638 desc
->rcvbufsize
= so
->so_rcv
.sb_hiwat
;
1639 desc
->rcvbufused
= so
->so_rcv
.sb_cc
;
1640 desc
->traffic_class
= so
->so_traffic_class
;
1647 nstat_init_udp_provider(void)
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
;
1663 #pragma mark -- ifnet Provider --
1665 static nstat_provider nstat_ifnet_provider
;
1668 * We store a pointer to the ifnet and the original threshold
1669 * requested by the client.
1671 struct nstat_ifnet_cookie
1681 nstat_provider_cookie_t
*out_cookie
)
1683 const nstat_ifnet_add_param
*param
= (nstat_ifnet_add_param
*)data
;
1685 boolean_t changed
= FALSE
;
1686 nstat_control_state
*state
;
1688 struct nstat_ifnet_cookie
*cookie
;
1690 if (length
< sizeof(*param
) || param
->threshold
< 1024*1024)
1692 if (nstat_privcheck
!= 0) {
1693 errno_t result
= priv_check_cred(kauth_cred_get(),
1694 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS
, 0);
1698 cookie
= OSMalloc(sizeof(*cookie
), nstat_malloc_tag
);
1701 bzero(cookie
, sizeof(*cookie
));
1703 ifnet_head_lock_shared();
1704 TAILQ_FOREACH(ifp
, &ifnet_head
, if_link
)
1706 ifnet_lock_exclusive(ifp
);
1707 if (ifp
->if_index
== param
->ifindex
)
1710 cookie
->threshold
= param
->threshold
;
1711 *out_cookie
= cookie
;
1712 if (!ifp
->if_data_threshold
||
1713 ifp
->if_data_threshold
> param
->threshold
)
1716 ifp
->if_data_threshold
= param
->threshold
;
1718 ifnet_lock_done(ifp
);
1719 ifnet_reference(ifp
);
1722 ifnet_lock_done(ifp
);
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.
1734 lck_mtx_lock(&nstat_mtx
);
1735 for (state
= nstat_controls
; state
; state
= state
->ncs_next
)
1737 lck_mtx_lock(&state
->mtx
);
1738 for (src
= state
->ncs_srcs
; src
; src
= src
->next
)
1740 if (src
->provider
!= &nstat_ifnet_provider
)
1742 nstat_control_send_description(state
, src
, 0);
1744 lck_mtx_unlock(&state
->mtx
);
1746 lck_mtx_unlock(&nstat_mtx
);
1748 if (cookie
->ifp
== NULL
)
1749 OSFree(cookie
, sizeof(*cookie
), nstat_malloc_tag
);
1751 return ifp
? 0 : EINVAL
;
1756 nstat_provider_cookie_t cookie
)
1759 struct nstat_ifnet_cookie
*ifcookie
=
1760 (struct nstat_ifnet_cookie
*)cookie
;
1762 ifnet_head_lock_shared();
1763 TAILQ_FOREACH(ifp
, &ifnet_head
, if_link
)
1765 if (ifp
== ifcookie
->ifp
)
1775 nstat_provider_cookie_t cookie
,
1776 struct nstat_counts
*out_counts
,
1779 struct nstat_ifnet_cookie
*ifcookie
=
1780 (struct nstat_ifnet_cookie
*)cookie
;
1781 struct ifnet
*ifp
= ifcookie
->ifp
;
1785 // if the ifnet is gone, we should stop using it
1786 if (nstat_ifnet_gone(cookie
))
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;
1803 nstat_ifnet_release(
1804 nstat_provider_cookie_t cookie
,
1805 __unused
int locked
)
1807 struct nstat_ifnet_cookie
*ifcookie
;
1809 nstat_control_state
*state
;
1811 uint64_t minthreshold
= UINT64_MAX
;
1814 * Find all the clients that requested a threshold
1815 * for this ifnet and re-calculate if_data_threshold.
1817 lck_mtx_lock(&nstat_mtx
);
1818 for (state
= nstat_controls
; state
; state
= state
->ncs_next
)
1820 lck_mtx_lock(&state
->mtx
);
1821 for (src
= state
->ncs_srcs
; src
; src
= src
->next
)
1823 /* Skip the provider we are about to detach. */
1824 if (src
->provider
!= &nstat_ifnet_provider
||
1825 src
->cookie
== cookie
)
1827 ifcookie
= (struct nstat_ifnet_cookie
*)src
->cookie
;
1828 if (ifcookie
->threshold
< minthreshold
)
1829 minthreshold
= ifcookie
->threshold
;
1831 lck_mtx_unlock(&state
->mtx
);
1833 lck_mtx_unlock(&nstat_mtx
);
1835 * Reset if_data_threshold or disable it.
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;
1844 ifp
->if_data_threshold
= minthreshold
;
1845 ifnet_lock_done(ifp
);
1846 ifnet_decr_iorefcnt(ifp
);
1849 OSFree(ifcookie
, sizeof(*ifcookie
), nstat_malloc_tag
);
1853 nstat_ifnet_copy_descriptor(
1854 nstat_provider_cookie_t cookie
,
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
;
1863 if (len
< sizeof(nstat_ifnet_descriptor
))
1866 if (nstat_ifnet_gone(cookie
))
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
);
1884 nstat_init_ifnet_provider(void)
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
;
1900 __private_extern__
void
1901 nstat_ifnet_threshold_reached(unsigned int ifindex
)
1903 nstat_control_state
*state
;
1906 struct nstat_ifnet_cookie
*ifcookie
;
1908 lck_mtx_lock(&nstat_mtx
);
1909 for (state
= nstat_controls
; state
; state
= state
->ncs_next
)
1911 lck_mtx_lock(&state
->mtx
);
1912 for (src
= state
->ncs_srcs
; src
; src
= src
->next
)
1914 if (src
->provider
!= &nstat_ifnet_provider
)
1916 ifcookie
= (struct nstat_ifnet_cookie
*)src
->cookie
;
1917 ifp
= ifcookie
->ifp
;
1918 if (ifp
->if_index
!= ifindex
)
1920 nstat_control_send_counts(state
, src
, 0, NULL
);
1922 lck_mtx_unlock(&state
->mtx
);
1924 lck_mtx_unlock(&nstat_mtx
);
1927 #pragma mark -- Sysinfo Provider --
1929 static nstat_provider nstat_sysinfo_provider
;
1931 /* We store the flags requested by the client */
1932 typedef struct nstat_sysinfo_cookie
1935 } nstat_sysinfo_cookie
;
1938 nstat_sysinfo_lookup(
1941 nstat_provider_cookie_t
*out_cookie
)
1943 const nstat_sysinfo_add_param
*param
= (nstat_sysinfo_add_param
*)data
;
1944 nstat_sysinfo_cookie
*cookie
;
1946 if (length
< sizeof(*param
))
1949 if (nstat_privcheck
!= 0) {
1950 errno_t result
= priv_check_cred(kauth_cred_get(),
1951 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS
, 0);
1956 cookie
= OSMalloc(sizeof(*cookie
), nstat_malloc_tag
);
1959 cookie
->flags
= param
->flags
;
1960 *out_cookie
= cookie
;
1966 __unused nstat_provider_cookie_t cookie
)
1968 /* Sysinfo always exists */
1973 nstat_sysinfo_copy_descriptor(
1974 nstat_provider_cookie_t cookie
,
1978 nstat_sysinfo_descriptor
*desc
= (nstat_sysinfo_descriptor
*)data
;
1979 struct nstat_sysinfo_cookie
*syscookie
=
1980 (struct nstat_sysinfo_cookie
*)cookie
;
1982 if (len
< sizeof(nstat_sysinfo_descriptor
))
1984 desc
->flags
= syscookie
->flags
;
1989 nstat_sysinfo_release(
1990 nstat_provider_cookie_t cookie
,
1991 __unused boolean_t locked
)
1993 struct nstat_sysinfo_cookie
*syscookie
=
1994 (struct nstat_sysinfo_cookie
*)cookie
;
1995 OSFree(syscookie
, sizeof(*syscookie
), nstat_malloc_tag
);
1999 nstat_enqueue_success(
2001 nstat_control_state
*state
)
2003 nstat_msg_hdr success
;
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
);
2012 printf("%s: could not enqueue success message %d\n",
2014 nstat_successmsgfailures
+= 1;
2020 nstat_init_sysinfo_provider(void)
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
;
2037 nstat_sysinfo_send_data_internal(
2038 nstat_control_state
*control
,
2040 nstat_sysinfo_data
*data
)
2042 nstat_msg_sysinfo_counts
*syscnt
= NULL
;
2043 size_t allocsize
= 0, countsize
= 0, nkeyvals
= 0;
2044 nstat_sysinfo_keyval
*kv
;
2047 allocsize
= offsetof(nstat_msg_sysinfo_counts
, counts
);
2048 countsize
= offsetof(nstat_sysinfo_counts
, nstat_sysinfo_keyvals
);
2050 /* get number of key-vals for each kind of stat */
2051 switch (data
->flags
)
2053 case NSTAT_SYSINFO_MBUF_STATS
:
2056 case NSTAT_SYSINFO_TCP_STATS
:
2062 countsize
+= sizeof(nstat_sysinfo_keyval
) * nkeyvals
;
2063 allocsize
+= countsize
;
2065 syscnt
= OSMalloc(allocsize
, nstat_malloc_tag
);
2068 bzero(syscnt
, allocsize
);
2070 syscnt
->hdr
.type
= NSTAT_MSG_TYPE_SYSINFO_COUNTS
;
2071 syscnt
->counts
.nstat_sysinfo_len
= countsize
;
2072 syscnt
->srcref
= src
->srcref
;
2074 kv
= (nstat_sysinfo_keyval
*) &syscnt
->counts
.nstat_sysinfo_keyvals
;
2075 switch (data
->flags
)
2077 case NSTAT_SYSINFO_MBUF_STATS
:
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
;
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
;
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
;
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
;
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
;
2101 case NSTAT_SYSINFO_TCP_STATS
:
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
;
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
;
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
;
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
;
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
;
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
;
2132 result
= ctl_enqueuedata(control
->ncs_kctl
,
2133 control
->ncs_unit
, syscnt
, allocsize
, CTL_DATA_EOR
);
2135 nstat_sysinfofailures
+= 1;
2136 OSFree(syscnt
, allocsize
, nstat_malloc_tag
);
2141 __private_extern__
void
2142 nstat_sysinfo_send_data(
2143 nstat_sysinfo_data
*data
)
2145 nstat_control_state
*control
;
2147 lck_mtx_lock(&nstat_mtx
);
2148 for (control
= nstat_controls
; control
; control
= control
->ncs_next
)
2150 lck_mtx_lock(&control
->mtx
);
2152 for (src
= control
->ncs_srcs
; src
; src
= src
->next
)
2154 if (src
->provider
->nstat_provider_id
==
2155 NSTAT_PROVIDER_SYSINFO
)
2157 struct nstat_sysinfo_cookie
*syscookie
;
2158 syscookie
= (struct nstat_sysinfo_cookie
*) src
->cookie
;
2159 if (syscookie
->flags
& data
->flags
)
2161 nstat_sysinfo_send_data_internal(control
,
2166 lck_mtx_unlock(&control
->mtx
);
2168 lck_mtx_unlock(&nstat_mtx
);
2173 nstat_sysinfo_generate_report(void)
2175 mbuf_report_peak_usage();
2179 #pragma mark -- Kernel Control Socket --
2181 static kern_ctl_ref nstat_ctlref
= NULL
;
2182 static lck_grp_t
*nstat_lck_grp
= NULL
;
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
);
2191 __unused thread_call_param_t p0
,
2192 __unused thread_call_param_t p1
)
2194 lck_mtx_lock(&nstat_mtx
);
2196 nstat_idle_time
= 0;
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
)
2203 lck_mtx_lock(&control
->mtx
);
2204 nstat_src
**srcpp
= &control
->ncs_srcs
;
2206 if (!(control
->ncs_flags
& NSTAT_FLAG_REQCOUNTS
))
2208 while(*srcpp
!= NULL
)
2210 if ((*srcpp
)->provider
->nstat_gone((*srcpp
)->cookie
))
2214 // Pull it off the list
2216 *srcpp
= (*srcpp
)->next
;
2218 // send one last counts notification
2219 result
= nstat_control_send_counts(control
, dead
,
2221 if (result
!= 0 && nstat_debug
)
2222 printf("%s - nstat_control_send_counts() %d\n",
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",
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",
2237 // Put this on the list to release later
2238 dead
->next
= dead_list
;
2243 srcpp
= &(*srcpp
)->next
;
2247 control
->ncs_flags
&= ~NSTAT_FLAG_REQCOUNTS
;
2248 lck_mtx_unlock(&control
->mtx
);
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
);
2257 lck_mtx_unlock(&nstat_mtx
);
2259 /* Generate any system level reports, if needed */
2260 nstat_sysinfo_generate_report();
2262 // Release the sources now that we aren't holding lots of locks
2266 dead_list
= dead
->next
;
2268 nstat_control_cleanup_source(NULL
, dead
, FALSE
);
2275 nstat_control_register(void)
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
);
2283 lck_mtx_init(&nstat_mtx
, nstat_lck_grp
, NULL
);
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
;
2296 ctl_register(&nstat_control
, &nstat_ctlref
);
2300 nstat_control_cleanup_source(
2301 nstat_control_state
*state
,
2302 struct nstat_src
*src
,
2308 result
= nstat_control_send_removed(state
, src
);
2309 if (result
!= 0 && nstat_debug
)
2310 printf("%s - nstat_control_send_removed() %d\n",
2313 // Cleanup the source if we found it.
2314 src
->provider
->nstat_release(src
->cookie
, locked
);
2315 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
2319 nstat_control_connect(
2321 struct sockaddr_ctl
*sac
,
2324 nstat_control_state
*state
= OSMalloc(sizeof(*state
), nstat_malloc_tag
);
2325 if (state
== NULL
) return ENOMEM
;
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
;
2334 lck_mtx_lock(&nstat_mtx
);
2335 state
->ncs_next
= nstat_controls
;
2336 nstat_controls
= state
;
2338 if (nstat_idle_time
== 0)
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
);
2344 lck_mtx_unlock(&nstat_mtx
);
2350 nstat_control_disconnect(
2351 __unused kern_ctl_ref kctl
,
2352 __unused u_int32_t unit
,
2356 nstat_control_state
*state
= (nstat_control_state
*)uinfo
;
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
)
2363 if (*statepp
== state
)
2365 *statepp
= state
->ncs_next
;
2369 lck_mtx_unlock(&nstat_mtx
);
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
)
2378 if ((watching
& (1 << provider
->nstat_provider_id
)) != 0)
2380 watching
&= ~(1 << provider
->nstat_provider_id
);
2381 provider
->nstat_watcher_remove(state
);
2385 // set cleanup flags
2386 state
->ncs_flags
|= NSTAT_FLAG_CLEANUP
;
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
);
2397 // pull it out of the list
2402 nstat_control_cleanup_source(NULL
, src
, FALSE
);
2404 lck_mtx_destroy(&state
->mtx
, nstat_lck_grp
);
2405 OSFree(state
, sizeof(*state
), nstat_malloc_tag
);
2410 static nstat_src_ref_t
2411 nstat_control_next_src_ref(
2412 nstat_control_state
*state
)
2415 nstat_src_ref_t toReturn
= NSTAT_SRC_REF_INVALID
;
2417 for (i
= 0; i
< 1000 && toReturn
== NSTAT_SRC_REF_INVALID
; i
++)
2419 if (state
->ncs_next_srcref
== NSTAT_SRC_REF_INVALID
||
2420 state
->ncs_next_srcref
== NSTAT_SRC_REF_ALL
)
2422 state
->ncs_next_srcref
= 1;
2426 for (src
= state
->ncs_srcs
; src
; src
= src
->next
)
2428 if (src
->srcref
== state
->ncs_next_srcref
)
2432 if (src
== NULL
) toReturn
= state
->ncs_next_srcref
;
2433 state
->ncs_next_srcref
++;
2440 nstat_control_send_counts(
2441 nstat_control_state
*state
,
2443 unsigned long long context
,
2446 nstat_msg_src_counts counts
;
2450 /* Some providers may not have any counts to send */
2451 if (src
->provider
->nstat_counts
== NULL
)
2454 bzero(&counts
, sizeof(counts
));
2455 counts
.hdr
.type
= NSTAT_MSG_TYPE_SRC_COUNTS
;
2456 counts
.hdr
.context
= context
;
2457 counts
.srcref
= src
->srcref
;
2459 if (src
->provider
->nstat_counts(src
->cookie
, &counts
.counts
,
2461 if ((src
->filter
& NSTAT_FILTER_NOZEROBYTES
) &&
2462 counts
.counts
.nstat_rxbytes
== 0 &&
2463 counts
.counts
.nstat_txbytes
== 0) {
2466 result
= ctl_enqueuedata(state
->ncs_kctl
,
2467 state
->ncs_unit
, &counts
, sizeof(counts
),
2470 nstat_srccountfailures
+= 1;
2479 nstat_control_send_description(
2480 nstat_control_state
*state
,
2484 // Provider doesn't support getting the descriptor? Done.
2485 if (src
->provider
->nstat_descriptor_length
== 0 ||
2486 src
->provider
->nstat_copy_descriptor
== NULL
)
2491 // Allocate storage for the descriptor message
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)
2500 nstat_msg_src_description
*desc
= (nstat_msg_src_description
*)mbuf_data(msg
);
2502 mbuf_setlen(msg
, size
);
2503 mbuf_pkthdr_setlen(msg
, mbuf_len(msg
));
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
);
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
;
2519 result
= ctl_enqueuembuf(state
->ncs_kctl
, state
->ncs_unit
, msg
, CTL_DATA_EOR
);
2522 nstat_descriptionfailures
+= 1;
2530 nstat_control_send_removed(
2531 nstat_control_state
*state
,
2534 nstat_msg_src_removed removed
;
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
);
2544 nstat_msgremovedfailures
+= 1;
2550 nstat_control_handle_add_request(
2551 nstat_control_state
*state
,
2556 // Verify the header fits in the first mbuf
2557 if (mbuf_len(m
) < offsetof(nstat_msg_add_src_req
, param
))
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)
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
))
2574 // parameter is too large, we need to make a contiguous copy
2575 void *data
= OSMalloc(paramlength
, nstat_malloc_tag
);
2577 if (!data
) return ENOMEM
;
2578 result
= mbuf_copydata(m
, offsetof(nstat_msg_add_src_req
, param
), paramlength
, data
);
2580 result
= nstat_lookup_entry(req
->provider
, data
, paramlength
, &provider
, &cookie
);
2581 OSFree(data
, paramlength
, nstat_malloc_tag
);
2585 result
= nstat_lookup_entry(req
->provider
, (void*)&req
->param
, paramlength
, &provider
, &cookie
);
2593 result
= nstat_control_source_add(req
->hdr
.context
, state
, provider
, cookie
);
2595 provider
->nstat_release(cookie
, 0);
2601 nstat_control_handle_add_all(
2602 nstat_control_state
*state
,
2607 // Verify the header fits in the first mbuf
2608 if (mbuf_len(m
) < sizeof(nstat_msg_add_all_srcs
))
2613 nstat_msg_add_all_srcs
*req
= mbuf_data(m
);
2614 nstat_provider
*provider
= nstat_find_provider_by_id(req
->provider
);
2616 if (!provider
) return ENOENT
;
2617 if (provider
->nstat_watcher_add
== NULL
) return ENOTSUP
;
2619 if (nstat_privcheck
!= 0) {
2620 result
= priv_check_cred(kauth_cred_get(),
2621 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS
, 0);
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)
2630 state
->ncs_watching
|= (1 << provider
->nstat_provider_id
);
2631 lck_mtx_unlock(&state
->mtx
);
2632 if (result
!= 0) return result
;
2634 result
= provider
->nstat_watcher_add(state
);
2637 lck_mtx_lock(&state
->mtx
);
2638 state
->ncs_watching
&= ~(1 << provider
->nstat_provider_id
);
2639 lck_mtx_unlock(&state
->mtx
);
2642 nstat_enqueue_success(req
->hdr
.context
, state
);
2648 nstat_control_source_add(
2650 nstat_control_state
*state
,
2651 nstat_provider
*provider
,
2652 nstat_provider_cookie_t cookie
)
2654 // Fill out source added message
2656 unsigned int one
= 1;
2658 if (mbuf_allocpacket(MBUF_DONTWAIT
, sizeof(nstat_msg_src_added
), &one
,
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
;
2670 // Allocate storage for the source
2671 nstat_src
*src
= OSMalloc(sizeof(*src
), nstat_malloc_tag
);
2678 // Fill in the source, including picking an unused source ref
2679 lck_mtx_lock(&state
->mtx
);
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
)
2684 lck_mtx_unlock(&state
->mtx
);
2685 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
2689 src
->provider
= provider
;
2690 src
->cookie
= cookie
;
2693 // send the source added message
2694 errno_t result
= ctl_enqueuembuf(state
->ncs_kctl
, state
->ncs_unit
, msg
,
2698 nstat_srcaddedfailures
+= 1;
2699 lck_mtx_unlock(&state
->mtx
);
2700 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
2705 // Put the source in the list
2706 src
->next
= state
->ncs_srcs
;
2707 state
->ncs_srcs
= src
;
2709 // send the description message
2710 // not useful as the source is often not complete
2711 // nstat_control_send_description(state, src, 0);
2713 lck_mtx_unlock(&state
->mtx
);
2719 nstat_control_handle_remove_request(
2720 nstat_control_state
*state
,
2723 nstat_src_ref_t srcref
= NSTAT_SRC_REF_INVALID
;
2725 if (mbuf_copydata(m
, offsetof(nstat_msg_rem_src_req
, srcref
), sizeof(srcref
), &srcref
) != 0)
2730 lck_mtx_lock(&state
->mtx
);
2732 // Remove this source as we look for it
2734 nstat_src
*src
= NULL
;
2735 for (nextp
= &state
->ncs_srcs
; *nextp
; nextp
= &(*nextp
)->next
)
2737 if ((*nextp
)->srcref
== srcref
)
2745 lck_mtx_unlock(&state
->mtx
);
2747 if (src
) nstat_control_cleanup_source(state
, src
, FALSE
);
2749 return src
? 0 : ENOENT
;
2753 nstat_control_handle_query_request(
2754 nstat_control_state
*state
,
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
2765 nstat_src
*dead_srcs
= NULL
;
2766 errno_t result
= ENOENT
;
2767 nstat_msg_query_src_req req
;
2769 if (mbuf_copydata(m
, 0, sizeof(req
), &req
) != 0)
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
)
2783 // XXX ignore IFACE types?
2784 if (req
.srcref
== NSTAT_SRC_REF_ALL
||
2785 (*srcpp
)->srcref
== req
.srcref
)
2789 result
= nstat_control_send_counts(state
, *srcpp
,
2790 req
.hdr
.context
, &gone
);
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.
2795 state
->ncs_flags
&= ~NSTAT_FLAG_REQCOUNTS
;
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",
2807 state
->ncs_flags
&= ~NSTAT_FLAG_REQCOUNTS
;
2811 // pull src out of the list
2812 nstat_src
*src
= *srcpp
;
2815 src
->next
= dead_srcs
;
2819 if (req
.srcref
!= NSTAT_SRC_REF_ALL
)
2824 srcpp
= &(*srcpp
)->next
;
2826 lck_mtx_unlock(&state
->mtx
);
2828 if (req
.srcref
== NSTAT_SRC_REF_ALL
)
2830 nstat_enqueue_success(req
.hdr
.context
, state
);
2839 dead_srcs
= src
->next
;
2841 // release src and send notification
2842 nstat_control_cleanup_source(state
, src
, FALSE
);
2849 nstat_control_handle_get_src_description(
2850 nstat_control_state
*state
,
2853 nstat_msg_get_src_description req
;
2857 if (mbuf_copydata(m
, 0, sizeof(req
), &req
) != 0)
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
)
2869 result
= nstat_control_send_description(state
, src
,
2872 state
->ncs_flags
&= ~NSTAT_FLAG_REQDESCS
;
2873 if (req
.srcref
!= NSTAT_SRC_REF_ALL
)
2876 lck_mtx_unlock(&state
->mtx
);
2877 if (req
.srcref
!= NSTAT_SRC_REF_ALL
&& src
== NULL
)
2879 else if (req
.srcref
== NSTAT_SRC_REF_ALL
)
2881 nstat_enqueue_success(req
.hdr
.context
, state
);
2889 nstat_control_handle_set_filter(
2890 nstat_control_state
*state
,
2893 nstat_msg_set_filter req
;
2896 if (mbuf_copydata(m
, 0, sizeof(req
), &req
) != 0)
2898 if (req
.srcref
== NSTAT_SRC_REF_ALL
||
2899 req
.srcref
== NSTAT_SRC_REF_INVALID
)
2902 lck_mtx_lock(&state
->mtx
);
2903 for (src
= state
->ncs_srcs
; src
; src
= src
->next
)
2904 if (req
.srcref
== src
->srcref
)
2906 src
->filter
= req
.filter
;
2909 lck_mtx_unlock(&state
->mtx
);
2925 nstat_control_state
*state
= (nstat_control_state
*)uinfo
;
2926 struct nstat_msg_hdr
*hdr
;
2927 struct nstat_msg_hdr storage
;
2930 if (mbuf_pkthdr_len(m
) < sizeof(hdr
))
2932 // Is this the right thing to do?
2937 if (mbuf_len(m
) >= sizeof(*hdr
))
2943 mbuf_copydata(m
, 0, sizeof(storage
), &storage
);
2949 case NSTAT_MSG_TYPE_ADD_SRC
:
2950 result
= nstat_control_handle_add_request(state
, m
);
2953 case NSTAT_MSG_TYPE_ADD_ALL_SRCS
:
2954 result
= nstat_control_handle_add_all(state
, m
);
2957 case NSTAT_MSG_TYPE_REM_SRC
:
2958 result
= nstat_control_handle_remove_request(state
, m
);
2961 case NSTAT_MSG_TYPE_QUERY_SRC
:
2962 result
= nstat_control_handle_query_request(state
, m
);
2965 case NSTAT_MSG_TYPE_GET_SRC_DESC
:
2966 result
= nstat_control_handle_get_src_description(state
, m
);
2969 case NSTAT_MSG_TYPE_SET_FILTER
:
2970 result
= nstat_control_handle_set_filter(state
, m
);
2980 struct nstat_msg_error err
;
2982 bzero(&err
, sizeof(err
));
2983 err
.hdr
.type
= NSTAT_MSG_TYPE_ERROR
;
2984 err
.hdr
.context
= hdr
->context
;
2987 result
= ctl_enqueuedata(kctl
, unit
, &err
, sizeof(err
),
2988 CTL_DATA_EOR
| CTL_DATA_CRIT
);
2990 nstat_descriptionfailures
+= 1;