This is xnu-12377.1.9. See this file in:
/*
* Copyright (c) 2025 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#define __KPI__
#include <stdint.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/kauth.h>
#include <sys/mcache.h>
#include <kern/kalloc.h>
#include <kern/locks.h>
#include <kern/lock_group.h>
#include <os/log.h>
#include <net/aop/kpi_aop.h>
#include <net/aop/aop_stats.h>
#include <libkern/libkern.h>
#include <netinet/tcp_cc.h>
#include <IOKit/IOBSD.h>
static LCK_GRP_DECLARE(kaop_lock_group, "net_aop");
static LCK_ATTR_DECLARE(kaop_lock_attr, 0, 0);
static LCK_MTX_DECLARE_ATTR(kaop_lock, &kaop_lock_group, &kaop_lock_attr);
#define KAOP_LOCK() \
lck_mtx_lock(&kaop_lock)
#define KAOP_LOCK_ASSERT_HELD() \
LCK_MTX_ASSERT(&kaop_lock, LCK_MTX_ASSERT_OWNED)
#define KAOP_LOCK_ASSERT_NOTHELD() \
LCK_MTX_ASSERT(&kaop_lock, LCK_MTX_ASSERT_NOTOWNED)
#define KAOP_UNLOCK() \
lck_mtx_unlock(&kaop_lock)
os_log_t kaop_log_handle = NULL;
#define _KAOPLOG(level, type, fmt, ...) do { \
os_log_with_type(kaop_log_handle, type, "%s - " fmt, __func__, ##__VA_ARGS__); \
} while(0)
#define KAOPLOG(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_DEFAULT, fmt, ##__VA_ARGS__)
#define KAOPLOG_DEBUG(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_DEBUG, fmt, ##__VA_ARGS__)
#define KAOPLOG_INFO(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_INFO, fmt, ##__VA_ARGS__)
#define KAOPLOG_ERR(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_ERROR, fmt, ##__VA_ARGS__)
os_refgrp_decl(static, kaop_refgrp, "kaop_ref_group", NULL);
#define KAOP_DRIVER_STATS (((uint32_t)1) << 1)
#define KAOP_PROC_ACTIVITY_BITMAPS (((uint32_t)1) << 2)
#define KAOP_IP_STATS (((uint32_t)1) << 24)
#define KAOP_IP6_STATS (((uint32_t)1) << 25)
#define KAOP_TCP_STATS (((uint32_t)1) << 26)
#define KAOP_UDP_STATS (((uint32_t)1) << 27)
#define KAOP_PROTOCOL_STATS (KAOP_IP_STATS | KAOP_IP6_STATS \
| KAOP_TCP_STATS | KAOP_UDP_STATS)
/*
* sysctl interfaces
*/
static int net_aop_stats_get_sysctl SYSCTL_HANDLER_ARGS;
SYSCTL_NODE(_net, OID_AUTO, aop, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "AOP");
SYSCTL_PROC(_net_aop, OID_AUTO, driver_stats,
CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, KAOP_DRIVER_STATS,
net_aop_stats_get_sysctl, "S,aop_driver_stats",
"AOP driver statistics counter");
SYSCTL_PROC(_net_aop, OID_AUTO, protocol_stats,
CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, KAOP_PROTOCOL_STATS,
net_aop_stats_get_sysctl, "S,net_aop_protocol_stats",
"AOP protocol statistics counter");
SYSCTL_PROC(_net_aop, OID_AUTO, proc_activity_bitmaps,
CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, KAOP_PROC_ACTIVITY_BITMAPS,
net_aop_stats_get_sysctl, "S,aop_proc_activity_bitmap",
"AOP process activity bitmaps");
#define KAOP_CAPAB_FLOW_SETUP 0x00000001
#define KAOP_CAPAB_FLOW_STATS 0x00000002
#define KAOP_CAPAB_STATS 0x00000004
#define KAOP_CAPAB_PROC_ACTIVITY_BITMAPS 0x00000008
struct net_aop_flow_setup {
net_aop_flow_setup_fn_t fsp_flow_setup;
void *fsp_prov_ctx;
};
struct net_aop_flow_stats {
net_aop_flow_stats_fn_t fs_flow_stats;
void *fs_prov_ctx;
};
struct net_aop_stats {
net_aop_stats_fn_t gs_stats;
void *gs_prov_ctx;
};
struct net_aop_proc_activity_bitmaps {
net_aop_proc_activity_bitmap_fn_t pab_activity_bitmap;
void *pab_prov_ctx;
};
#define KAOP_FLAG_ATTACHED 0x00000001
struct net_aop_provider_handle {
struct net_aop_provider_init kaop_ext;
void *kaop_prov_ctx;
struct net_aop_flow_setup kaop_fsp;
struct net_aop_flow_stats kaop_fs;
struct net_aop_stats kaop_gs;
struct net_aop_proc_activity_bitmaps kaop_pb;
uint32_t kaop_capabilities;
uint32_t kaop_flags;
os_refcnt_t kaop_refcnt;
};
static struct net_aop_provider_handle g_aop_net_provider;
static errno_t
net_aop_validate_init_params(
const struct net_aop_provider_init *init, const uint32_t init_len)
{
errno_t err = 0;
static_assert(__builtin_offsetof(struct net_aop_provider_init, kaopi_version) == 0);
static_assert(sizeof(init->kaopi_version) == sizeof(uint32_t));
if (init == NULL) {
KAOPLOG_ERR("init is null");
return EINVAL;
}
if (init_len < sizeof(uint32_t)) {
KAOPLOG_ERR("init_len[%u] < sizeof(uint32_t)", init_len);
return EINVAL;
}
switch (init->kaopi_version) {
case NET_AOP_VERSION_1:
if (init_len != sizeof(struct net_aop_provider_init)) {
KAOPLOG_ERR("init_len[%u] != sizeof(struct net_aop_provider_init", init_len);
err = EINVAL;
break;
}
if (init->kaopi_config_capab == NULL) {
KAOPLOG_ERR("kaopi_config_capab is null");
err = EINVAL;
break;
}
break;
default:
KAOPLOG_ERR("invalid version[%u]", init->kaopi_version);
err = EINVAL;
break;
}
return err;
}
static int
configure_capab_flow_setup(net_aop_provider_handle_t prov,
net_aop_config_fn_t capab_fn)
{
struct net_aop_capab_flow_setup capab;
uint32_t capab_len;
int error;
bzero(&capab, sizeof(capab));
capab.kaopcfsp_version = NET_AOP_CAPAB_FLOW_SETUP_VERSION_1;
capab_len = sizeof(capab);
error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_FLOW_SETUP,
&capab, &capab_len);
if (error != 0) {
KAOPLOG_ERR("Failed to get flow setup capability for provider");
return error;
}
VERIFY(capab.kaopcfsp_config != NULL);
VERIFY(capab.kaopcfsp_prov_ctx != NULL);
prov->kaop_fsp.fsp_prov_ctx = capab.kaopcfsp_prov_ctx;
prov->kaop_fsp.fsp_flow_setup = capab.kaopcfsp_config;
prov->kaop_capabilities |= KAOP_CAPAB_FLOW_SETUP;
return 0;
}
static void
unconfigure_capab_flow_setup(net_aop_provider_handle_t prov)
{
if ((prov->kaop_capabilities & KAOP_CAPAB_FLOW_SETUP) == 0) {
return;
}
bzero(&prov->kaop_fsp, sizeof(prov->kaop_fsp));
prov->kaop_capabilities &= ~KAOP_CAPAB_FLOW_SETUP;
}
static int
configure_capab_flow_stats(net_aop_provider_handle_t prov,
net_aop_config_fn_t capab_fn)
{
struct net_aop_capab_flow_stats capab;
uint32_t capab_len;
int error;
bzero(&capab, sizeof(capab));
capab.kaopcfs_version = NET_AOP_CAPAB_FLOW_STATS_VERSION_1;
capab_len = sizeof(capab);
error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_FLOW_STATS,
&capab, &capab_len);
if (error != 0) {
KAOPLOG_ERR("Failed to get flow stats capability for KAOP provider");
return error;
}
VERIFY(capab.kaopcfs_config != NULL);
VERIFY(capab.kaopcfs_prov_ctx != NULL);
prov->kaop_fs.fs_prov_ctx = capab.kaopcfs_prov_ctx;
prov->kaop_fs.fs_flow_stats = capab.kaopcfs_config;
prov->kaop_capabilities |= KAOP_CAPAB_FLOW_STATS;
return 0;
}
static void
unconfigure_capab_flow_stats(net_aop_provider_handle_t prov)
{
if ((prov->kaop_capabilities & KAOP_CAPAB_FLOW_STATS) == 0) {
return;
}
bzero(&prov->kaop_fs, sizeof(prov->kaop_fs));
prov->kaop_capabilities &= ~KAOP_CAPAB_FLOW_STATS;
}
static int
configure_capab_stats(net_aop_provider_handle_t prov,
net_aop_config_fn_t capab_fn)
{
struct net_aop_capab_stats capab;
uint32_t capab_len;
int error;
bzero(&capab, sizeof(capab));
capab.kaopcgs_version = NET_AOP_CAPAB_STATS_VERSION_1;
capab_len = sizeof(capab);
error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_STATS,
&capab, &capab_len);
if (error != 0) {
KAOPLOG_ERR("Failed to get stats capability provider");
return error;
}
VERIFY(capab.kaopcgs_config != NULL);
VERIFY(capab.kaopcgs_prov_ctx != NULL);
prov->kaop_gs.gs_prov_ctx = capab.kaopcgs_prov_ctx;
prov->kaop_gs.gs_stats = capab.kaopcgs_config;
prov->kaop_capabilities |= KAOP_CAPAB_STATS;
return 0;
}
static void
unconfigure_capab_stats(net_aop_provider_handle_t prov)
{
if ((prov->kaop_capabilities & KAOP_CAPAB_STATS) == 0) {
return;
}
bzero(&prov->kaop_gs, sizeof(prov->kaop_gs));
prov->kaop_capabilities &= ~KAOP_CAPAB_STATS;
}
static int
configure_capab_process_bitmaps(net_aop_provider_handle_t prov,
net_aop_config_fn_t capab_fn)
{
struct net_aop_capab_proc_activity_bitmap capab;
uint32_t capab_len;
int error;
bzero(&capab, sizeof(capab));
capab.kaopbm_version = NET_AOP_CAPAB_PROC_ACTIVITY_BITMAP_VERSION_1;
capab_len = sizeof(capab);
error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_PROC_ACTIVITY_BITMAP,
&capab, &capab_len);
if (error != 0) {
KAOPLOG_ERR("Failed to get proc bitmap capability provider");
return error;
}
VERIFY(capab.kaopbm_config != NULL);
VERIFY(capab.kaopbm_prov_ctx != NULL);
prov->kaop_pb.pab_prov_ctx = capab.kaopbm_prov_ctx;
prov->kaop_pb.pab_activity_bitmap = capab.kaopbm_config;
prov->kaop_capabilities |= KAOP_CAPAB_PROC_ACTIVITY_BITMAPS;
return 0;
}
static void
unconfigure_capab_process_bitmaps(net_aop_provider_handle_t prov)
{
if ((prov->kaop_capabilities & KAOP_CAPAB_PROC_ACTIVITY_BITMAPS) == 0) {
return;
}
bzero(&prov->kaop_pb, sizeof(prov->kaop_pb));
prov->kaop_capabilities &= ~KAOP_CAPAB_PROC_ACTIVITY_BITMAPS;
}
static int
net_aop_provider_initialize(net_aop_provider_handle_t prov)
{
net_aop_config_fn_t capab_fn = prov->kaop_ext.kaopi_config_capab;
if (capab_fn == NULL) {
KAOPLOG_ERR("kaop provider missing capability function");
return EINVAL;
}
configure_capab_flow_setup(prov, capab_fn);
configure_capab_flow_stats(prov, capab_fn);
configure_capab_stats(prov, capab_fn);
configure_capab_process_bitmaps(prov, capab_fn);
return 0;
}
static void
net_aop_provider_cleanup(net_aop_provider_handle_t prov)
{
KAOP_LOCK_ASSERT_HELD();
prov->kaop_flags &= ~KAOP_FLAG_ATTACHED;
if (os_ref_release(&prov->kaop_refcnt) != 0) {
while (os_ref_get_count(&prov->kaop_refcnt) > 0) {
msleep(&prov->kaop_refcnt,
&kaop_lock, (PZERO + 1), __FUNCTION__, NULL);
}
}
unconfigure_capab_flow_setup(prov);
unconfigure_capab_flow_stats(prov);
unconfigure_capab_stats(prov);
unconfigure_capab_process_bitmaps(prov);
memset(&prov->kaop_ext, 0, sizeof(prov->kaop_ext));
prov->kaop_prov_ctx = NULL;
}
static void
net_aop_release_refcnt(net_aop_provider_handle_t prov)
{
KAOP_LOCK();
if (os_ref_release(&prov->kaop_refcnt) == 0) {
wakeup(&prov->kaop_refcnt);
}
KAOP_UNLOCK();
}
int
net_aop_setup_flow(uint32_t flow_id, bool add, uint32_t *stats_index)
{
net_aop_flow_setup_fn_t fsp = NULL;
void *__single fsp_ctx = NULL;
int err = 0;
if (stats_index == NULL) {
KAOPLOG_ERR("invalid stats index param");
return EINVAL;
}
KAOP_LOCK();
if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_FLOW_SETUP) == 0) {
KAOP_UNLOCK();
KAOPLOG_ERR("kern aop provider does not support flow setup");
return ENOTSUP;
}
if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
!os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
KAOP_UNLOCK();
KAOPLOG_ERR("kernel aop provider is not valid");
return ENOENT;
}
fsp = g_aop_net_provider.kaop_fsp.fsp_flow_setup;
fsp_ctx = g_aop_net_provider.kaop_fsp.fsp_prov_ctx;
KAOP_UNLOCK();
err = fsp(fsp_ctx, flow_id, add, stats_index);
net_aop_release_refcnt(&g_aop_net_provider);
return err;
}
int
net_aop_get_flow_stats(uint32_t stats_index, struct aop_flow_stats *flow_stats)
{
net_aop_flow_stats_fn_t fs = NULL;
void *__single fs_ctx = NULL;
int err = 0;
if (flow_stats == NULL) {
KAOPLOG_ERR("invalid flow stats param");
return EINVAL;
}
KAOP_LOCK();
if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_FLOW_STATS) == 0) {
KAOP_UNLOCK();
KAOPLOG_ERR("kern aop provider does not support flow stats");
return ENOTSUP;
}
if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
!os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
KAOP_UNLOCK();
KAOPLOG_ERR("kernel aop provider is not valid");
return ENOENT;
}
fs = g_aop_net_provider.kaop_fs.fs_flow_stats;
fs_ctx = g_aop_net_provider.kaop_fs.fs_prov_ctx;
KAOP_UNLOCK();
err = fs(fs_ctx, stats_index, flow_stats);
net_aop_release_refcnt(&g_aop_net_provider);
return err;
}
int
net_aop_get_stats(net_aop_stats_type_t type, uint8_t *__sized_by(stats_len) stats, size_t stats_len)
{
net_aop_stats_fn_t gs = NULL;
void *__single gs_ctx = NULL;
int err = 0;
if (type == NET_AOP_STATS_TYPE_INVALID ||
type > NET_AOP_STATS_TYPE_MAX) {
KAOPLOG_ERR("invalid stats type %u", type);
return EINVAL;
}
if (stats == NULL || stats_len == 0) {
KAOPLOG_ERR("invalid stats param");
return EINVAL;
}
KAOP_LOCK();
if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_STATS) == 0) {
KAOP_UNLOCK();
KAOPLOG_ERR("kern aop provider does not support stats");
return ENOTSUP;
}
if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
!os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
KAOP_UNLOCK();
KAOPLOG_ERR("kernel aop provider is not valid");
return ENOENT;
}
gs = g_aop_net_provider.kaop_gs.gs_stats;
gs_ctx = g_aop_net_provider.kaop_gs.gs_prov_ctx;
KAOP_UNLOCK();
err = gs(gs_ctx, type, stats, stats_len);
net_aop_release_refcnt(&g_aop_net_provider);
return err;
}
int
net_aop_get_proc_activity_bitmaps(struct aop_proc_activity_bitmap *proc_activity_bitmaps,
uint16_t *inout_count)
{
net_aop_proc_activity_bitmap_fn_t pb = NULL;
void *__single pb_ctx = NULL;
int err = 0;
if (inout_count == NULL) {
KAOPLOG_ERR("invalid inout_count param");
return EINVAL;
}
KAOP_LOCK();
if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_PROC_ACTIVITY_BITMAPS) == 0) {
KAOP_UNLOCK();
KAOPLOG_ERR("kern aop provider does not support proc bitmaps");
return ENOTSUP;
}
if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
!os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
KAOP_UNLOCK();
KAOPLOG_ERR("kernel aop provider is not valid");
return ENOENT;
}
pb = g_aop_net_provider.kaop_pb.pab_activity_bitmap;
pb_ctx = g_aop_net_provider.kaop_pb.pab_prov_ctx;
KAOP_UNLOCK();
err = pb(pb_ctx, proc_activity_bitmaps, inout_count);
net_aop_release_refcnt(&g_aop_net_provider);
return err;
}
net_aop_provider_handle_t
net_aop_register_provider(const struct net_aop_provider_init *init,
const uint32_t init_len, void *ctx)
{
errno_t err = 0;
err = net_aop_validate_init_params(init, init_len);
if (err != 0) {
return NULL;
}
KAOP_LOCK();
if (g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) {
KAOP_UNLOCK();
KAOPLOG_ERR("kernel aop provider already registered");
return NULL;
}
os_ref_init(&g_aop_net_provider.kaop_refcnt, &kaop_refgrp);
memcpy(&g_aop_net_provider.kaop_ext, init, sizeof(g_aop_net_provider.kaop_ext));
g_aop_net_provider.kaop_prov_ctx = ctx;
err = net_aop_provider_initialize(&g_aop_net_provider);
if (err != 0) {
KAOP_UNLOCK();
KAOPLOG_ERR("provider type failed to initialize");
goto done;
}
g_aop_net_provider.kaop_flags |= KAOP_FLAG_ATTACHED;
KAOP_UNLOCK();
done:
KAOP_LOCK_ASSERT_NOTHELD();
if (err != 0) {
KAOP_LOCK();
net_aop_provider_cleanup(&g_aop_net_provider);
KAOP_UNLOCK();
return NULL;
}
return &g_aop_net_provider;
}
void
net_aop_deregister_provider(net_aop_provider_handle_t prov)
{
if (prov == NULL) {
return;
}
KAOP_LOCK();
ASSERT(prov->kaop_flags & KAOP_FLAG_ATTACHED);
net_aop_provider_cleanup(prov);
KAOP_UNLOCK();
return;
}
static int
net_aop_get_protocol_stats(struct net_aop_protocol_stats *aop_proto_stats)
{
int error = 0;
error = net_aop_get_stats(NET_AOP_STATS_TYPE_IP,
(uint8_t *)&aop_proto_stats->aop_ip, sizeof(aop_proto_stats->aop_ip));
if (error != 0) {
return error;
}
error = net_aop_get_stats(NET_AOP_STATS_TYPE_IPV6,
(uint8_t *)&aop_proto_stats->aop_ip6, sizeof(aop_proto_stats->aop_ip6));
if (error != 0) {
return error;
}
error = net_aop_get_stats(NET_AOP_STATS_TYPE_TCP,
(uint8_t *)&aop_proto_stats->aop_tcp, sizeof(aop_proto_stats->aop_tcp));
if (error != 0) {
return error;
}
error = net_aop_get_stats(NET_AOP_STATS_TYPE_UDP,
(uint8_t *)&aop_proto_stats->aop_udp, sizeof(aop_proto_stats->aop_udp));
if (error != 0) {
return error;
}
return error;
}
static int
net_aop_get_driver_stats(struct aop_driver_stats *driver_stats)
{
int error = 0;
error = net_aop_get_stats(NET_AOP_STATS_TYPE_DRIVER,
(uint8_t *)(struct aop_driver_stats *__bidi_indexable)driver_stats, sizeof(struct aop_driver_stats));
return error;
}
static int
aop_get_process_activity_bitmaps(struct aop_proc_activity_bitmap **bitmaps, size_t requested_buffer_space,
size_t *out_len)
{
size_t bitmap_size = 0;
struct aop_proc_activity_bitmap *__sized_by(bitmap_size) proc_activity_bitmap = NULL;
uint16_t proc_bitmap_count = 0;
int err = 0;
net_aop_get_proc_activity_bitmaps(NULL, &proc_bitmap_count);
if (proc_bitmap_count > 0) {
size_t requested_count = (requested_buffer_space / (sizeof(struct aop_proc_activity_bitmap)));
requested_count = (requested_count > proc_bitmap_count) ? proc_bitmap_count : requested_count;
size_t required_buffer_len = (requested_count * sizeof(struct aop_proc_activity_bitmap));
proc_activity_bitmap = (struct aop_proc_activity_bitmap *)kalloc_data(required_buffer_len, Z_WAITOK | Z_ZERO);
bitmap_size = required_buffer_len;
if (proc_activity_bitmap == NULL) {
return ENOBUFS;
}
err = net_aop_get_proc_activity_bitmaps(proc_activity_bitmap, (uint16_t *)&requested_count);
if (err != 0) {
kfree_data_sized_by(proc_activity_bitmap, bitmap_size);
return err;
}
*bitmaps = proc_activity_bitmap;
*out_len = required_buffer_len;
return 0;
}
return ENOENT;
}
static int
net_aop_stats_get_sysctl SYSCTL_HANDLER_ARGS
{
#pragma unused(arg1, arg2)
struct net_aop_protocol_stats proto_stats = {};
struct aop_driver_stats driver_stats = {};
struct proc *p = NULL;
task_t __single task = NULL;
size_t buffer_space;
uint8_t *out_buffer = NULL;
size_t out_size = 0;
int error = 0;
if (!kauth_cred_issuser(kauth_cred_get())) {
p = current_proc();
task = proc_task(p);
bool has_aop_stats_entitlement = IOTaskHasEntitlement(task, "com.apple.private.network.aop_stats");
if (!has_aop_stats_entitlement) {
KAOPLOG_ERR("aop stats request rejected, EPERM");
return EPERM;
}
}
buffer_space = req->oldlen;
if (req->oldptr != USER_ADDR_NULL && buffer_space != 0) {
if (oidp->oid_arg2 == KAOP_PROTOCOL_STATS) {
if (buffer_space < sizeof(proto_stats)) {
return ENOMEM;
}
error = net_aop_get_protocol_stats(&proto_stats);
out_buffer = (uint8_t *)&proto_stats;
out_size = sizeof(proto_stats);
} else if (oidp->oid_arg2 == KAOP_DRIVER_STATS) {
if (buffer_space < sizeof(driver_stats)) {
return ENOMEM;
}
error = net_aop_get_driver_stats(&driver_stats);
out_buffer = (uint8_t *)(&driver_stats);
out_size = sizeof(driver_stats);
} else if (oidp->oid_arg2 == KAOP_PROC_ACTIVITY_BITMAPS) {
struct aop_proc_activity_bitmap *__single bitmap = NULL;
error = aop_get_process_activity_bitmaps(&bitmap, buffer_space, &out_size);
out_buffer = (uint8_t *)bitmap;
}
if (error == 0) {
error = SYSCTL_OUT(req, out_buffer, out_size);
}
} else if (req->oldptr == USER_ADDR_NULL) {
if (oidp->oid_arg2 == KAOP_PROTOCOL_STATS) {
buffer_space = sizeof(proto_stats);
} else if (oidp->oid_arg2 == KAOP_DRIVER_STATS) {
buffer_space = sizeof(driver_stats);
} else if (oidp->oid_arg2 == KAOP_PROC_ACTIVITY_BITMAPS) {
uint16_t proc_bitmap_count = 0;
net_aop_get_proc_activity_bitmaps(NULL, &proc_bitmap_count);
buffer_space = (proc_bitmap_count * sizeof(struct aop_proc_activity_bitmap));
}
error = SYSCTL_OUT(req, NULL, buffer_space);
}
return error;
}
void
net_aop_init(void)
{
kaop_log_handle = os_log_create("com.apple.xnu.net.aopnet", "aopnet");
}