mirror of
https://github.com/darlinghq/darling-xnu.git
synced 2024-11-23 04:29:53 +00:00
459 lines
12 KiB
C
459 lines
12 KiB
C
/*
|
|
* Copyright (c) 2004-2020 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 "kpi_protocol.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kpi_mbuf.h>
|
|
#include <sys/domain.h>
|
|
#include <net/if.h>
|
|
#include <net/dlil.h>
|
|
#include <libkern/OSAtomic.h>
|
|
|
|
void proto_input_run(void);
|
|
|
|
typedef int (*attach_t)(struct ifnet *ifp, uint32_t protocol_family);
|
|
typedef int (*detach_t)(struct ifnet *ifp, uint32_t protocol_family);
|
|
|
|
struct proto_input_entry {
|
|
struct proto_input_entry *next;
|
|
int detach;
|
|
struct domain *domain;
|
|
int hash;
|
|
int chain;
|
|
|
|
protocol_family_t protocol;
|
|
proto_input_handler input;
|
|
proto_input_detached_handler detached;
|
|
|
|
mbuf_t inject_first;
|
|
mbuf_t inject_last;
|
|
|
|
struct proto_input_entry *input_next;
|
|
mbuf_t input_first;
|
|
mbuf_t input_last;
|
|
};
|
|
|
|
|
|
struct proto_family_str {
|
|
TAILQ_ENTRY(proto_family_str) proto_fam_next;
|
|
protocol_family_t proto_family;
|
|
ifnet_family_t if_family;
|
|
proto_plumb_handler attach_proto;
|
|
proto_unplumb_handler detach_proto;
|
|
};
|
|
|
|
static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS];
|
|
static int proto_total_waiting = 0;
|
|
static struct proto_input_entry *proto_input_add_list = NULL;
|
|
decl_lck_mtx_data(static, proto_family_mutex_data);
|
|
static lck_mtx_t *proto_family_mutex = &proto_family_mutex_data;
|
|
static TAILQ_HEAD(, proto_family_str) proto_family_head =
|
|
TAILQ_HEAD_INITIALIZER(proto_family_head);
|
|
|
|
__private_extern__ void
|
|
proto_kpi_init(void)
|
|
{
|
|
lck_grp_attr_t *grp_attrib = NULL;
|
|
lck_attr_t *lck_attrib = NULL;
|
|
lck_grp_t *lck_group = NULL;
|
|
|
|
/* Allocate a mtx lock */
|
|
grp_attrib = lck_grp_attr_alloc_init();
|
|
lck_group = lck_grp_alloc_init("protocol kpi", grp_attrib);
|
|
lck_grp_attr_free(grp_attrib);
|
|
lck_attrib = lck_attr_alloc_init();
|
|
lck_mtx_init(proto_family_mutex, lck_group, lck_attrib);
|
|
lck_grp_free(lck_group);
|
|
lck_attr_free(lck_attrib);
|
|
|
|
bzero(proto_hash, sizeof(proto_hash));
|
|
}
|
|
|
|
__private_extern__ errno_t
|
|
proto_register_input(protocol_family_t protocol, proto_input_handler input,
|
|
proto_input_detached_handler detached, int chains)
|
|
{
|
|
struct proto_input_entry *entry;
|
|
struct dlil_threading_info *inp = dlil_main_input_thread;
|
|
struct domain *dp;
|
|
domain_guard_t guard;
|
|
|
|
entry = _MALLOC(sizeof(*entry), M_IFADDR, M_WAITOK | M_ZERO);
|
|
if (entry == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
entry->protocol = protocol;
|
|
entry->input = input;
|
|
entry->detached = detached;
|
|
entry->hash = proto_hash_value(protocol);
|
|
entry->chain = chains;
|
|
|
|
guard = domain_guard_deploy();
|
|
TAILQ_FOREACH(dp, &domains, dom_entry) {
|
|
if (dp->dom_family == (int)protocol) {
|
|
break;
|
|
}
|
|
}
|
|
domain_guard_release(guard);
|
|
if (dp == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
entry->domain = dp;
|
|
|
|
lck_mtx_lock(&inp->dlth_lock);
|
|
entry->next = proto_input_add_list;
|
|
proto_input_add_list = entry;
|
|
|
|
inp->dlth_flags |= DLIL_PROTO_REGISTER;
|
|
if ((inp->dlth_flags & DLIL_INPUT_RUNNING) == 0) {
|
|
wakeup((caddr_t)&inp->dlth_flags);
|
|
}
|
|
lck_mtx_unlock(&inp->dlth_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
__private_extern__ void
|
|
proto_unregister_input(protocol_family_t protocol)
|
|
{
|
|
struct proto_input_entry *entry = NULL;
|
|
|
|
for (entry = proto_hash[proto_hash_value(protocol)]; entry != NULL;
|
|
entry = entry->next) {
|
|
if (entry->protocol == protocol) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (entry != NULL) {
|
|
entry->detach = 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
proto_delayed_attach(struct proto_input_entry *entry)
|
|
{
|
|
struct proto_input_entry *next_entry;
|
|
|
|
for (next_entry = entry->next; entry != NULL; entry = next_entry) {
|
|
struct proto_input_entry *exist;
|
|
int hash_slot;
|
|
|
|
hash_slot = proto_hash_value(entry->protocol);
|
|
next_entry = entry->next;
|
|
|
|
for (exist = proto_hash[hash_slot]; exist != NULL;
|
|
exist = exist->next) {
|
|
if (exist->protocol == entry->protocol) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If the entry already exists, call detached and dispose */
|
|
if (exist != NULL) {
|
|
if (entry->detached) {
|
|
entry->detached(entry->protocol);
|
|
}
|
|
FREE(entry, M_IFADDR);
|
|
} else {
|
|
entry->next = proto_hash[hash_slot];
|
|
proto_hash[hash_slot] = entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
__private_extern__ void
|
|
proto_input_run(void)
|
|
{
|
|
struct proto_input_entry *entry;
|
|
struct dlil_threading_info *inp = dlil_main_input_thread;
|
|
mbuf_t packet_list;
|
|
int i, locked = 0;
|
|
|
|
LCK_MTX_ASSERT(&inp->dlth_lock, LCK_MTX_ASSERT_NOTOWNED);
|
|
|
|
if (inp->dlth_flags & DLIL_PROTO_REGISTER) {
|
|
lck_mtx_lock_spin(&inp->dlth_lock);
|
|
entry = proto_input_add_list;
|
|
proto_input_add_list = NULL;
|
|
inp->dlth_flags &= ~DLIL_PROTO_REGISTER;
|
|
lck_mtx_unlock(&inp->dlth_lock);
|
|
proto_delayed_attach(entry);
|
|
}
|
|
|
|
/*
|
|
* Move everything from the lock protected list to the thread
|
|
* specific list.
|
|
*/
|
|
for (i = 0; proto_total_waiting != 0 && i < PROTO_HASH_SLOTS; i++) {
|
|
for (entry = proto_hash[i];
|
|
entry != NULL && proto_total_waiting; entry = entry->next) {
|
|
if (entry->inject_first != NULL) {
|
|
lck_mtx_lock_spin(&inp->dlth_lock);
|
|
inp->dlth_flags &= ~DLIL_PROTO_WAITING;
|
|
|
|
packet_list = entry->inject_first;
|
|
|
|
entry->inject_first = NULL;
|
|
entry->inject_last = NULL;
|
|
proto_total_waiting--;
|
|
|
|
lck_mtx_unlock(&inp->dlth_lock);
|
|
|
|
if (entry->domain != NULL && !(entry->domain->
|
|
dom_flags & DOM_REENTRANT)) {
|
|
lck_mtx_lock(entry->domain->dom_mtx);
|
|
locked = 1;
|
|
}
|
|
|
|
if (entry->chain) {
|
|
entry->input(entry->protocol,
|
|
packet_list);
|
|
} else {
|
|
mbuf_t packet;
|
|
|
|
for (packet = packet_list;
|
|
packet != NULL;
|
|
packet = packet_list) {
|
|
packet_list =
|
|
mbuf_nextpkt(packet);
|
|
mbuf_setnextpkt(packet, NULL);
|
|
entry->input(entry->protocol,
|
|
packet);
|
|
}
|
|
}
|
|
if (locked) {
|
|
locked = 0;
|
|
lck_mtx_unlock(entry->domain->dom_mtx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
errno_t
|
|
proto_input(protocol_family_t protocol, mbuf_t packet_list)
|
|
{
|
|
struct proto_input_entry *entry;
|
|
errno_t locked = 0, result = 0;
|
|
|
|
for (entry = proto_hash[proto_hash_value(protocol)]; entry != NULL;
|
|
entry = entry->next) {
|
|
if (entry->protocol == protocol) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (entry == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (entry->domain && !(entry->domain->dom_flags & DOM_REENTRANT)) {
|
|
lck_mtx_lock(entry->domain->dom_mtx);
|
|
locked = 1;
|
|
}
|
|
|
|
if (entry->chain) {
|
|
entry->input(entry->protocol, packet_list);
|
|
} else {
|
|
mbuf_t packet;
|
|
|
|
for (packet = packet_list; packet != NULL;
|
|
packet = packet_list) {
|
|
packet_list = mbuf_nextpkt(packet);
|
|
mbuf_setnextpkt(packet, NULL);
|
|
entry->input(entry->protocol, packet);
|
|
}
|
|
}
|
|
|
|
if (locked) {
|
|
lck_mtx_unlock(entry->domain->dom_mtx);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
errno_t
|
|
proto_inject(protocol_family_t protocol, mbuf_t packet_list)
|
|
{
|
|
struct proto_input_entry *entry;
|
|
mbuf_t last_packet;
|
|
int hash_slot = proto_hash_value(protocol);
|
|
struct dlil_threading_info *inp = dlil_main_input_thread;
|
|
|
|
for (last_packet = packet_list; mbuf_nextpkt(last_packet) != NULL;
|
|
last_packet = mbuf_nextpkt(last_packet)) {
|
|
/* find the last packet */;
|
|
}
|
|
|
|
for (entry = proto_hash[hash_slot]; entry != NULL;
|
|
entry = entry->next) {
|
|
if (entry->protocol == protocol) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (entry != NULL) {
|
|
lck_mtx_lock(&inp->dlth_lock);
|
|
if (entry->inject_first == NULL) {
|
|
proto_total_waiting++;
|
|
inp->dlth_flags |= DLIL_PROTO_WAITING;
|
|
entry->inject_first = packet_list;
|
|
} else {
|
|
mbuf_setnextpkt(entry->inject_last, packet_list);
|
|
}
|
|
entry->inject_last = last_packet;
|
|
if ((inp->dlth_flags & DLIL_INPUT_RUNNING) == 0) {
|
|
wakeup((caddr_t)&inp->dlth_flags);
|
|
}
|
|
lck_mtx_unlock(&inp->dlth_lock);
|
|
} else {
|
|
return ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct proto_family_str *
|
|
proto_plumber_find(protocol_family_t proto_family, ifnet_family_t if_family)
|
|
{
|
|
struct proto_family_str *mod = NULL;
|
|
|
|
TAILQ_FOREACH(mod, &proto_family_head, proto_fam_next) {
|
|
if ((mod->proto_family == (proto_family & 0xffff)) &&
|
|
(mod->if_family == (if_family & 0xffff))) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
errno_t
|
|
proto_register_plumber(protocol_family_t protocol_family,
|
|
ifnet_family_t interface_family, proto_plumb_handler attach,
|
|
proto_unplumb_handler detach)
|
|
{
|
|
struct proto_family_str *proto_family;
|
|
|
|
if (attach == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
lck_mtx_lock(proto_family_mutex);
|
|
|
|
TAILQ_FOREACH(proto_family, &proto_family_head, proto_fam_next) {
|
|
if (proto_family->proto_family == protocol_family &&
|
|
proto_family->if_family == interface_family) {
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
return EEXIST;
|
|
}
|
|
}
|
|
|
|
proto_family = (struct proto_family_str *)
|
|
_MALLOC(sizeof(struct proto_family_str), M_IFADDR,
|
|
M_WAITOK | M_ZERO);
|
|
if (!proto_family) {
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
return ENOMEM;
|
|
}
|
|
|
|
proto_family->proto_family = protocol_family;
|
|
proto_family->if_family = interface_family & 0xffff;
|
|
proto_family->attach_proto = attach;
|
|
proto_family->detach_proto = detach;
|
|
|
|
TAILQ_INSERT_TAIL(&proto_family_head, proto_family, proto_fam_next);
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
proto_unregister_plumber(protocol_family_t protocol_family,
|
|
ifnet_family_t interface_family)
|
|
{
|
|
struct proto_family_str *proto_family;
|
|
|
|
lck_mtx_lock(proto_family_mutex);
|
|
|
|
proto_family = proto_plumber_find(protocol_family, interface_family);
|
|
if (proto_family == NULL) {
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
return;
|
|
}
|
|
|
|
TAILQ_REMOVE(&proto_family_head, proto_family, proto_fam_next);
|
|
FREE(proto_family, M_IFADDR);
|
|
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
}
|
|
|
|
__private_extern__ errno_t
|
|
proto_plumb(protocol_family_t protocol_family, ifnet_t ifp)
|
|
{
|
|
struct proto_family_str *proto_family;
|
|
int ret = 0;
|
|
|
|
lck_mtx_lock(proto_family_mutex);
|
|
proto_family = proto_plumber_find(protocol_family, ifp->if_family);
|
|
if (proto_family == NULL) {
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
return ENXIO;
|
|
}
|
|
|
|
ret = proto_family->attach_proto(ifp, protocol_family);
|
|
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
return ret;
|
|
}
|
|
|
|
|
|
__private_extern__ errno_t
|
|
proto_unplumb(protocol_family_t protocol_family, ifnet_t ifp)
|
|
{
|
|
struct proto_family_str *proto_family;
|
|
int ret = 0;
|
|
|
|
lck_mtx_lock(proto_family_mutex);
|
|
|
|
proto_family = proto_plumber_find(protocol_family, ifp->if_family);
|
|
if (proto_family != NULL && proto_family->detach_proto) {
|
|
proto_family->detach_proto(ifp, protocol_family);
|
|
} else {
|
|
ret = ifnet_detach_protocol(ifp, protocol_family);
|
|
}
|
|
|
|
lck_mtx_unlock(proto_family_mutex);
|
|
return ret;
|
|
}
|