/* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This 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 OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include "kpi_protocol.h" #include #include #include #include #include #include #include #include #include void proto_kpi_init(void); 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); /****************************************************************************/ /* WARNING: Big assumption made here - there can be only one input thread */ struct proto_input_entry { struct proto_input_entry *next; int detach; struct domain *domain; protocol_family_t protocol; proto_input_handler input; proto_input_detached_handler detached; mbuf_t first_packet; mbuf_t last_packet; }; #define PROTO_HASH_SLOTS 5 static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS]; static struct proto_input_entry *proto_input_add_list; static lck_mtx_t *proto_input_lock = 0; __private_extern__ u_int32_t inject_buckets = 0; extern thread_t dlil_input_thread_ptr; extern int dlil_input_thread_wakeup; 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_grp_attr_setdefault(grp_attrib); lck_group = lck_grp_alloc_init("protocol kpi", grp_attrib); lck_grp_attr_free(grp_attrib); lck_attrib = lck_attr_alloc_init(); lck_attr_setdefault(lck_attrib); proto_input_lock = lck_mtx_alloc_init(lck_group, lck_attrib); lck_grp_free(lck_group); lck_attr_free(lck_attrib); } __private_extern__ errno_t proto_register_input( protocol_family_t protocol, proto_input_handler input, proto_input_detached_handler detached) { struct proto_input_entry *entry; 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; { struct domain *dp = domains; extern lck_mtx_t *domain_proto_mtx; lck_mtx_assert(domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); lck_mtx_lock(domain_proto_mtx); while (dp && dp->dom_family != protocol) dp = dp->dom_next; entry->domain = dp; lck_mtx_unlock(domain_proto_mtx); } do { entry->next = proto_input_add_list; } while(!OSCompareAndSwap((UInt32)entry->next, (UInt32)entry, (UInt32*)&proto_input_add_list)); wakeup((caddr_t)&dlil_input_thread_wakeup); 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; } } } static void proto_delayed_inject( struct proto_input_entry *entry) { mbuf_t packet_list; mbuf_t packet; int locked = 0; lck_mtx_lock(proto_input_lock); packet_list = entry->first_packet; entry->first_packet = entry->last_packet = 0; lck_mtx_unlock(proto_input_lock); if (packet_list == NULL) return; if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) { lck_mtx_lock(entry->domain->dom_mtx); locked = 1; } 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); } } /* This function must be called from a single dlil input thread */ __private_extern__ void proto_input_run(void) { struct proto_input_entry *entry; u_int32_t inject; int i; if (current_thread() != dlil_input_thread_ptr) panic("proto_input_run called from a thread other than dlil_input_thread!\n"); do { entry = proto_input_add_list; } while (entry && !OSCompareAndSwap((UInt32)entry, 0, (UInt32*)&proto_input_add_list)); if (entry) proto_delayed_attach(entry); do { inject = inject_buckets; } while (inject && !OSCompareAndSwap(inject, 0, (UInt32*)&inject_buckets)); if (inject) { for (i = 0; i < PROTO_HASH_SLOTS; i++) { if ((inject & (1L << i)) != 0) { for (entry = proto_hash[i]; entry; entry = entry->next) { if (entry->first_packet) { proto_delayed_inject(entry); } } } } } } errno_t proto_input( protocol_family_t protocol, mbuf_t packet_list) { struct proto_input_entry *entry; if (current_thread() != dlil_input_thread_ptr) panic("proto_input called from a thread other than dlil_input_thread!\n"); for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next) { if (entry->protocol == protocol) break; } if (entry) { mbuf_t packet; #if DIRECT_PROTO_INPUT // See for why this is disabled // We need to release the dlil lock before taking the protocol lock for (packet = packet_list; packet; packet = packet_list) { packet_list = mbuf_nextpkt(packet); mbuf_setnextpkt(packet, NULL); entry->input(entry->protocol, packet); } #else mbuf_t last_packet; int hash_slot = proto_hash_value(protocol); for (last_packet = packet_list; mbuf_nextpkt(last_packet); last_packet = mbuf_nextpkt(last_packet)) /* find the last packet */; lck_mtx_lock(proto_input_lock); if (entry->first_packet == NULL) { entry->first_packet = packet_list; } else { mbuf_setnextpkt(entry->last_packet, packet_list); } entry->last_packet = last_packet; lck_mtx_unlock(proto_input_lock); OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets); #endif } else { return ENOENT; } return 0; } 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); 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(proto_input_lock); if (entry->first_packet == NULL) { entry->first_packet = packet_list; } else { mbuf_setnextpkt(entry->last_packet, packet_list); } entry->last_packet = last_packet; lck_mtx_unlock(proto_input_lock); OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets); wakeup((caddr_t)&dlil_input_thread_wakeup); } else { return ENOENT; } return 0; } errno_t proto_register_plumber( protocol_family_t proto_fam, ifnet_family_t if_fam, proto_plumb_handler plumb, proto_unplumb_handler unplumb) { return dlil_reg_proto_module(proto_fam, if_fam, (attach_t)plumb, (detach_t)unplumb); } void proto_unregister_plumber( protocol_family_t proto_fam, ifnet_family_t if_fam) { (void)dlil_dereg_proto_module(proto_fam, if_fam); }