xref: /xnu-12377.61.12/bsd/net/aop/kpi_aop.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
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