1 /*
2 * Copyright (c) 2025 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #define __KPI__
30 #include <stdint.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/errno.h>
34 #include <sys/syslog.h>
35 #include <sys/proc.h>
36 #include <sys/sysctl.h>
37 #include <sys/kauth.h>
38 #include <sys/mcache.h>
39 #include <kern/kalloc.h>
40 #include <kern/locks.h>
41 #include <kern/lock_group.h>
42 #include <os/log.h>
43 #include <net/aop/kpi_aop.h>
44 #include <net/aop/aop_stats.h>
45 #include <libkern/libkern.h>
46 #include <netinet/tcp_cc.h>
47 #include <IOKit/IOBSD.h>
48
49 static LCK_GRP_DECLARE(kaop_lock_group, "net_aop");
50 static LCK_ATTR_DECLARE(kaop_lock_attr, 0, 0);
51 static LCK_MTX_DECLARE_ATTR(kaop_lock, &kaop_lock_group, &kaop_lock_attr);
52
53 #define KAOP_LOCK() \
54 lck_mtx_lock(&kaop_lock)
55 #define KAOP_LOCK_ASSERT_HELD() \
56 LCK_MTX_ASSERT(&kaop_lock, LCK_MTX_ASSERT_OWNED)
57 #define KAOP_LOCK_ASSERT_NOTHELD() \
58 LCK_MTX_ASSERT(&kaop_lock, LCK_MTX_ASSERT_NOTOWNED)
59 #define KAOP_UNLOCK() \
60 lck_mtx_unlock(&kaop_lock)
61
62 os_log_t kaop_log_handle = NULL;
63
64 #define _KAOPLOG(level, type, fmt, ...) do { \
65 os_log_with_type(kaop_log_handle, type, "%s - " fmt, __func__, ##__VA_ARGS__); \
66 } while(0)
67
68 #define KAOPLOG(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_DEFAULT, fmt, ##__VA_ARGS__)
69 #define KAOPLOG_DEBUG(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_DEBUG, fmt, ##__VA_ARGS__)
70 #define KAOPLOG_INFO(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_INFO, fmt, ##__VA_ARGS__)
71 #define KAOPLOG_ERR(fmt, ...) _KAOPLOG(kaop_log_handle, OS_LOG_TYPE_ERROR, fmt, ##__VA_ARGS__)
72
73 os_refgrp_decl(static, kaop_refgrp, "kaop_ref_group", NULL);
74
75 #define KAOP_DRIVER_STATS (((uint32_t)1) << 1)
76 #define KAOP_PROC_ACTIVITY_BITMAPS (((uint32_t)1) << 2)
77
78 #define KAOP_IP_STATS (((uint32_t)1) << 24)
79 #define KAOP_IP6_STATS (((uint32_t)1) << 25)
80 #define KAOP_TCP_STATS (((uint32_t)1) << 26)
81 #define KAOP_UDP_STATS (((uint32_t)1) << 27)
82
83 #define KAOP_PROTOCOL_STATS (KAOP_IP_STATS | KAOP_IP6_STATS \
84 | KAOP_TCP_STATS | KAOP_UDP_STATS)
85
86 /*
87 * sysctl interfaces
88 */
89 static int net_aop_stats_get_sysctl SYSCTL_HANDLER_ARGS;
90
91 SYSCTL_NODE(_net, OID_AUTO, aop, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "AOP");
92 SYSCTL_PROC(_net_aop, OID_AUTO, driver_stats,
93 CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, KAOP_DRIVER_STATS,
94 net_aop_stats_get_sysctl, "S,aop_driver_stats",
95 "AOP driver statistics counter");
96 SYSCTL_PROC(_net_aop, OID_AUTO, protocol_stats,
97 CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, KAOP_PROTOCOL_STATS,
98 net_aop_stats_get_sysctl, "S,net_aop_protocol_stats",
99 "AOP protocol statistics counter");
100 SYSCTL_PROC(_net_aop, OID_AUTO, proc_activity_bitmaps,
101 CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, KAOP_PROC_ACTIVITY_BITMAPS,
102 net_aop_stats_get_sysctl, "S,aop_proc_activity_bitmap",
103 "AOP process activity bitmaps");
104
105 #define KAOP_CAPAB_FLOW_SETUP 0x00000001
106 #define KAOP_CAPAB_FLOW_STATS 0x00000002
107 #define KAOP_CAPAB_STATS 0x00000004
108 #define KAOP_CAPAB_PROC_ACTIVITY_BITMAPS 0x00000008
109
110 struct net_aop_flow_setup {
111 net_aop_flow_setup_fn_t fsp_flow_setup;
112 void *fsp_prov_ctx;
113 };
114
115 struct net_aop_flow_stats {
116 net_aop_flow_stats_fn_t fs_flow_stats;
117 void *fs_prov_ctx;
118 };
119
120 struct net_aop_stats {
121 net_aop_stats_fn_t gs_stats;
122 void *gs_prov_ctx;
123 };
124
125 struct net_aop_proc_activity_bitmaps {
126 net_aop_proc_activity_bitmap_fn_t pab_activity_bitmap;
127 void *pab_prov_ctx;
128 };
129
130 #define KAOP_FLAG_ATTACHED 0x00000001
131
132 struct net_aop_provider_handle {
133 struct net_aop_provider_init kaop_ext;
134 void *kaop_prov_ctx;
135 struct net_aop_flow_setup kaop_fsp;
136 struct net_aop_flow_stats kaop_fs;
137 struct net_aop_stats kaop_gs;
138 struct net_aop_proc_activity_bitmaps kaop_pb;
139 uint32_t kaop_capabilities;
140 uint32_t kaop_flags;
141 os_refcnt_t kaop_refcnt;
142 };
143
144 static struct net_aop_provider_handle g_aop_net_provider;
145
146 static errno_t
net_aop_validate_init_params(const struct net_aop_provider_init * init,const uint32_t init_len)147 net_aop_validate_init_params(
148 const struct net_aop_provider_init *init, const uint32_t init_len)
149 {
150 errno_t err = 0;
151
152 static_assert(__builtin_offsetof(struct net_aop_provider_init, kaopi_version) == 0);
153 static_assert(sizeof(init->kaopi_version) == sizeof(uint32_t));
154
155 if (init == NULL) {
156 KAOPLOG_ERR("init is null");
157 return EINVAL;
158 }
159
160 if (init_len < sizeof(uint32_t)) {
161 KAOPLOG_ERR("init_len[%u] < sizeof(uint32_t)", init_len);
162 return EINVAL;
163 }
164
165 switch (init->kaopi_version) {
166 case NET_AOP_VERSION_1:
167 if (init_len != sizeof(struct net_aop_provider_init)) {
168 KAOPLOG_ERR("init_len[%u] != sizeof(struct net_aop_provider_init", init_len);
169 err = EINVAL;
170 break;
171 }
172 if (init->kaopi_config_capab == NULL) {
173 KAOPLOG_ERR("kaopi_config_capab is null");
174 err = EINVAL;
175 break;
176 }
177 break;
178 default:
179 KAOPLOG_ERR("invalid version[%u]", init->kaopi_version);
180 err = EINVAL;
181 break;
182 }
183
184 return err;
185 }
186
187 static int
configure_capab_flow_setup(net_aop_provider_handle_t prov,net_aop_config_fn_t capab_fn)188 configure_capab_flow_setup(net_aop_provider_handle_t prov,
189 net_aop_config_fn_t capab_fn)
190 {
191 struct net_aop_capab_flow_setup capab;
192 uint32_t capab_len;
193 int error;
194
195 bzero(&capab, sizeof(capab));
196 capab.kaopcfsp_version = NET_AOP_CAPAB_FLOW_SETUP_VERSION_1;
197 capab_len = sizeof(capab);
198
199 error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_FLOW_SETUP,
200 &capab, &capab_len);
201 if (error != 0) {
202 KAOPLOG_ERR("Failed to get flow setup capability for provider");
203 return error;
204 }
205
206 VERIFY(capab.kaopcfsp_config != NULL);
207 VERIFY(capab.kaopcfsp_prov_ctx != NULL);
208 prov->kaop_fsp.fsp_prov_ctx = capab.kaopcfsp_prov_ctx;
209 prov->kaop_fsp.fsp_flow_setup = capab.kaopcfsp_config;
210 prov->kaop_capabilities |= KAOP_CAPAB_FLOW_SETUP;
211 return 0;
212 }
213
214 static void
unconfigure_capab_flow_setup(net_aop_provider_handle_t prov)215 unconfigure_capab_flow_setup(net_aop_provider_handle_t prov)
216 {
217 if ((prov->kaop_capabilities & KAOP_CAPAB_FLOW_SETUP) == 0) {
218 return;
219 }
220
221 bzero(&prov->kaop_fsp, sizeof(prov->kaop_fsp));
222 prov->kaop_capabilities &= ~KAOP_CAPAB_FLOW_SETUP;
223 }
224
225 static int
configure_capab_flow_stats(net_aop_provider_handle_t prov,net_aop_config_fn_t capab_fn)226 configure_capab_flow_stats(net_aop_provider_handle_t prov,
227 net_aop_config_fn_t capab_fn)
228 {
229 struct net_aop_capab_flow_stats capab;
230 uint32_t capab_len;
231 int error;
232
233 bzero(&capab, sizeof(capab));
234 capab.kaopcfs_version = NET_AOP_CAPAB_FLOW_STATS_VERSION_1;
235 capab_len = sizeof(capab);
236 error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_FLOW_STATS,
237 &capab, &capab_len);
238 if (error != 0) {
239 KAOPLOG_ERR("Failed to get flow stats capability for KAOP provider");
240 return error;
241 }
242
243 VERIFY(capab.kaopcfs_config != NULL);
244 VERIFY(capab.kaopcfs_prov_ctx != NULL);
245 prov->kaop_fs.fs_prov_ctx = capab.kaopcfs_prov_ctx;
246 prov->kaop_fs.fs_flow_stats = capab.kaopcfs_config;
247 prov->kaop_capabilities |= KAOP_CAPAB_FLOW_STATS;
248 return 0;
249 }
250
251 static void
unconfigure_capab_flow_stats(net_aop_provider_handle_t prov)252 unconfigure_capab_flow_stats(net_aop_provider_handle_t prov)
253 {
254 if ((prov->kaop_capabilities & KAOP_CAPAB_FLOW_STATS) == 0) {
255 return;
256 }
257
258 bzero(&prov->kaop_fs, sizeof(prov->kaop_fs));
259 prov->kaop_capabilities &= ~KAOP_CAPAB_FLOW_STATS;
260 }
261
262 static int
configure_capab_stats(net_aop_provider_handle_t prov,net_aop_config_fn_t capab_fn)263 configure_capab_stats(net_aop_provider_handle_t prov,
264 net_aop_config_fn_t capab_fn)
265 {
266 struct net_aop_capab_stats capab;
267 uint32_t capab_len;
268 int error;
269
270 bzero(&capab, sizeof(capab));
271 capab.kaopcgs_version = NET_AOP_CAPAB_STATS_VERSION_1;
272 capab_len = sizeof(capab);
273 error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_STATS,
274 &capab, &capab_len);
275 if (error != 0) {
276 KAOPLOG_ERR("Failed to get stats capability provider");
277 return error;
278 }
279
280 VERIFY(capab.kaopcgs_config != NULL);
281 VERIFY(capab.kaopcgs_prov_ctx != NULL);
282 prov->kaop_gs.gs_prov_ctx = capab.kaopcgs_prov_ctx;
283 prov->kaop_gs.gs_stats = capab.kaopcgs_config;
284 prov->kaop_capabilities |= KAOP_CAPAB_STATS;
285 return 0;
286 }
287
288 static void
unconfigure_capab_stats(net_aop_provider_handle_t prov)289 unconfigure_capab_stats(net_aop_provider_handle_t prov)
290 {
291 if ((prov->kaop_capabilities & KAOP_CAPAB_STATS) == 0) {
292 return;
293 }
294
295 bzero(&prov->kaop_gs, sizeof(prov->kaop_gs));
296 prov->kaop_capabilities &= ~KAOP_CAPAB_STATS;
297 }
298
299 static int
configure_capab_process_bitmaps(net_aop_provider_handle_t prov,net_aop_config_fn_t capab_fn)300 configure_capab_process_bitmaps(net_aop_provider_handle_t prov,
301 net_aop_config_fn_t capab_fn)
302 {
303 struct net_aop_capab_proc_activity_bitmap capab;
304 uint32_t capab_len;
305 int error;
306
307 bzero(&capab, sizeof(capab));
308 capab.kaopbm_version = NET_AOP_CAPAB_PROC_ACTIVITY_BITMAP_VERSION_1;
309 capab_len = sizeof(capab);
310 error = capab_fn(prov->kaop_prov_ctx, NET_AOP_CAPAB_PROC_ACTIVITY_BITMAP,
311 &capab, &capab_len);
312 if (error != 0) {
313 KAOPLOG_ERR("Failed to get proc bitmap capability provider");
314 return error;
315 }
316
317 VERIFY(capab.kaopbm_config != NULL);
318 VERIFY(capab.kaopbm_prov_ctx != NULL);
319 prov->kaop_pb.pab_prov_ctx = capab.kaopbm_prov_ctx;
320 prov->kaop_pb.pab_activity_bitmap = capab.kaopbm_config;
321 prov->kaop_capabilities |= KAOP_CAPAB_PROC_ACTIVITY_BITMAPS;
322 return 0;
323 }
324
325 static void
unconfigure_capab_process_bitmaps(net_aop_provider_handle_t prov)326 unconfigure_capab_process_bitmaps(net_aop_provider_handle_t prov)
327 {
328 if ((prov->kaop_capabilities & KAOP_CAPAB_PROC_ACTIVITY_BITMAPS) == 0) {
329 return;
330 }
331
332 bzero(&prov->kaop_pb, sizeof(prov->kaop_pb));
333 prov->kaop_capabilities &= ~KAOP_CAPAB_PROC_ACTIVITY_BITMAPS;
334 }
335
336 static int
net_aop_provider_initialize(net_aop_provider_handle_t prov)337 net_aop_provider_initialize(net_aop_provider_handle_t prov)
338 {
339 net_aop_config_fn_t capab_fn = prov->kaop_ext.kaopi_config_capab;
340 if (capab_fn == NULL) {
341 KAOPLOG_ERR("kaop provider missing capability function");
342 return EINVAL;
343 }
344
345 configure_capab_flow_setup(prov, capab_fn);
346 configure_capab_flow_stats(prov, capab_fn);
347 configure_capab_stats(prov, capab_fn);
348 configure_capab_process_bitmaps(prov, capab_fn);
349 return 0;
350 }
351
352 static void
net_aop_provider_cleanup(net_aop_provider_handle_t prov)353 net_aop_provider_cleanup(net_aop_provider_handle_t prov)
354 {
355 KAOP_LOCK_ASSERT_HELD();
356
357 prov->kaop_flags &= ~KAOP_FLAG_ATTACHED;
358
359 if (os_ref_release(&prov->kaop_refcnt) != 0) {
360 while (os_ref_get_count(&prov->kaop_refcnt) > 0) {
361 msleep(&prov->kaop_refcnt,
362 &kaop_lock, (PZERO + 1), __FUNCTION__, NULL);
363 }
364 }
365
366 unconfigure_capab_flow_setup(prov);
367 unconfigure_capab_flow_stats(prov);
368 unconfigure_capab_stats(prov);
369 unconfigure_capab_process_bitmaps(prov);
370 memset(&prov->kaop_ext, 0, sizeof(prov->kaop_ext));
371 prov->kaop_prov_ctx = NULL;
372 }
373
374 static void
net_aop_release_refcnt(net_aop_provider_handle_t prov)375 net_aop_release_refcnt(net_aop_provider_handle_t prov)
376 {
377 KAOP_LOCK();
378 if (os_ref_release(&prov->kaop_refcnt) == 0) {
379 wakeup(&prov->kaop_refcnt);
380 }
381 KAOP_UNLOCK();
382 }
383
384 int
net_aop_setup_flow(uint32_t flow_id,bool add,uint32_t * stats_index)385 net_aop_setup_flow(uint32_t flow_id, bool add, uint32_t *stats_index)
386 {
387 net_aop_flow_setup_fn_t fsp = NULL;
388 void *__single fsp_ctx = NULL;
389 int err = 0;
390
391 if (stats_index == NULL) {
392 KAOPLOG_ERR("invalid stats index param");
393 return EINVAL;
394 }
395
396 KAOP_LOCK();
397 if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_FLOW_SETUP) == 0) {
398 KAOP_UNLOCK();
399 KAOPLOG_ERR("kern aop provider does not support flow setup");
400 return ENOTSUP;
401 }
402
403 if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
404 !os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
405 KAOP_UNLOCK();
406 KAOPLOG_ERR("kernel aop provider is not valid");
407 return ENOENT;
408 }
409
410 fsp = g_aop_net_provider.kaop_fsp.fsp_flow_setup;
411 fsp_ctx = g_aop_net_provider.kaop_fsp.fsp_prov_ctx;
412 KAOP_UNLOCK();
413
414 err = fsp(fsp_ctx, flow_id, add, stats_index);
415 net_aop_release_refcnt(&g_aop_net_provider);
416 return err;
417 }
418
419 int
net_aop_get_flow_stats(uint32_t stats_index,struct aop_flow_stats * flow_stats)420 net_aop_get_flow_stats(uint32_t stats_index, struct aop_flow_stats *flow_stats)
421 {
422 net_aop_flow_stats_fn_t fs = NULL;
423 void *__single fs_ctx = NULL;
424 int err = 0;
425
426 if (flow_stats == NULL) {
427 KAOPLOG_ERR("invalid flow stats param");
428 return EINVAL;
429 }
430
431 KAOP_LOCK();
432 if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_FLOW_STATS) == 0) {
433 KAOP_UNLOCK();
434 KAOPLOG_ERR("kern aop provider does not support flow stats");
435 return ENOTSUP;
436 }
437
438 if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
439 !os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
440 KAOP_UNLOCK();
441 KAOPLOG_ERR("kernel aop provider is not valid");
442 return ENOENT;
443 }
444
445 fs = g_aop_net_provider.kaop_fs.fs_flow_stats;
446 fs_ctx = g_aop_net_provider.kaop_fs.fs_prov_ctx;
447 KAOP_UNLOCK();
448
449 err = fs(fs_ctx, stats_index, flow_stats);
450 net_aop_release_refcnt(&g_aop_net_provider);
451 return err;
452 }
453
454 int
net_aop_get_stats(net_aop_stats_type_t type,uint8_t * __sized_by (stats_len)stats,size_t stats_len)455 net_aop_get_stats(net_aop_stats_type_t type, uint8_t *__sized_by(stats_len) stats, size_t stats_len)
456 {
457 net_aop_stats_fn_t gs = NULL;
458 void *__single gs_ctx = NULL;
459 int err = 0;
460
461 if (type == NET_AOP_STATS_TYPE_INVALID ||
462 type > NET_AOP_STATS_TYPE_MAX) {
463 KAOPLOG_ERR("invalid stats type %u", type);
464 return EINVAL;
465 }
466
467 if (stats == NULL || stats_len == 0) {
468 KAOPLOG_ERR("invalid stats param");
469 return EINVAL;
470 }
471
472 KAOP_LOCK();
473 if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_STATS) == 0) {
474 KAOP_UNLOCK();
475 KAOPLOG_ERR("kern aop provider does not support stats");
476 return ENOTSUP;
477 }
478
479 if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
480 !os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
481 KAOP_UNLOCK();
482 KAOPLOG_ERR("kernel aop provider is not valid");
483 return ENOENT;
484 }
485
486 gs = g_aop_net_provider.kaop_gs.gs_stats;
487 gs_ctx = g_aop_net_provider.kaop_gs.gs_prov_ctx;
488 KAOP_UNLOCK();
489
490 err = gs(gs_ctx, type, stats, stats_len);
491 net_aop_release_refcnt(&g_aop_net_provider);
492 return err;
493 }
494
495 int
net_aop_get_proc_activity_bitmaps(struct aop_proc_activity_bitmap * proc_activity_bitmaps,uint16_t * inout_count)496 net_aop_get_proc_activity_bitmaps(struct aop_proc_activity_bitmap *proc_activity_bitmaps,
497 uint16_t *inout_count)
498 {
499 net_aop_proc_activity_bitmap_fn_t pb = NULL;
500 void *__single pb_ctx = NULL;
501 int err = 0;
502
503 if (inout_count == NULL) {
504 KAOPLOG_ERR("invalid inout_count param");
505 return EINVAL;
506 }
507
508 KAOP_LOCK();
509 if ((g_aop_net_provider.kaop_capabilities & KAOP_CAPAB_PROC_ACTIVITY_BITMAPS) == 0) {
510 KAOP_UNLOCK();
511 KAOPLOG_ERR("kern aop provider does not support proc bitmaps");
512 return ENOTSUP;
513 }
514
515 if (!(g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) ||
516 !os_ref_retain_try(&g_aop_net_provider.kaop_refcnt)) {
517 KAOP_UNLOCK();
518 KAOPLOG_ERR("kernel aop provider is not valid");
519 return ENOENT;
520 }
521
522 pb = g_aop_net_provider.kaop_pb.pab_activity_bitmap;
523 pb_ctx = g_aop_net_provider.kaop_pb.pab_prov_ctx;
524 KAOP_UNLOCK();
525
526 err = pb(pb_ctx, proc_activity_bitmaps, inout_count);
527 net_aop_release_refcnt(&g_aop_net_provider);
528 return err;
529 }
530
531 net_aop_provider_handle_t
net_aop_register_provider(const struct net_aop_provider_init * init,const uint32_t init_len,void * ctx)532 net_aop_register_provider(const struct net_aop_provider_init *init,
533 const uint32_t init_len, void *ctx)
534 {
535 errno_t err = 0;
536
537 err = net_aop_validate_init_params(init, init_len);
538 if (err != 0) {
539 return NULL;
540 }
541
542 KAOP_LOCK();
543 if (g_aop_net_provider.kaop_flags & KAOP_FLAG_ATTACHED) {
544 KAOP_UNLOCK();
545 KAOPLOG_ERR("kernel aop provider already registered");
546 return NULL;
547 }
548
549 os_ref_init(&g_aop_net_provider.kaop_refcnt, &kaop_refgrp);
550 memcpy(&g_aop_net_provider.kaop_ext, init, sizeof(g_aop_net_provider.kaop_ext));
551 g_aop_net_provider.kaop_prov_ctx = ctx;
552
553 err = net_aop_provider_initialize(&g_aop_net_provider);
554 if (err != 0) {
555 KAOP_UNLOCK();
556 KAOPLOG_ERR("provider type failed to initialize");
557 goto done;
558 }
559
560 g_aop_net_provider.kaop_flags |= KAOP_FLAG_ATTACHED;
561 KAOP_UNLOCK();
562 done:
563 KAOP_LOCK_ASSERT_NOTHELD();
564 if (err != 0) {
565 KAOP_LOCK();
566 net_aop_provider_cleanup(&g_aop_net_provider);
567 KAOP_UNLOCK();
568 return NULL;
569 }
570 return &g_aop_net_provider;
571 }
572
573 void
net_aop_deregister_provider(net_aop_provider_handle_t prov)574 net_aop_deregister_provider(net_aop_provider_handle_t prov)
575 {
576 if (prov == NULL) {
577 return;
578 }
579
580 KAOP_LOCK();
581 ASSERT(prov->kaop_flags & KAOP_FLAG_ATTACHED);
582 net_aop_provider_cleanup(prov);
583 KAOP_UNLOCK();
584 return;
585 }
586
587 static int
net_aop_get_protocol_stats(struct net_aop_protocol_stats * aop_proto_stats)588 net_aop_get_protocol_stats(struct net_aop_protocol_stats *aop_proto_stats)
589 {
590 int error = 0;
591
592 error = net_aop_get_stats(NET_AOP_STATS_TYPE_IP,
593 (uint8_t *)&aop_proto_stats->aop_ip, sizeof(aop_proto_stats->aop_ip));
594 if (error != 0) {
595 return error;
596 }
597 error = net_aop_get_stats(NET_AOP_STATS_TYPE_IPV6,
598 (uint8_t *)&aop_proto_stats->aop_ip6, sizeof(aop_proto_stats->aop_ip6));
599 if (error != 0) {
600 return error;
601 }
602 error = net_aop_get_stats(NET_AOP_STATS_TYPE_TCP,
603 (uint8_t *)&aop_proto_stats->aop_tcp, sizeof(aop_proto_stats->aop_tcp));
604 if (error != 0) {
605 return error;
606 }
607 error = net_aop_get_stats(NET_AOP_STATS_TYPE_UDP,
608 (uint8_t *)&aop_proto_stats->aop_udp, sizeof(aop_proto_stats->aop_udp));
609 if (error != 0) {
610 return error;
611 }
612
613 return error;
614 }
615
616 static int
net_aop_get_driver_stats(struct aop_driver_stats * driver_stats)617 net_aop_get_driver_stats(struct aop_driver_stats *driver_stats)
618 {
619 int error = 0;
620 error = net_aop_get_stats(NET_AOP_STATS_TYPE_DRIVER,
621 (uint8_t *)(struct aop_driver_stats *__bidi_indexable)driver_stats, sizeof(struct aop_driver_stats));
622 return error;
623 }
624
625 static int
aop_get_process_activity_bitmaps(struct aop_proc_activity_bitmap ** bitmaps,size_t requested_buffer_space,size_t * out_len)626 aop_get_process_activity_bitmaps(struct aop_proc_activity_bitmap **bitmaps, size_t requested_buffer_space,
627 size_t *out_len)
628 {
629 size_t bitmap_size = 0;
630 struct aop_proc_activity_bitmap *__sized_by(bitmap_size) proc_activity_bitmap = NULL;
631 uint16_t proc_bitmap_count = 0;
632 int err = 0;
633
634 net_aop_get_proc_activity_bitmaps(NULL, &proc_bitmap_count);
635
636 if (proc_bitmap_count > 0) {
637 size_t requested_count = (requested_buffer_space / (sizeof(struct aop_proc_activity_bitmap)));
638 requested_count = (requested_count > proc_bitmap_count) ? proc_bitmap_count : requested_count;
639
640 size_t required_buffer_len = (requested_count * sizeof(struct aop_proc_activity_bitmap));
641 proc_activity_bitmap = (struct aop_proc_activity_bitmap *)kalloc_data(required_buffer_len, Z_WAITOK | Z_ZERO);
642 bitmap_size = required_buffer_len;
643 if (proc_activity_bitmap == NULL) {
644 return ENOBUFS;
645 }
646
647 err = net_aop_get_proc_activity_bitmaps(proc_activity_bitmap, (uint16_t *)&requested_count);
648 if (err != 0) {
649 kfree_data_sized_by(proc_activity_bitmap, bitmap_size);
650 return err;
651 }
652
653 *bitmaps = proc_activity_bitmap;
654 *out_len = required_buffer_len;
655 return 0;
656 }
657
658 return ENOENT;
659 }
660
661 static int
662 net_aop_stats_get_sysctl SYSCTL_HANDLER_ARGS
663 {
664 #pragma unused(arg1, arg2)
665 struct net_aop_protocol_stats proto_stats = {};
666 struct aop_driver_stats driver_stats = {};
667 struct proc *p = NULL;
668 task_t __single task = NULL;
669 size_t buffer_space;
670 uint8_t *out_buffer = NULL;
671 size_t out_size = 0;
672 int error = 0;
673
674 if (!kauth_cred_issuser(kauth_cred_get())) {
675 p = current_proc();
676 task = proc_task(p);
677 bool has_aop_stats_entitlement = IOTaskHasEntitlement(task, "com.apple.private.network.aop_stats");
678 if (!has_aop_stats_entitlement) {
679 KAOPLOG_ERR("aop stats request rejected, EPERM");
680 return EPERM;
681 }
682 }
683
684 buffer_space = req->oldlen;
685 if (req->oldptr != USER_ADDR_NULL && buffer_space != 0) {
686 if (oidp->oid_arg2 == KAOP_PROTOCOL_STATS) {
687 if (buffer_space < sizeof(proto_stats)) {
688 return ENOMEM;
689 }
690
691 error = net_aop_get_protocol_stats(&proto_stats);
692 out_buffer = (uint8_t *)&proto_stats;
693 out_size = sizeof(proto_stats);
694 } else if (oidp->oid_arg2 == KAOP_DRIVER_STATS) {
695 if (buffer_space < sizeof(driver_stats)) {
696 return ENOMEM;
697 }
698
699 error = net_aop_get_driver_stats(&driver_stats);
700 out_buffer = (uint8_t *)(&driver_stats);
701 out_size = sizeof(driver_stats);
702 } else if (oidp->oid_arg2 == KAOP_PROC_ACTIVITY_BITMAPS) {
703 struct aop_proc_activity_bitmap *__single bitmap = NULL;
704 error = aop_get_process_activity_bitmaps(&bitmap, buffer_space, &out_size);
705 out_buffer = (uint8_t *)bitmap;
706 }
707
708 if (error == 0) {
709 error = SYSCTL_OUT(req, out_buffer, out_size);
710 }
711 } else if (req->oldptr == USER_ADDR_NULL) {
712 if (oidp->oid_arg2 == KAOP_PROTOCOL_STATS) {
713 buffer_space = sizeof(proto_stats);
714 } else if (oidp->oid_arg2 == KAOP_DRIVER_STATS) {
715 buffer_space = sizeof(driver_stats);
716 } else if (oidp->oid_arg2 == KAOP_PROC_ACTIVITY_BITMAPS) {
717 uint16_t proc_bitmap_count = 0;
718 net_aop_get_proc_activity_bitmaps(NULL, &proc_bitmap_count);
719 buffer_space = (proc_bitmap_count * sizeof(struct aop_proc_activity_bitmap));
720 }
721 error = SYSCTL_OUT(req, NULL, buffer_space);
722 }
723
724 return error;
725 }
726
727 void
net_aop_init(void)728 net_aop_init(void)
729 {
730 kaop_log_handle = os_log_create("com.apple.xnu.net.aopnet", "aopnet");
731 }
732