]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/ntstat.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / bsd / net / ntstat.c
index eb83ac3c957f4ebdba749fd6b610854376f8f448..49380d88427d0cc6412b0dc94641be780f0b2466 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2010-2012 Apple Inc. All rights reserved.
+ * Copyright (c) 2010-2014 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -34,6 +34,9 @@
 #include <sys/mcache.h>
 #include <sys/socketvar.h>
 #include <sys/sysctl.h>
 #include <sys/mcache.h>
 #include <sys/socketvar.h>
 #include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/priv.h>
+#include <sys/protosw.h>
 
 #include <kern/clock.h>
 #include <kern/debug.h>
 
 #include <kern/clock.h>
 #include <kern/debug.h>
@@ -44,6 +47,8 @@
 #include <libkern/locks.h>
 
 #include <net/if.h>
 #include <libkern/locks.h>
 
 #include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
 #include <net/route.h>
 #include <net/ntstat.h>
 
 #include <net/route.h>
 #include <net/ntstat.h>
 
@@ -53,6 +58,7 @@
 #include <netinet/tcp.h>
 #include <netinet/tcp_var.h>
 #include <netinet/tcp_fsm.h>
 #include <netinet/tcp.h>
 #include <netinet/tcp_var.h>
 #include <netinet/tcp_fsm.h>
+#include <netinet/tcp_cc.h>
 #include <netinet/udp.h>
 #include <netinet/udp_var.h>
 #include <netinet6/in6_pcb.h>
 #include <netinet/udp.h>
 #include <netinet/udp_var.h>
 #include <netinet6/in6_pcb.h>
@@ -62,22 +68,75 @@ __private_extern__ int      nstat_collect = 1;
 SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
     &nstat_collect, 0, "Collect detailed statistics");
 
 SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED,
     &nstat_collect, 0, "Collect detailed statistics");
 
+static int nstat_privcheck = 0;
+SYSCTL_INT(_net, OID_AUTO, statistics_privcheck, CTLFLAG_RW | CTLFLAG_LOCKED,
+    &nstat_privcheck, 0, "Entitlement check");
+
+SYSCTL_NODE(_net, OID_AUTO, stats,
+    CTLFLAG_RW|CTLFLAG_LOCKED, 0, "network statistics");
+
+static int nstat_debug = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED,
+    &nstat_debug, 0, "");
+
+static int nstat_sendspace = 2048;
+SYSCTL_INT(_net_stats, OID_AUTO, sendspace, CTLFLAG_RW | CTLFLAG_LOCKED,
+    &nstat_sendspace, 0, "");
+
+static int nstat_recvspace = 8192;
+SYSCTL_INT(_net_stats, OID_AUTO, recvspace, CTLFLAG_RW | CTLFLAG_LOCKED,
+    &nstat_recvspace, 0, "");
+
+static int nstat_successmsgfailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, successmsgfailures, CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_successmsgfailures, 0, "");
+
+static int nstat_sendountfailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, sendountfailures, CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_sendountfailures, 0, "");
+
+static int nstat_sysinfofailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, sysinfofalures, CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_sysinfofailures, 0, "");
+
+static int nstat_srccountfailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, srccountfailures, CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_srccountfailures, 0, "");
+
+static int nstat_descriptionfailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, descriptionfailures, CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_descriptionfailures, 0, "");
+
+static int nstat_msgremovedfailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, msgremovedfailures , CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_msgremovedfailures, 0, "");
+
+static int nstat_srcaddedfailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, srcaddedfailures , CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_srcaddedfailures, 0, "");
+
+static int nstat_msgerrorfailures = 0;
+SYSCTL_INT(_net_stats, OID_AUTO, msgerrorfailures , CTLFLAG_RD| CTLFLAG_LOCKED,
+    &nstat_msgerrorfailures, 0, "");
+
+
 enum
 {
 enum
 {
-       NSTAT_FLAG_CLEANUP              = (0x1 << 0),
-       NSTAT_FLAG_REQCOUNTS    = (0x1 << 1)
+       NSTAT_FLAG_CLEANUP      = (1 << 0),
+       NSTAT_FLAG_REQCOUNTS    = (1 << 1),
+       NSTAT_FLAG_REQDESCS     = (1 << 2)
 };
 
 typedef struct nstat_control_state
 {
        struct nstat_control_state      *ncs_next;
 };
 
 typedef struct nstat_control_state
 {
        struct nstat_control_state      *ncs_next;
-       u_int32_t                                       ncs_watching;
+       u_int32_t                       ncs_watching;
        decl_lck_mtx_data(, mtx);
        decl_lck_mtx_data(, mtx);
-       kern_ctl_ref                            ncs_kctl;
-       u_int32_t                                       ncs_unit;
-       nstat_src_ref_t                         ncs_next_srcref;
-       struct nstat_src                        *ncs_srcs;
-       u_int32_t                                       ncs_flags;
+       kern_ctl_ref                    ncs_kctl;
+       u_int32_t                       ncs_unit;
+       nstat_src_ref_t                 ncs_next_srcref;
+       struct nstat_src                *ncs_srcs;
+       u_int32_t                       ncs_flags;
 } nstat_control_state;
 
 typedef struct nstat_provider
 } nstat_control_state;
 
 typedef struct nstat_provider
@@ -100,7 +159,8 @@ typedef struct nstat_src
        struct nstat_src                *next;
        nstat_src_ref_t                 srcref;
        nstat_provider                  *provider;
        struct nstat_src                *next;
        nstat_src_ref_t                 srcref;
        nstat_provider                  *provider;
-       nstat_provider_cookie_t cookie;
+       nstat_provider_cookie_t         cookie;
+       uint32_t                        filter;
 } nstat_src;
 
 static errno_t         nstat_control_send_counts(nstat_control_state *,
 } nstat_src;
 
 static errno_t         nstat_control_send_counts(nstat_control_state *,
@@ -115,11 +175,22 @@ static u_int32_t  nstat_tcp_watchers = 0;
 
 static void nstat_control_register(void);
 
 
 static void nstat_control_register(void);
 
+/*
+ * The lock order is as follows:
+ *
+ * socket_lock (inpcb)
+ *     nstat_mtx
+ *         state->mtx
+ */
 static volatile OSMallocTag    nstat_malloc_tag = NULL;
 static nstat_control_state     *nstat_controls = NULL;
 static uint64_t                                nstat_idle_time = 0;
 static decl_lck_mtx_data(, nstat_mtx);
 
 static volatile OSMallocTag    nstat_malloc_tag = NULL;
 static nstat_control_state     *nstat_controls = NULL;
 static uint64_t                                nstat_idle_time = 0;
 static decl_lck_mtx_data(, nstat_mtx);
 
+/* some extern definitions */
+extern void mbuf_report_peak_usage(void);
+extern void tcp_report_stats(void);
+
 static void
 nstat_copy_sa_out(
        const struct sockaddr   *src,
 static void
 nstat_copy_sa_out(
        const struct sockaddr   *src,
@@ -136,8 +207,8 @@ nstat_copy_sa_out(
                if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
                {
                        if (sin6->sin6_scope_id == 0)
                if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
                {
                        if (sin6->sin6_scope_id == 0)
-                               sin6->sin6_scope_id = ntohs(sin6->sin6_addr.__u6_addr.__u6_addr16[1]);
-                       sin6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
+                               sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
+                       sin6->sin6_addr.s6_addr16[1] = 0;
                }
        }
 }
                }
        }
 }
@@ -174,8 +245,8 @@ nstat_ip6_to_sockaddr(
        sin6->sin6_addr = *ip6;
        if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
        {
        sin6->sin6_addr = *ip6;
        if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
        {
-               sin6->sin6_scope_id = ntohs(sin6->sin6_addr.__u6_addr.__u6_addr16[1]);
-               sin6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
+               sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);
+               sin6->sin6_addr.s6_addr16[1] = 0;
        }
 }
 
        }
 }
 
@@ -219,6 +290,8 @@ nstat_lookup_entry(
 static void nstat_init_route_provider(void);
 static void nstat_init_tcp_provider(void);
 static void nstat_init_udp_provider(void);
 static void nstat_init_route_provider(void);
 static void nstat_init_tcp_provider(void);
 static void nstat_init_udp_provider(void);
+static void nstat_init_ifnet_provider(void);
+static void nstat_init_sysinfo_provider(void);
 
 __private_extern__ void
 nstat_init(void)
 
 __private_extern__ void
 nstat_init(void)
@@ -237,6 +310,8 @@ nstat_init(void)
                nstat_init_route_provider();
                nstat_init_tcp_provider();
                nstat_init_udp_provider();
                nstat_init_route_provider();
                nstat_init_tcp_provider();
                nstat_init_udp_provider();
+               nstat_init_ifnet_provider();
+               nstat_init_sysinfo_provider();
                nstat_control_register();
        }
 }
                nstat_control_register();
        }
 }
@@ -318,8 +393,14 @@ nstat_route_lookup(
        {
                return EINVAL;
        }
        {
                return EINVAL;
        }
+       if ((param->dst.v4.sin_family == AF_INET &&
+           param->dst.v4.sin_len < sizeof(struct sockaddr_in)) ||
+           (param->dst.v6.sin6_family == AF_INET6 &&
+           param->dst.v6.sin6_len < sizeof(struct sockaddr_in6)))
+       {
+               return EINVAL;
+       }
        
        
-       // TBD: Need to validate length of sockaddr for different families?
        dst.const_sa = (const struct sockaddr*)&param->dst;
        mask.const_sa = param->mask.v4.sin_family ? (const struct sockaddr*)&param->mask : NULL;
        
        dst.const_sa = (const struct sockaddr*)&param->dst;
        mask.const_sa = param->mask.v4.sin_family ? (const struct sockaddr*)&param->mask : NULL;
        
@@ -370,6 +451,7 @@ nstat_route_counts(
                out_counts->nstat_min_rtt = rt_stats->nstat_min_rtt;
                out_counts->nstat_avg_rtt = rt_stats->nstat_avg_rtt;
                out_counts->nstat_var_rtt = rt_stats->nstat_var_rtt;
                out_counts->nstat_min_rtt = rt_stats->nstat_min_rtt;
                out_counts->nstat_avg_rtt = rt_stats->nstat_avg_rtt;
                out_counts->nstat_var_rtt = rt_stats->nstat_var_rtt;
+               out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
        }
        else
                bzero(out_counts, sizeof(*out_counts));
        }
        else
                bzero(out_counts, sizeof(*out_counts));
@@ -498,9 +580,9 @@ nstat_route_copy_descriptor(
        bzero(desc, sizeof(*desc));
        
        struct rtentry  *rt = (struct rtentry*)cookie;
        bzero(desc, sizeof(*desc));
        
        struct rtentry  *rt = (struct rtentry*)cookie;
-       desc->id = (uintptr_t)rt;
-       desc->parent_id = (uintptr_t)rt->rt_parent;
-       desc->gateway_id = (uintptr_t)rt->rt_gwroute;
+       desc->id = (uint64_t)VM_KERNEL_ADDRPERM(rt);
+       desc->parent_id = (uint64_t)VM_KERNEL_ADDRPERM(rt->rt_parent);
+       desc->gateway_id = (uint64_t)VM_KERNEL_ADDRPERM(rt->rt_gwroute);
 
        
        // key/dest
 
        
        // key/dest
@@ -731,15 +813,124 @@ nstat_route_rtt(
 
 #pragma mark -- TCP Provider --
 
 
 #pragma mark -- TCP Provider --
 
+/*
+ * Due to the way the kernel deallocates a process (the process structure
+ * might be gone by the time we get the PCB detach notification),
+ * we need to cache the process name. Without this, proc_name() would
+ * return null and the process name would never be sent to userland.
+ *
+ * For UDP sockets, we also store the cached the connection tuples along with
+ * the interface index. This is necessary because when UDP sockets are
+ * disconnected, the connection tuples are forever lost from the inpcb, thus
+ * we need to keep track of the last call to connect() in ntstat.
+ */
+struct nstat_tucookie {
+       struct inpcb    *inp;
+       char            pname[MAXCOMLEN+1];
+       bool            cached;
+       union
+       {
+               struct sockaddr_in      v4;
+               struct sockaddr_in6     v6;
+       } local;
+       union
+       {
+               struct sockaddr_in      v4;
+               struct sockaddr_in6     v6;
+       } remote;
+       unsigned int    if_index;
+};
+
+static struct nstat_tucookie *
+nstat_tucookie_alloc_internal(
+    struct inpcb *inp,
+    bool         ref,
+    bool         locked)
+{
+       struct nstat_tucookie *cookie;
+
+       cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag);
+       if (cookie == NULL)
+               return NULL;
+       if (!locked)
+               lck_mtx_assert(&nstat_mtx, LCK_MTX_ASSERT_NOTOWNED);
+       if (ref && in_pcb_checkstate(inp, WNT_ACQUIRE, locked) == WNT_STOPUSING)
+       {
+               OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
+               return NULL;
+       }
+       bzero(cookie, sizeof(*cookie));
+       cookie->inp = inp;
+       proc_name(inp->inp_socket->last_pid, cookie->pname,
+           sizeof(cookie->pname));
+       /*
+        * We only increment the reference count for UDP sockets because we
+        * only cache UDP socket tuples.
+        */
+       if (SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP)
+               OSIncrementAtomic(&inp->inp_nstat_refcnt);
+
+       return cookie;
+}
+
+static struct nstat_tucookie *
+nstat_tucookie_alloc(
+    struct inpcb *inp)
+{
+       return nstat_tucookie_alloc_internal(inp, false, false);
+}
+
+static struct nstat_tucookie *
+nstat_tucookie_alloc_ref(
+    struct inpcb *inp)
+{
+       return nstat_tucookie_alloc_internal(inp, true, false);
+}
+
+static struct nstat_tucookie *
+nstat_tucookie_alloc_ref_locked(
+    struct inpcb *inp)
+{
+       return nstat_tucookie_alloc_internal(inp, true, true);
+}
+
+static void
+nstat_tucookie_release_internal(
+    struct nstat_tucookie *cookie,
+    int                                inplock)
+{
+       if (SOCK_PROTO(cookie->inp->inp_socket) == IPPROTO_UDP)
+               OSDecrementAtomic(&cookie->inp->inp_nstat_refcnt);
+       in_pcb_checkstate(cookie->inp, WNT_RELEASE, inplock);
+       OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
+}
+
+static void
+nstat_tucookie_release(
+    struct nstat_tucookie *cookie)
+{
+       nstat_tucookie_release_internal(cookie, false);
+}
+
+static void
+nstat_tucookie_release_locked(
+    struct nstat_tucookie *cookie)
+{
+       nstat_tucookie_release_internal(cookie, true);
+}
+
+
 static nstat_provider  nstat_tcp_provider;
 
 static errno_t
 nstat_tcpudp_lookup(
 static nstat_provider  nstat_tcp_provider;
 
 static errno_t
 nstat_tcpudp_lookup(
-       struct inpcbinfo                *inpinfo,
-       const void                              *data,
-       u_int32_t                               length,
+       struct inpcbinfo        *inpinfo,
+       const void              *data,
+       u_int32_t               length,
        nstat_provider_cookie_t *out_cookie)
 {
        nstat_provider_cookie_t *out_cookie)
 {
+       struct inpcb *inp = NULL;
+
        // parameter validation
        const nstat_tcp_add_param       *param = (const nstat_tcp_add_param*)data;
        if (length < sizeof(*param))
        // parameter validation
        const nstat_tcp_add_param       *param = (const nstat_tcp_add_param*)data;
        if (length < sizeof(*param))
@@ -754,7 +945,6 @@ nstat_tcpudp_lookup(
                return EINVAL;
        }
        
                return EINVAL;
        }
        
-       struct inpcb *inp = NULL;
        
        switch (param->local.v4.sin_family)
        {
        
        switch (param->local.v4.sin_family)
        {
@@ -801,10 +991,14 @@ nstat_tcpudp_lookup(
                        return EINVAL;
        }
        
                        return EINVAL;
        }
        
-       if (inp == NULL) return ENOENT;
+       if (inp == NULL)
+               return ENOENT;
        
        // At this point we have a ref to the inpcb
        
        // At this point we have a ref to the inpcb
-       *out_cookie = inp;
+       *out_cookie = nstat_tucookie_alloc(inp);
+       if (*out_cookie == NULL)
+               in_pcb_checkstate(inp, WNT_RELEASE, 0);
+
        return 0;
 }
 
        return 0;
 }
 
@@ -821,9 +1015,14 @@ static int
 nstat_tcp_gone(
        nstat_provider_cookie_t cookie)
 {
 nstat_tcp_gone(
        nstat_provider_cookie_t cookie)
 {
-       struct inpcb    *inp = (struct inpcb*)cookie;
-       struct tcpcb    *tp = intotcpcb(inp);
-       return (inp->inp_state == INPCB_STATE_DEAD || tp->t_state == TCPS_TIME_WAIT) ? 1 : 0;
+       struct nstat_tucookie *tucookie =
+           (struct nstat_tucookie *)cookie;
+       struct inpcb *inp;
+       struct tcpcb *tp;
+       
+       return (!(inp = tucookie->inp) ||
+           !(tp = intotcpcb(inp)) ||
+           inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
 }
 
 static errno_t
 }
 
 static errno_t
@@ -832,18 +1031,23 @@ nstat_tcp_counts(
        struct nstat_counts             *out_counts,
        int                                             *out_gone)
 {
        struct nstat_counts             *out_counts,
        int                                             *out_gone)
 {
-       struct inpcb    *inp = (struct inpcb*)cookie;
-       struct tcpcb    *tp = intotcpcb(inp);
-       
+       struct nstat_tucookie *tucookie =
+           (struct nstat_tucookie *)cookie;
+       struct inpcb *inp;
+
        bzero(out_counts, sizeof(*out_counts));
        
        *out_gone = 0;
        
        // if the pcb is in the dead state, we should stop using it
        bzero(out_counts, sizeof(*out_counts));
        
        *out_gone = 0;
        
        // if the pcb is in the dead state, we should stop using it
-       if (inp->inp_state == INPCB_STATE_DEAD || tp->t_state == TCPS_TIME_WAIT)
+       if (nstat_tcp_gone(cookie))
        {
                *out_gone = 1;
        {
                *out_gone = 1;
-       }
+               if (!(inp = tucookie->inp) || !intotcpcb(inp))
+                       return EINVAL;
+       } 
+       inp = tucookie->inp;
+       struct tcpcb *tp = intotcpcb(inp);
        
        atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
        atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
        
        atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
        atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
@@ -859,6 +1063,12 @@ nstat_tcp_counts(
        out_counts->nstat_var_rtt = tp->t_rttvar;
        if (out_counts->nstat_avg_rtt < out_counts->nstat_min_rtt)
                out_counts->nstat_min_rtt = out_counts->nstat_avg_rtt;
        out_counts->nstat_var_rtt = tp->t_rttvar;
        if (out_counts->nstat_avg_rtt < out_counts->nstat_min_rtt)
                out_counts->nstat_min_rtt = out_counts->nstat_avg_rtt;
+       atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes);
+       atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes);
+       atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes);
+       atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes);
+       atomic_get_64(out_counts->nstat_wired_rxbytes, &inp->inp_Wstat->rxbytes);
+       atomic_get_64(out_counts->nstat_wired_txbytes, &inp->inp_Wstat->txbytes);
        
        return 0;
 }
        
        return 0;
 }
@@ -868,8 +1078,10 @@ nstat_tcp_release(
        nstat_provider_cookie_t cookie,
        int locked)
 {
        nstat_provider_cookie_t cookie,
        int locked)
 {
-       struct inpcb *inp = (struct inpcb*)cookie;
-       in_pcb_checkstate(inp, WNT_RELEASE, locked);
+       struct nstat_tucookie *tucookie =
+           (struct nstat_tucookie *)cookie;
+
+       nstat_tucookie_release_internal(tucookie, locked);
 }
 
 static errno_t
 }
 
 static errno_t
@@ -878,23 +1090,25 @@ nstat_tcp_add_watcher(
 {
        OSIncrementAtomic(&nstat_tcp_watchers);
        
 {
        OSIncrementAtomic(&nstat_tcp_watchers);
        
-       lck_rw_lock_shared(tcbinfo.mtx);
+       lck_rw_lock_shared(tcbinfo.ipi_lock);
        
        // Add all current tcp inpcbs. Ignore those in timewait
        struct inpcb *inp;
        
        // Add all current tcp inpcbs. Ignore those in timewait
        struct inpcb *inp;
-       for (inp = LIST_FIRST(tcbinfo.listhead); inp; inp = LIST_NEXT(inp, inp_list))
+       struct nstat_tucookie *cookie;
+       LIST_FOREACH(inp, tcbinfo.ipi_listhead, inp_list)
        {
        {
-               if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
+               cookie = nstat_tucookie_alloc_ref(inp);
+               if (cookie == NULL)
                        continue;
                        continue;
-               
-               if (nstat_control_source_add(0, state, &nstat_tcp_provider, inp) != 0)
+               if (nstat_control_source_add(0, state, &nstat_tcp_provider,
+                   cookie) != 0)
                {
                {
-                       in_pcb_checkstate(inp, WNT_RELEASE, 0);
+                       nstat_tucookie_release(cookie);
                        break;
                }
        }
        
                        break;
                }
        }
        
-       lck_rw_done(tcbinfo.mtx);
+       lck_rw_done(tcbinfo.ipi_lock);
        
        return 0;
 }
        
        return 0;
 }
@@ -910,9 +1124,12 @@ __private_extern__ void
 nstat_tcp_new_pcb(
        struct inpcb    *inp)
 {
 nstat_tcp_new_pcb(
        struct inpcb    *inp)
 {
+       struct nstat_tucookie *cookie;
+
        if (nstat_tcp_watchers == 0)
                return;
        if (nstat_tcp_watchers == 0)
                return;
-       
+
+       socket_lock(inp->inp_socket, 0);
        lck_mtx_lock(&nstat_mtx);
        nstat_control_state     *state;
        for (state = nstat_controls; state; state = state->ncs_next)
        lck_mtx_lock(&nstat_mtx);
        nstat_control_state     *state;
        for (state = nstat_controls; state; state = state->ncs_next)
@@ -921,18 +1138,20 @@ nstat_tcp_new_pcb(
                {
                        // this client is watching tcp
                        // acquire a reference for it
                {
                        // this client is watching tcp
                        // acquire a reference for it
-                       if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
-                               break;
-                       
+                       cookie = nstat_tucookie_alloc_ref_locked(inp);
+                       if (cookie == NULL)
+                               continue;
                        // add the source, if that fails, release the reference
                        // add the source, if that fails, release the reference
-                       if (nstat_control_source_add(0, state, &nstat_tcp_provider, inp) != 0)
+                       if (nstat_control_source_add(0, state,
+                           &nstat_tcp_provider, cookie) != 0)
                        {
                        {
-                               in_pcb_checkstate(inp, WNT_RELEASE, 0);
+                               nstat_tucookie_release_locked(cookie);
                                break;
                        }
                }
        }
        lck_mtx_unlock(&nstat_mtx);
                                break;
                        }
                }
        }
        lck_mtx_unlock(&nstat_mtx);
+       socket_unlock(inp->inp_socket, 0);
 }
 
 __private_extern__ void
 }
 
 __private_extern__ void
@@ -941,6 +1160,8 @@ nstat_pcb_detach(struct inpcb *inp)
        nstat_control_state *state;
        nstat_src *src, *prevsrc;
        nstat_src *dead_list = NULL;
        nstat_control_state *state;
        nstat_src *src, *prevsrc;
        nstat_src *dead_list = NULL;
+       struct nstat_tucookie *tucookie;
+       errno_t result;
 
        if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0))
                return;
 
        if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0))
                return;
@@ -949,19 +1170,31 @@ nstat_pcb_detach(struct inpcb *inp)
        for (state = nstat_controls; state; state = state->ncs_next) {
                lck_mtx_lock(&state->mtx);
                for (prevsrc = NULL, src = state->ncs_srcs; src;
        for (state = nstat_controls; state; state = state->ncs_next) {
                lck_mtx_lock(&state->mtx);
                for (prevsrc = NULL, src = state->ncs_srcs; src;
-                   prevsrc = src, src = src->next)
-                       if (src->cookie == inp)
+                   prevsrc = src, src = src->next) 
+               {
+                       tucookie = (struct nstat_tucookie *)src->cookie;
+                       if (tucookie->inp == inp)
                                break;
                                break;
+               }
 
                if (src) {
                        // send one last counts notification
 
                if (src) {
                        // send one last counts notification
-                       nstat_control_send_counts(state, src, 0, NULL);
+                       result = nstat_control_send_counts(state, src, 0, NULL);
+                       if (result != 0 && nstat_debug)
+                               printf("%s - nstat_control_send_counts() %d\n",
+                                       __func__, result);
 
                        // send a last description
 
                        // send a last description
-                       nstat_control_send_description(state, src, 0);
+                       result = nstat_control_send_description(state, src, 0);
+                       if (result != 0 && nstat_debug)
+                               printf("%s - nstat_control_send_description() %d\n",
+                                       __func__, result);
 
                        // send the source removed notification
 
                        // send the source removed notification
-                       nstat_control_send_removed(state, src);
+                       result = nstat_control_send_removed(state, src);
+                       if (result != 0 && nstat_debug)
+                               printf("%s - nstat_control_send_removed() %d\n",
+                                       __func__, result);
 
                        if (prevsrc)
                                prevsrc->next = src->next;
 
                        if (prevsrc)
                                prevsrc->next = src->next;
@@ -983,24 +1216,106 @@ nstat_pcb_detach(struct inpcb *inp)
        }
 }
 
        }
 }
 
+__private_extern__ void
+nstat_pcb_cache(struct inpcb *inp)
+{
+       nstat_control_state *state;
+       nstat_src *src;
+       struct nstat_tucookie *tucookie;
+
+       if (inp == NULL || nstat_udp_watchers == 0 || 
+           inp->inp_nstat_refcnt == 0)
+               return;
+       VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
+       lck_mtx_lock(&nstat_mtx);
+       for (state = nstat_controls; state; state = state->ncs_next) {
+               lck_mtx_lock(&state->mtx);
+               for (src = state->ncs_srcs; src; src = src->next) 
+               {
+                       tucookie = (struct nstat_tucookie *)src->cookie;
+                       if (tucookie->inp == inp)
+                       {
+                               if (inp->inp_vflag & INP_IPV6)
+                               {
+                                       nstat_ip6_to_sockaddr(&inp->in6p_laddr,
+                                           inp->inp_lport, 
+                                           &tucookie->local.v6,
+                                           sizeof(tucookie->local));
+                                       nstat_ip6_to_sockaddr(&inp->in6p_faddr,
+                                           inp->inp_fport,
+                                           &tucookie->remote.v6,
+                                           sizeof(tucookie->remote));
+                               }
+                               else if (inp->inp_vflag & INP_IPV4)
+                               {
+                                       nstat_ip_to_sockaddr(&inp->inp_laddr,
+                                           inp->inp_lport, 
+                                           &tucookie->local.v4,
+                                           sizeof(tucookie->local));
+                                       nstat_ip_to_sockaddr(&inp->inp_faddr,
+                                           inp->inp_fport, 
+                                           &tucookie->remote.v4,
+                                           sizeof(tucookie->remote));
+                               }
+                               if (inp->inp_last_outifp)
+                                       tucookie->if_index = 
+                                           inp->inp_last_outifp->if_index;
+                               tucookie->cached = true;
+                               break;
+                       }
+               }
+               lck_mtx_unlock(&state->mtx);
+       }
+       lck_mtx_unlock(&nstat_mtx);
+}
+
+__private_extern__ void
+nstat_pcb_invalidate_cache(struct inpcb *inp)
+{
+       nstat_control_state *state;
+       nstat_src *src;
+       struct nstat_tucookie *tucookie;
+
+       if (inp == NULL || nstat_udp_watchers == 0 ||
+           inp->inp_nstat_refcnt == 0)
+               return;
+       VERIFY(SOCK_PROTO(inp->inp_socket) == IPPROTO_UDP);
+       lck_mtx_lock(&nstat_mtx);
+       for (state = nstat_controls; state; state = state->ncs_next) {
+               lck_mtx_lock(&state->mtx);
+               for (src = state->ncs_srcs; src; src = src->next) 
+               {
+                       tucookie = (struct nstat_tucookie *)src->cookie;
+                       if (tucookie->inp == inp)
+                       {
+                               tucookie->cached = false;
+                               break;
+                       }
+               }
+               lck_mtx_unlock(&state->mtx);
+       }
+       lck_mtx_unlock(&nstat_mtx);
+}
+
 static errno_t
 nstat_tcp_copy_descriptor(
        nstat_provider_cookie_t cookie,
 static errno_t
 nstat_tcp_copy_descriptor(
        nstat_provider_cookie_t cookie,
-       void                                    *data,
-       u_int32_t                               len)
+       void                    *data,
+       u_int32_t               len)
 {
        if (len < sizeof(nstat_tcp_descriptor))
        {
                return EINVAL;
        }
 {
        if (len < sizeof(nstat_tcp_descriptor))
        {
                return EINVAL;
        }
-       
-       nstat_tcp_descriptor    *desc = (nstat_tcp_descriptor*)data;
-       struct inpcb                    *inp = (struct inpcb*)cookie;
-       struct tcpcb                    *tp = intotcpcb(inp);
 
 
-       if (inp->inp_state == INPCB_STATE_DEAD)
+       if (nstat_tcp_gone(cookie))
                return EINVAL;
        
                return EINVAL;
        
+       nstat_tcp_descriptor    *desc = (nstat_tcp_descriptor*)data;
+       struct nstat_tucookie *tucookie =
+           (struct nstat_tucookie *)cookie;
+       struct inpcb            *inp = tucookie->inp;
+       struct tcpcb            *tp = intotcpcb(inp);
        bzero(desc, sizeof(*desc));
        
        if (inp->inp_vflag & INP_IPV6)
        bzero(desc, sizeof(*desc));
        
        if (inp->inp_vflag & INP_IPV6)
@@ -1026,6 +1341,11 @@ nstat_tcp_copy_descriptor(
        desc->txunacked = tp->snd_max - tp->snd_una;
        desc->txwindow = tp->snd_wnd;
        desc->txcwindow = tp->snd_cwnd;
        desc->txunacked = tp->snd_max - tp->snd_una;
        desc->txwindow = tp->snd_wnd;
        desc->txcwindow = tp->snd_cwnd;
+
+       if (CC_ALGO(tp)->name != NULL) {
+               strlcpy(desc->cc_algo, CC_ALGO(tp)->name,
+                   sizeof(desc->cc_algo));
+       }
        
        struct socket *so = inp->inp_socket;
        if (so)
        
        struct socket *so = inp->inp_socket;
        if (so)
@@ -1035,10 +1355,30 @@ nstat_tcp_copy_descriptor(
                desc->upid = so->last_upid;
                desc->pid = so->last_pid;
                desc->traffic_class = so->so_traffic_class;
                desc->upid = so->last_upid;
                desc->pid = so->last_pid;
                desc->traffic_class = so->so_traffic_class;
-               
+               desc->traffic_mgt_flags = so->so_traffic_mgt_flags;
                proc_name(desc->pid, desc->pname, sizeof(desc->pname));
                proc_name(desc->pid, desc->pname, sizeof(desc->pname));
-               desc->pname[sizeof(desc->pname) - 1] = 0;
-               
+               if (desc->pname == NULL || desc->pname[0] == 0)
+               {
+                       strlcpy(desc->pname, tucookie->pname,
+                           sizeof(desc->pname));
+               }
+               else
+               {
+                       desc->pname[sizeof(desc->pname) - 1] = 0;
+                       strlcpy(tucookie->pname, desc->pname,
+                           sizeof(tucookie->pname));
+               }
+               memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
+               memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
+               if (so->so_flags & SOF_DELEGATED) {
+                       desc->eupid = so->e_upid;
+                       desc->epid = so->e_pid;
+                       memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
+               } else {
+                       desc->eupid = desc->upid;
+                       desc->epid = desc->pid;
+                       memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
+               }
                desc->sndbufsize = so->so_snd.sb_hiwat;
                desc->sndbufused = so->so_snd.sb_cc;
                desc->rcvbufsize = so->so_rcv.sb_hiwat;
                desc->sndbufsize = so->so_snd.sb_hiwat;
                desc->sndbufused = so->so_snd.sb_cc;
                desc->rcvbufsize = so->so_rcv.sb_hiwat;
@@ -1082,30 +1422,44 @@ static int
 nstat_udp_gone(
        nstat_provider_cookie_t cookie)
 {
 nstat_udp_gone(
        nstat_provider_cookie_t cookie)
 {
-       struct inpcb    *inp = (struct inpcb*)cookie;
-       return (inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
+       struct nstat_tucookie *tucookie =
+           (struct nstat_tucookie *)cookie;
+       struct inpcb *inp;
+
+       return (!(inp = tucookie->inp) ||
+               inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0;
 }
 
 static errno_t
 nstat_udp_counts(
        nstat_provider_cookie_t cookie,
 }
 
 static errno_t
 nstat_udp_counts(
        nstat_provider_cookie_t cookie,
-       struct nstat_counts             *out_counts,
-       int                                             *out_gone)
+       struct nstat_counts     *out_counts,
+       int                     *out_gone)
 {
 {
-       struct inpcb    *inp = (struct inpcb*)cookie;
+       struct nstat_tucookie *tucookie =
+           (struct nstat_tucookie *)cookie;
        
        *out_gone = 0;
        
        // if the pcb is in the dead state, we should stop using it
        
        *out_gone = 0;
        
        // if the pcb is in the dead state, we should stop using it
-       if (inp->inp_state == INPCB_STATE_DEAD)
+       if (nstat_udp_gone(cookie))
        {
                *out_gone = 1;
        {
                *out_gone = 1;
+               if (!tucookie->inp)
+                       return EINVAL;
        }
        }
+       struct inpcb *inp = tucookie->inp;
        
        atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
        atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
        atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
        atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
        
        atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets);
        atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes);
        atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets);
        atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes);
+       atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes);
+       atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes);
+       atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes);
+       atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes);
+       atomic_get_64(out_counts->nstat_wired_rxbytes, &inp->inp_Wstat->rxbytes);
+       atomic_get_64(out_counts->nstat_wired_txbytes, &inp->inp_Wstat->txbytes);
        
        return 0;
 }
        
        return 0;
 }
@@ -1115,33 +1469,37 @@ nstat_udp_release(
        nstat_provider_cookie_t cookie,
        int locked)
 {
        nstat_provider_cookie_t cookie,
        int locked)
 {
-       struct inpcb *inp = (struct inpcb*)cookie;
-       in_pcb_checkstate(inp, WNT_RELEASE, locked);
+       struct nstat_tucookie *tucookie =
+           (struct nstat_tucookie *)cookie;
+
+       nstat_tucookie_release_internal(tucookie, locked);
 }
 
 static errno_t
 nstat_udp_add_watcher(
        nstat_control_state     *state)
 {
 }
 
 static errno_t
 nstat_udp_add_watcher(
        nstat_control_state     *state)
 {
+       struct inpcb *inp;
+       struct nstat_tucookie *cookie;
+
        OSIncrementAtomic(&nstat_udp_watchers);
        
        OSIncrementAtomic(&nstat_udp_watchers);
        
-       lck_rw_lock_shared(tcbinfo.mtx);
-       
-       // Add all current tcp inpcbs. Ignore those in timewait
-       struct inpcb *inp;
-       for (inp = LIST_FIRST(udbinfo.listhead); inp; inp = LIST_NEXT(inp, inp_list))
+       lck_rw_lock_shared(udbinfo.ipi_lock);
+       // Add all current UDP inpcbs.
+       LIST_FOREACH(inp, udbinfo.ipi_listhead, inp_list)
        {
        {
-               if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
+               cookie = nstat_tucookie_alloc_ref(inp);
+               if (cookie == NULL)
                        continue;
                        continue;
-               
-               if (nstat_control_source_add(0, state, &nstat_udp_provider, inp) != 0)
+               if (nstat_control_source_add(0, state, &nstat_udp_provider,
+                   cookie) != 0)
                {
                {
-                       in_pcb_checkstate(inp, WNT_RELEASE, 0);
+                       nstat_tucookie_release(cookie);
                        break;
                }
        }
        
                        break;
                }
        }
        
-       lck_rw_done(tcbinfo.mtx);
+       lck_rw_done(udbinfo.ipi_lock);
        
        return 0;
 }
        
        return 0;
 }
@@ -1157,9 +1515,12 @@ __private_extern__ void
 nstat_udp_new_pcb(
        struct inpcb    *inp)
 {
 nstat_udp_new_pcb(
        struct inpcb    *inp)
 {
+       struct nstat_tucookie *cookie;
+
        if (nstat_udp_watchers == 0)
                return;
        
        if (nstat_udp_watchers == 0)
                return;
        
+       socket_lock(inp->inp_socket, 0);
        lck_mtx_lock(&nstat_mtx);
        nstat_control_state     *state;
        for (state = nstat_controls; state; state = state->ncs_next)
        lck_mtx_lock(&nstat_mtx);
        nstat_control_state     *state;
        for (state = nstat_controls; state; state = state->ncs_next)
@@ -1168,18 +1529,20 @@ nstat_udp_new_pcb(
                {
                        // this client is watching tcp
                        // acquire a reference for it
                {
                        // this client is watching tcp
                        // acquire a reference for it
-                       if (in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING)
-                               break;
-                       
+                       cookie = nstat_tucookie_alloc_ref_locked(inp);
+                       if (cookie == NULL)
+                               continue;
                        // add the source, if that fails, release the reference
                        // add the source, if that fails, release the reference
-                       if (nstat_control_source_add(0, state, &nstat_udp_provider, inp) != 0)
+                       if (nstat_control_source_add(0, state, 
+                           &nstat_udp_provider, cookie) != 0)
                        {
                        {
-                               in_pcb_checkstate(inp, WNT_RELEASE, 0);
+                               nstat_tucookie_release_locked(cookie);
                                break;
                        }
                }
        }
        lck_mtx_unlock(&nstat_mtx);
                                break;
                        }
                }
        }
        lck_mtx_unlock(&nstat_mtx);
+       socket_unlock(inp->inp_socket, 0);
 }
 
 static errno_t
 }
 
 static errno_t
@@ -1193,31 +1556,54 @@ nstat_udp_copy_descriptor(
                return EINVAL;
        }
        
                return EINVAL;
        }
        
-       nstat_udp_descriptor    *desc = (nstat_udp_descriptor*)data;
-       struct inpcb                    *inp = (struct inpcb*)cookie;
-       
-       if (inp->inp_state == INPCB_STATE_DEAD)
+       if (nstat_udp_gone(cookie))
                return EINVAL;
 
                return EINVAL;
 
+       struct nstat_tucookie   *tucookie =
+           (struct nstat_tucookie *)cookie;
+       nstat_udp_descriptor            *desc = (nstat_udp_descriptor*)data;
+       struct inpcb                    *inp = tucookie->inp;
+
        bzero(desc, sizeof(*desc));
        
        bzero(desc, sizeof(*desc));
        
-       if (inp->inp_vflag & INP_IPV6)
-       {
-               nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
-                       &desc->local.v6, sizeof(desc->local));
-               nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
-                       &desc->remote.v6, sizeof(desc->remote));
+       if (tucookie->cached == false) {
+               if (inp->inp_vflag & INP_IPV6)
+               {
+                       nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport,
+                               &desc->local.v6, sizeof(desc->local));
+                       nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport,
+                               &desc->remote.v6, sizeof(desc->remote));
+               }
+               else if (inp->inp_vflag & INP_IPV4)
+               {
+                       nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
+                               &desc->local.v4, sizeof(desc->local));
+                       nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
+                               &desc->remote.v4, sizeof(desc->remote));
+               }
        }
        }
-       else if (inp->inp_vflag & INP_IPV4)
+       else
        {
        {
-               nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport,
-                       &desc->local.v4, sizeof(desc->local));
-               nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport,
-                       &desc->remote.v4, sizeof(desc->remote));
+               if (inp->inp_vflag & INP_IPV6)
+               {
+                       memcpy(&desc->local.v6, &tucookie->local.v6,
+                           sizeof(desc->local));
+                       memcpy(&desc->remote.v6, &tucookie->remote.v6,
+                           sizeof(desc->remote));
+               }
+               else if (inp->inp_vflag & INP_IPV4)
+               {
+                       memcpy(&desc->local.v4, &tucookie->local.v4,
+                           sizeof(desc->local));
+                       memcpy(&desc->remote.v4, &tucookie->remote.v4,
+                           sizeof(desc->remote));
+               }
        }
        
        }
        
-       desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 :
-           inp->inp_last_outifp->if_index;
+       if (inp->inp_last_outifp)
+               desc->ifindex = inp->inp_last_outifp->if_index;
+       else
+               desc->ifindex = tucookie->if_index;
                
        struct socket *so = inp->inp_socket;
        if (so)
                
        struct socket *so = inp->inp_socket;
        if (so)
@@ -1226,13 +1612,32 @@ nstat_udp_copy_descriptor(
                // they're in sync?
                desc->upid = so->last_upid;
                desc->pid = so->last_pid;
                // they're in sync?
                desc->upid = so->last_upid;
                desc->pid = so->last_pid;
-               
+               proc_name(desc->pid, desc->pname, sizeof(desc->pname));
+               if (desc->pname == NULL || desc->pname[0] == 0)
+               {
+                       strlcpy(desc->pname, tucookie->pname,
+                           sizeof(desc->pname));
+               }
+               else
+               {
+                       desc->pname[sizeof(desc->pname) - 1] = 0;
+                       strlcpy(tucookie->pname, desc->pname,
+                           sizeof(tucookie->pname));
+               }
+               memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid));
+               memcpy(desc->vuuid, so->so_vuuid, sizeof(so->so_vuuid));
+               if (so->so_flags & SOF_DELEGATED) {
+                       desc->eupid = so->e_upid;
+                       desc->epid = so->e_pid;
+                       memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid));
+               } else {
+                       desc->eupid = desc->upid;
+                       desc->epid = desc->pid;
+                       memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid));
+               }
                desc->rcvbufsize = so->so_rcv.sb_hiwat;
                desc->rcvbufused = so->so_rcv.sb_cc;
                desc->traffic_class = so->so_traffic_class;
                desc->rcvbufsize = so->so_rcv.sb_hiwat;
                desc->rcvbufused = so->so_rcv.sb_cc;
                desc->traffic_class = so->so_traffic_class;
-               
-               proc_name(desc->pid, desc->pname, sizeof(desc->pname));
-               desc->pname[sizeof(desc->pname) - 1] = 0;
        }
        
        return 0;
        }
        
        return 0;
@@ -1255,6 +1660,522 @@ nstat_init_udp_provider(void)
        nstat_providers = &nstat_udp_provider;
 }
 
        nstat_providers = &nstat_udp_provider;
 }
 
+#pragma mark -- ifnet Provider --
+
+static nstat_provider  nstat_ifnet_provider;
+
+/*
+ * We store a pointer to the ifnet and the original threshold
+ * requested by the client.
+ */
+struct nstat_ifnet_cookie
+{
+       struct ifnet    *ifp;
+       uint64_t        threshold;
+};
+
+static errno_t
+nstat_ifnet_lookup(
+       const void              *data,
+       u_int32_t               length,
+       nstat_provider_cookie_t *out_cookie)
+{
+       const nstat_ifnet_add_param *param = (nstat_ifnet_add_param *)data;
+       struct ifnet *ifp;
+       boolean_t changed = FALSE;
+       nstat_control_state *state;
+       nstat_src *src;
+       struct nstat_ifnet_cookie *cookie;
+
+       if (length < sizeof(*param) || param->threshold < 1024*1024)
+               return EINVAL;
+       if (nstat_privcheck != 0) {
+               errno_t result = priv_check_cred(kauth_cred_get(), 
+                   PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
+               if (result != 0)
+                       return result;
+       }
+       cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag);
+       if (cookie == NULL)
+               return ENOMEM;
+       bzero(cookie, sizeof(*cookie));
+
+       ifnet_head_lock_shared();
+       TAILQ_FOREACH(ifp, &ifnet_head, if_link)
+       {
+               ifnet_lock_exclusive(ifp);
+               if (ifp->if_index == param->ifindex)
+               {
+                       cookie->ifp = ifp;
+                       cookie->threshold = param->threshold;
+                       *out_cookie = cookie;
+                       if (!ifp->if_data_threshold ||
+                           ifp->if_data_threshold > param->threshold)
+                       {
+                               changed = TRUE;
+                               ifp->if_data_threshold = param->threshold;
+                       }
+                       ifnet_lock_done(ifp);
+                       ifnet_reference(ifp);
+                       break;
+               }
+               ifnet_lock_done(ifp);
+       }
+       ifnet_head_done();
+
+       /*
+        * When we change the threshold to something smaller, we notify
+        * all of our clients with a description message.
+        * We won't send a message to the client we are currently serving
+        * because it has no `ifnet source' yet.
+        */
+       if (changed)
+       {
+               lck_mtx_lock(&nstat_mtx);
+               for (state = nstat_controls; state; state = state->ncs_next)
+               {
+                       lck_mtx_lock(&state->mtx);
+                       for (src = state->ncs_srcs; src; src = src->next)
+                       {
+                               if (src->provider != &nstat_ifnet_provider)
+                                       continue;
+                               nstat_control_send_description(state, src, 0);
+                       }
+                       lck_mtx_unlock(&state->mtx);
+               }
+               lck_mtx_unlock(&nstat_mtx);
+       }
+       if (cookie->ifp == NULL)
+               OSFree(cookie, sizeof(*cookie), nstat_malloc_tag);
+
+       return ifp ? 0 : EINVAL;
+}
+
+static int
+nstat_ifnet_gone(
+       nstat_provider_cookie_t cookie)
+{
+       struct ifnet *ifp;
+       struct nstat_ifnet_cookie *ifcookie =
+           (struct nstat_ifnet_cookie *)cookie;
+
+       ifnet_head_lock_shared();
+       TAILQ_FOREACH(ifp, &ifnet_head, if_link)
+       {
+               if (ifp == ifcookie->ifp)
+                       break;
+       }
+       ifnet_head_done();
+
+       return ifp ? 0 : 1;
+}
+
+static errno_t
+nstat_ifnet_counts(
+       nstat_provider_cookie_t cookie,
+       struct nstat_counts     *out_counts,
+       int                     *out_gone)
+{
+       struct nstat_ifnet_cookie *ifcookie =
+           (struct nstat_ifnet_cookie *)cookie;
+       struct ifnet *ifp = ifcookie->ifp;
+
+       *out_gone = 0;
+       
+       // if the ifnet is gone, we should stop using it
+       if (nstat_ifnet_gone(cookie))
+       {
+               *out_gone = 1;
+               return EINVAL;
+       }
+
+       bzero(out_counts, sizeof(*out_counts));
+       out_counts->nstat_rxpackets = ifp->if_ipackets;
+       out_counts->nstat_rxbytes = ifp->if_ibytes;
+       out_counts->nstat_txpackets = ifp->if_opackets;
+       out_counts->nstat_txbytes = ifp->if_obytes;
+       out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0;
+
+       return 0;
+}
+
+static void
+nstat_ifnet_release(
+       nstat_provider_cookie_t cookie,
+       __unused int            locked)
+{
+       struct nstat_ifnet_cookie *ifcookie;
+       struct ifnet *ifp;
+       nstat_control_state *state;
+       nstat_src *src;
+       uint64_t minthreshold = UINT64_MAX;
+
+       /*
+        * Find all the clients that requested a threshold
+        * for this ifnet and re-calculate if_data_threshold.
+        */
+       lck_mtx_lock(&nstat_mtx);
+       for (state = nstat_controls; state; state = state->ncs_next)
+       {
+               lck_mtx_lock(&state->mtx);
+               for (src = state->ncs_srcs; src; src = src->next)
+               {
+                       /* Skip the provider we are about to detach. */
+                       if (src->provider != &nstat_ifnet_provider ||
+                           src->cookie == cookie)
+                               continue;
+                       ifcookie = (struct nstat_ifnet_cookie *)src->cookie;
+                       if (ifcookie->threshold < minthreshold)
+                               minthreshold = ifcookie->threshold; 
+               }
+               lck_mtx_unlock(&state->mtx);
+       }
+       lck_mtx_unlock(&nstat_mtx);
+       /*
+        * Reset if_data_threshold or disable it.
+        */
+       ifcookie = (struct nstat_ifnet_cookie *)cookie;
+       ifp = ifcookie->ifp;
+       if (ifnet_is_attached(ifp, 1)) {
+               ifnet_lock_exclusive(ifp);
+               if (minthreshold == UINT64_MAX)
+                       ifp->if_data_threshold = 0;
+               else
+                       ifp->if_data_threshold = minthreshold;
+               ifnet_lock_done(ifp);
+               ifnet_decr_iorefcnt(ifp);                               
+       }
+       ifnet_release(ifp);
+       OSFree(ifcookie, sizeof(*ifcookie), nstat_malloc_tag);
+}
+
+static errno_t
+nstat_ifnet_copy_descriptor(
+       nstat_provider_cookie_t cookie,
+       void                    *data,
+       u_int32_t               len)
+{      
+       nstat_ifnet_descriptor *desc = (nstat_ifnet_descriptor *)data;
+       struct nstat_ifnet_cookie *ifcookie =
+           (struct nstat_ifnet_cookie *)cookie;
+       struct ifnet *ifp = ifcookie->ifp;
+
+       if (len < sizeof(nstat_ifnet_descriptor))
+               return EINVAL;
+       
+       if (nstat_ifnet_gone(cookie))
+               return EINVAL;
+
+       bzero(desc, sizeof(*desc));
+       ifnet_lock_shared(ifp);
+       strlcpy(desc->name, ifp->if_xname, sizeof(desc->name));
+       desc->ifindex = ifp->if_index;
+       desc->threshold = ifp->if_data_threshold;
+       desc->type = ifp->if_type;
+       if (ifp->if_desc.ifd_len < sizeof(desc->description))
+               memcpy(desc->description, ifp->if_desc.ifd_desc,
+                   sizeof(desc->description));
+       ifnet_lock_done(ifp);
+       
+       return 0;
+}
+
+static void
+nstat_init_ifnet_provider(void)
+{
+       bzero(&nstat_ifnet_provider, sizeof(nstat_ifnet_provider));
+       nstat_ifnet_provider.nstat_provider_id = NSTAT_PROVIDER_IFNET;
+       nstat_ifnet_provider.nstat_descriptor_length = sizeof(nstat_ifnet_descriptor);
+       nstat_ifnet_provider.nstat_lookup = nstat_ifnet_lookup;
+       nstat_ifnet_provider.nstat_gone = nstat_ifnet_gone;
+       nstat_ifnet_provider.nstat_counts = nstat_ifnet_counts;
+       nstat_ifnet_provider.nstat_watcher_add = NULL;
+       nstat_ifnet_provider.nstat_watcher_remove = NULL;
+       nstat_ifnet_provider.nstat_copy_descriptor = nstat_ifnet_copy_descriptor;
+       nstat_ifnet_provider.nstat_release = nstat_ifnet_release;
+       nstat_ifnet_provider.next = nstat_providers;
+       nstat_providers = &nstat_ifnet_provider;
+}
+
+__private_extern__ void
+nstat_ifnet_threshold_reached(unsigned int ifindex)
+{
+       nstat_control_state *state;
+       nstat_src *src;
+       struct ifnet *ifp;
+       struct nstat_ifnet_cookie *ifcookie;
+
+       lck_mtx_lock(&nstat_mtx);
+       for (state = nstat_controls; state; state = state->ncs_next)
+       {
+               lck_mtx_lock(&state->mtx);
+               for (src = state->ncs_srcs; src; src = src->next)
+               {
+                       if (src->provider != &nstat_ifnet_provider)
+                               continue;
+                       ifcookie = (struct nstat_ifnet_cookie *)src->cookie;
+                       ifp = ifcookie->ifp;
+                       if (ifp->if_index != ifindex)
+                               continue;
+                       nstat_control_send_counts(state, src, 0, NULL);
+               }
+               lck_mtx_unlock(&state->mtx);
+       }
+       lck_mtx_unlock(&nstat_mtx);
+}
+
+#pragma mark -- Sysinfo Provider --
+
+static nstat_provider nstat_sysinfo_provider;
+
+/* We store the flags requested by the client */
+typedef struct nstat_sysinfo_cookie
+{
+       u_int32_t       flags;
+} nstat_sysinfo_cookie;
+
+static errno_t
+nstat_sysinfo_lookup(
+       const void              *data,
+       u_int32_t               length,
+       nstat_provider_cookie_t *out_cookie)
+{
+       const nstat_sysinfo_add_param *param = (nstat_sysinfo_add_param *)data;
+       nstat_sysinfo_cookie *cookie;
+
+       if (length < sizeof(*param))
+               return (EINVAL);
+
+       if (nstat_privcheck != 0) {
+               errno_t result = priv_check_cred(kauth_cred_get(),
+                   PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
+               if (result != 0)
+                       return (result);
+       }
+
+       cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag);
+       if (cookie == NULL)
+               return (ENOMEM);
+       cookie->flags = param->flags;
+       *out_cookie = cookie;
+       return (0);
+}
+
+static int
+nstat_sysinfo_gone(
+       __unused nstat_provider_cookie_t cookie)
+{
+       /* Sysinfo always exists */
+       return (0);
+}
+
+static errno_t
+nstat_sysinfo_copy_descriptor(
+       nstat_provider_cookie_t cookie,
+       void                    *data,
+       u_int32_t               len)
+{
+       nstat_sysinfo_descriptor *desc = (nstat_sysinfo_descriptor *)data;
+       struct nstat_sysinfo_cookie *syscookie =
+               (struct nstat_sysinfo_cookie *)cookie;
+
+       if (len < sizeof(nstat_sysinfo_descriptor))
+               return (EINVAL);
+       desc->flags = syscookie->flags;
+       return (0);
+}
+
+static void
+nstat_sysinfo_release(
+       nstat_provider_cookie_t cookie,
+       __unused boolean_t locked)
+{
+       struct nstat_sysinfo_cookie *syscookie =
+               (struct nstat_sysinfo_cookie *)cookie;
+       OSFree(syscookie, sizeof(*syscookie), nstat_malloc_tag);
+}
+
+static errno_t
+nstat_enqueue_success(
+    uint64_t context,
+    nstat_control_state        *state)
+{
+       nstat_msg_hdr success;
+       errno_t result;
+
+       bzero(&success, sizeof(success));
+       success.context = context;
+       success.type = NSTAT_MSG_TYPE_SUCCESS;
+       result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success,
+           sizeof(success), CTL_DATA_EOR | CTL_DATA_CRIT);
+       if (result != 0) {
+               printf("%s: could not enqueue success message %d\n",
+                   __func__, result);
+               nstat_successmsgfailures += 1;
+       }
+       return result;
+}
+
+static void
+nstat_init_sysinfo_provider(void)
+{
+       bzero(&nstat_sysinfo_provider, sizeof(nstat_sysinfo_provider));
+       nstat_sysinfo_provider.nstat_provider_id = NSTAT_PROVIDER_SYSINFO;
+       nstat_sysinfo_provider.nstat_descriptor_length = sizeof(nstat_sysinfo_descriptor);
+       nstat_sysinfo_provider.nstat_lookup = nstat_sysinfo_lookup;
+       nstat_sysinfo_provider.nstat_gone = nstat_sysinfo_gone;
+       nstat_sysinfo_provider.nstat_counts = NULL;
+       nstat_sysinfo_provider.nstat_watcher_add = NULL;
+       nstat_sysinfo_provider.nstat_watcher_remove = NULL;
+       nstat_sysinfo_provider.nstat_copy_descriptor = nstat_sysinfo_copy_descriptor;
+       nstat_sysinfo_provider.nstat_release = nstat_sysinfo_release;
+       nstat_sysinfo_provider.next = nstat_providers;
+       nstat_providers = &nstat_sysinfo_provider;
+}
+
+static void
+nstat_sysinfo_send_data_internal(
+       nstat_control_state *control,
+       nstat_src *src,
+       nstat_sysinfo_data *data)
+{
+       nstat_msg_sysinfo_counts *syscnt = NULL;
+       size_t allocsize = 0, countsize = 0, nkeyvals = 0;
+       nstat_sysinfo_keyval *kv;
+       errno_t result = 0;
+       
+       allocsize = offsetof(nstat_msg_sysinfo_counts, counts);
+       countsize = offsetof(nstat_sysinfo_counts, nstat_sysinfo_keyvals);
+
+       /* get number of key-vals for each kind of stat */
+       switch (data->flags)
+       {
+               case NSTAT_SYSINFO_MBUF_STATS:
+                       nkeyvals = 5;
+                       break;
+               case NSTAT_SYSINFO_TCP_STATS:
+                       nkeyvals = 6;
+                       break;
+               default:
+                       return;
+       }
+       countsize += sizeof(nstat_sysinfo_keyval) * nkeyvals;
+       allocsize += countsize;
+
+       syscnt = OSMalloc(allocsize, nstat_malloc_tag);
+       if (syscnt == NULL)
+               return;
+       bzero(syscnt, allocsize);
+
+       syscnt->hdr.type = NSTAT_MSG_TYPE_SYSINFO_COUNTS;
+       syscnt->counts.nstat_sysinfo_len = countsize;
+       syscnt->srcref = src->srcref;
+
+       kv = (nstat_sysinfo_keyval *) &syscnt->counts.nstat_sysinfo_keyvals;
+       switch (data->flags)
+       {
+               case NSTAT_SYSINFO_MBUF_STATS:
+               {
+                       kv[0].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_MBUF_256B_TOTAL;
+                       kv[0].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[0].u.nstat_sysinfo_scalar = data->u.mb_stats.total_256b;
+
+                       kv[1].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_MBUF_2KB_TOTAL;
+                       kv[1].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[1].u.nstat_sysinfo_scalar = data->u.mb_stats.total_2kb;
+
+                       kv[2].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_MBUF_4KB_TOTAL;
+                       kv[2].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[2].u.nstat_sysinfo_scalar = data->u.mb_stats.total_4kb;
+
+                       kv[3].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_SOCK_MBCNT;
+                       kv[3].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[3].u.nstat_sysinfo_scalar = data->u.mb_stats.sbmb_total;
+
+
+                       kv[4].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_SOCK_ATMBLIMIT;
+                       kv[4].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[4].u.nstat_sysinfo_scalar = data->u.mb_stats.sb_atmbuflimit;
+                       break;
+               }
+               case NSTAT_SYSINFO_TCP_STATS:
+               {
+                       kv[0].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_IPV4_AVGRTT;
+                       kv[0].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[0].u.nstat_sysinfo_scalar = data->u.tcp_stats.ipv4_avgrtt;
+
+                       kv[1].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_IPV6_AVGRTT;
+                       kv[1].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[1].u.nstat_sysinfo_scalar = data->u.tcp_stats.ipv6_avgrtt;
+
+                       kv[2].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_SEND_PLR;
+                       kv[2].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[2].u.nstat_sysinfo_scalar = data->u.tcp_stats.send_plr;
+
+                       kv[3].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_RECV_PLR;
+                       kv[3].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[3].u.nstat_sysinfo_scalar = data->u.tcp_stats.recv_plr;
+
+                       kv[4].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_SEND_TLRTO;
+                       kv[4].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[4].u.nstat_sysinfo_scalar = data->u.tcp_stats.send_tlrto_rate;
+
+                       kv[5].nstat_sysinfo_key = NSTAT_SYSINFO_KEY_SEND_REORDERRATE;
+                       kv[5].nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+                       kv[5].u.nstat_sysinfo_scalar = data->u.tcp_stats.send_reorder_rate;
+                       break;
+               }
+       }
+       
+       if (syscnt != NULL)
+       {
+               result = ctl_enqueuedata(control->ncs_kctl,
+                   control->ncs_unit, syscnt, allocsize, CTL_DATA_EOR);
+               if (result != 0)
+                       nstat_sysinfofailures += 1;
+               OSFree(syscnt, allocsize, nstat_malloc_tag);
+       }
+       return;
+}
+
+__private_extern__ void
+nstat_sysinfo_send_data(
+       nstat_sysinfo_data *data)
+{
+       nstat_control_state *control;
+
+       lck_mtx_lock(&nstat_mtx);
+       for (control = nstat_controls; control; control = control->ncs_next)
+       {
+               lck_mtx_lock(&control->mtx);
+               nstat_src       *src;
+               for (src = control->ncs_srcs; src; src = src->next)
+               {
+                       if (src->provider->nstat_provider_id == 
+                               NSTAT_PROVIDER_SYSINFO)
+                       {
+                               struct nstat_sysinfo_cookie *syscookie;
+                               syscookie = (struct nstat_sysinfo_cookie *) src->cookie;
+                               if (syscookie->flags & data->flags)
+                               {
+                                       nstat_sysinfo_send_data_internal(control,
+                                               src, data);
+                               }
+                       }
+               }       
+               lck_mtx_unlock(&control->mtx);
+       }
+       lck_mtx_unlock(&nstat_mtx);
+
+}
+
+static void
+nstat_sysinfo_generate_report(void)
+{
+       mbuf_report_peak_usage();
+       tcp_report_stats();
+}
+
 #pragma mark -- Kernel Control Socket --
 
 static kern_ctl_ref    nstat_ctlref = NULL;
 #pragma mark -- Kernel Control Socket --
 
 static kern_ctl_ref    nstat_ctlref = NULL;
@@ -1288,19 +2209,30 @@ nstat_idle_check(
                        {
                                if ((*srcpp)->provider->nstat_gone((*srcpp)->cookie))
                                {
                        {
                                if ((*srcpp)->provider->nstat_gone((*srcpp)->cookie))
                                {
+                                       errno_t result;
+                                       
                                        // Pull it off the list
                                        dead = *srcpp;
                                        *srcpp = (*srcpp)->next;
                                        
                                        // send one last counts notification
                                        // Pull it off the list
                                        dead = *srcpp;
                                        *srcpp = (*srcpp)->next;
                                        
                                        // send one last counts notification
-                                       nstat_control_send_counts(control, dead,
+                                       result = nstat_control_send_counts(control, dead,
                                            0, NULL);
                                            0, NULL);
+                                       if (result != 0 && nstat_debug)
+                                               printf("%s - nstat_control_send_counts() %d\n",
+                                                       __func__, result);
                                                
                                        // send a last description
                                                
                                        // send a last description
-                                       nstat_control_send_description(control, dead, 0);
+                                       result = nstat_control_send_description(control, dead, 0);
+                                       if (result != 0 && nstat_debug)
+                                               printf("%s - nstat_control_send_description() %d\n",
+                                                       __func__, result);
                                        
                                        // send the source removed notification
                                        
                                        // send the source removed notification
-                                       nstat_control_send_removed(control, dead);
+                                       result = nstat_control_send_removed(control, dead);
+                                       if (result != 0 && nstat_debug)
+                                               printf("%s - nstat_control_send_removed() %d\n",
+                                                       __func__, result);
                                        
                                        // Put this on the list to release later
                                        dead->next = dead_list;
                                        
                                        // Put this on the list to release later
                                        dead->next = dead_list;
@@ -1315,7 +2247,7 @@ nstat_idle_check(
                control->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
                lck_mtx_unlock(&control->mtx);
        }
                control->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
                lck_mtx_unlock(&control->mtx);
        }
-       
+
        if (nstat_controls)
        {
                clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
        if (nstat_controls)
        {
                clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time);
@@ -1324,6 +2256,9 @@ nstat_idle_check(
        
        lck_mtx_unlock(&nstat_mtx);
        
        
        lck_mtx_unlock(&nstat_mtx);
        
+       /* Generate any system level reports, if needed */
+       nstat_sysinfo_generate_report();
+       
        // Release the sources now that we aren't holding lots of locks
        while (dead_list)
        {
        // Release the sources now that we aren't holding lots of locks
        while (dead_list)
        {
@@ -1351,6 +2286,9 @@ nstat_control_register(void)
        struct kern_ctl_reg     nstat_control;
        bzero(&nstat_control, sizeof(nstat_control));   
        strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name));
        struct kern_ctl_reg     nstat_control;
        bzero(&nstat_control, sizeof(nstat_control));   
        strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name));
+       nstat_control.ctl_flags = CTL_FLAG_REG_EXTENDED | CTL_FLAG_REG_CRIT;
+       nstat_control.ctl_sendsize = nstat_sendspace;
+       nstat_control.ctl_recvsize = nstat_recvspace;
        nstat_control.ctl_connect = nstat_control_connect;
        nstat_control.ctl_disconnect = nstat_control_disconnect;
        nstat_control.ctl_send = nstat_control_send;
        nstat_control.ctl_connect = nstat_control_connect;
        nstat_control.ctl_disconnect = nstat_control_disconnect;
        nstat_control.ctl_send = nstat_control_send;
@@ -1364,9 +2302,14 @@ nstat_control_cleanup_source(
        struct nstat_src        *src,
        boolean_t               locked)
 {
        struct nstat_src        *src,
        boolean_t               locked)
 {
-       if (state)
-               nstat_control_send_removed(state, src);
+       errno_t result;
        
        
+       if (state) {
+               result = nstat_control_send_removed(state, src);
+               if (result != 0 && nstat_debug)
+                       printf("%s - nstat_control_send_removed() %d\n",
+                               __func__, result);
+       }
        // Cleanup the source if we found it.
        src->provider->nstat_release(src->cookie, locked);
        OSFree(src, sizeof(*src), nstat_malloc_tag);
        // Cleanup the source if we found it.
        src->provider->nstat_release(src->cookie, locked);
        OSFree(src, sizeof(*src), nstat_malloc_tag);
@@ -1407,7 +2350,7 @@ static errno_t
 nstat_control_disconnect(
        __unused kern_ctl_ref   kctl,
        __unused u_int32_t              unit,
 nstat_control_disconnect(
        __unused kern_ctl_ref   kctl,
        __unused u_int32_t              unit,
-       __unused void                   *uinfo)
+       void                    *uinfo)
 {
        u_int32_t       watching;
        nstat_control_state     *state = (nstat_control_state*)uinfo;
 {
        u_int32_t       watching;
        nstat_control_state     *state = (nstat_control_state*)uinfo;
@@ -1458,7 +2401,7 @@ nstat_control_disconnect(
                // clean it up
                nstat_control_cleanup_source(NULL, src, FALSE);
        }
                // clean it up
                nstat_control_cleanup_source(NULL, src, FALSE);
        }
-       
+       lck_mtx_destroy(&state->mtx, nstat_lck_grp);
        OSFree(state, sizeof(*state), nstat_malloc_tag);
        
        return 0;
        OSFree(state, sizeof(*state), nstat_malloc_tag);
        
        return 0;
@@ -1504,14 +2447,28 @@ nstat_control_send_counts(
        int localgone = 0;
        errno_t result = 0;
 
        int localgone = 0;
        errno_t result = 0;
 
+       /* Some providers may not have any counts to send */
+       if (src->provider->nstat_counts == NULL)
+               return (0);
+
+       bzero(&counts, sizeof(counts));
        counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
        counts.hdr.context = context;
        counts.srcref = src->srcref;
        counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS;
        counts.hdr.context = context;
        counts.srcref = src->srcref;
-       bzero(&counts.counts, sizeof(counts.counts));
+       
        if (src->provider->nstat_counts(src->cookie, &counts.counts,
            &localgone) == 0) {
        if (src->provider->nstat_counts(src->cookie, &counts.counts,
            &localgone) == 0) {
-               result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &counts,
-                   sizeof(counts), CTL_DATA_EOR);
+               if ((src->filter & NSTAT_FILTER_NOZEROBYTES) &&
+                   counts.counts.nstat_rxbytes == 0 && 
+                   counts.counts.nstat_txbytes == 0) {
+                       result = EAGAIN;
+               } else {
+                       result = ctl_enqueuedata(state->ncs_kctl,
+                           state->ncs_unit, &counts, sizeof(counts),
+                           CTL_DATA_EOR);
+                       if (result != 0)
+                               nstat_srccountfailures += 1;
+               }
        }
        if (gone)
                *gone = localgone;
        }
        if (gone)
                *gone = localgone;
@@ -1530,40 +2487,42 @@ nstat_control_send_description(
        {
                return EOPNOTSUPP;
        }
        {
                return EOPNOTSUPP;
        }
-       
+
        // Allocate storage for the descriptor message
        mbuf_t                  msg;
        unsigned int    one = 1;
        u_int32_t               size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
        // Allocate storage for the descriptor message
        mbuf_t                  msg;
        unsigned int    one = 1;
        u_int32_t               size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length;
-       if (mbuf_allocpacket(MBUF_WAITOK, size, &one, &msg) != 0)
+       if (mbuf_allocpacket(MBUF_DONTWAIT, size, &one, &msg) != 0)
        {
                return ENOMEM;
        }
        {
                return ENOMEM;
        }
-       
+
        nstat_msg_src_description       *desc = (nstat_msg_src_description*)mbuf_data(msg);
        nstat_msg_src_description       *desc = (nstat_msg_src_description*)mbuf_data(msg);
+       bzero(desc, size);
        mbuf_setlen(msg, size);
        mbuf_pkthdr_setlen(msg, mbuf_len(msg));
        mbuf_setlen(msg, size);
        mbuf_pkthdr_setlen(msg, mbuf_len(msg));
-       
+
        // Query the provider for the provider specific bits
        errno_t result = src->provider->nstat_copy_descriptor(src->cookie, desc->data, src->provider->nstat_descriptor_length);
        // Query the provider for the provider specific bits
        errno_t result = src->provider->nstat_copy_descriptor(src->cookie, desc->data, src->provider->nstat_descriptor_length);
-       
+
        if (result != 0)
        {
                mbuf_freem(msg);
                return result;
        }
        if (result != 0)
        {
                mbuf_freem(msg);
                return result;
        }
-       
+
        desc->hdr.context = context;
        desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
        desc->srcref = src->srcref;
        desc->provider = src->provider->nstat_provider_id;
        desc->hdr.context = context;
        desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC;
        desc->srcref = src->srcref;
        desc->provider = src->provider->nstat_provider_id;
-       
+
        result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
        if (result != 0)
        {
        result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
        if (result != 0)
        {
+               nstat_descriptionfailures += 1;
                mbuf_freem(msg);
        }
                mbuf_freem(msg);
        }
-       
+
        return result;
 }
 
        return result;
 }
 
@@ -1575,11 +2534,14 @@ nstat_control_send_removed(
        nstat_msg_src_removed removed;
        errno_t result;
 
        nstat_msg_src_removed removed;
        errno_t result;
 
+       bzero(&removed, sizeof(removed));
        removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED;
        removed.hdr.context = 0;
        removed.srcref = src->srcref;
        result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &removed,
        removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED;
        removed.hdr.context = 0;
        removed.srcref = src->srcref;
        result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &removed,
-           sizeof(removed), CTL_DATA_EOR);
+           sizeof(removed), CTL_DATA_EOR | CTL_DATA_CRIT);
+       if (result != 0)
+               nstat_msgremovedfailures += 1;
 
        return result;
 }
 
        return result;
 }
@@ -1590,7 +2552,7 @@ nstat_control_handle_add_request(
        mbuf_t                          m)
 {
        errno_t result;
        mbuf_t                          m)
 {
        errno_t result;
-       
+
        // Verify the header fits in the first mbuf
        if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param))
        {
        // Verify the header fits in the first mbuf
        if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param))
        {
@@ -1654,6 +2616,13 @@ nstat_control_handle_add_all(
        if (!provider) return ENOENT;
        if (provider->nstat_watcher_add == NULL) return ENOTSUP;
        
        if (!provider) return ENOENT;
        if (provider->nstat_watcher_add == NULL) return ENOTSUP;
        
+       if (nstat_privcheck != 0) {
+               result = priv_check_cred(kauth_cred_get(), 
+                   PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0);
+               if (result != 0)
+                       return result;
+       }
+
        // Make sure we don't add the provider twice
        lck_mtx_lock(&state->mtx);
        if ((state->ncs_watching & (1 << provider->nstat_provider_id)) != 0)
        // Make sure we don't add the provider twice
        lck_mtx_lock(&state->mtx);
        if ((state->ncs_watching & (1 << provider->nstat_provider_id)) != 0)
@@ -1661,7 +2630,7 @@ nstat_control_handle_add_all(
        state->ncs_watching |= (1 << provider->nstat_provider_id);
        lck_mtx_unlock(&state->mtx);
        if (result != 0) return result;
        state->ncs_watching |= (1 << provider->nstat_provider_id);
        lck_mtx_unlock(&state->mtx);
        if (result != 0) return result;
-       
+
        result = provider->nstat_watcher_add(state);
        if (result != 0)
        {
        result = provider->nstat_watcher_add(state);
        if (result != 0)
        {
@@ -1669,16 +2638,8 @@ nstat_control_handle_add_all(
                state->ncs_watching &= ~(1 << provider->nstat_provider_id);
                lck_mtx_unlock(&state->mtx);
        }
                state->ncs_watching &= ~(1 << provider->nstat_provider_id);
                lck_mtx_unlock(&state->mtx);
        }
-       
        if (result == 0)
        if (result == 0)
-       {
-               // Notify the client
-               nstat_msg_hdr   success;
-               success.context = req->hdr.context;
-               success.type = NSTAT_MSG_TYPE_SUCCESS;
-               success.pad = 0;
-               ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR);
-       }
+               nstat_enqueue_success(req->hdr.context, state);
        
        return result;
 }
        
        return result;
 }
@@ -1694,7 +2655,8 @@ nstat_control_source_add(
        mbuf_t                                  msg = NULL;
        unsigned int                    one = 1;
        
        mbuf_t                                  msg = NULL;
        unsigned int                    one = 1;
        
-       if (mbuf_allocpacket(MBUF_WAITOK, sizeof(nstat_msg_src_added), &one, &msg) != 0)
+       if (mbuf_allocpacket(MBUF_DONTWAIT, sizeof(nstat_msg_src_added), &one,
+           &msg) != 0)
                return ENOMEM;
        
        mbuf_setlen(msg, sizeof(nstat_msg_src_added));
                return ENOMEM;
        
        mbuf_setlen(msg, sizeof(nstat_msg_src_added));
@@ -1726,11 +2688,14 @@ nstat_control_source_add(
        }
        src->provider = provider;
        src->cookie = cookie;
        }
        src->provider = provider;
        src->cookie = cookie;
+       src->filter = 0;
        
        // send the source added message
        
        // send the source added message
-       errno_t result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR);
+       errno_t result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg,
+                                       CTL_DATA_EOR);
        if (result != 0)
        {
        if (result != 0)
        {
+               nstat_srcaddedfailures += 1;
                lck_mtx_unlock(&state->mtx);
                OSFree(src, sizeof(*src), nstat_malloc_tag);
                mbuf_freem(msg);
                lck_mtx_unlock(&state->mtx);
                OSFree(src, sizeof(*src), nstat_malloc_tag);
                mbuf_freem(msg);
@@ -1800,6 +2765,7 @@ nstat_control_handle_query_request(
        nstat_src                               *dead_srcs = NULL;
        errno_t                                 result = ENOENT;
        nstat_msg_query_src_req req;
        nstat_src                               *dead_srcs = NULL;
        errno_t                                 result = ENOENT;
        nstat_msg_query_src_req req;
+
        if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
        {
                return EINVAL;
        if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
        {
                return EINVAL;
@@ -1812,11 +2778,14 @@ nstat_control_handle_query_request(
        while (*srcpp != NULL)
        {
                int     gone;
        while (*srcpp != NULL)
        {
                int     gone;
+
                gone = 0;
                gone = 0;
-               
+               // XXX ignore IFACE types?
                if (req.srcref == NSTAT_SRC_REF_ALL ||
                if (req.srcref == NSTAT_SRC_REF_ALL ||
-                       (*srcpp)->srcref == req.srcref)
+                   (*srcpp)->srcref == req.srcref)
                {
                {
+                       gone = 0;
+
                        result = nstat_control_send_counts(state, *srcpp,
                            req.hdr.context, &gone);
                        
                        result = nstat_control_send_counts(state, *srcpp,
                            req.hdr.context, &gone);
                        
@@ -1828,10 +2797,17 @@ nstat_control_handle_query_request(
                        if (gone)
                        {
                                // send one last descriptor message so client may see last state
                        if (gone)
                        {
                                // send one last descriptor message so client may see last state
+                               // If we can't send the notification now, it
+                               // will be sent in the idle cleanup.
+                               result = nstat_control_send_description(state, *srcpp, 0);
+                               if (result != 0 && nstat_debug)
+                                       printf("%s - nstat_control_send_description() %d\n",
+                                               __func__, result);
+                               if (result != 0) {
+                                       state->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS;
+                                       break;
+                               }       
 
 
-                               nstat_control_send_description(state, *srcpp,
-                                   0);
-                               
                                // pull src out of the list
                                nstat_src       *src = *srcpp;
                                *srcpp = src->next;
                                // pull src out of the list
                                nstat_src       *src = *srcpp;
                                *srcpp = src->next;
@@ -1849,6 +2825,12 @@ nstat_control_handle_query_request(
        }
        lck_mtx_unlock(&state->mtx);
        
        }
        lck_mtx_unlock(&state->mtx);
        
+       if (req.srcref == NSTAT_SRC_REF_ALL)
+       {
+               nstat_enqueue_success(req.hdr.context, state);
+               result = 0;
+       }
+       
        while (dead_srcs)
        {
                nstat_src       *src;
        while (dead_srcs)
        {
                nstat_src       *src;
@@ -1860,57 +2842,83 @@ nstat_control_handle_query_request(
                nstat_control_cleanup_source(state, src, FALSE);
        }
        
                nstat_control_cleanup_source(state, src, FALSE);
        }
        
-       if (req.srcref == NSTAT_SRC_REF_ALL)
-       {
-               nstat_msg_hdr   success;
-               success.context = req.hdr.context;
-               success.type = NSTAT_MSG_TYPE_SUCCESS;
-               success.pad = 0;
-               ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR);
-               result = 0;
-       }
-       
        return result;
 }
 
 static errno_t
 nstat_control_handle_get_src_description(
        nstat_control_state     *state,
        return result;
 }
 
 static errno_t
 nstat_control_handle_get_src_description(
        nstat_control_state     *state,
-       mbuf_t                          m)
+       mbuf_t                  m)
 {
        nstat_msg_get_src_description   req;
 {
        nstat_msg_get_src_description   req;
+       errno_t result = 0;
+       nstat_src *src;
+
        if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
        {
                return EINVAL;
        }
        
        if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
        {
                return EINVAL;
        }
        
-       // Find the source
        lck_mtx_lock(&state->mtx);
        lck_mtx_lock(&state->mtx);
-       nstat_src       *src;
+       if (req.srcref == NSTAT_SRC_REF_ALL)
+               state->ncs_flags |= NSTAT_FLAG_REQDESCS;
        for (src = state->ncs_srcs; src; src = src->next)
        for (src = state->ncs_srcs; src; src = src->next)
+               if (req.srcref == NSTAT_SRC_REF_ALL ||
+                   src->srcref == req.srcref)
+               {
+                       result = nstat_control_send_description(state, src,
+                           req.hdr.context);
+                       if (result != 0)
+                               state->ncs_flags &= ~NSTAT_FLAG_REQDESCS;
+                       if (req.srcref != NSTAT_SRC_REF_ALL)
+                               break;
+               }
+       lck_mtx_unlock(&state->mtx);
+       if (req.srcref != NSTAT_SRC_REF_ALL && src == NULL)
+               result = ENOENT;
+       else if (req.srcref == NSTAT_SRC_REF_ALL)
        {
        {
-               if (src->srcref == req.srcref)
-                       break;
-       }
-       
-       // No source? Done.
-       if (!src)
-       {
-               lck_mtx_unlock(&state->mtx);
-               return ENOENT;
+               nstat_enqueue_success(req.hdr.context, state);
+               result = 0;
        }
        
        }
        
-       errno_t result = nstat_control_send_description(state, src, req.hdr.context);
-       lck_mtx_unlock(&state->mtx);
-       
        return result;
 }
 
        return result;
 }
 
+static errno_t
+nstat_control_handle_set_filter(
+    nstat_control_state                *state,
+    mbuf_t                     m)
+{
+       nstat_msg_set_filter req;
+       nstat_src *src;
+
+       if (mbuf_copydata(m, 0, sizeof(req), &req) != 0)
+               return EINVAL;
+       if (req.srcref == NSTAT_SRC_REF_ALL ||
+           req.srcref == NSTAT_SRC_REF_INVALID)
+               return EINVAL;
+
+       lck_mtx_lock(&state->mtx);
+       for (src = state->ncs_srcs; src; src = src->next)
+               if (req.srcref == src->srcref)
+               {
+                       src->filter = req.filter;
+                       break;
+               }
+       lck_mtx_unlock(&state->mtx);
+       if (src == NULL)
+               return ENOENT;
+
+       return 0;
+
+}
+
 static errno_t
 nstat_control_send(
        kern_ctl_ref    kctl,
        u_int32_t               unit,
 static errno_t
 nstat_control_send(
        kern_ctl_ref    kctl,
        u_int32_t               unit,
-       __unused void   *uinfo,
+       void    *uinfo,
        mbuf_t                  m,
        __unused int    flags)
 {
        mbuf_t                  m,
        __unused int    flags)
 {
@@ -1957,7 +2965,11 @@ nstat_control_send(
                case NSTAT_MSG_TYPE_GET_SRC_DESC:
                        result = nstat_control_handle_get_src_description(state, m);
                        break;
                case NSTAT_MSG_TYPE_GET_SRC_DESC:
                        result = nstat_control_handle_get_src_description(state, m);
                        break;
-               
+
+               case NSTAT_MSG_TYPE_SET_FILTER:
+                       result = nstat_control_handle_set_filter(state, m);
+                       break;
+
                default:
                        result = EINVAL;
                        break;
                default:
                        result = EINVAL;
                        break;
@@ -1967,11 +2979,15 @@ nstat_control_send(
        {
                struct nstat_msg_error  err;
                
        {
                struct nstat_msg_error  err;
                
+               bzero(&err, sizeof(err));
                err.hdr.type = NSTAT_MSG_TYPE_ERROR;
                err.hdr.context = hdr->context;
                err.error = result;
                
                err.hdr.type = NSTAT_MSG_TYPE_ERROR;
                err.hdr.context = hdr->context;
                err.error = result;
                
-               result = ctl_enqueuedata(kctl, unit, &err, sizeof(err), CTL_DATA_EOR);
+               result = ctl_enqueuedata(kctl, unit, &err, sizeof(err),
+                                       CTL_DATA_EOR | CTL_DATA_CRIT);
+               if (result != 0)
+                       nstat_descriptionfailures += 1;
        }
        
        mbuf_freem(m);
        }
        
        mbuf_freem(m);