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