+ if (inp->inp_vflag & INP_IPV6)
+ {
+ memcpy(&desc->local.v6, &tucookie->local.v6,
+ sizeof(desc->local.v6));
+ memcpy(&desc->remote.v6, &tucookie->remote.v6,
+ sizeof(desc->remote.v6));
+ }
+ else if (inp->inp_vflag & INP_IPV4)
+ {
+ memcpy(&desc->local.v4, &tucookie->local.v4,
+ sizeof(desc->local.v4));
+ memcpy(&desc->remote.v4, &tucookie->remote.v4,
+ sizeof(desc->remote.v4));
+ }
+ desc->ifnet_properties = tucookie->ifnet_properties;
+ }
+
+ 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)
+ {
+ // TBD - take the socket lock around these to make sure
+ // 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[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;
+ }
+
+ return 0;
+}
+
+static void
+nstat_init_udp_provider(void)
+{
+ bzero(&nstat_udp_provider, sizeof(nstat_udp_provider));
+ nstat_udp_provider.nstat_provider_id = NSTAT_PROVIDER_UDP;
+ nstat_udp_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor);
+ nstat_udp_provider.nstat_lookup = nstat_udp_lookup;
+ nstat_udp_provider.nstat_gone = nstat_udp_gone;
+ nstat_udp_provider.nstat_counts = nstat_udp_counts;
+ nstat_udp_provider.nstat_watcher_add = nstat_udp_add_watcher;
+ nstat_udp_provider.nstat_watcher_remove = nstat_udp_remove_watcher;
+ nstat_udp_provider.nstat_copy_descriptor = nstat_udp_copy_descriptor;
+ nstat_udp_provider.nstat_release = nstat_udp_release;
+ nstat_udp_provider.nstat_reporting_allowed = nstat_tcpudp_reporting_allowed;
+ nstat_udp_provider.next = nstat_providers;
+ 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 = (const 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, 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;
+
+ if (out_gone) *out_gone = 0;
+
+ // if the ifnet is gone, we should stop using it
+ if (nstat_ifnet_gone(cookie))
+ {
+ if (out_gone) *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 void
+nstat_ifnet_copy_link_status(
+ struct ifnet *ifp,
+ struct nstat_ifnet_descriptor *desc)
+{
+ struct if_link_status *ifsr = ifp->if_link_status;
+ nstat_ifnet_desc_link_status *link_status = &desc->link_status;
+
+ link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_NONE;
+ if (ifsr == NULL)
+ return;
+
+ lck_rw_lock_shared(&ifp->if_link_status_lock);
+
+ if (ifp->if_type == IFT_CELLULAR) {
+
+ nstat_ifnet_desc_cellular_status *cell_status = &link_status->u.cellular;
+ struct if_cellular_status_v1 *if_cell_sr =
+ &ifsr->ifsr_u.ifsr_cell.if_cell_u.if_status_v1;
+
+ if (ifsr->ifsr_version != IF_CELLULAR_STATUS_REPORT_VERSION_1)
+ goto done;
+
+ link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_CELLULAR;
+
+ if (if_cell_sr->valid_bitmask & IF_CELL_LINK_QUALITY_METRIC_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_LINK_QUALITY_METRIC_VALID;
+ cell_status->link_quality_metric = if_cell_sr->link_quality_metric;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_BANDWIDTH_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_BANDWIDTH_VALID;
+ cell_status->ul_effective_bandwidth = if_cell_sr->ul_effective_bandwidth;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_BANDWIDTH_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_BANDWIDTH_VALID;
+ cell_status->ul_max_bandwidth = if_cell_sr->ul_max_bandwidth;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_LATENCY_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_LATENCY_VALID;
+ cell_status->ul_min_latency = if_cell_sr->ul_min_latency;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_EFFECTIVE_LATENCY_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_EFFECTIVE_LATENCY_VALID;
+ cell_status->ul_effective_latency = if_cell_sr->ul_effective_latency;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_LATENCY_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_LATENCY_VALID;
+ cell_status->ul_max_latency = if_cell_sr->ul_max_latency;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_RETXT_LEVEL_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
+ if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_NONE)
+ cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_NONE;
+ else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_LOW)
+ cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_LOW;
+ else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_MEDIUM)
+ cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_MEDIUM;
+ else if (if_cell_sr->ul_retxt_level == IF_CELL_UL_RETXT_LEVEL_HIGH)
+ cell_status->ul_retxt_level = NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_HIGH;
+ else
+ cell_status->valid_bitmask &= ~NSTAT_IFNET_DESC_CELL_UL_RETXT_LEVEL_VALID;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_BYTES_LOST_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_BYTES_LOST_VALID;
+ cell_status->ul_bytes_lost = if_cell_sr->ul_bytes_lost;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_MIN_QUEUE_SIZE_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MIN_QUEUE_SIZE_VALID;
+ cell_status->ul_min_queue_size = if_cell_sr->ul_min_queue_size;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_AVG_QUEUE_SIZE_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_AVG_QUEUE_SIZE_VALID;
+ cell_status->ul_avg_queue_size = if_cell_sr->ul_avg_queue_size;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_UL_MAX_QUEUE_SIZE_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_UL_MAX_QUEUE_SIZE_VALID;
+ cell_status->ul_max_queue_size = if_cell_sr->ul_max_queue_size;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_DL_EFFECTIVE_BANDWIDTH_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_EFFECTIVE_BANDWIDTH_VALID;
+ cell_status->dl_effective_bandwidth = if_cell_sr->dl_effective_bandwidth;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_DL_MAX_BANDWIDTH_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_DL_MAX_BANDWIDTH_VALID;
+ cell_status->dl_max_bandwidth = if_cell_sr->dl_max_bandwidth;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_INACTIVITY_TIME_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_INACTIVITY_TIME_VALID;
+ cell_status->config_inactivity_time = if_cell_sr->config_inactivity_time;
+ }
+ if (if_cell_sr->valid_bitmask & IF_CELL_CONFIG_BACKOFF_TIME_VALID) {
+ cell_status->valid_bitmask |= NSTAT_IFNET_DESC_CELL_CONFIG_BACKOFF_TIME_VALID;
+ cell_status->config_backoff_time = if_cell_sr->config_backoff_time;
+ }
+
+ } else if (ifp->if_subfamily == IFNET_SUBFAMILY_WIFI) {
+
+ nstat_ifnet_desc_wifi_status *wifi_status = &link_status->u.wifi;
+ struct if_wifi_status_v1 *if_wifi_sr =
+ &ifsr->ifsr_u.ifsr_wifi.if_wifi_u.if_status_v1;
+
+ if (ifsr->ifsr_version != IF_WIFI_STATUS_REPORT_VERSION_1)
+ goto done;
+
+ link_status->link_status_type = NSTAT_IFNET_DESC_LINK_STATUS_TYPE_WIFI;
+
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_LINK_QUALITY_METRIC_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_LINK_QUALITY_METRIC_VALID;
+ wifi_status->link_quality_metric = if_wifi_sr->link_quality_metric;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_BANDWIDTH_VALID;
+ wifi_status->ul_effective_bandwidth = if_wifi_sr->ul_effective_bandwidth;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_BANDWIDTH_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_BANDWIDTH_VALID;
+ wifi_status->ul_max_bandwidth = if_wifi_sr->ul_max_bandwidth;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MIN_LATENCY_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MIN_LATENCY_VALID;
+ wifi_status->ul_min_latency = if_wifi_sr->ul_min_latency;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_EFFECTIVE_LATENCY_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_EFFECTIVE_LATENCY_VALID;
+ wifi_status->ul_effective_latency = if_wifi_sr->ul_effective_latency;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_MAX_LATENCY_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_MAX_LATENCY_VALID;
+ wifi_status->ul_max_latency = if_wifi_sr->ul_max_latency;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_RETXT_LEVEL_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
+ if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_NONE)
+ wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_NONE;
+ else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_LOW)
+ wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_LOW;
+ else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_MEDIUM)
+ wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_MEDIUM;
+ else if (if_wifi_sr->ul_retxt_level == IF_WIFI_UL_RETXT_LEVEL_HIGH)
+ wifi_status->ul_retxt_level = NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_HIGH;
+ else
+ wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_UL_RETXT_LEVEL_VALID;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_BYTES_LOST_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_BYTES_LOST_VALID;
+ wifi_status->ul_bytes_lost = if_wifi_sr->ul_bytes_lost;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_UL_ERROR_RATE_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_UL_ERROR_RATE_VALID;
+ wifi_status->ul_error_rate = if_wifi_sr->ul_error_rate;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_BANDWIDTH_VALID;
+ wifi_status->dl_effective_bandwidth = if_wifi_sr->dl_effective_bandwidth;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_BANDWIDTH_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_BANDWIDTH_VALID;
+ wifi_status->dl_max_bandwidth = if_wifi_sr->dl_max_bandwidth;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MIN_LATENCY_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MIN_LATENCY_VALID;
+ wifi_status->dl_min_latency = if_wifi_sr->dl_min_latency;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_EFFECTIVE_LATENCY_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_EFFECTIVE_LATENCY_VALID;
+ wifi_status->dl_effective_latency = if_wifi_sr->dl_effective_latency;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_MAX_LATENCY_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_MAX_LATENCY_VALID;
+ wifi_status->dl_max_latency = if_wifi_sr->dl_max_latency;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_DL_ERROR_RATE_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_DL_ERROR_RATE_VALID;
+ wifi_status->dl_error_rate = if_wifi_sr->dl_error_rate;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_FREQUENCY_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
+ if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_2_4_GHZ)
+ wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_2_4_GHZ;
+ else if (if_wifi_sr->config_frequency == IF_WIFI_CONFIG_FREQUENCY_5_0_GHZ)
+ wifi_status->config_frequency = NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_5_0_GHZ;
+ else
+ wifi_status->valid_bitmask &= ~NSTAT_IFNET_DESC_WIFI_CONFIG_FREQUENCY_VALID;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_MULTICAST_RATE_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_MULTICAST_RATE_VALID;
+ wifi_status->config_multicast_rate = if_wifi_sr->config_multicast_rate;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_COUNT_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_COUNT_VALID;
+ wifi_status->scan_count = if_wifi_sr->scan_count;
+ }
+ if (if_wifi_sr->valid_bitmask & IF_WIFI_CONFIG_SCAN_DURATION_VALID) {
+ wifi_status->valid_bitmask |= NSTAT_IFNET_DESC_WIFI_CONFIG_SCAN_DURATION_VALID;
+ wifi_status->scan_duration = if_wifi_sr->scan_duration;
+ }
+ }
+
+done:
+ lck_rw_done(&ifp->if_link_status_lock);
+}
+
+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));
+ nstat_ifnet_copy_link_status(ifp, desc);
+ 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, 0, NULL);
+ }
+ lck_mtx_unlock(&state->mtx);
+ }
+ lck_mtx_unlock(&nstat_mtx);
+}
+
+#pragma mark -- Sysinfo --
+static void
+nstat_set_keyval_scalar(nstat_sysinfo_keyval *kv, int key, u_int32_t val)
+{
+ kv->nstat_sysinfo_key = key;
+ kv->nstat_sysinfo_flags = NSTAT_SYSINFO_FLAG_SCALAR;
+ kv->u.nstat_sysinfo_scalar = val;
+}
+
+static void
+nstat_sysinfo_send_data_internal(
+ nstat_control_state *control,
+ 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;
+ size_t i = 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 = sizeof(struct nstat_sysinfo_mbuf_stats) /
+ sizeof(u_int32_t);
+ break;
+ case NSTAT_SYSINFO_TCP_STATS:
+ nkeyvals = sizeof(struct nstat_sysinfo_tcp_stats) /
+ sizeof(u_int32_t);
+ 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->hdr.length = allocsize;
+ syscnt->counts.nstat_sysinfo_len = countsize;
+
+ kv = (nstat_sysinfo_keyval *) &syscnt->counts.nstat_sysinfo_keyvals;
+ switch (data->flags)
+ {
+ case NSTAT_SYSINFO_MBUF_STATS:
+ {
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_MBUF_256B_TOTAL,
+ data->u.mb_stats.total_256b);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_MBUF_2KB_TOTAL,
+ data->u.mb_stats.total_2kb);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_MBUF_4KB_TOTAL,
+ data->u.mb_stats.total_4kb);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_MBUF_16KB_TOTAL,
+ data->u.mb_stats.total_16kb);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_SOCK_MBCNT,
+ data->u.mb_stats.sbmb_total);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_SOCK_ATMBLIMIT,
+ data->u.mb_stats.sb_atmbuflimit);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_MBUF_DRAIN_CNT,
+ data->u.mb_stats.draincnt);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_MBUF_MEM_RELEASED,
+ data->u.mb_stats.memreleased);
+ VERIFY(i == nkeyvals);
+ break;
+ }
+ case NSTAT_SYSINFO_TCP_STATS:
+ {
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_IPV4_AVGRTT,
+ data->u.tcp_stats.ipv4_avgrtt);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_IPV6_AVGRTT,
+ data->u.tcp_stats.ipv6_avgrtt);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_SEND_PLR,
+ data->u.tcp_stats.send_plr);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_RECV_PLR,
+ data->u.tcp_stats.recv_plr);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_SEND_TLRTO,
+ data->u.tcp_stats.send_tlrto_rate);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_KEY_SEND_REORDERRATE,
+ data->u.tcp_stats.send_reorder_rate);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_CONNECTION_ATTEMPTS,
+ data->u.tcp_stats.connection_attempts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_CONNECTION_ACCEPTS,
+ data->u.tcp_stats.connection_accepts);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CLIENT_ENABLED,
+ data->u.tcp_stats.ecn_client_enabled);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_SERVER_ENABLED,
+ data->u.tcp_stats.ecn_server_enabled);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CLIENT_SETUP,
+ data->u.tcp_stats.ecn_client_setup);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_SERVER_SETUP,
+ data->u.tcp_stats.ecn_server_setup);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CLIENT_SUCCESS,
+ data->u.tcp_stats.ecn_client_success);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_SERVER_SUCCESS,
+ data->u.tcp_stats.ecn_server_success);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_NOT_SUPPORTED,
+ data->u.tcp_stats.ecn_not_supported);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_LOST_SYN,
+ data->u.tcp_stats.ecn_lost_syn);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_LOST_SYNACK,
+ data->u.tcp_stats.ecn_lost_synack);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_RECV_CE,
+ data->u.tcp_stats.ecn_recv_ce);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_RECV_ECE,
+ data->u.tcp_stats.ecn_recv_ece);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_SENT_ECE,
+ data->u.tcp_stats.ecn_sent_ece);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CONN_RECV_CE,
+ data->u.tcp_stats.ecn_conn_recv_ce);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CONN_RECV_ECE,
+ data->u.tcp_stats.ecn_conn_recv_ece);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CONN_PLNOCE,
+ data->u.tcp_stats.ecn_conn_plnoce);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CONN_PL_CE,
+ data->u.tcp_stats.ecn_conn_pl_ce);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_ECN_CONN_NOPL_CE,
+ data->u.tcp_stats.ecn_conn_nopl_ce);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_SYN_DATA_RCV,
+ data->u.tcp_stats.tfo_syn_data_rcv);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_COOKIE_REQ_RCV,
+ data->u.tcp_stats.tfo_cookie_req_rcv);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_COOKIE_SENT,
+ data->u.tcp_stats.tfo_cookie_sent);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_COOKIE_INVALID,
+ data->u.tcp_stats.tfo_cookie_invalid);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_COOKIE_REQ,
+ data->u.tcp_stats.tfo_cookie_req);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_COOKIE_RCV,
+ data->u.tcp_stats.tfo_cookie_rcv);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_SYN_DATA_SENT,
+ data->u.tcp_stats.tfo_syn_data_sent);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_SYN_DATA_ACKED,
+ data->u.tcp_stats.tfo_syn_data_acked);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_SYN_LOSS,
+ data->u.tcp_stats.tfo_syn_loss);
+ nstat_set_keyval_scalar(&kv[i++],
+ NSTAT_SYSINFO_TFO_BLACKHOLE,
+ data->u.tcp_stats.tfo_blackhole);
+
+ VERIFY(i == nkeyvals);
+ break;
+ }
+ }
+
+ if (syscnt != NULL)
+ {
+ result = ctl_enqueuedata(control->ncs_kctl,
+ control->ncs_unit, syscnt, allocsize, CTL_DATA_EOR);
+ if (result != 0)
+ {
+ nstat_stats.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);
+ if ((control->ncs_flags & NSTAT_FLAG_SYSINFO_SUBSCRIBED) != 0)
+ {
+ nstat_sysinfo_send_data_internal(control, 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;
+static lck_grp_t *nstat_lck_grp = NULL;
+
+static errno_t nstat_control_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo);
+static errno_t nstat_control_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo);
+static errno_t nstat_control_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags);
+
+static errno_t
+nstat_enqueue_success(
+ uint64_t context,
+ nstat_control_state *state,
+ u_int16_t flags)
+{
+ nstat_msg_hdr success;
+ errno_t result;
+
+ bzero(&success, sizeof(success));
+ success.context = context;
+ success.type = NSTAT_MSG_TYPE_SUCCESS;
+ success.length = sizeof(success);
+ success.flags = flags;
+ result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success,
+ sizeof(success), CTL_DATA_EOR | CTL_DATA_CRIT);
+ if (result != 0) {
+ if (nstat_debug != 0)
+ printf("%s: could not enqueue success message %d\n",
+ __func__, result);
+ nstat_stats.nstat_successmsgfailures += 1;
+ }
+ return result;
+}
+
+static errno_t
+nstat_control_send_goodbye(
+ nstat_control_state *state,
+ nstat_src *src)
+{
+ errno_t result = 0;
+ int failed = 0;
+
+ if (nstat_control_reporting_allowed(state, src))
+ {
+ if ((state->ncs_flags & NSTAT_FLAG_SUPPORTS_UPDATES) != 0)
+ {
+ result = nstat_control_send_update(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
+ if (result != 0)
+ {
+ failed = 1;
+ if (nstat_debug != 0)
+ printf("%s - nstat_control_send_update() %d\n", __func__, result);
+ }
+ }
+ else
+ {
+ // send one last counts notification
+ result = nstat_control_send_counts(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING, NULL);
+ if (result != 0)
+ {
+ failed = 1;
+ if (nstat_debug != 0)
+ printf("%s - nstat_control_send_counts() %d\n", __func__, result);
+ }
+
+ // send a last description
+ result = nstat_control_send_description(state, src, 0, NSTAT_MSG_HDR_FLAG_CLOSING);
+ if (result != 0)
+ {
+ failed = 1;
+ if (nstat_debug != 0)
+ printf("%s - nstat_control_send_description() %d\n", __func__, result);
+ }
+ }
+ }
+
+ // send the source removed notification
+ result = nstat_control_send_removed(state, src);
+ if (result != 0 && nstat_debug)
+ {
+ failed = 1;
+ if (nstat_debug != 0)
+ printf("%s - nstat_control_send_removed() %d\n", __func__, result);
+ }
+
+ if (failed != 0)
+ nstat_stats.nstat_control_send_goodbye_failures++;
+
+
+ return result;
+}
+
+static errno_t
+nstat_flush_accumulated_msgs(
+ nstat_control_state *state)
+{
+ errno_t result = 0;
+ if (state->ncs_accumulated && mbuf_len(state->ncs_accumulated))
+ {
+ mbuf_pkthdr_setlen(state->ncs_accumulated, mbuf_len(state->ncs_accumulated));
+ result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, state->ncs_accumulated, CTL_DATA_EOR);
+ if (result != 0 && nstat_debug)
+ {
+ nstat_stats.nstat_flush_accumulated_msgs_failures++;
+ if (nstat_debug != 0)
+ printf("%s - ctl_enqueuembuf failed: %d\n", __func__, result);
+ mbuf_freem(state->ncs_accumulated);
+ }
+ state->ncs_accumulated = NULL;
+ }
+ return result;
+}
+
+static errno_t
+nstat_accumulate_msg(
+ nstat_control_state *state,
+ nstat_msg_hdr *hdr,
+ size_t length)
+{
+ if (state->ncs_accumulated && mbuf_trailingspace(state->ncs_accumulated) < length)
+ {
+ // Will send the current mbuf
+ nstat_flush_accumulated_msgs(state);
+ }
+
+ errno_t result = 0;
+
+ if (state->ncs_accumulated == NULL)
+ {
+ unsigned int one = 1;
+ if (mbuf_allocpacket(MBUF_DONTWAIT, NSTAT_MAX_MSG_SIZE, &one, &state->ncs_accumulated) != 0)
+ {
+ if (nstat_debug != 0)
+ printf("%s - mbuf_allocpacket failed\n", __func__);
+ result = ENOMEM;
+ }
+ else
+ {
+ mbuf_setlen(state->ncs_accumulated, 0);
+ }