/* * Copyright (c) 2004 Apple Computer, 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 #include #include #include #include #include #include #include #include void proto_input_run(void); typedef int (*attach_t)(struct ifnet *ifp, u_long protocol_family); typedef int (*detach_t)(struct ifnet *ifp, u_long 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; }; #define PROTO_HASH_SLOTS 5 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; static lck_mtx_t *proto_family_mutex = 0; static TAILQ_HEAD(, proto_family_str) proto_family_head = TAILQ_HEAD_INITIALIZER(proto_family_head); extern lck_mtx_t *domain_proto_mtx; extern struct dlil_threading_info *dlil_lo_thread_ptr; static int proto_hash_value( protocol_family_t protocol) { switch(protocol) { case PF_INET: return 0; case PF_INET6: return 1; case PF_APPLETALK: return 2; case PF_VLAN: return 3; } return 4; } __private_extern__ void proto_kpi_init(void) { lck_grp_attr_t *grp_attrib = 0; lck_attr_t *lck_attrib = 0; lck_grp_t *lck_group = 0; /* 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(); proto_family_mutex = lck_mtx_alloc_init(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 *thread = dlil_lo_thread_ptr; entry = _MALLOC(sizeof(*entry), M_IFADDR, M_WAITOK); if (entry == NULL) return ENOMEM; bzero(entry, sizeof(*entry)); entry->protocol = protocol; entry->input = input; entry->detached = detached; entry->hash = proto_hash_value(protocol); entry->chain = chains; { struct domain *dp = domains; lck_mtx_assert(domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); lck_mtx_lock(domain_proto_mtx); while (dp && (protocol_family_t)dp->dom_family != protocol) dp = dp->dom_next; entry->domain = dp; lck_mtx_unlock(domain_proto_mtx); } lck_mtx_lock(thread->input_lck); entry->next = proto_input_add_list; proto_input_add_list = entry; thread->input_waiting |= DLIL_PROTO_REGISTER; if ((thread->input_waiting & DLIL_INPUT_RUNNING) == 0) wakeup((caddr_t)&thread->input_waiting); lck_mtx_unlock(thread->input_lck); 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; entry = entry->next) if (entry->protocol == protocol) break; if (entry) 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; 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; exist = exist->next) if (exist->protocol == entry->protocol) break; /* If the entry already exists, call detached and dispose */ if (exist) { 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 *thread = dlil_lo_thread_ptr; mbuf_t packet_list; int i, locked = 0; lck_mtx_assert(thread->input_lck, LCK_MTX_ASSERT_NOTOWNED); if ((thread->input_waiting & DLIL_PROTO_REGISTER) != 0) { lck_mtx_lock(thread->input_lck); entry = proto_input_add_list; proto_input_add_list = NULL; thread->input_waiting &= ~DLIL_PROTO_REGISTER; lck_mtx_unlock(thread->input_lck); 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 && proto_total_waiting; entry = entry->next) { if (entry->inject_first) { lck_mtx_lock(thread->input_lck); thread->input_waiting &= ~DLIL_PROTO_WAITING; packet_list = entry->inject_first; entry->inject_first = NULL; entry->inject_last = NULL; proto_total_waiting--; lck_mtx_unlock(thread->input_lck); if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) { 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; 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; entry = entry->next) { if (entry->protocol == protocol) break; } if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) { 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; 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 *thread = dlil_lo_thread_ptr; for (last_packet = packet_list; mbuf_nextpkt(last_packet); last_packet = mbuf_nextpkt(last_packet)) /* find the last packet */; for (entry = proto_hash[hash_slot]; entry; entry = entry->next) { if (entry->protocol == protocol) break; } if (entry) { lck_mtx_lock(thread->input_lck); if (entry->inject_first == NULL) { proto_total_waiting++; thread->input_waiting |= DLIL_PROTO_WAITING; entry->inject_first = packet_list; } else { mbuf_setnextpkt(entry->inject_last, packet_list); } entry->inject_last = last_packet; if ((thread->input_waiting & DLIL_INPUT_RUNNING) == 0) { wakeup((caddr_t)&thread->input_waiting); } lck_mtx_unlock(thread->input_lck); } 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); if (!proto_family) { lck_mtx_unlock(proto_family_mutex); return ENOMEM; } bzero(proto_family, sizeof(struct proto_family_str)); 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 == 0) { 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); return; } __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 == 0) { 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 && 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; }