2 * Copyright (c) 2010-2011 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>
38 #include <kern/clock.h>
39 #include <kern/debug.h>
41 #include <libkern/libkern.h>
42 #include <libkern/OSMalloc.h>
43 #include <libkern/OSAtomic.h>
44 #include <libkern/locks.h>
47 #include <net/route.h>
48 #include <net/ntstat.h>
50 #include <netinet/ip_var.h>
51 #include <netinet/in_pcb.h>
52 #include <netinet/in_var.h>
53 #include <netinet/tcp.h>
54 #include <netinet/tcp_var.h>
55 #include <netinet/tcp_fsm.h>
56 #include <netinet/udp.h>
57 #include <netinet/udp_var.h>
58 #include <netinet6/in6_pcb.h>
59 #include <netinet6/in6_var.h>
61 __private_extern__
int nstat_collect
= 1;
62 SYSCTL_INT(_net
, OID_AUTO
, statistics
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
63 &nstat_collect
, 0, "Collect detailed statistics");
65 typedef struct nstat_control_state
67 struct nstat_control_state
*next
;
69 decl_lck_mtx_data(, mtx
);
72 nstat_src_ref_t next_srcref
;
73 struct nstat_src
*srcs
;
76 } nstat_control_state
;
78 static void nstat_control_register(void);
80 static volatile OSMallocTag nstat_malloc_tag
= NULL
;
81 static nstat_control_state
*nstat_controls
= NULL
;
82 static uint64_t nstat_idle_time
= 0ULL;
83 static decl_lck_mtx_data(, nstat_mtx
);
87 const struct sockaddr
*src
,
91 if (src
->sa_len
> maxlen
) return;
93 bcopy(src
, dst
, src
->sa_len
);
94 if (src
->sa_family
== AF_INET6
&&
95 src
->sa_len
>= sizeof(struct sockaddr_in6
))
97 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)dst
;
98 if (IN6_IS_SCOPE_EMBED(&sin6
->sin6_addr
))
100 if (sin6
->sin6_scope_id
== 0)
101 sin6
->sin6_scope_id
= ntohs(sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1]);
102 sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
108 nstat_ip_to_sockaddr(
109 const struct in_addr
*ip
,
111 struct sockaddr_in
*sin
,
114 if (maxlen
< sizeof(struct sockaddr_in
))
117 sin
->sin_family
= AF_INET
;
118 sin
->sin_len
= sizeof(*sin
);
119 sin
->sin_port
= port
;
124 nstat_ip6_to_sockaddr(
125 const struct in6_addr
*ip6
,
127 struct sockaddr_in6
*sin6
,
130 if (maxlen
< sizeof(struct sockaddr_in6
))
133 sin6
->sin6_family
= AF_INET6
;
134 sin6
->sin6_len
= sizeof(*sin6
);
135 sin6
->sin6_port
= port
;
136 sin6
->sin6_addr
= *ip6
;
137 if (IN6_IS_SCOPE_EMBED(&sin6
->sin6_addr
))
139 sin6
->sin6_scope_id
= ntohs(sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1]);
140 sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
144 #pragma mark -- Network Statistic Providers --
146 typedef struct nstat_provider
148 struct nstat_provider
*next
;
149 nstat_provider_id_t nstat_provider_id
;
150 size_t nstat_descriptor_length
;
151 errno_t (*nstat_lookup
)(const void *data
, u_int32_t length
, nstat_provider_cookie_t
*out_cookie
);
152 int (*nstat_gone
)(nstat_provider_cookie_t cookie
);
153 errno_t (*nstat_counts
)(nstat_provider_cookie_t cookie
, struct nstat_counts
*out_counts
, int *out_gone
);
154 errno_t (*nstat_watcher_add
)(nstat_control_state
*state
);
155 void (*nstat_watcher_remove
)(nstat_control_state
*state
);
156 errno_t (*nstat_copy_descriptor
)(nstat_provider_cookie_t cookie
, void *data
, u_int32_t len
);
157 void (*nstat_release
)(nstat_provider_cookie_t cookie
);
160 static errno_t
nstat_control_source_add(u_int64_t context
, nstat_control_state
*state
, nstat_provider
*provider
, nstat_provider_cookie_t cookie
);
161 struct nstat_provider
*nstat_providers
= NULL
;
163 static struct nstat_provider
*
164 nstat_find_provider_by_id(
165 nstat_provider_id_t id
)
167 struct nstat_provider
*provider
;
169 for (provider
= nstat_providers
; provider
!= NULL
; provider
= provider
->next
)
171 if (provider
->nstat_provider_id
== id
)
180 nstat_provider_id_t id
,
183 nstat_provider
**out_provider
,
184 nstat_provider_cookie_t
*out_cookie
)
186 *out_provider
= nstat_find_provider_by_id(id
);
187 if (*out_provider
== NULL
)
189 printf("%s:%d: provider %u not found\n", __FUNCTION__
, __LINE__
, id
);
193 return (*out_provider
)->nstat_lookup(data
, length
, out_cookie
);
196 static void nstat_init_route_provider(void);
197 static void nstat_init_tcp_provider(void);
198 static void nstat_init_udp_provider(void);
203 if (nstat_malloc_tag
!= NULL
) return;
205 OSMallocTag tag
= OSMalloc_Tagalloc(NET_STAT_CONTROL_NAME
, OSMT_DEFAULT
);
206 if (!OSCompareAndSwapPtr(NULL
, tag
, &nstat_malloc_tag
))
208 OSMalloc_Tagfree(tag
);
209 tag
= nstat_malloc_tag
;
213 // we need to initialize other things, we do it here as this code path will only be hit once;
214 nstat_init_route_provider();
215 nstat_init_tcp_provider();
216 nstat_init_udp_provider();
217 nstat_control_register();
221 #pragma mark -- Aligned Buffer Allocation --
230 nstat_malloc_aligned(
235 struct align_header
*hdr
= NULL
;
236 u_int32_t size
= length
+ sizeof(*hdr
) + alignment
- 1;
238 u_int8_t
*buffer
= OSMalloc(size
, tag
);
239 if (buffer
== NULL
) return NULL
;
241 u_int8_t
*aligned
= buffer
+ sizeof(*hdr
);
242 aligned
= (u_int8_t
*)P2ROUNDUP(aligned
, alignment
);
244 hdr
= (struct align_header
*)(aligned
- sizeof(*hdr
));
245 hdr
->offset
= aligned
- buffer
;
256 struct align_header
*hdr
= (struct align_header
*)((u_int8_t
*)buffer
- sizeof(*hdr
));
257 OSFree(((char*)buffer
) - hdr
->offset
, hdr
->length
, tag
);
260 #pragma mark -- Route Provider --
262 static nstat_provider nstat_route_provider
;
268 nstat_provider_cookie_t
*out_cookie
)
270 // rt_lookup doesn't take const params but it doesn't modify the parameters for
271 // the lookup. So...we use a union to eliminate the warning.
275 const struct sockaddr
*const_sa
;
278 const nstat_route_add_param
*param
= (const nstat_route_add_param
*)data
;
281 if (length
< sizeof(*param
))
283 printf("%s:%d: expected %lu byte param, received %u\n", __FUNCTION__
, __LINE__
, sizeof(*param
), length
);
287 if (param
->dst
.v4
.sin_family
== 0 ||
288 param
->dst
.v4
.sin_family
> AF_MAX
||
289 (param
->mask
.v4
.sin_family
!= 0 && param
->mask
.v4
.sin_family
!= param
->dst
.v4
.sin_family
))
291 printf("%s:%d invalid family (dst=%d, mask=%d)\n", __FUNCTION__
, __LINE__
,
292 param
->dst
.v4
.sin_family
, param
->mask
.v4
.sin_family
);
296 if (param
->dst
.v4
.sin_len
> sizeof(param
->dst
) ||
297 (param
->mask
.v4
.sin_family
&& param
->mask
.v4
.sin_len
> sizeof(param
->mask
.v4
.sin_len
)))
299 printf("%s:%d invalid length (dst=%d, mask=%d)\n", __FUNCTION__
, __LINE__
,
300 param
->dst
.v4
.sin_len
, param
->mask
.v4
.sin_len
);
303 // TBD: Need to validate length of sockaddr for different families?
304 dst
.const_sa
= (const struct sockaddr
*)¶m
->dst
;
305 mask
.const_sa
= param
->mask
.v4
.sin_family
? (const struct sockaddr
*)¶m
->mask
: NULL
;
307 struct radix_node_head
*rnh
= rt_tables
[dst
.sa
->sa_family
];
308 if (rnh
== NULL
) return EAFNOSUPPORT
;
310 lck_mtx_lock(rnh_lock
);
311 struct rtentry
*rt
= rt_lookup(TRUE
, dst
.sa
, mask
.sa
, rnh
, param
->ifindex
);
312 lck_mtx_unlock(rnh_lock
);
314 if (rt
) *out_cookie
= (nstat_provider_cookie_t
)rt
;
316 return rt
? 0 : ENOENT
;
321 nstat_provider_cookie_t cookie
)
323 struct rtentry
*rt
= (struct rtentry
*)cookie
;
324 return ((rt
->rt_flags
& RTF_UP
) == 0) ? 1 : 0;
329 nstat_provider_cookie_t cookie
,
330 struct nstat_counts
*out_counts
,
333 struct rtentry
*rt
= (struct rtentry
*)cookie
;
334 struct nstat_counts
*rt_stats
= rt
->rt_stats
;
338 if ((rt
->rt_flags
& RTF_UP
) == 0) *out_gone
= 1;
342 atomic_get_64(out_counts
->nstat_rxpackets
, &rt_stats
->nstat_rxpackets
);
343 atomic_get_64(out_counts
->nstat_rxbytes
, &rt_stats
->nstat_rxbytes
);
344 atomic_get_64(out_counts
->nstat_txpackets
, &rt_stats
->nstat_txpackets
);
345 atomic_get_64(out_counts
->nstat_txbytes
, &rt_stats
->nstat_txbytes
);
346 out_counts
->nstat_rxduplicatebytes
= rt_stats
->nstat_rxduplicatebytes
;
347 out_counts
->nstat_rxoutoforderbytes
= rt_stats
->nstat_rxoutoforderbytes
;
348 out_counts
->nstat_txretransmit
= rt_stats
->nstat_txretransmit
;
349 out_counts
->nstat_connectattempts
= rt_stats
->nstat_connectattempts
;
350 out_counts
->nstat_connectsuccesses
= rt_stats
->nstat_connectsuccesses
;
351 out_counts
->nstat_min_rtt
= rt_stats
->nstat_min_rtt
;
352 out_counts
->nstat_avg_rtt
= rt_stats
->nstat_avg_rtt
;
353 out_counts
->nstat_var_rtt
= rt_stats
->nstat_var_rtt
;
356 bzero(out_counts
, sizeof(*out_counts
));
363 nstat_provider_cookie_t cookie
)
365 rtfree((struct rtentry
*)cookie
);
368 static u_int32_t nstat_route_watchers
= 0;
371 nstat_route_walktree_add(
372 struct radix_node
*rn
,
376 struct rtentry
*rt
= (struct rtentry
*)rn
;
377 nstat_control_state
*state
= (nstat_control_state
*)context
;
379 lck_mtx_assert(rnh_lock
, LCK_MTX_ASSERT_OWNED
);
381 /* RTF_UP can't change while rnh_lock is held */
382 if ((rt
->rt_flags
& RTF_UP
) != 0)
384 /* Clear RTPRF_OURS if the route is still usable */
386 if (rt_validate(rt
)) {
387 RT_ADDREF_LOCKED(rt
);
394 /* Otherwise if RTF_CONDEMNED, treat it as if it were down */
398 result
= nstat_control_source_add(0, state
, &nstat_route_provider
, rt
);
407 nstat_route_add_watcher(
408 nstat_control_state
*state
)
412 OSIncrementAtomic(&nstat_route_watchers
);
414 lck_mtx_lock(rnh_lock
);
415 for (i
= 1; i
< AF_MAX
; i
++)
417 struct radix_node_head
*rnh
;
421 result
= rnh
->rnh_walktree(rnh
, nstat_route_walktree_add
, state
);
424 printf("%s:%d rnh_walktree failed: %d\n", __FUNCTION__
, __LINE__
, result
);
428 lck_mtx_unlock(rnh_lock
);
433 __private_extern__
void
434 nstat_route_new_entry(
437 if (nstat_route_watchers
== 0)
440 lck_mtx_lock(&nstat_mtx
);
441 if ((rt
->rt_flags
& RTF_UP
) != 0)
443 nstat_control_state
*state
;
444 for (state
= nstat_controls
; state
; state
= state
->next
)
446 if ((state
->watching
& (1 << NSTAT_PROVIDER_ROUTE
)) != 0)
448 // this client is watching routes
449 // acquire a reference for the route
452 // add the source, if that fails, release the reference
453 if (nstat_control_source_add(0, state
, &nstat_route_provider
, rt
) != 0)
458 lck_mtx_unlock(&nstat_mtx
);
462 nstat_route_remove_watcher(
463 __unused nstat_control_state
*state
)
465 OSDecrementAtomic(&nstat_route_watchers
);
469 nstat_route_copy_descriptor(
470 nstat_provider_cookie_t cookie
,
474 nstat_route_descriptor
*desc
= (nstat_route_descriptor
*)data
;
475 if (len
< sizeof(*desc
))
477 printf("%s:%d invalid length, wanted %lu, got %d\n", __FUNCTION__
, __LINE__
, sizeof(*desc
), len
);
480 bzero(desc
, sizeof(*desc
));
482 struct rtentry
*rt
= (struct rtentry
*)cookie
;
483 desc
->id
= (uintptr_t)rt
;
484 desc
->parent_id
= (uintptr_t)rt
->rt_parent
;
485 desc
->gateway_id
= (uintptr_t)rt
->rt_gwroute
;
490 if ((sa
= rt_key(rt
)))
491 nstat_copy_sa_out(sa
, &desc
->dst
.sa
, sizeof(desc
->dst
));
494 if ((sa
= rt_mask(rt
)) && sa
->sa_len
<= sizeof(desc
->mask
))
495 memcpy(&desc
->mask
, sa
, sa
->sa_len
);
498 if ((sa
= rt
->rt_gateway
))
499 nstat_copy_sa_out(sa
, &desc
->gateway
.sa
, sizeof(desc
->gateway
));
502 desc
->ifindex
= rt
->rt_ifp
->if_index
;
504 desc
->flags
= rt
->rt_flags
;
510 nstat_init_route_provider(void)
512 bzero(&nstat_route_provider
, sizeof(nstat_route_provider
));
513 nstat_route_provider
.nstat_descriptor_length
= sizeof(nstat_route_descriptor
);
514 nstat_route_provider
.nstat_provider_id
= NSTAT_PROVIDER_ROUTE
;
515 nstat_route_provider
.nstat_lookup
= nstat_route_lookup
;
516 nstat_route_provider
.nstat_gone
= nstat_route_gone
;
517 nstat_route_provider
.nstat_counts
= nstat_route_counts
;
518 nstat_route_provider
.nstat_release
= nstat_route_release
;
519 nstat_route_provider
.nstat_watcher_add
= nstat_route_add_watcher
;
520 nstat_route_provider
.nstat_watcher_remove
= nstat_route_remove_watcher
;
521 nstat_route_provider
.nstat_copy_descriptor
= nstat_route_copy_descriptor
;
522 nstat_route_provider
.next
= nstat_providers
;
523 nstat_providers
= &nstat_route_provider
;
526 #pragma mark -- Route Collection --
528 static struct nstat_counts
*
532 struct nstat_counts
*result
= rte
->rt_stats
;
533 if (result
) return result
;
535 if (nstat_malloc_tag
== NULL
) nstat_init();
537 result
= nstat_malloc_aligned(sizeof(*result
), sizeof(u_int64_t
), nstat_malloc_tag
);
538 if (!result
) return result
;
540 bzero(result
, sizeof(*result
));
542 if (!OSCompareAndSwapPtr(NULL
, result
, &rte
->rt_stats
))
544 nstat_free_aligned(result
, nstat_malloc_tag
);
545 result
= rte
->rt_stats
;
551 __private_extern__
void
557 nstat_free_aligned(rte
->rt_stats
, nstat_malloc_tag
);
558 rte
->rt_stats
= NULL
;
562 __private_extern__
void
563 nstat_route_connect_attempt(
568 struct nstat_counts
* stats
= nstat_route_attach(rte
);
571 OSIncrementAtomic(&stats
->nstat_connectattempts
);
574 rte
= rte
->rt_parent
;
578 __private_extern__
void
579 nstat_route_connect_success(
585 struct nstat_counts
* stats
= nstat_route_attach(rte
);
588 OSIncrementAtomic(&stats
->nstat_connectsuccesses
);
591 rte
= rte
->rt_parent
;
595 __private_extern__
void
604 struct nstat_counts
* stats
= nstat_route_attach(rte
);
607 if ((flags
& NSTAT_TX_FLAG_RETRANSMIT
) != 0)
609 OSAddAtomic(bytes
, &stats
->nstat_txretransmit
);
613 OSAddAtomic64((SInt64
)packets
, (SInt64
*)&stats
->nstat_txpackets
);
614 OSAddAtomic64((SInt64
)bytes
, (SInt64
*)&stats
->nstat_txbytes
);
618 rte
= rte
->rt_parent
;
622 __private_extern__
void
631 struct nstat_counts
* stats
= nstat_route_attach(rte
);
636 OSAddAtomic64((SInt64
)packets
, (SInt64
*)&stats
->nstat_rxpackets
);
637 OSAddAtomic64((SInt64
)bytes
, (SInt64
*)&stats
->nstat_rxbytes
);
641 if (flags
& NSTAT_RX_FLAG_OUT_OF_ORDER
)
642 OSAddAtomic(bytes
, &stats
->nstat_rxoutoforderbytes
);
643 if (flags
& NSTAT_RX_FLAG_DUPLICATE
)
644 OSAddAtomic(bytes
, &stats
->nstat_rxduplicatebytes
);
648 rte
= rte
->rt_parent
;
652 __private_extern__
void
658 const int32_t factor
= 8;
662 struct nstat_counts
* stats
= nstat_route_attach(rte
);
671 oldrtt
= stats
->nstat_avg_rtt
;
678 newrtt
= oldrtt
- (oldrtt
- (int32_t)rtt
) / factor
;
680 if (oldrtt
== newrtt
) break;
681 } while (!OSCompareAndSwap(oldrtt
, newrtt
, &stats
->nstat_avg_rtt
));
686 oldrtt
= stats
->nstat_min_rtt
;
687 if (oldrtt
!= 0 && oldrtt
< (int32_t)rtt
)
691 } while (!OSCompareAndSwap(oldrtt
, rtt
, &stats
->nstat_min_rtt
));
696 oldrtt
= stats
->nstat_var_rtt
;
703 newrtt
= oldrtt
- (oldrtt
- (int32_t)rtt_var
) / factor
;
705 if (oldrtt
== newrtt
) break;
706 } while (!OSCompareAndSwap(oldrtt
, newrtt
, &stats
->nstat_var_rtt
));
709 rte
= rte
->rt_parent
;
713 #pragma mark -- TCP Provider --
715 static nstat_provider nstat_tcp_provider
;
719 struct inpcbinfo
*inpinfo
,
722 nstat_provider_cookie_t
*out_cookie
)
724 // parameter validation
725 const nstat_tcp_add_param
*param
= (const nstat_tcp_add_param
*)data
;
726 if (length
< sizeof(*param
))
728 printf("%s:%d expected %lu byte param, received %u\n", __FUNCTION__
, __LINE__
, sizeof(*param
), length
);
732 // src and dst must match
733 if (param
->remote
.v4
.sin_family
!= 0 &&
734 param
->remote
.v4
.sin_family
!= param
->local
.v4
.sin_family
)
736 printf("%s:%d src family (%d) and dst family (%d) don't match\n",
737 __FUNCTION__
, __LINE__
, param
->local
.v4
.sin_family
, param
->remote
.v4
.sin_family
);
741 struct inpcb
*inp
= NULL
;
743 switch (param
->local
.v4
.sin_family
)
747 if (param
->local
.v4
.sin_len
!= sizeof(param
->local
.v4
) ||
748 (param
->remote
.v4
.sin_family
!= 0 &&
749 param
->remote
.v4
.sin_len
!= sizeof(param
->remote
.v4
)))
751 printf("%s:%d invalid length for v4 src (%d) or dst (%d), should be %lu\n",
752 __FUNCTION__
, __LINE__
, param
->local
.v4
.sin_len
, param
->remote
.v4
.sin_len
,
753 sizeof(param
->remote
.v4
));
757 inp
= in_pcblookup_hash(inpinfo
, param
->remote
.v4
.sin_addr
, param
->remote
.v4
.sin_port
,
758 param
->local
.v4
.sin_addr
, param
->local
.v4
.sin_port
, 1, NULL
);
767 const struct in6_addr
*in6c
;
768 struct in6_addr
*in6
;
771 if (param
->local
.v6
.sin6_len
!= sizeof(param
->local
.v6
) ||
772 (param
->remote
.v6
.sin6_family
!= 0 &&
773 param
->remote
.v6
.sin6_len
!= sizeof(param
->remote
.v6
)))
775 printf("%s:%d invalid length for v6 src (%d) or dst (%d), should be %lu\n",
776 __FUNCTION__
, __LINE__
, param
->local
.v6
.sin6_len
, param
->remote
.v6
.sin6_len
,
777 sizeof(param
->remote
.v6
));
781 local
.in6c
= ¶m
->local
.v6
.sin6_addr
;
782 remote
.in6c
= ¶m
->remote
.v6
.sin6_addr
;
784 inp
= in6_pcblookup_hash(inpinfo
, remote
.in6
, param
->remote
.v6
.sin6_port
,
785 local
.in6
, param
->local
.v6
.sin6_port
, 1, NULL
);
791 printf("%s:%d unsupported address family %d\n", __FUNCTION__
, __LINE__
, param
->local
.v4
.sin_family
);
795 if (inp
== NULL
) return ENOENT
;
797 // At this point we have a ref to the inpcb
806 nstat_provider_cookie_t
*out_cookie
)
808 return nstat_tcpudp_lookup(&tcbinfo
, data
, length
, out_cookie
);
813 nstat_provider_cookie_t cookie
)
815 struct inpcb
*inp
= (struct inpcb
*)cookie
;
816 struct tcpcb
*tp
= intotcpcb(inp
);
817 return (inp
->inp_state
== INPCB_STATE_DEAD
|| tp
->t_state
== TCPS_TIME_WAIT
) ? 1 : 0;
822 nstat_provider_cookie_t cookie
,
823 struct nstat_counts
*out_counts
,
826 struct inpcb
*inp
= (struct inpcb
*)cookie
;
827 struct tcpcb
*tp
= intotcpcb(inp
);
829 bzero(out_counts
, sizeof(*out_counts
));
833 // if the pcb is in the dead state, we should stop using it
834 if (inp
->inp_state
== INPCB_STATE_DEAD
|| tp
->t_state
== TCPS_TIME_WAIT
)
839 if (tp
->t_state
> TCPS_LISTEN
)
841 atomic_get_64(out_counts
->nstat_rxpackets
, &inp
->inp_stat
->rxpackets
);
842 atomic_get_64(out_counts
->nstat_rxbytes
, &inp
->inp_stat
->rxbytes
);
843 atomic_get_64(out_counts
->nstat_txpackets
, &inp
->inp_stat
->txpackets
);
844 atomic_get_64(out_counts
->nstat_txbytes
, &inp
->inp_stat
->txbytes
);
845 out_counts
->nstat_rxduplicatebytes
= tp
->t_stat
.rxduplicatebytes
;
846 out_counts
->nstat_rxoutoforderbytes
= tp
->t_stat
.rxoutoforderbytes
;
847 out_counts
->nstat_txretransmit
= tp
->t_stat
.txretransmitbytes
;
848 out_counts
->nstat_connectattempts
= tp
->t_state
>= TCPS_SYN_SENT
? 1 : 0;
849 out_counts
->nstat_connectsuccesses
= tp
->t_state
>= TCPS_ESTABLISHED
? 1 : 0;
850 out_counts
->nstat_avg_rtt
= tp
->t_srtt
;
851 out_counts
->nstat_min_rtt
= tp
->t_rttbest
;
852 out_counts
->nstat_var_rtt
= tp
->t_rttvar
;
860 nstat_provider_cookie_t cookie
)
862 struct inpcb
*inp
= (struct inpcb
*)cookie
;
863 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
866 static u_int32_t nstat_tcp_watchers
= 0;
869 nstat_tcp_add_watcher(
870 nstat_control_state
*state
)
872 OSIncrementAtomic(&nstat_tcp_watchers
);
874 lck_rw_lock_shared(tcbinfo
.mtx
);
876 // Add all current tcp inpcbs. Ignore those in timewait
878 for (inp
= LIST_FIRST(tcbinfo
.listhead
); inp
; inp
= LIST_NEXT(inp
, inp_list
))
880 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
883 if (nstat_control_source_add(0, state
, &nstat_tcp_provider
, inp
) != 0)
885 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
890 lck_rw_done(tcbinfo
.mtx
);
896 nstat_tcp_remove_watcher(
897 __unused nstat_control_state
*state
)
899 OSDecrementAtomic(&nstat_tcp_watchers
);
902 __private_extern__
void
906 if (nstat_tcp_watchers
== 0)
909 lck_mtx_lock(&nstat_mtx
);
910 nstat_control_state
*state
;
911 for (state
= nstat_controls
; state
; state
= state
->next
)
913 if ((state
->watching
& (1 << NSTAT_PROVIDER_TCP
)) != 0)
915 // this client is watching tcp
916 // acquire a reference for it
917 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
920 // add the source, if that fails, release the reference
921 if (nstat_control_source_add(0, state
, &nstat_tcp_provider
, inp
) != 0)
923 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
928 lck_mtx_unlock(&nstat_mtx
);
932 nstat_tcp_copy_descriptor(
933 nstat_provider_cookie_t cookie
,
937 if (len
< sizeof(nstat_tcp_descriptor
))
939 printf("%s:%d invalid length, wanted %lu, got %d\n", __FUNCTION__
, __LINE__
, sizeof(nstat_tcp_descriptor
), len
);
943 nstat_tcp_descriptor
*desc
= (nstat_tcp_descriptor
*)data
;
944 struct inpcb
*inp
= (struct inpcb
*)cookie
;
945 struct tcpcb
*tp
= intotcpcb(inp
);
947 bzero(desc
, sizeof(*desc
));
949 if (inp
->inp_vflag
& INP_IPV6
)
951 nstat_ip6_to_sockaddr(&inp
->in6p_laddr
, inp
->inp_lport
,
952 &desc
->local
.v6
, sizeof(desc
->local
));
953 nstat_ip6_to_sockaddr(&inp
->in6p_faddr
, inp
->inp_fport
,
954 &desc
->remote
.v6
, sizeof(desc
->remote
));
956 else if (inp
->inp_vflag
& INP_IPV4
)
958 nstat_ip_to_sockaddr(&inp
->inp_laddr
, inp
->inp_lport
,
959 &desc
->local
.v4
, sizeof(desc
->local
));
960 nstat_ip_to_sockaddr(&inp
->inp_faddr
, inp
->inp_fport
,
961 &desc
->remote
.v4
, sizeof(desc
->remote
));
964 desc
->state
= intotcpcb(inp
)->t_state
;
965 if (inp
->inp_route
.ro_rt
&& inp
->inp_route
.ro_rt
->rt_ifp
)
966 desc
->ifindex
= inp
->inp_route
.ro_rt
->rt_ifp
->if_index
;
968 // danger - not locked, values could be bogus
969 desc
->txunacked
= tp
->snd_max
- tp
->snd_una
;
970 desc
->txwindow
= tp
->snd_wnd
;
971 desc
->txcwindow
= tp
->snd_cwnd
;
973 struct socket
*so
= inp
->inp_socket
;
976 // TBD - take the socket lock around these to make sure
978 desc
->upid
= so
->last_upid
;
979 desc
->pid
= so
->last_pid
;
981 proc_name(desc
->pid
, desc
->pname
, sizeof(desc
->pname
));
982 desc
->pname
[sizeof(desc
->pname
) - 1] = 0;
984 desc
->sndbufsize
= so
->so_snd
.sb_hiwat
;
985 desc
->sndbufused
= so
->so_snd
.sb_cc
;
986 desc
->rcvbufsize
= so
->so_rcv
.sb_hiwat
;
987 desc
->rcvbufused
= so
->so_rcv
.sb_cc
;
994 nstat_init_tcp_provider(void)
996 bzero(&nstat_tcp_provider
, sizeof(nstat_tcp_provider
));
997 nstat_tcp_provider
.nstat_descriptor_length
= sizeof(nstat_tcp_descriptor
);
998 nstat_tcp_provider
.nstat_provider_id
= NSTAT_PROVIDER_TCP
;
999 nstat_tcp_provider
.nstat_lookup
= nstat_tcp_lookup
;
1000 nstat_tcp_provider
.nstat_gone
= nstat_tcp_gone
;
1001 nstat_tcp_provider
.nstat_counts
= nstat_tcp_counts
;
1002 nstat_tcp_provider
.nstat_release
= nstat_tcp_release
;
1003 nstat_tcp_provider
.nstat_watcher_add
= nstat_tcp_add_watcher
;
1004 nstat_tcp_provider
.nstat_watcher_remove
= nstat_tcp_remove_watcher
;
1005 nstat_tcp_provider
.nstat_copy_descriptor
= nstat_tcp_copy_descriptor
;
1006 nstat_tcp_provider
.next
= nstat_providers
;
1007 nstat_providers
= &nstat_tcp_provider
;
1010 #pragma mark -- UDP Provider --
1012 static nstat_provider nstat_udp_provider
;
1018 nstat_provider_cookie_t
*out_cookie
)
1020 return nstat_tcpudp_lookup(&udbinfo
, data
, length
, out_cookie
);
1025 nstat_provider_cookie_t cookie
)
1027 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1028 return (inp
->inp_state
== INPCB_STATE_DEAD
) ? 1 : 0;
1033 nstat_provider_cookie_t cookie
,
1034 struct nstat_counts
*out_counts
,
1037 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1041 // if the pcb is in the dead state, we should stop using it
1042 if (inp
->inp_state
== INPCB_STATE_DEAD
)
1047 atomic_get_64(out_counts
->nstat_rxpackets
, &inp
->inp_stat
->rxpackets
);
1048 atomic_get_64(out_counts
->nstat_rxbytes
, &inp
->inp_stat
->rxbytes
);
1049 atomic_get_64(out_counts
->nstat_txpackets
, &inp
->inp_stat
->txpackets
);
1050 atomic_get_64(out_counts
->nstat_txbytes
, &inp
->inp_stat
->txbytes
);
1057 nstat_provider_cookie_t cookie
)
1059 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1060 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
1063 static u_int32_t nstat_udp_watchers
= 0;
1066 nstat_udp_add_watcher(
1067 nstat_control_state
*state
)
1069 OSIncrementAtomic(&nstat_udp_watchers
);
1071 lck_rw_lock_shared(tcbinfo
.mtx
);
1073 // Add all current tcp inpcbs. Ignore those in timewait
1075 for (inp
= LIST_FIRST(udbinfo
.listhead
); inp
; inp
= LIST_NEXT(inp
, inp_list
))
1077 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
1080 if (nstat_control_source_add(0, state
, &nstat_udp_provider
, inp
) != 0)
1082 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
1087 lck_rw_done(tcbinfo
.mtx
);
1093 nstat_udp_remove_watcher(
1094 __unused nstat_control_state
*state
)
1096 OSDecrementAtomic(&nstat_udp_watchers
);
1099 __private_extern__
void
1103 if (nstat_udp_watchers
== 0)
1106 lck_mtx_lock(&nstat_mtx
);
1107 nstat_control_state
*state
;
1108 for (state
= nstat_controls
; state
; state
= state
->next
)
1110 if ((state
->watching
& (1 << NSTAT_PROVIDER_UDP
)) != 0)
1112 // this client is watching tcp
1113 // acquire a reference for it
1114 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
1117 // add the source, if that fails, release the reference
1118 if (nstat_control_source_add(0, state
, &nstat_udp_provider
, inp
) != 0)
1120 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
1125 lck_mtx_unlock(&nstat_mtx
);
1129 nstat_udp_copy_descriptor(
1130 nstat_provider_cookie_t cookie
,
1134 if (len
< sizeof(nstat_udp_descriptor
))
1136 printf("%s:%d invalid length, wanted %lu, got %d\n", __FUNCTION__
, __LINE__
, sizeof(nstat_tcp_descriptor
), len
);
1140 nstat_udp_descriptor
*desc
= (nstat_udp_descriptor
*)data
;
1141 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1143 bzero(desc
, sizeof(*desc
));
1145 if (inp
->inp_vflag
& INP_IPV6
)
1147 nstat_ip6_to_sockaddr(&inp
->in6p_laddr
, inp
->inp_lport
,
1148 &desc
->local
.v6
, sizeof(desc
->local
));
1149 nstat_ip6_to_sockaddr(&inp
->in6p_faddr
, inp
->inp_fport
,
1150 &desc
->remote
.v6
, sizeof(desc
->remote
));
1152 else if (inp
->inp_vflag
& INP_IPV4
)
1154 nstat_ip_to_sockaddr(&inp
->inp_laddr
, inp
->inp_lport
,
1155 &desc
->local
.v4
, sizeof(desc
->local
));
1156 nstat_ip_to_sockaddr(&inp
->inp_faddr
, inp
->inp_fport
,
1157 &desc
->remote
.v4
, sizeof(desc
->remote
));
1160 if (inp
->inp_route
.ro_rt
&& inp
->inp_route
.ro_rt
->rt_ifp
)
1161 desc
->ifindex
= inp
->inp_route
.ro_rt
->rt_ifp
->if_index
;
1163 struct socket
*so
= inp
->inp_socket
;
1166 // TBD - take the socket lock around these to make sure
1168 desc
->upid
= so
->last_upid
;
1169 desc
->pid
= so
->last_pid
;
1171 desc
->rcvbufsize
= so
->so_rcv
.sb_hiwat
;
1172 desc
->rcvbufused
= so
->so_rcv
.sb_cc
;
1174 proc_name(desc
->pid
, desc
->pname
, sizeof(desc
->pname
));
1175 desc
->pname
[sizeof(desc
->pname
) - 1] = 0;
1182 nstat_init_udp_provider(void)
1184 bzero(&nstat_udp_provider
, sizeof(nstat_udp_provider
));
1185 nstat_udp_provider
.nstat_provider_id
= NSTAT_PROVIDER_UDP
;
1186 nstat_udp_provider
.nstat_descriptor_length
= sizeof(nstat_udp_descriptor
);
1187 nstat_udp_provider
.nstat_lookup
= nstat_udp_lookup
;
1188 nstat_udp_provider
.nstat_gone
= nstat_udp_gone
;
1189 nstat_udp_provider
.nstat_counts
= nstat_udp_counts
;
1190 nstat_udp_provider
.nstat_watcher_add
= nstat_udp_add_watcher
;
1191 nstat_udp_provider
.nstat_watcher_remove
= nstat_udp_remove_watcher
;
1192 nstat_udp_provider
.nstat_copy_descriptor
= nstat_udp_copy_descriptor
;
1193 nstat_udp_provider
.nstat_release
= nstat_udp_release
;
1194 nstat_udp_provider
.next
= nstat_providers
;
1195 nstat_providers
= &nstat_udp_provider
;
1198 #pragma mark -- Kernel Control Socket --
1200 typedef struct nstat_src
1202 struct nstat_src
*next
;
1203 nstat_src_ref_t srcref
;
1204 nstat_provider
*provider
;
1205 nstat_provider_cookie_t cookie
;
1208 static kern_ctl_ref nstat_ctlref
= NULL
;
1209 static lck_grp_t
*nstat_lck_grp
= NULL
;
1211 static errno_t
nstat_control_connect(kern_ctl_ref kctl
, struct sockaddr_ctl
*sac
, void **uinfo
);
1212 static errno_t
nstat_control_disconnect(kern_ctl_ref kctl
, u_int32_t unit
, void *uinfo
);
1213 static errno_t
nstat_control_send(kern_ctl_ref kctl
, u_int32_t unit
, void *uinfo
, mbuf_t m
, int flags
);
1214 static int nstat_control_send_description(nstat_control_state
*state
, nstat_src
*src
, u_int64_t context
);
1215 static void nstat_control_cleanup_source(nstat_control_state
*state
, struct nstat_src
*src
);
1220 __unused thread_call_param_t p0
,
1221 __unused thread_call_param_t p1
)
1223 lck_mtx_lock(&nstat_mtx
);
1225 nstat_idle_time
= 0ULL;
1227 nstat_control_state
*control
;
1228 nstat_src
*dead
= NULL
;
1229 nstat_src
*dead_list
= NULL
;
1230 for (control
= nstat_controls
; control
; control
= control
->next
)
1232 lck_mtx_lock(&control
->mtx
);
1233 nstat_src
**srcpp
= &control
->srcs
;
1235 while(*srcpp
!= NULL
)
1237 if ((*srcpp
)->provider
->nstat_gone((*srcpp
)->cookie
))
1239 // Pull it off the list
1241 *srcpp
= (*srcpp
)->next
;
1243 // send a last description
1244 nstat_control_send_description(control
, dead
, 0ULL);
1246 // send the source removed notification
1247 nstat_msg_src_removed removed
;
1248 removed
.hdr
.type
= NSTAT_MSG_TYPE_SRC_REMOVED
;
1249 removed
.hdr
.context
= 0;
1250 removed
.srcref
= dead
->srcref
;
1251 (void)ctl_enqueuedata(control
->kctl
, control
->unit
, &removed
, sizeof(removed
), CTL_DATA_EOR
);
1253 // Put this on the list to release later
1254 dead
->next
= dead_list
;
1259 srcpp
= &(*srcpp
)->next
;
1262 lck_mtx_unlock(&control
->mtx
);
1267 clock_interval_to_deadline(60, NSEC_PER_SEC
, &nstat_idle_time
);
1268 thread_call_func_delayed((thread_call_func_t
)nstat_idle_check
, NULL
, nstat_idle_time
);
1271 lck_mtx_unlock(&nstat_mtx
);
1273 // Release the sources now that we aren't holding lots of locks
1277 dead_list
= dead
->next
;
1279 nstat_control_cleanup_source(NULL
, dead
);
1286 nstat_control_register(void)
1288 // Create our lock group first
1289 lck_grp_attr_t
*grp_attr
= lck_grp_attr_alloc_init();
1290 lck_grp_attr_setdefault(grp_attr
);
1291 nstat_lck_grp
= lck_grp_alloc_init("network statistics kctl", grp_attr
);
1292 lck_grp_attr_free(grp_attr
);
1294 lck_mtx_init(&nstat_mtx
, nstat_lck_grp
, NULL
);
1296 // Register the control
1297 struct kern_ctl_reg nstat_control
;
1298 bzero(&nstat_control
, sizeof(nstat_control
));
1299 strlcpy(nstat_control
.ctl_name
, NET_STAT_CONTROL_NAME
, sizeof(nstat_control
.ctl_name
));
1300 nstat_control
.ctl_connect
= nstat_control_connect
;
1301 nstat_control
.ctl_disconnect
= nstat_control_disconnect
;
1302 nstat_control
.ctl_send
= nstat_control_send
;
1304 errno_t result
= ctl_register(&nstat_control
, &nstat_ctlref
);
1306 printf("%s:%d ctl_register failed: %d", __FUNCTION__
, __LINE__
, result
);
1310 nstat_control_cleanup_source(
1311 nstat_control_state
*state
,
1312 struct nstat_src
*src
)
1316 nstat_msg_src_removed removed
;
1317 removed
.hdr
.type
= NSTAT_MSG_TYPE_SRC_REMOVED
;
1318 removed
.hdr
.context
= 0;
1319 removed
.srcref
= src
->srcref
;
1320 (void)ctl_enqueuedata(state
->kctl
, state
->unit
, &removed
, sizeof(removed
), CTL_DATA_EOR
);
1323 // Cleanup the source if we found it.
1324 src
->provider
->nstat_release(src
->cookie
);
1325 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
1329 nstat_control_connect(
1331 struct sockaddr_ctl
*sac
,
1334 nstat_control_state
*state
= OSMalloc(sizeof(*state
), nstat_malloc_tag
);
1335 if (state
== NULL
) return ENOMEM
;
1337 bzero(state
, sizeof(*state
));
1338 lck_mtx_init(&state
->mtx
, nstat_lck_grp
, NULL
);
1340 state
->unit
= sac
->sc_unit
;
1343 // check if we're super user
1344 proc_t pself
= proc_self();
1345 state
->suser
= proc_suser(pself
) == 0;
1348 lck_mtx_lock(&nstat_mtx
);
1349 state
->next
= nstat_controls
;
1350 nstat_controls
= state
;
1352 if (nstat_idle_time
== 0ULL)
1354 clock_interval_to_deadline(60, NSEC_PER_SEC
, &nstat_idle_time
);
1355 thread_call_func_delayed((thread_call_func_t
)nstat_idle_check
, NULL
, nstat_idle_time
);
1358 lck_mtx_unlock(&nstat_mtx
);
1364 nstat_control_disconnect(
1365 __unused kern_ctl_ref kctl
,
1366 __unused u_int32_t unit
,
1367 __unused
void *uinfo
)
1370 nstat_control_state
*state
= (nstat_control_state
*)uinfo
;
1372 // pull it out of the global list of states
1373 lck_mtx_lock(&nstat_mtx
);
1374 nstat_control_state
**statepp
;
1375 for (statepp
= &nstat_controls
; *statepp
; statepp
= &(*statepp
)->next
)
1377 if (*statepp
== state
)
1379 *statepp
= state
->next
;
1383 lck_mtx_unlock(&nstat_mtx
);
1385 lck_mtx_lock(&state
->mtx
);
1386 // Stop watching for sources
1387 nstat_provider
*provider
;
1388 watching
= state
->watching
;
1389 state
->watching
= 0;
1390 for (provider
= nstat_providers
; provider
&& watching
; provider
= provider
->next
)
1392 if ((watching
& (1 << provider
->nstat_provider_id
)) != 0)
1394 watching
&= ~(1 << provider
->nstat_provider_id
);
1395 provider
->nstat_watcher_remove(state
);
1399 // set cleanup flags
1400 state
->cleanup
= TRUE
;
1402 // Copy out the list of sources
1403 nstat_src
*srcs
= state
->srcs
;
1405 lck_mtx_unlock(&state
->mtx
);
1411 // pull it out of the list
1416 nstat_control_cleanup_source(NULL
, src
);
1419 OSFree(state
, sizeof(*state
), nstat_malloc_tag
);
1424 static nstat_src_ref_t
1425 nstat_control_next_src_ref(
1426 nstat_control_state
*state
)
1429 nstat_src_ref_t toReturn
= NSTAT_SRC_REF_INVALID
;
1431 for (i
= 0; i
< 1000 && toReturn
== NSTAT_SRC_REF_INVALID
; i
++)
1433 if (state
->next_srcref
== NSTAT_SRC_REF_INVALID
||
1434 state
->next_srcref
== NSTAT_SRC_REF_ALL
)
1436 state
->next_srcref
= 1;
1440 for (src
= state
->srcs
; src
; src
= src
->next
)
1442 if (src
->srcref
== state
->next_srcref
)
1446 if (src
== NULL
) toReturn
= state
->next_srcref
;
1447 state
->next_srcref
++;
1454 nstat_control_send_description(
1455 nstat_control_state
*state
,
1459 // Provider doesn't support getting the descriptor? Done.
1460 if (src
->provider
->nstat_descriptor_length
== 0 ||
1461 src
->provider
->nstat_copy_descriptor
== NULL
)
1463 lck_mtx_unlock(&state
->mtx
);
1464 printf("%s:%d - provider doesn't support descriptions\n", __FUNCTION__
, __LINE__
);
1468 // Allocate storage for the descriptor message
1470 unsigned int one
= 1;
1471 u_int32_t size
= offsetof(nstat_msg_src_description
, data
) + src
->provider
->nstat_descriptor_length
;
1472 if (mbuf_allocpacket(MBUF_WAITOK
, size
, &one
, &msg
) != 0)
1474 lck_mtx_unlock(&state
->mtx
);
1475 printf("%s:%d - failed to allocate response\n", __FUNCTION__
, __LINE__
);
1479 nstat_msg_src_description
*desc
= (nstat_msg_src_description
*)mbuf_data(msg
);
1480 mbuf_setlen(msg
, size
);
1481 mbuf_pkthdr_setlen(msg
, mbuf_len(msg
));
1483 // Query the provider for the provider specific bits
1484 errno_t result
= src
->provider
->nstat_copy_descriptor(src
->cookie
, desc
->data
, src
->provider
->nstat_descriptor_length
);
1489 printf("%s:%d - provider failed to copy descriptor %d\n", __FUNCTION__
, __LINE__
, result
);
1493 desc
->hdr
.context
= context
;
1494 desc
->hdr
.type
= NSTAT_MSG_TYPE_SRC_DESC
;
1495 desc
->srcref
= src
->srcref
;
1496 desc
->provider
= src
->provider
->nstat_provider_id
;
1498 result
= ctl_enqueuembuf(state
->kctl
, state
->unit
, msg
, CTL_DATA_EOR
);
1501 printf("%s:%d ctl_enqueuembuf returned error %d\n", __FUNCTION__
, __LINE__
, result
);
1509 nstat_control_handle_add_request(
1510 nstat_control_state
*state
,
1515 // Verify the header fits in the first mbuf
1516 if (mbuf_len(m
) < offsetof(nstat_msg_add_src_req
, param
))
1518 printf("mbuf_len(m)=%lu, offsetof(nstat_msg_add_src_req*, param)=%lu\n",
1519 mbuf_len(m
), offsetof(nstat_msg_add_src_req
, param
));
1523 // Calculate the length of the parameter field
1524 int32_t paramlength
= mbuf_pkthdr_len(m
) - offsetof(nstat_msg_add_src_req
, param
);
1525 if (paramlength
< 0 || paramlength
> 2 * 1024)
1527 printf("invalid paramlength=%d\n", paramlength
);
1531 nstat_provider
*provider
;
1532 nstat_provider_cookie_t cookie
;
1533 nstat_msg_add_src_req
*req
= mbuf_data(m
);
1534 if (mbuf_pkthdr_len(m
) > mbuf_len(m
))
1536 // parameter is too large, we need to make a contiguous copy
1537 void *data
= OSMalloc(paramlength
, nstat_malloc_tag
);
1539 if (!data
) return ENOMEM
;
1540 result
= mbuf_copydata(m
, offsetof(nstat_msg_add_src_req
, param
), paramlength
, data
);
1542 result
= nstat_lookup_entry(req
->provider
, data
, paramlength
, &provider
, &cookie
);
1543 OSFree(data
, paramlength
, nstat_malloc_tag
);
1547 result
= nstat_lookup_entry(req
->provider
, (void*)&req
->param
, paramlength
, &provider
, &cookie
);
1555 result
= nstat_control_source_add(req
->hdr
.context
, state
, provider
, cookie
);
1557 provider
->nstat_release(cookie
);
1564 __unused nstat_control_state
*state
)
1567 #if !REQUIRE_ROOT_FOR_STATS
1570 // If the socket was created by a priv process, allow
1571 if (state
->suser
) return 1;
1573 // If the current process is priv, allow
1574 proc_t self
= proc_self();
1575 allow
= proc_suser(self
) == 0;
1578 // TBD: check for entitlement, root check is too coarse
1579 #endif /* REQUIRE_ROOT_FOR_STATS */
1585 nstat_control_handle_add_all(
1586 nstat_control_state
*state
,
1591 if (!nstat_perm_check(state
))
1596 // Verify the header fits in the first mbuf
1597 if (mbuf_len(m
) < sizeof(nstat_msg_add_all_srcs
))
1599 printf("mbuf_len(m)=%lu, sizeof(nstat_msg_add_all_srcs)=%lu\n",
1600 mbuf_len(m
), sizeof(nstat_msg_add_all_srcs
));
1604 nstat_msg_add_all_srcs
*req
= mbuf_data(m
);
1605 nstat_provider
*provider
= nstat_find_provider_by_id(req
->provider
);
1607 if (!provider
) return ENOENT
;
1608 if (provider
->nstat_watcher_add
== NULL
) return ENOTSUP
;
1610 // Make sure we don't add the provider twice
1611 lck_mtx_lock(&state
->mtx
);
1612 if ((state
->watching
& (1 << provider
->nstat_provider_id
)) != 0)
1614 state
->watching
|= (1 << provider
->nstat_provider_id
);
1615 lck_mtx_unlock(&state
->mtx
);
1616 if (result
!= 0) return result
;
1618 result
= provider
->nstat_watcher_add(state
);
1621 lck_mtx_lock(&state
->mtx
);
1622 state
->watching
&= ~(1 << provider
->nstat_provider_id
);
1623 lck_mtx_unlock(&state
->mtx
);
1628 // Notify the client
1629 nstat_msg_hdr success
;
1630 success
.context
= req
->hdr
.context
;
1631 success
.type
= NSTAT_MSG_TYPE_SUCCESS
;
1633 if (ctl_enqueuedata(state
->kctl
, state
->unit
, &success
, sizeof(success
), CTL_DATA_EOR
) != 0)
1634 printf("%s:%d - failed to enqueue success message\n", __FUNCTION__
, __LINE__
);
1641 nstat_control_source_add(
1643 nstat_control_state
*state
,
1644 nstat_provider
*provider
,
1645 nstat_provider_cookie_t cookie
)
1647 // Fill out source added message
1649 unsigned int one
= 1;
1651 if (mbuf_allocpacket(MBUF_WAITOK
, sizeof(nstat_msg_src_added
), &one
, &msg
) != 0)
1654 mbuf_setlen(msg
, sizeof(nstat_msg_src_added
));
1655 mbuf_pkthdr_setlen(msg
, mbuf_len(msg
));
1656 nstat_msg_src_added
*add
= mbuf_data(msg
);
1657 bzero(add
, sizeof(*add
));
1658 add
->hdr
.type
= NSTAT_MSG_TYPE_SRC_ADDED
;
1659 add
->hdr
.context
= context
;
1660 add
->provider
= provider
->nstat_provider_id
;
1662 // Allocate storage for the source
1663 nstat_src
*src
= OSMalloc(sizeof(*src
), nstat_malloc_tag
);
1670 // Fill in the source, including picking an unused source ref
1671 lck_mtx_lock(&state
->mtx
);
1673 add
->srcref
= src
->srcref
= nstat_control_next_src_ref(state
);
1674 if (state
->cleanup
|| src
->srcref
== NSTAT_SRC_REF_INVALID
)
1676 lck_mtx_unlock(&state
->mtx
);
1677 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
1681 src
->provider
= provider
;
1682 src
->cookie
= cookie
;
1684 // send the source added message
1685 errno_t result
= ctl_enqueuembuf(state
->kctl
, state
->unit
, msg
, CTL_DATA_EOR
);
1688 lck_mtx_unlock(&state
->mtx
);
1689 printf("%s:%d ctl_enqueuembuf failed: %d\n", __FUNCTION__
, __LINE__
, result
);
1690 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
1695 // Put the source in the list
1696 src
->next
= state
->srcs
;
1699 // send the description message
1700 // not useful as the source is often not complete
1701 // nstat_control_send_description(state, src, 0ULL);
1703 lck_mtx_unlock(&state
->mtx
);
1709 nstat_control_handle_remove_request(
1710 nstat_control_state
*state
,
1713 nstat_src_ref_t srcref
= NSTAT_SRC_REF_INVALID
;
1715 if (mbuf_copydata(m
, offsetof(nstat_msg_rem_src_req
, srcref
), sizeof(srcref
), &srcref
) != 0)
1717 printf("%s:%d - invalid length %u, expected %lu\n", __FUNCTION__
, __LINE__
, (u_int32_t
)mbuf_pkthdr_len(m
), sizeof(nstat_msg_rem_src_req
));
1721 lck_mtx_lock(&state
->mtx
);
1723 // Remove this source as we look for it
1725 nstat_src
*src
= NULL
;
1726 for (nextp
= &state
->srcs
; *nextp
; nextp
= &(*nextp
)->next
)
1728 if ((*nextp
)->srcref
== srcref
)
1736 lck_mtx_unlock(&state
->mtx
);
1738 if (src
) nstat_control_cleanup_source(state
, src
);
1740 return src
? 0 : ENOENT
;
1744 nstat_control_handle_query_request(
1745 nstat_control_state
*state
,
1748 // TBD: handle this from another thread so we can enqueue a lot of data
1749 // As written, if a client requests query all, this function will be
1750 // called from their send of the request message. We will attempt to write
1751 // responses and succeed until the buffer fills up. Since the clients thread
1752 // is blocked on send, it won't be reading unless the client has two threads
1753 // using this socket, one for read and one for write. Two threads probably
1754 // won't work with this code anyhow since we don't have proper locking in
1756 nstat_src
*dead_srcs
= NULL
;
1757 errno_t result
= ENOENT
;
1758 nstat_msg_query_src_req req
;
1759 if (mbuf_copydata(m
, 0, sizeof(req
), &req
) != 0)
1761 printf("%s:%d - invalid length %u, expected %lu\n", __FUNCTION__
, __LINE__
, (u_int32_t
)mbuf_pkthdr_len(m
), sizeof(req
));
1765 lck_mtx_lock(&state
->mtx
);
1766 nstat_src
**srcpp
= &state
->srcs
;
1767 while (*srcpp
!= NULL
)
1772 if (req
.srcref
== NSTAT_SRC_REF_ALL
||
1773 (*srcpp
)->srcref
== req
.srcref
)
1775 nstat_msg_src_counts counts
;
1776 counts
.hdr
.type
= NSTAT_MSG_TYPE_SRC_COUNTS
;
1777 counts
.hdr
.context
= req
.hdr
.context
;
1778 counts
.srcref
= (*srcpp
)->srcref
;
1779 bzero(&counts
.counts
, sizeof(counts
.counts
));
1780 result
= (*srcpp
)->provider
->nstat_counts((*srcpp
)->cookie
, &counts
.counts
, &gone
);
1784 result
= ctl_enqueuedata(state
->kctl
, state
->unit
, &counts
, sizeof(counts
), CTL_DATA_EOR
);
1788 printf("%s:%d provider->nstat_counts failed: %d\n", __FUNCTION__
, __LINE__
, result
);
1793 // send one last descriptor message so client may see last state
1794 nstat_control_send_description(state
, *srcpp
, 0ULL);
1796 // pull src out of the list
1797 nstat_src
*src
= *srcpp
;
1800 src
->next
= dead_srcs
;
1804 if (req
.srcref
!= NSTAT_SRC_REF_ALL
)
1809 srcpp
= &(*srcpp
)->next
;
1811 lck_mtx_unlock(&state
->mtx
);
1818 dead_srcs
= src
->next
;
1820 // release src and send notification
1821 nstat_control_cleanup_source(state
, src
);
1824 if (req
.srcref
== NSTAT_SRC_REF_ALL
)
1826 nstat_msg_hdr success
;
1827 success
.context
= req
.hdr
.context
;
1828 success
.type
= NSTAT_MSG_TYPE_SUCCESS
;
1830 if (ctl_enqueuedata(state
->kctl
, state
->unit
, &success
, sizeof(success
), CTL_DATA_EOR
) != 0)
1831 printf("%s:%d - failed to enqueue success message\n", __FUNCTION__
, __LINE__
);
1839 nstat_control_handle_get_src_description(
1840 nstat_control_state
*state
,
1843 nstat_msg_get_src_description req
;
1844 if (mbuf_copydata(m
, 0, sizeof(req
), &req
) != 0)
1846 printf("%s:%d - invalid length %u, expected %lu\n", __FUNCTION__
, __LINE__
, (u_int32_t
)mbuf_pkthdr_len(m
), sizeof(req
));
1851 lck_mtx_lock(&state
->mtx
);
1853 for (src
= state
->srcs
; src
; src
= src
->next
)
1855 if (src
->srcref
== req
.srcref
)
1862 lck_mtx_unlock(&state
->mtx
);
1863 printf("%s:%d - no matching source\n", __FUNCTION__
, __LINE__
);
1867 errno_t result
= nstat_control_send_description(state
, src
, req
.hdr
.context
);
1868 lck_mtx_unlock(&state
->mtx
);
1877 __unused
void *uinfo
,
1881 nstat_control_state
*state
= (nstat_control_state
*)uinfo
;
1882 struct nstat_msg_hdr
*hdr
;
1883 struct nstat_msg_hdr storage
;
1886 if (mbuf_pkthdr_len(m
) < sizeof(hdr
))
1888 // Is this the right thing to do?
1889 printf("%s:%d - message too short, was %ld expected %lu\n", __FUNCTION__
, __LINE__
,
1890 mbuf_pkthdr_len(m
), sizeof(*hdr
));
1895 if (mbuf_len(m
) >= sizeof(*hdr
))
1901 mbuf_copydata(m
, 0, sizeof(storage
), &storage
);
1907 case NSTAT_MSG_TYPE_ADD_SRC
:
1908 result
= nstat_control_handle_add_request(state
, m
);
1911 case NSTAT_MSG_TYPE_ADD_ALL_SRCS
:
1912 result
= nstat_control_handle_add_all(state
, m
);
1915 case NSTAT_MSG_TYPE_REM_SRC
:
1916 result
= nstat_control_handle_remove_request(state
, m
);
1919 case NSTAT_MSG_TYPE_QUERY_SRC
:
1920 result
= nstat_control_handle_query_request(state
, m
);
1923 case NSTAT_MSG_TYPE_GET_SRC_DESC
:
1924 result
= nstat_control_handle_get_src_description(state
, m
);
1928 printf("%s:%d - unknown message type %d\n", __FUNCTION__
, __LINE__
, hdr
->type
);
1935 struct nstat_msg_error err
;
1937 err
.hdr
.type
= NSTAT_MSG_TYPE_ERROR
;
1938 err
.hdr
.context
= hdr
->context
;
1941 result
= ctl_enqueuedata(kctl
, unit
, &err
, sizeof(err
), CTL_DATA_EOR
);