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