2 * Copyright (c) 2010-2012 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");
67 NSTAT_FLAG_CLEANUP
= (0x1 << 0),
68 NSTAT_FLAG_REQCOUNTS
= (0x1 << 1)
71 typedef struct nstat_control_state
73 struct nstat_control_state
*ncs_next
;
74 u_int32_t ncs_watching
;
75 decl_lck_mtx_data(, mtx
);
76 kern_ctl_ref ncs_kctl
;
78 nstat_src_ref_t ncs_next_srcref
;
79 struct nstat_src
*ncs_srcs
;
81 } nstat_control_state
;
83 typedef struct nstat_provider
85 struct nstat_provider
*next
;
86 nstat_provider_id_t nstat_provider_id
;
87 size_t nstat_descriptor_length
;
88 errno_t (*nstat_lookup
)(const void *data
, u_int32_t length
, nstat_provider_cookie_t
*out_cookie
);
89 int (*nstat_gone
)(nstat_provider_cookie_t cookie
);
90 errno_t (*nstat_counts
)(nstat_provider_cookie_t cookie
, struct nstat_counts
*out_counts
, int *out_gone
);
91 errno_t (*nstat_watcher_add
)(nstat_control_state
*state
);
92 void (*nstat_watcher_remove
)(nstat_control_state
*state
);
93 errno_t (*nstat_copy_descriptor
)(nstat_provider_cookie_t cookie
, void *data
, u_int32_t len
);
94 void (*nstat_release
)(nstat_provider_cookie_t cookie
, boolean_t locked
);
98 typedef struct nstat_src
100 struct nstat_src
*next
;
101 nstat_src_ref_t srcref
;
102 nstat_provider
*provider
;
103 nstat_provider_cookie_t cookie
;
106 static errno_t
nstat_control_send_counts(nstat_control_state
*,
107 nstat_src
*, unsigned long long, int *);
108 static int nstat_control_send_description(nstat_control_state
*state
, nstat_src
*src
, u_int64_t context
);
109 static errno_t
nstat_control_send_removed(nstat_control_state
*, nstat_src
*);
110 static void nstat_control_cleanup_source(nstat_control_state
*state
, nstat_src
*src
,
113 static u_int32_t nstat_udp_watchers
= 0;
114 static u_int32_t nstat_tcp_watchers
= 0;
116 static void nstat_control_register(void);
118 static volatile OSMallocTag nstat_malloc_tag
= NULL
;
119 static nstat_control_state
*nstat_controls
= NULL
;
120 static uint64_t nstat_idle_time
= 0;
121 static decl_lck_mtx_data(, nstat_mtx
);
125 const struct sockaddr
*src
,
126 struct sockaddr
*dst
,
129 if (src
->sa_len
> maxlen
) return;
131 bcopy(src
, dst
, src
->sa_len
);
132 if (src
->sa_family
== AF_INET6
&&
133 src
->sa_len
>= sizeof(struct sockaddr_in6
))
135 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)dst
;
136 if (IN6_IS_SCOPE_EMBED(&sin6
->sin6_addr
))
138 if (sin6
->sin6_scope_id
== 0)
139 sin6
->sin6_scope_id
= ntohs(sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1]);
140 sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
146 nstat_ip_to_sockaddr(
147 const struct in_addr
*ip
,
149 struct sockaddr_in
*sin
,
152 if (maxlen
< sizeof(struct sockaddr_in
))
155 sin
->sin_family
= AF_INET
;
156 sin
->sin_len
= sizeof(*sin
);
157 sin
->sin_port
= port
;
162 nstat_ip6_to_sockaddr(
163 const struct in6_addr
*ip6
,
165 struct sockaddr_in6
*sin6
,
168 if (maxlen
< sizeof(struct sockaddr_in6
))
171 sin6
->sin6_family
= AF_INET6
;
172 sin6
->sin6_len
= sizeof(*sin6
);
173 sin6
->sin6_port
= port
;
174 sin6
->sin6_addr
= *ip6
;
175 if (IN6_IS_SCOPE_EMBED(&sin6
->sin6_addr
))
177 sin6
->sin6_scope_id
= ntohs(sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1]);
178 sin6
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
182 #pragma mark -- Network Statistic Providers --
184 static errno_t
nstat_control_source_add(u_int64_t context
, nstat_control_state
*state
, nstat_provider
*provider
, nstat_provider_cookie_t cookie
);
185 struct nstat_provider
*nstat_providers
= NULL
;
187 static struct nstat_provider
*
188 nstat_find_provider_by_id(
189 nstat_provider_id_t id
)
191 struct nstat_provider
*provider
;
193 for (provider
= nstat_providers
; provider
!= NULL
; provider
= provider
->next
)
195 if (provider
->nstat_provider_id
== id
)
204 nstat_provider_id_t id
,
207 nstat_provider
**out_provider
,
208 nstat_provider_cookie_t
*out_cookie
)
210 *out_provider
= nstat_find_provider_by_id(id
);
211 if (*out_provider
== NULL
)
216 return (*out_provider
)->nstat_lookup(data
, length
, out_cookie
);
219 static void nstat_init_route_provider(void);
220 static void nstat_init_tcp_provider(void);
221 static void nstat_init_udp_provider(void);
223 __private_extern__
void
226 if (nstat_malloc_tag
!= NULL
) return;
228 OSMallocTag tag
= OSMalloc_Tagalloc(NET_STAT_CONTROL_NAME
, OSMT_DEFAULT
);
229 if (!OSCompareAndSwapPtr(NULL
, tag
, &nstat_malloc_tag
))
231 OSMalloc_Tagfree(tag
);
232 tag
= nstat_malloc_tag
;
236 // we need to initialize other things, we do it here as this code path will only be hit once;
237 nstat_init_route_provider();
238 nstat_init_tcp_provider();
239 nstat_init_udp_provider();
240 nstat_control_register();
244 #pragma mark -- Aligned Buffer Allocation --
253 nstat_malloc_aligned(
258 struct align_header
*hdr
= NULL
;
259 u_int32_t size
= length
+ sizeof(*hdr
) + alignment
- 1;
261 u_int8_t
*buffer
= OSMalloc(size
, tag
);
262 if (buffer
== NULL
) return NULL
;
264 u_int8_t
*aligned
= buffer
+ sizeof(*hdr
);
265 aligned
= (u_int8_t
*)P2ROUNDUP(aligned
, alignment
);
267 hdr
= (struct align_header
*)(void *)(aligned
- sizeof(*hdr
));
268 hdr
->offset
= aligned
- buffer
;
279 struct align_header
*hdr
= (struct align_header
*)(void *)((u_int8_t
*)buffer
- sizeof(*hdr
));
280 OSFree(((char*)buffer
) - hdr
->offset
, hdr
->length
, tag
);
283 #pragma mark -- Route Provider --
285 static nstat_provider nstat_route_provider
;
291 nstat_provider_cookie_t
*out_cookie
)
293 // rt_lookup doesn't take const params but it doesn't modify the parameters for
294 // the lookup. So...we use a union to eliminate the warning.
298 const struct sockaddr
*const_sa
;
301 const nstat_route_add_param
*param
= (const nstat_route_add_param
*)data
;
304 if (length
< sizeof(*param
))
309 if (param
->dst
.v4
.sin_family
== 0 ||
310 param
->dst
.v4
.sin_family
> AF_MAX
||
311 (param
->mask
.v4
.sin_family
!= 0 && param
->mask
.v4
.sin_family
!= param
->dst
.v4
.sin_family
))
316 if (param
->dst
.v4
.sin_len
> sizeof(param
->dst
) ||
317 (param
->mask
.v4
.sin_family
&& param
->mask
.v4
.sin_len
> sizeof(param
->mask
.v4
.sin_len
)))
322 // TBD: Need to validate length of sockaddr for different families?
323 dst
.const_sa
= (const struct sockaddr
*)¶m
->dst
;
324 mask
.const_sa
= param
->mask
.v4
.sin_family
? (const struct sockaddr
*)¶m
->mask
: NULL
;
326 struct radix_node_head
*rnh
= rt_tables
[dst
.sa
->sa_family
];
327 if (rnh
== NULL
) return EAFNOSUPPORT
;
329 lck_mtx_lock(rnh_lock
);
330 struct rtentry
*rt
= rt_lookup(TRUE
, dst
.sa
, mask
.sa
, rnh
, param
->ifindex
);
331 lck_mtx_unlock(rnh_lock
);
333 if (rt
) *out_cookie
= (nstat_provider_cookie_t
)rt
;
335 return rt
? 0 : ENOENT
;
340 nstat_provider_cookie_t cookie
)
342 struct rtentry
*rt
= (struct rtentry
*)cookie
;
343 return ((rt
->rt_flags
& RTF_UP
) == 0) ? 1 : 0;
348 nstat_provider_cookie_t cookie
,
349 struct nstat_counts
*out_counts
,
352 struct rtentry
*rt
= (struct rtentry
*)cookie
;
353 struct nstat_counts
*rt_stats
= rt
->rt_stats
;
357 if ((rt
->rt_flags
& RTF_UP
) == 0) *out_gone
= 1;
361 atomic_get_64(out_counts
->nstat_rxpackets
, &rt_stats
->nstat_rxpackets
);
362 atomic_get_64(out_counts
->nstat_rxbytes
, &rt_stats
->nstat_rxbytes
);
363 atomic_get_64(out_counts
->nstat_txpackets
, &rt_stats
->nstat_txpackets
);
364 atomic_get_64(out_counts
->nstat_txbytes
, &rt_stats
->nstat_txbytes
);
365 out_counts
->nstat_rxduplicatebytes
= rt_stats
->nstat_rxduplicatebytes
;
366 out_counts
->nstat_rxoutoforderbytes
= rt_stats
->nstat_rxoutoforderbytes
;
367 out_counts
->nstat_txretransmit
= rt_stats
->nstat_txretransmit
;
368 out_counts
->nstat_connectattempts
= rt_stats
->nstat_connectattempts
;
369 out_counts
->nstat_connectsuccesses
= rt_stats
->nstat_connectsuccesses
;
370 out_counts
->nstat_min_rtt
= rt_stats
->nstat_min_rtt
;
371 out_counts
->nstat_avg_rtt
= rt_stats
->nstat_avg_rtt
;
372 out_counts
->nstat_var_rtt
= rt_stats
->nstat_var_rtt
;
375 bzero(out_counts
, sizeof(*out_counts
));
382 nstat_provider_cookie_t cookie
,
385 rtfree((struct rtentry
*)cookie
);
388 static u_int32_t nstat_route_watchers
= 0;
391 nstat_route_walktree_add(
392 struct radix_node
*rn
,
396 struct rtentry
*rt
= (struct rtentry
*)rn
;
397 nstat_control_state
*state
= (nstat_control_state
*)context
;
399 lck_mtx_assert(rnh_lock
, LCK_MTX_ASSERT_OWNED
);
401 /* RTF_UP can't change while rnh_lock is held */
402 if ((rt
->rt_flags
& RTF_UP
) != 0)
404 /* Clear RTPRF_OURS if the route is still usable */
406 if (rt_validate(rt
)) {
407 RT_ADDREF_LOCKED(rt
);
414 /* Otherwise if RTF_CONDEMNED, treat it as if it were down */
418 result
= nstat_control_source_add(0, state
, &nstat_route_provider
, rt
);
427 nstat_route_add_watcher(
428 nstat_control_state
*state
)
432 OSIncrementAtomic(&nstat_route_watchers
);
434 lck_mtx_lock(rnh_lock
);
435 for (i
= 1; i
< AF_MAX
; i
++)
437 struct radix_node_head
*rnh
;
441 result
= rnh
->rnh_walktree(rnh
, nstat_route_walktree_add
, state
);
447 lck_mtx_unlock(rnh_lock
);
452 __private_extern__
void
453 nstat_route_new_entry(
456 if (nstat_route_watchers
== 0)
459 lck_mtx_lock(&nstat_mtx
);
460 if ((rt
->rt_flags
& RTF_UP
) != 0)
462 nstat_control_state
*state
;
463 for (state
= nstat_controls
; state
; state
= state
->ncs_next
)
465 if ((state
->ncs_watching
& (1 << NSTAT_PROVIDER_ROUTE
)) != 0)
467 // this client is watching routes
468 // acquire a reference for the route
471 // add the source, if that fails, release the reference
472 if (nstat_control_source_add(0, state
, &nstat_route_provider
, rt
) != 0)
477 lck_mtx_unlock(&nstat_mtx
);
481 nstat_route_remove_watcher(
482 __unused nstat_control_state
*state
)
484 OSDecrementAtomic(&nstat_route_watchers
);
488 nstat_route_copy_descriptor(
489 nstat_provider_cookie_t cookie
,
493 nstat_route_descriptor
*desc
= (nstat_route_descriptor
*)data
;
494 if (len
< sizeof(*desc
))
498 bzero(desc
, sizeof(*desc
));
500 struct rtentry
*rt
= (struct rtentry
*)cookie
;
501 desc
->id
= (uintptr_t)rt
;
502 desc
->parent_id
= (uintptr_t)rt
->rt_parent
;
503 desc
->gateway_id
= (uintptr_t)rt
->rt_gwroute
;
508 if ((sa
= rt_key(rt
)))
509 nstat_copy_sa_out(sa
, &desc
->dst
.sa
, sizeof(desc
->dst
));
512 if ((sa
= rt_mask(rt
)) && sa
->sa_len
<= sizeof(desc
->mask
))
513 memcpy(&desc
->mask
, sa
, sa
->sa_len
);
516 if ((sa
= rt
->rt_gateway
))
517 nstat_copy_sa_out(sa
, &desc
->gateway
.sa
, sizeof(desc
->gateway
));
520 desc
->ifindex
= rt
->rt_ifp
->if_index
;
522 desc
->flags
= rt
->rt_flags
;
528 nstat_init_route_provider(void)
530 bzero(&nstat_route_provider
, sizeof(nstat_route_provider
));
531 nstat_route_provider
.nstat_descriptor_length
= sizeof(nstat_route_descriptor
);
532 nstat_route_provider
.nstat_provider_id
= NSTAT_PROVIDER_ROUTE
;
533 nstat_route_provider
.nstat_lookup
= nstat_route_lookup
;
534 nstat_route_provider
.nstat_gone
= nstat_route_gone
;
535 nstat_route_provider
.nstat_counts
= nstat_route_counts
;
536 nstat_route_provider
.nstat_release
= nstat_route_release
;
537 nstat_route_provider
.nstat_watcher_add
= nstat_route_add_watcher
;
538 nstat_route_provider
.nstat_watcher_remove
= nstat_route_remove_watcher
;
539 nstat_route_provider
.nstat_copy_descriptor
= nstat_route_copy_descriptor
;
540 nstat_route_provider
.next
= nstat_providers
;
541 nstat_providers
= &nstat_route_provider
;
544 #pragma mark -- Route Collection --
546 static struct nstat_counts
*
550 struct nstat_counts
*result
= rte
->rt_stats
;
551 if (result
) return result
;
553 if (nstat_malloc_tag
== NULL
) nstat_init();
555 result
= nstat_malloc_aligned(sizeof(*result
), sizeof(u_int64_t
), nstat_malloc_tag
);
556 if (!result
) return result
;
558 bzero(result
, sizeof(*result
));
560 if (!OSCompareAndSwapPtr(NULL
, result
, &rte
->rt_stats
))
562 nstat_free_aligned(result
, nstat_malloc_tag
);
563 result
= rte
->rt_stats
;
569 __private_extern__
void
575 nstat_free_aligned(rte
->rt_stats
, nstat_malloc_tag
);
576 rte
->rt_stats
= NULL
;
580 __private_extern__
void
581 nstat_route_connect_attempt(
586 struct nstat_counts
* stats
= nstat_route_attach(rte
);
589 OSIncrementAtomic(&stats
->nstat_connectattempts
);
592 rte
= rte
->rt_parent
;
596 __private_extern__
void
597 nstat_route_connect_success(
603 struct nstat_counts
* stats
= nstat_route_attach(rte
);
606 OSIncrementAtomic(&stats
->nstat_connectsuccesses
);
609 rte
= rte
->rt_parent
;
613 __private_extern__
void
622 struct nstat_counts
* stats
= nstat_route_attach(rte
);
625 if ((flags
& NSTAT_TX_FLAG_RETRANSMIT
) != 0)
627 OSAddAtomic(bytes
, &stats
->nstat_txretransmit
);
631 OSAddAtomic64((SInt64
)packets
, (SInt64
*)&stats
->nstat_txpackets
);
632 OSAddAtomic64((SInt64
)bytes
, (SInt64
*)&stats
->nstat_txbytes
);
636 rte
= rte
->rt_parent
;
640 __private_extern__
void
649 struct nstat_counts
* stats
= nstat_route_attach(rte
);
654 OSAddAtomic64((SInt64
)packets
, (SInt64
*)&stats
->nstat_rxpackets
);
655 OSAddAtomic64((SInt64
)bytes
, (SInt64
*)&stats
->nstat_rxbytes
);
659 if (flags
& NSTAT_RX_FLAG_OUT_OF_ORDER
)
660 OSAddAtomic(bytes
, &stats
->nstat_rxoutoforderbytes
);
661 if (flags
& NSTAT_RX_FLAG_DUPLICATE
)
662 OSAddAtomic(bytes
, &stats
->nstat_rxduplicatebytes
);
666 rte
= rte
->rt_parent
;
670 __private_extern__
void
676 const int32_t factor
= 8;
680 struct nstat_counts
* stats
= nstat_route_attach(rte
);
689 oldrtt
= stats
->nstat_avg_rtt
;
696 newrtt
= oldrtt
- (oldrtt
- (int32_t)rtt
) / factor
;
698 if (oldrtt
== newrtt
) break;
699 } while (!OSCompareAndSwap(oldrtt
, newrtt
, &stats
->nstat_avg_rtt
));
704 oldrtt
= stats
->nstat_min_rtt
;
705 if (oldrtt
!= 0 && oldrtt
< (int32_t)rtt
)
709 } while (!OSCompareAndSwap(oldrtt
, rtt
, &stats
->nstat_min_rtt
));
714 oldrtt
= stats
->nstat_var_rtt
;
721 newrtt
= oldrtt
- (oldrtt
- (int32_t)rtt_var
) / factor
;
723 if (oldrtt
== newrtt
) break;
724 } while (!OSCompareAndSwap(oldrtt
, newrtt
, &stats
->nstat_var_rtt
));
727 rte
= rte
->rt_parent
;
732 #pragma mark -- TCP Provider --
734 static nstat_provider nstat_tcp_provider
;
738 struct inpcbinfo
*inpinfo
,
741 nstat_provider_cookie_t
*out_cookie
)
743 // parameter validation
744 const nstat_tcp_add_param
*param
= (const nstat_tcp_add_param
*)data
;
745 if (length
< sizeof(*param
))
750 // src and dst must match
751 if (param
->remote
.v4
.sin_family
!= 0 &&
752 param
->remote
.v4
.sin_family
!= param
->local
.v4
.sin_family
)
757 struct inpcb
*inp
= NULL
;
759 switch (param
->local
.v4
.sin_family
)
763 if (param
->local
.v4
.sin_len
!= sizeof(param
->local
.v4
) ||
764 (param
->remote
.v4
.sin_family
!= 0 &&
765 param
->remote
.v4
.sin_len
!= sizeof(param
->remote
.v4
)))
770 inp
= in_pcblookup_hash(inpinfo
, param
->remote
.v4
.sin_addr
, param
->remote
.v4
.sin_port
,
771 param
->local
.v4
.sin_addr
, param
->local
.v4
.sin_port
, 1, NULL
);
780 const struct in6_addr
*in6c
;
781 struct in6_addr
*in6
;
784 if (param
->local
.v6
.sin6_len
!= sizeof(param
->local
.v6
) ||
785 (param
->remote
.v6
.sin6_family
!= 0 &&
786 param
->remote
.v6
.sin6_len
!= sizeof(param
->remote
.v6
)))
791 local
.in6c
= ¶m
->local
.v6
.sin6_addr
;
792 remote
.in6c
= ¶m
->remote
.v6
.sin6_addr
;
794 inp
= in6_pcblookup_hash(inpinfo
, remote
.in6
, param
->remote
.v6
.sin6_port
,
795 local
.in6
, param
->local
.v6
.sin6_port
, 1, NULL
);
804 if (inp
== NULL
) return ENOENT
;
806 // At this point we have a ref to the inpcb
815 nstat_provider_cookie_t
*out_cookie
)
817 return nstat_tcpudp_lookup(&tcbinfo
, data
, length
, out_cookie
);
822 nstat_provider_cookie_t cookie
)
824 struct inpcb
*inp
= (struct inpcb
*)cookie
;
825 struct tcpcb
*tp
= intotcpcb(inp
);
826 return (inp
->inp_state
== INPCB_STATE_DEAD
|| tp
->t_state
== TCPS_TIME_WAIT
) ? 1 : 0;
831 nstat_provider_cookie_t cookie
,
832 struct nstat_counts
*out_counts
,
835 struct inpcb
*inp
= (struct inpcb
*)cookie
;
836 struct tcpcb
*tp
= intotcpcb(inp
);
838 bzero(out_counts
, sizeof(*out_counts
));
842 // if the pcb is in the dead state, we should stop using it
843 if (inp
->inp_state
== INPCB_STATE_DEAD
|| tp
->t_state
== TCPS_TIME_WAIT
)
848 atomic_get_64(out_counts
->nstat_rxpackets
, &inp
->inp_stat
->rxpackets
);
849 atomic_get_64(out_counts
->nstat_rxbytes
, &inp
->inp_stat
->rxbytes
);
850 atomic_get_64(out_counts
->nstat_txpackets
, &inp
->inp_stat
->txpackets
);
851 atomic_get_64(out_counts
->nstat_txbytes
, &inp
->inp_stat
->txbytes
);
852 out_counts
->nstat_rxduplicatebytes
= tp
->t_stat
.rxduplicatebytes
;
853 out_counts
->nstat_rxoutoforderbytes
= tp
->t_stat
.rxoutoforderbytes
;
854 out_counts
->nstat_txretransmit
= tp
->t_stat
.txretransmitbytes
;
855 out_counts
->nstat_connectattempts
= tp
->t_state
>= TCPS_SYN_SENT
? 1 : 0;
856 out_counts
->nstat_connectsuccesses
= tp
->t_state
>= TCPS_ESTABLISHED
? 1 : 0;
857 out_counts
->nstat_avg_rtt
= tp
->t_srtt
;
858 out_counts
->nstat_min_rtt
= tp
->t_rttbest
;
859 out_counts
->nstat_var_rtt
= tp
->t_rttvar
;
860 if (out_counts
->nstat_avg_rtt
< out_counts
->nstat_min_rtt
)
861 out_counts
->nstat_min_rtt
= out_counts
->nstat_avg_rtt
;
868 nstat_provider_cookie_t cookie
,
871 struct inpcb
*inp
= (struct inpcb
*)cookie
;
872 in_pcb_checkstate(inp
, WNT_RELEASE
, locked
);
876 nstat_tcp_add_watcher(
877 nstat_control_state
*state
)
879 OSIncrementAtomic(&nstat_tcp_watchers
);
881 lck_rw_lock_shared(tcbinfo
.mtx
);
883 // Add all current tcp inpcbs. Ignore those in timewait
885 for (inp
= LIST_FIRST(tcbinfo
.listhead
); inp
; inp
= LIST_NEXT(inp
, inp_list
))
887 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
890 if (nstat_control_source_add(0, state
, &nstat_tcp_provider
, inp
) != 0)
892 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
897 lck_rw_done(tcbinfo
.mtx
);
903 nstat_tcp_remove_watcher(
904 __unused nstat_control_state
*state
)
906 OSDecrementAtomic(&nstat_tcp_watchers
);
909 __private_extern__
void
913 if (nstat_tcp_watchers
== 0)
916 lck_mtx_lock(&nstat_mtx
);
917 nstat_control_state
*state
;
918 for (state
= nstat_controls
; state
; state
= state
->ncs_next
)
920 if ((state
->ncs_watching
& (1 << NSTAT_PROVIDER_TCP
)) != 0)
922 // this client is watching tcp
923 // acquire a reference for it
924 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
927 // add the source, if that fails, release the reference
928 if (nstat_control_source_add(0, state
, &nstat_tcp_provider
, inp
) != 0)
930 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
935 lck_mtx_unlock(&nstat_mtx
);
938 __private_extern__
void
939 nstat_pcb_detach(struct inpcb
*inp
)
941 nstat_control_state
*state
;
942 nstat_src
*src
, *prevsrc
;
943 nstat_src
*dead_list
= NULL
;
945 if (inp
== NULL
|| (nstat_tcp_watchers
== 0 && nstat_udp_watchers
== 0))
948 lck_mtx_lock(&nstat_mtx
);
949 for (state
= nstat_controls
; state
; state
= state
->ncs_next
) {
950 lck_mtx_lock(&state
->mtx
);
951 for (prevsrc
= NULL
, src
= state
->ncs_srcs
; src
;
952 prevsrc
= src
, src
= src
->next
)
953 if (src
->cookie
== inp
)
957 // send one last counts notification
958 nstat_control_send_counts(state
, src
, 0, NULL
);
960 // send a last description
961 nstat_control_send_description(state
, src
, 0);
963 // send the source removed notification
964 nstat_control_send_removed(state
, src
);
967 prevsrc
->next
= src
->next
;
969 state
->ncs_srcs
= src
->next
;
971 src
->next
= dead_list
;
974 lck_mtx_unlock(&state
->mtx
);
976 lck_mtx_unlock(&nstat_mtx
);
980 dead_list
= src
->next
;
982 nstat_control_cleanup_source(NULL
, src
, TRUE
);
987 nstat_tcp_copy_descriptor(
988 nstat_provider_cookie_t cookie
,
992 if (len
< sizeof(nstat_tcp_descriptor
))
997 nstat_tcp_descriptor
*desc
= (nstat_tcp_descriptor
*)data
;
998 struct inpcb
*inp
= (struct inpcb
*)cookie
;
999 struct tcpcb
*tp
= intotcpcb(inp
);
1001 if (inp
->inp_state
== INPCB_STATE_DEAD
)
1004 bzero(desc
, sizeof(*desc
));
1006 if (inp
->inp_vflag
& INP_IPV6
)
1008 nstat_ip6_to_sockaddr(&inp
->in6p_laddr
, inp
->inp_lport
,
1009 &desc
->local
.v6
, sizeof(desc
->local
));
1010 nstat_ip6_to_sockaddr(&inp
->in6p_faddr
, inp
->inp_fport
,
1011 &desc
->remote
.v6
, sizeof(desc
->remote
));
1013 else if (inp
->inp_vflag
& INP_IPV4
)
1015 nstat_ip_to_sockaddr(&inp
->inp_laddr
, inp
->inp_lport
,
1016 &desc
->local
.v4
, sizeof(desc
->local
));
1017 nstat_ip_to_sockaddr(&inp
->inp_faddr
, inp
->inp_fport
,
1018 &desc
->remote
.v4
, sizeof(desc
->remote
));
1021 desc
->state
= intotcpcb(inp
)->t_state
;
1022 desc
->ifindex
= (inp
->inp_last_outifp
== NULL
) ? 0 :
1023 inp
->inp_last_outifp
->if_index
;
1025 // danger - not locked, values could be bogus
1026 desc
->txunacked
= tp
->snd_max
- tp
->snd_una
;
1027 desc
->txwindow
= tp
->snd_wnd
;
1028 desc
->txcwindow
= tp
->snd_cwnd
;
1030 struct socket
*so
= inp
->inp_socket
;
1033 // TBD - take the socket lock around these to make sure
1035 desc
->upid
= so
->last_upid
;
1036 desc
->pid
= so
->last_pid
;
1037 desc
->traffic_class
= so
->so_traffic_class
;
1039 proc_name(desc
->pid
, desc
->pname
, sizeof(desc
->pname
));
1040 desc
->pname
[sizeof(desc
->pname
) - 1] = 0;
1042 desc
->sndbufsize
= so
->so_snd
.sb_hiwat
;
1043 desc
->sndbufused
= so
->so_snd
.sb_cc
;
1044 desc
->rcvbufsize
= so
->so_rcv
.sb_hiwat
;
1045 desc
->rcvbufused
= so
->so_rcv
.sb_cc
;
1052 nstat_init_tcp_provider(void)
1054 bzero(&nstat_tcp_provider
, sizeof(nstat_tcp_provider
));
1055 nstat_tcp_provider
.nstat_descriptor_length
= sizeof(nstat_tcp_descriptor
);
1056 nstat_tcp_provider
.nstat_provider_id
= NSTAT_PROVIDER_TCP
;
1057 nstat_tcp_provider
.nstat_lookup
= nstat_tcp_lookup
;
1058 nstat_tcp_provider
.nstat_gone
= nstat_tcp_gone
;
1059 nstat_tcp_provider
.nstat_counts
= nstat_tcp_counts
;
1060 nstat_tcp_provider
.nstat_release
= nstat_tcp_release
;
1061 nstat_tcp_provider
.nstat_watcher_add
= nstat_tcp_add_watcher
;
1062 nstat_tcp_provider
.nstat_watcher_remove
= nstat_tcp_remove_watcher
;
1063 nstat_tcp_provider
.nstat_copy_descriptor
= nstat_tcp_copy_descriptor
;
1064 nstat_tcp_provider
.next
= nstat_providers
;
1065 nstat_providers
= &nstat_tcp_provider
;
1068 #pragma mark -- UDP Provider --
1070 static nstat_provider nstat_udp_provider
;
1076 nstat_provider_cookie_t
*out_cookie
)
1078 return nstat_tcpudp_lookup(&udbinfo
, data
, length
, out_cookie
);
1083 nstat_provider_cookie_t cookie
)
1085 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1086 return (inp
->inp_state
== INPCB_STATE_DEAD
) ? 1 : 0;
1091 nstat_provider_cookie_t cookie
,
1092 struct nstat_counts
*out_counts
,
1095 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1099 // if the pcb is in the dead state, we should stop using it
1100 if (inp
->inp_state
== INPCB_STATE_DEAD
)
1105 atomic_get_64(out_counts
->nstat_rxpackets
, &inp
->inp_stat
->rxpackets
);
1106 atomic_get_64(out_counts
->nstat_rxbytes
, &inp
->inp_stat
->rxbytes
);
1107 atomic_get_64(out_counts
->nstat_txpackets
, &inp
->inp_stat
->txpackets
);
1108 atomic_get_64(out_counts
->nstat_txbytes
, &inp
->inp_stat
->txbytes
);
1115 nstat_provider_cookie_t cookie
,
1118 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1119 in_pcb_checkstate(inp
, WNT_RELEASE
, locked
);
1123 nstat_udp_add_watcher(
1124 nstat_control_state
*state
)
1126 OSIncrementAtomic(&nstat_udp_watchers
);
1128 lck_rw_lock_shared(tcbinfo
.mtx
);
1130 // Add all current tcp inpcbs. Ignore those in timewait
1132 for (inp
= LIST_FIRST(udbinfo
.listhead
); inp
; inp
= LIST_NEXT(inp
, inp_list
))
1134 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
1137 if (nstat_control_source_add(0, state
, &nstat_udp_provider
, inp
) != 0)
1139 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
1144 lck_rw_done(tcbinfo
.mtx
);
1150 nstat_udp_remove_watcher(
1151 __unused nstat_control_state
*state
)
1153 OSDecrementAtomic(&nstat_udp_watchers
);
1156 __private_extern__
void
1160 if (nstat_udp_watchers
== 0)
1163 lck_mtx_lock(&nstat_mtx
);
1164 nstat_control_state
*state
;
1165 for (state
= nstat_controls
; state
; state
= state
->ncs_next
)
1167 if ((state
->ncs_watching
& (1 << NSTAT_PROVIDER_UDP
)) != 0)
1169 // this client is watching tcp
1170 // acquire a reference for it
1171 if (in_pcb_checkstate(inp
, WNT_ACQUIRE
, 0) == WNT_STOPUSING
)
1174 // add the source, if that fails, release the reference
1175 if (nstat_control_source_add(0, state
, &nstat_udp_provider
, inp
) != 0)
1177 in_pcb_checkstate(inp
, WNT_RELEASE
, 0);
1182 lck_mtx_unlock(&nstat_mtx
);
1186 nstat_udp_copy_descriptor(
1187 nstat_provider_cookie_t cookie
,
1191 if (len
< sizeof(nstat_udp_descriptor
))
1196 nstat_udp_descriptor
*desc
= (nstat_udp_descriptor
*)data
;
1197 struct inpcb
*inp
= (struct inpcb
*)cookie
;
1199 if (inp
->inp_state
== INPCB_STATE_DEAD
)
1202 bzero(desc
, sizeof(*desc
));
1204 if (inp
->inp_vflag
& INP_IPV6
)
1206 nstat_ip6_to_sockaddr(&inp
->in6p_laddr
, inp
->inp_lport
,
1207 &desc
->local
.v6
, sizeof(desc
->local
));
1208 nstat_ip6_to_sockaddr(&inp
->in6p_faddr
, inp
->inp_fport
,
1209 &desc
->remote
.v6
, sizeof(desc
->remote
));
1211 else if (inp
->inp_vflag
& INP_IPV4
)
1213 nstat_ip_to_sockaddr(&inp
->inp_laddr
, inp
->inp_lport
,
1214 &desc
->local
.v4
, sizeof(desc
->local
));
1215 nstat_ip_to_sockaddr(&inp
->inp_faddr
, inp
->inp_fport
,
1216 &desc
->remote
.v4
, sizeof(desc
->remote
));
1219 desc
->ifindex
= (inp
->inp_last_outifp
== NULL
) ? 0 :
1220 inp
->inp_last_outifp
->if_index
;
1222 struct socket
*so
= inp
->inp_socket
;
1225 // TBD - take the socket lock around these to make sure
1227 desc
->upid
= so
->last_upid
;
1228 desc
->pid
= so
->last_pid
;
1230 desc
->rcvbufsize
= so
->so_rcv
.sb_hiwat
;
1231 desc
->rcvbufused
= so
->so_rcv
.sb_cc
;
1232 desc
->traffic_class
= so
->so_traffic_class
;
1234 proc_name(desc
->pid
, desc
->pname
, sizeof(desc
->pname
));
1235 desc
->pname
[sizeof(desc
->pname
) - 1] = 0;
1242 nstat_init_udp_provider(void)
1244 bzero(&nstat_udp_provider
, sizeof(nstat_udp_provider
));
1245 nstat_udp_provider
.nstat_provider_id
= NSTAT_PROVIDER_UDP
;
1246 nstat_udp_provider
.nstat_descriptor_length
= sizeof(nstat_udp_descriptor
);
1247 nstat_udp_provider
.nstat_lookup
= nstat_udp_lookup
;
1248 nstat_udp_provider
.nstat_gone
= nstat_udp_gone
;
1249 nstat_udp_provider
.nstat_counts
= nstat_udp_counts
;
1250 nstat_udp_provider
.nstat_watcher_add
= nstat_udp_add_watcher
;
1251 nstat_udp_provider
.nstat_watcher_remove
= nstat_udp_remove_watcher
;
1252 nstat_udp_provider
.nstat_copy_descriptor
= nstat_udp_copy_descriptor
;
1253 nstat_udp_provider
.nstat_release
= nstat_udp_release
;
1254 nstat_udp_provider
.next
= nstat_providers
;
1255 nstat_providers
= &nstat_udp_provider
;
1258 #pragma mark -- Kernel Control Socket --
1260 static kern_ctl_ref nstat_ctlref
= NULL
;
1261 static lck_grp_t
*nstat_lck_grp
= NULL
;
1263 static errno_t
nstat_control_connect(kern_ctl_ref kctl
, struct sockaddr_ctl
*sac
, void **uinfo
);
1264 static errno_t
nstat_control_disconnect(kern_ctl_ref kctl
, u_int32_t unit
, void *uinfo
);
1265 static errno_t
nstat_control_send(kern_ctl_ref kctl
, u_int32_t unit
, void *uinfo
, mbuf_t m
, int flags
);
1270 __unused thread_call_param_t p0
,
1271 __unused thread_call_param_t p1
)
1273 lck_mtx_lock(&nstat_mtx
);
1275 nstat_idle_time
= 0;
1277 nstat_control_state
*control
;
1278 nstat_src
*dead
= NULL
;
1279 nstat_src
*dead_list
= NULL
;
1280 for (control
= nstat_controls
; control
; control
= control
->ncs_next
)
1282 lck_mtx_lock(&control
->mtx
);
1283 nstat_src
**srcpp
= &control
->ncs_srcs
;
1285 if (!(control
->ncs_flags
& NSTAT_FLAG_REQCOUNTS
))
1287 while(*srcpp
!= NULL
)
1289 if ((*srcpp
)->provider
->nstat_gone((*srcpp
)->cookie
))
1291 // Pull it off the list
1293 *srcpp
= (*srcpp
)->next
;
1295 // send one last counts notification
1296 nstat_control_send_counts(control
, dead
,
1299 // send a last description
1300 nstat_control_send_description(control
, dead
, 0);
1302 // send the source removed notification
1303 nstat_control_send_removed(control
, dead
);
1305 // Put this on the list to release later
1306 dead
->next
= dead_list
;
1311 srcpp
= &(*srcpp
)->next
;
1315 control
->ncs_flags
&= ~NSTAT_FLAG_REQCOUNTS
;
1316 lck_mtx_unlock(&control
->mtx
);
1321 clock_interval_to_deadline(60, NSEC_PER_SEC
, &nstat_idle_time
);
1322 thread_call_func_delayed((thread_call_func_t
)nstat_idle_check
, NULL
, nstat_idle_time
);
1325 lck_mtx_unlock(&nstat_mtx
);
1327 // Release the sources now that we aren't holding lots of locks
1331 dead_list
= dead
->next
;
1333 nstat_control_cleanup_source(NULL
, dead
, FALSE
);
1340 nstat_control_register(void)
1342 // Create our lock group first
1343 lck_grp_attr_t
*grp_attr
= lck_grp_attr_alloc_init();
1344 lck_grp_attr_setdefault(grp_attr
);
1345 nstat_lck_grp
= lck_grp_alloc_init("network statistics kctl", grp_attr
);
1346 lck_grp_attr_free(grp_attr
);
1348 lck_mtx_init(&nstat_mtx
, nstat_lck_grp
, NULL
);
1350 // Register the control
1351 struct kern_ctl_reg nstat_control
;
1352 bzero(&nstat_control
, sizeof(nstat_control
));
1353 strlcpy(nstat_control
.ctl_name
, NET_STAT_CONTROL_NAME
, sizeof(nstat_control
.ctl_name
));
1354 nstat_control
.ctl_connect
= nstat_control_connect
;
1355 nstat_control
.ctl_disconnect
= nstat_control_disconnect
;
1356 nstat_control
.ctl_send
= nstat_control_send
;
1358 ctl_register(&nstat_control
, &nstat_ctlref
);
1362 nstat_control_cleanup_source(
1363 nstat_control_state
*state
,
1364 struct nstat_src
*src
,
1368 nstat_control_send_removed(state
, src
);
1370 // Cleanup the source if we found it.
1371 src
->provider
->nstat_release(src
->cookie
, locked
);
1372 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
1376 nstat_control_connect(
1378 struct sockaddr_ctl
*sac
,
1381 nstat_control_state
*state
= OSMalloc(sizeof(*state
), nstat_malloc_tag
);
1382 if (state
== NULL
) return ENOMEM
;
1384 bzero(state
, sizeof(*state
));
1385 lck_mtx_init(&state
->mtx
, nstat_lck_grp
, NULL
);
1386 state
->ncs_kctl
= kctl
;
1387 state
->ncs_unit
= sac
->sc_unit
;
1388 state
->ncs_flags
= NSTAT_FLAG_REQCOUNTS
;
1391 lck_mtx_lock(&nstat_mtx
);
1392 state
->ncs_next
= nstat_controls
;
1393 nstat_controls
= state
;
1395 if (nstat_idle_time
== 0)
1397 clock_interval_to_deadline(60, NSEC_PER_SEC
, &nstat_idle_time
);
1398 thread_call_func_delayed((thread_call_func_t
)nstat_idle_check
, NULL
, nstat_idle_time
);
1401 lck_mtx_unlock(&nstat_mtx
);
1407 nstat_control_disconnect(
1408 __unused kern_ctl_ref kctl
,
1409 __unused u_int32_t unit
,
1410 __unused
void *uinfo
)
1413 nstat_control_state
*state
= (nstat_control_state
*)uinfo
;
1415 // pull it out of the global list of states
1416 lck_mtx_lock(&nstat_mtx
);
1417 nstat_control_state
**statepp
;
1418 for (statepp
= &nstat_controls
; *statepp
; statepp
= &(*statepp
)->ncs_next
)
1420 if (*statepp
== state
)
1422 *statepp
= state
->ncs_next
;
1426 lck_mtx_unlock(&nstat_mtx
);
1428 lck_mtx_lock(&state
->mtx
);
1429 // Stop watching for sources
1430 nstat_provider
*provider
;
1431 watching
= state
->ncs_watching
;
1432 state
->ncs_watching
= 0;
1433 for (provider
= nstat_providers
; provider
&& watching
; provider
= provider
->next
)
1435 if ((watching
& (1 << provider
->nstat_provider_id
)) != 0)
1437 watching
&= ~(1 << provider
->nstat_provider_id
);
1438 provider
->nstat_watcher_remove(state
);
1442 // set cleanup flags
1443 state
->ncs_flags
|= NSTAT_FLAG_CLEANUP
;
1445 // Copy out the list of sources
1446 nstat_src
*srcs
= state
->ncs_srcs
;
1447 state
->ncs_srcs
= NULL
;
1448 lck_mtx_unlock(&state
->mtx
);
1454 // pull it out of the list
1459 nstat_control_cleanup_source(NULL
, src
, FALSE
);
1462 OSFree(state
, sizeof(*state
), nstat_malloc_tag
);
1467 static nstat_src_ref_t
1468 nstat_control_next_src_ref(
1469 nstat_control_state
*state
)
1472 nstat_src_ref_t toReturn
= NSTAT_SRC_REF_INVALID
;
1474 for (i
= 0; i
< 1000 && toReturn
== NSTAT_SRC_REF_INVALID
; i
++)
1476 if (state
->ncs_next_srcref
== NSTAT_SRC_REF_INVALID
||
1477 state
->ncs_next_srcref
== NSTAT_SRC_REF_ALL
)
1479 state
->ncs_next_srcref
= 1;
1483 for (src
= state
->ncs_srcs
; src
; src
= src
->next
)
1485 if (src
->srcref
== state
->ncs_next_srcref
)
1489 if (src
== NULL
) toReturn
= state
->ncs_next_srcref
;
1490 state
->ncs_next_srcref
++;
1497 nstat_control_send_counts(
1498 nstat_control_state
*state
,
1500 unsigned long long context
,
1503 nstat_msg_src_counts counts
;
1507 counts
.hdr
.type
= NSTAT_MSG_TYPE_SRC_COUNTS
;
1508 counts
.hdr
.context
= context
;
1509 counts
.srcref
= src
->srcref
;
1510 bzero(&counts
.counts
, sizeof(counts
.counts
));
1511 if (src
->provider
->nstat_counts(src
->cookie
, &counts
.counts
,
1513 result
= ctl_enqueuedata(state
->ncs_kctl
, state
->ncs_unit
, &counts
,
1514 sizeof(counts
), CTL_DATA_EOR
);
1522 nstat_control_send_description(
1523 nstat_control_state
*state
,
1527 // Provider doesn't support getting the descriptor? Done.
1528 if (src
->provider
->nstat_descriptor_length
== 0 ||
1529 src
->provider
->nstat_copy_descriptor
== NULL
)
1534 // Allocate storage for the descriptor message
1536 unsigned int one
= 1;
1537 u_int32_t size
= offsetof(nstat_msg_src_description
, data
) + src
->provider
->nstat_descriptor_length
;
1538 if (mbuf_allocpacket(MBUF_WAITOK
, size
, &one
, &msg
) != 0)
1543 nstat_msg_src_description
*desc
= (nstat_msg_src_description
*)mbuf_data(msg
);
1544 mbuf_setlen(msg
, size
);
1545 mbuf_pkthdr_setlen(msg
, mbuf_len(msg
));
1547 // Query the provider for the provider specific bits
1548 errno_t result
= src
->provider
->nstat_copy_descriptor(src
->cookie
, desc
->data
, src
->provider
->nstat_descriptor_length
);
1556 desc
->hdr
.context
= context
;
1557 desc
->hdr
.type
= NSTAT_MSG_TYPE_SRC_DESC
;
1558 desc
->srcref
= src
->srcref
;
1559 desc
->provider
= src
->provider
->nstat_provider_id
;
1561 result
= ctl_enqueuembuf(state
->ncs_kctl
, state
->ncs_unit
, msg
, CTL_DATA_EOR
);
1571 nstat_control_send_removed(
1572 nstat_control_state
*state
,
1575 nstat_msg_src_removed removed
;
1578 removed
.hdr
.type
= NSTAT_MSG_TYPE_SRC_REMOVED
;
1579 removed
.hdr
.context
= 0;
1580 removed
.srcref
= src
->srcref
;
1581 result
= ctl_enqueuedata(state
->ncs_kctl
, state
->ncs_unit
, &removed
,
1582 sizeof(removed
), CTL_DATA_EOR
);
1588 nstat_control_handle_add_request(
1589 nstat_control_state
*state
,
1594 // Verify the header fits in the first mbuf
1595 if (mbuf_len(m
) < offsetof(nstat_msg_add_src_req
, param
))
1600 // Calculate the length of the parameter field
1601 int32_t paramlength
= mbuf_pkthdr_len(m
) - offsetof(nstat_msg_add_src_req
, param
);
1602 if (paramlength
< 0 || paramlength
> 2 * 1024)
1607 nstat_provider
*provider
;
1608 nstat_provider_cookie_t cookie
;
1609 nstat_msg_add_src_req
*req
= mbuf_data(m
);
1610 if (mbuf_pkthdr_len(m
) > mbuf_len(m
))
1612 // parameter is too large, we need to make a contiguous copy
1613 void *data
= OSMalloc(paramlength
, nstat_malloc_tag
);
1615 if (!data
) return ENOMEM
;
1616 result
= mbuf_copydata(m
, offsetof(nstat_msg_add_src_req
, param
), paramlength
, data
);
1618 result
= nstat_lookup_entry(req
->provider
, data
, paramlength
, &provider
, &cookie
);
1619 OSFree(data
, paramlength
, nstat_malloc_tag
);
1623 result
= nstat_lookup_entry(req
->provider
, (void*)&req
->param
, paramlength
, &provider
, &cookie
);
1631 result
= nstat_control_source_add(req
->hdr
.context
, state
, provider
, cookie
);
1633 provider
->nstat_release(cookie
, 0);
1639 nstat_control_handle_add_all(
1640 nstat_control_state
*state
,
1645 // Verify the header fits in the first mbuf
1646 if (mbuf_len(m
) < sizeof(nstat_msg_add_all_srcs
))
1651 nstat_msg_add_all_srcs
*req
= mbuf_data(m
);
1652 nstat_provider
*provider
= nstat_find_provider_by_id(req
->provider
);
1654 if (!provider
) return ENOENT
;
1655 if (provider
->nstat_watcher_add
== NULL
) return ENOTSUP
;
1657 // Make sure we don't add the provider twice
1658 lck_mtx_lock(&state
->mtx
);
1659 if ((state
->ncs_watching
& (1 << provider
->nstat_provider_id
)) != 0)
1661 state
->ncs_watching
|= (1 << provider
->nstat_provider_id
);
1662 lck_mtx_unlock(&state
->mtx
);
1663 if (result
!= 0) return result
;
1665 result
= provider
->nstat_watcher_add(state
);
1668 lck_mtx_lock(&state
->mtx
);
1669 state
->ncs_watching
&= ~(1 << provider
->nstat_provider_id
);
1670 lck_mtx_unlock(&state
->mtx
);
1675 // Notify the client
1676 nstat_msg_hdr success
;
1677 success
.context
= req
->hdr
.context
;
1678 success
.type
= NSTAT_MSG_TYPE_SUCCESS
;
1680 ctl_enqueuedata(state
->ncs_kctl
, state
->ncs_unit
, &success
, sizeof(success
), CTL_DATA_EOR
);
1687 nstat_control_source_add(
1689 nstat_control_state
*state
,
1690 nstat_provider
*provider
,
1691 nstat_provider_cookie_t cookie
)
1693 // Fill out source added message
1695 unsigned int one
= 1;
1697 if (mbuf_allocpacket(MBUF_WAITOK
, sizeof(nstat_msg_src_added
), &one
, &msg
) != 0)
1700 mbuf_setlen(msg
, sizeof(nstat_msg_src_added
));
1701 mbuf_pkthdr_setlen(msg
, mbuf_len(msg
));
1702 nstat_msg_src_added
*add
= mbuf_data(msg
);
1703 bzero(add
, sizeof(*add
));
1704 add
->hdr
.type
= NSTAT_MSG_TYPE_SRC_ADDED
;
1705 add
->hdr
.context
= context
;
1706 add
->provider
= provider
->nstat_provider_id
;
1708 // Allocate storage for the source
1709 nstat_src
*src
= OSMalloc(sizeof(*src
), nstat_malloc_tag
);
1716 // Fill in the source, including picking an unused source ref
1717 lck_mtx_lock(&state
->mtx
);
1719 add
->srcref
= src
->srcref
= nstat_control_next_src_ref(state
);
1720 if (state
->ncs_flags
& NSTAT_FLAG_CLEANUP
|| src
->srcref
== NSTAT_SRC_REF_INVALID
)
1722 lck_mtx_unlock(&state
->mtx
);
1723 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
1727 src
->provider
= provider
;
1728 src
->cookie
= cookie
;
1730 // send the source added message
1731 errno_t result
= ctl_enqueuembuf(state
->ncs_kctl
, state
->ncs_unit
, msg
, CTL_DATA_EOR
);
1734 lck_mtx_unlock(&state
->mtx
);
1735 OSFree(src
, sizeof(*src
), nstat_malloc_tag
);
1740 // Put the source in the list
1741 src
->next
= state
->ncs_srcs
;
1742 state
->ncs_srcs
= src
;
1744 // send the description message
1745 // not useful as the source is often not complete
1746 // nstat_control_send_description(state, src, 0);
1748 lck_mtx_unlock(&state
->mtx
);
1754 nstat_control_handle_remove_request(
1755 nstat_control_state
*state
,
1758 nstat_src_ref_t srcref
= NSTAT_SRC_REF_INVALID
;
1760 if (mbuf_copydata(m
, offsetof(nstat_msg_rem_src_req
, srcref
), sizeof(srcref
), &srcref
) != 0)
1765 lck_mtx_lock(&state
->mtx
);
1767 // Remove this source as we look for it
1769 nstat_src
*src
= NULL
;
1770 for (nextp
= &state
->ncs_srcs
; *nextp
; nextp
= &(*nextp
)->next
)
1772 if ((*nextp
)->srcref
== srcref
)
1780 lck_mtx_unlock(&state
->mtx
);
1782 if (src
) nstat_control_cleanup_source(state
, src
, FALSE
);
1784 return src
? 0 : ENOENT
;
1788 nstat_control_handle_query_request(
1789 nstat_control_state
*state
,
1792 // TBD: handle this from another thread so we can enqueue a lot of data
1793 // As written, if a client requests query all, this function will be
1794 // called from their send of the request message. We will attempt to write
1795 // responses and succeed until the buffer fills up. Since the clients thread
1796 // is blocked on send, it won't be reading unless the client has two threads
1797 // using this socket, one for read and one for write. Two threads probably
1798 // won't work with this code anyhow since we don't have proper locking in
1800 nstat_src
*dead_srcs
= NULL
;
1801 errno_t result
= ENOENT
;
1802 nstat_msg_query_src_req req
;
1803 if (mbuf_copydata(m
, 0, sizeof(req
), &req
) != 0)
1808 lck_mtx_lock(&state
->mtx
);
1809 if (req
.srcref
== NSTAT_SRC_REF_ALL
)
1810 state
->ncs_flags
|= NSTAT_FLAG_REQCOUNTS
;
1811 nstat_src
**srcpp
= &state
->ncs_srcs
;
1812 while (*srcpp
!= NULL
)
1817 if (req
.srcref
== NSTAT_SRC_REF_ALL
||
1818 (*srcpp
)->srcref
== req
.srcref
)
1820 result
= nstat_control_send_counts(state
, *srcpp
,
1821 req
.hdr
.context
, &gone
);
1823 // If the counts message failed to enqueue then we should clear our flag so
1824 // that a client doesn't miss anything on idle cleanup.
1826 state
->ncs_flags
&= ~NSTAT_FLAG_REQCOUNTS
;
1830 // send one last descriptor message so client may see last state
1832 nstat_control_send_description(state
, *srcpp
,
1835 // pull src out of the list
1836 nstat_src
*src
= *srcpp
;
1839 src
->next
= dead_srcs
;
1843 if (req
.srcref
!= NSTAT_SRC_REF_ALL
)
1848 srcpp
= &(*srcpp
)->next
;
1850 lck_mtx_unlock(&state
->mtx
);
1857 dead_srcs
= src
->next
;
1859 // release src and send notification
1860 nstat_control_cleanup_source(state
, src
, FALSE
);
1863 if (req
.srcref
== NSTAT_SRC_REF_ALL
)
1865 nstat_msg_hdr success
;
1866 success
.context
= req
.hdr
.context
;
1867 success
.type
= NSTAT_MSG_TYPE_SUCCESS
;
1869 ctl_enqueuedata(state
->ncs_kctl
, state
->ncs_unit
, &success
, sizeof(success
), CTL_DATA_EOR
);
1877 nstat_control_handle_get_src_description(
1878 nstat_control_state
*state
,
1881 nstat_msg_get_src_description req
;
1882 if (mbuf_copydata(m
, 0, sizeof(req
), &req
) != 0)
1888 lck_mtx_lock(&state
->mtx
);
1890 for (src
= state
->ncs_srcs
; src
; src
= src
->next
)
1892 if (src
->srcref
== req
.srcref
)
1899 lck_mtx_unlock(&state
->mtx
);
1903 errno_t result
= nstat_control_send_description(state
, src
, req
.hdr
.context
);
1904 lck_mtx_unlock(&state
->mtx
);
1913 __unused
void *uinfo
,
1917 nstat_control_state
*state
= (nstat_control_state
*)uinfo
;
1918 struct nstat_msg_hdr
*hdr
;
1919 struct nstat_msg_hdr storage
;
1922 if (mbuf_pkthdr_len(m
) < sizeof(hdr
))
1924 // Is this the right thing to do?
1929 if (mbuf_len(m
) >= sizeof(*hdr
))
1935 mbuf_copydata(m
, 0, sizeof(storage
), &storage
);
1941 case NSTAT_MSG_TYPE_ADD_SRC
:
1942 result
= nstat_control_handle_add_request(state
, m
);
1945 case NSTAT_MSG_TYPE_ADD_ALL_SRCS
:
1946 result
= nstat_control_handle_add_all(state
, m
);
1949 case NSTAT_MSG_TYPE_REM_SRC
:
1950 result
= nstat_control_handle_remove_request(state
, m
);
1953 case NSTAT_MSG_TYPE_QUERY_SRC
:
1954 result
= nstat_control_handle_query_request(state
, m
);
1957 case NSTAT_MSG_TYPE_GET_SRC_DESC
:
1958 result
= nstat_control_handle_get_src_description(state
, m
);
1968 struct nstat_msg_error err
;
1970 err
.hdr
.type
= NSTAT_MSG_TYPE_ERROR
;
1971 err
.hdr
.context
= hdr
->context
;
1974 result
= ctl_enqueuedata(kctl
, unit
, &err
, sizeof(err
), CTL_DATA_EOR
);