This is xnu-11215.1.10. See this file in:
/*
 * Copyright (c) 2021-2023 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@
 */

#include <stdbool.h>

#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/sbuf.h>
#include <sys/types.h>
#include <sys/mcache.h>
#include <sys/malloc.h>

#include <os/log.h>

#include <net/nwk_wq.h>
#include <skywalk/lib/net_filter_event.h>

static uint32_t net_filter_event_state;
static bool net_filter_event_initialized;
static struct eventhandler_lists_ctxt net_filter_evhdlr_ctxt;

EVENTHANDLER_DECLARE(net_filter_event, net_filter_event_callback_t);

static struct sbuf *
net_filter_event_description(uint32_t state)
{
	struct sbuf *sbuf;

	sbuf = sbuf_new(NULL, NULL, 128, SBUF_AUTOEXTEND);
	if (state & NET_FILTER_EVENT_PF) {
		sbuf_cat(sbuf, "pf ");
	}
	if (state & NET_FILTER_EVENT_SOCKET) {
		sbuf_cat(sbuf, "socket ");
	}
	if (state & NET_FILTER_EVENT_INTERFACE) {
		sbuf_cat(sbuf, "interface ");
	}
	if (state & NET_FILTER_EVENT_IP) {
		sbuf_cat(sbuf, "ip ");
	}
	if (state & NET_FILTER_EVENT_ALF) {
		sbuf_cat(sbuf, "application-firewall ");
	}
	if (state & NET_FILTER_EVENT_PARENTAL_CONTROLS) {
		sbuf_cat(sbuf, "parental-controls ");
	}
	sbuf_trim(sbuf);
	sbuf_finish(sbuf);

	return sbuf;
}

static void
net_filter_event_callback(struct eventhandler_entry_arg arg0 __unused,
    enum net_filter_event_subsystems state)
{
	struct sbuf *sbuf = net_filter_event_description(state);

	os_log(OS_LOG_DEFAULT, "net_filter_event: new state (0x%x) %s",
	    state, sbuf_data(sbuf));
	evhlog(debug, "%s: eventhandler saw event type=net_filter_event_state event_code=%s",
	    __func__, sbuf_data(sbuf));
	sbuf_delete(sbuf);
}

static void
net_filter_event_init(void)
{
	if (net_filter_event_initialized) {
		return;
	}
	net_filter_event_initialized = true;
	eventhandler_lists_ctxt_init(&net_filter_evhdlr_ctxt);
	net_filter_event_register(net_filter_event_callback);
}

static void
net_filter_event_enqueue_callback(struct nwk_wq_entry *nwk_kwqe)
{
	EVENTHANDLER_INVOKE(&net_filter_evhdlr_ctxt, net_filter_event,
	    net_filter_event_state);
	kfree_type(struct nwk_wq_entry, nwk_kwqe);
}

static void
net_filter_event_enqueue(void)
{
	struct nwk_wq_entry *__single nwk_wqe;

	struct sbuf *sbuf = net_filter_event_description(net_filter_event_state);
	evhlog(debug, "%s: eventhandler enqueuing event of type=net_filter_event_state event_code=%s",
	    __func__, sbuf_data(sbuf));
	sbuf_delete(sbuf);

	nwk_wqe = kalloc_type(struct nwk_wq_entry, Z_WAITOK | Z_ZERO | Z_NOFAIL);
	nwk_wqe->func = net_filter_event_enqueue_callback;
	nwk_wq_enqueue(nwk_wqe);
}

void
net_filter_event_mark(enum net_filter_event_subsystems subsystem, bool compatible)
{
	uint32_t old_state = net_filter_event_state;

	net_filter_event_init();
	if (!compatible) {
		os_atomic_or(&net_filter_event_state, subsystem, relaxed);
	} else {
		os_atomic_andnot(&net_filter_event_state, subsystem, relaxed);
	}
	if (old_state != net_filter_event_state) {
		net_filter_event_enqueue();
	}
}

enum net_filter_event_subsystems
net_filter_event_get_state(void)
{
	return net_filter_event_state;
}

void
net_filter_event_register(net_filter_event_callback_t callback)
{
	net_filter_event_init();
	(void)EVENTHANDLER_REGISTER(&net_filter_evhdlr_ctxt,
	    net_filter_event, callback,
	    eventhandler_entry_dummy_arg,
	    EVENTHANDLER_PRI_ANY);
}

static int
net_filter_event_sysctl(struct sysctl_oid *oidp, void *arg1, int arg2,
    struct sysctl_req *req)
{
#pragma unused(oidp, arg1, arg2)
	struct sbuf *sbuf = net_filter_event_description(net_filter_event_state);

	int error = sysctl_io_string(req, sbuf_data(sbuf), 0, 0, NULL);
	sbuf_delete(sbuf);

	return error;
}

SYSCTL_PROC(_net, OID_AUTO, filter_state,
    CTLTYPE_STRING | CTLFLAG_LOCKED, NULL, 0,
    net_filter_event_sysctl, "A", "State of the network filters");