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