1/* 2 * Copyright (c) 2004-2013 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#include "kpi_protocol.h" 30 31#include <sys/param.h> 32#include <sys/malloc.h> 33#include <sys/socket.h> 34#include <sys/systm.h> 35#include <sys/kpi_mbuf.h> 36#include <sys/domain.h> 37#include <net/if.h> 38#include <net/dlil.h> 39#include <libkern/OSAtomic.h> 40 41void proto_input_run(void); 42 43typedef int (*attach_t)(struct ifnet *ifp, uint32_t protocol_family); 44typedef int (*detach_t)(struct ifnet *ifp, uint32_t protocol_family); 45 46struct proto_input_entry { 47 struct proto_input_entry *next; 48 int detach; 49 struct domain *domain; 50 int hash; 51 int chain; 52 53 protocol_family_t protocol; 54 proto_input_handler input; 55 proto_input_detached_handler detached; 56 57 mbuf_t inject_first; 58 mbuf_t inject_last; 59 60 struct proto_input_entry *input_next; 61 mbuf_t input_first; 62 mbuf_t input_last; 63}; 64 65 66struct proto_family_str { 67 TAILQ_ENTRY(proto_family_str) proto_fam_next; 68 protocol_family_t proto_family; 69 ifnet_family_t if_family; 70 proto_plumb_handler attach_proto; 71 proto_unplumb_handler detach_proto; 72}; 73 74static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS]; 75static int proto_total_waiting = 0; 76static struct proto_input_entry *proto_input_add_list = NULL; 77decl_lck_mtx_data(static, proto_family_mutex_data); 78static lck_mtx_t *proto_family_mutex = &proto_family_mutex_data; 79static TAILQ_HEAD(, proto_family_str) proto_family_head = 80 TAILQ_HEAD_INITIALIZER(proto_family_head); 81 82__private_extern__ void 83proto_kpi_init(void) 84{ 85 lck_grp_attr_t *grp_attrib = NULL; 86 lck_attr_t *lck_attrib = NULL; 87 lck_grp_t *lck_group = NULL; 88 89 /* Allocate a mtx lock */ 90 grp_attrib = lck_grp_attr_alloc_init(); 91 lck_group = lck_grp_alloc_init("protocol kpi", grp_attrib); 92 lck_grp_attr_free(grp_attrib); 93 lck_attrib = lck_attr_alloc_init(); 94 lck_mtx_init(proto_family_mutex, lck_group, lck_attrib); 95 lck_grp_free(lck_group); 96 lck_attr_free(lck_attrib); 97 98 bzero(proto_hash, sizeof (proto_hash)); 99} 100 101__private_extern__ errno_t 102proto_register_input(protocol_family_t protocol, proto_input_handler input, 103 proto_input_detached_handler detached, int chains) 104{ 105 struct proto_input_entry *entry; 106 struct dlil_threading_info *inp = dlil_main_input_thread; 107 struct domain *dp; 108 domain_guard_t guard; 109 110 entry = _MALLOC(sizeof (*entry), M_IFADDR, M_WAITOK); 111 if (entry == NULL) 112 return (ENOMEM); 113 114 bzero(entry, sizeof (*entry)); 115 entry->protocol = protocol; 116 entry->input = input; 117 entry->detached = detached; 118 entry->hash = proto_hash_value(protocol); 119 entry->chain = chains; 120 121 guard = domain_guard_deploy(); 122 TAILQ_FOREACH(dp, &domains, dom_entry) { 123 if (dp->dom_family == (int)protocol) 124 break; 125 } 126 domain_guard_release(guard); 127 if (dp == NULL) 128 return (EINVAL); 129 130 entry->domain = dp; 131 132 lck_mtx_lock(&inp->input_lck); 133 entry->next = proto_input_add_list; 134 proto_input_add_list = entry; 135 136 inp->input_waiting |= DLIL_PROTO_REGISTER; 137 if ((inp->input_waiting & DLIL_INPUT_RUNNING) == 0) 138 wakeup((caddr_t)&inp->input_waiting); 139 lck_mtx_unlock(&inp->input_lck); 140 141 return (0); 142} 143 144__private_extern__ void 145proto_unregister_input(protocol_family_t protocol) 146{ 147 struct proto_input_entry *entry = NULL; 148 149 for (entry = proto_hash[proto_hash_value(protocol)]; entry != NULL; 150 entry = entry->next) { 151 if (entry->protocol == protocol) 152 break; 153 } 154 155 if (entry != NULL) 156 entry->detach = 1; 157} 158 159static void 160proto_delayed_attach(struct proto_input_entry *entry) 161{ 162 struct proto_input_entry *next_entry; 163 164 for (next_entry = entry->next; entry != NULL; entry = next_entry) { 165 struct proto_input_entry *exist; 166 int hash_slot; 167 168 hash_slot = proto_hash_value(entry->protocol); 169 next_entry = entry->next; 170 171 for (exist = proto_hash[hash_slot]; exist != NULL; 172 exist = exist->next) { 173 if (exist->protocol == entry->protocol) 174 break; 175 } 176 177 /* If the entry already exists, call detached and dispose */ 178 if (exist != NULL) { 179 if (entry->detached) 180 entry->detached(entry->protocol); 181 FREE(entry, M_IFADDR); 182 } else { 183 entry->next = proto_hash[hash_slot]; 184 proto_hash[hash_slot] = entry; 185 } 186 } 187} 188 189__private_extern__ void 190proto_input_run(void) 191{ 192 struct proto_input_entry *entry; 193 struct dlil_threading_info *inp = dlil_main_input_thread; 194 mbuf_t packet_list; 195 int i, locked = 0; 196 197 lck_mtx_assert(&inp->input_lck, LCK_MTX_ASSERT_NOTOWNED); 198 199 if (inp->input_waiting & DLIL_PROTO_REGISTER) { 200 lck_mtx_lock_spin(&inp->input_lck); 201 entry = proto_input_add_list; 202 proto_input_add_list = NULL; 203 inp->input_waiting &= ~DLIL_PROTO_REGISTER; 204 lck_mtx_unlock(&inp->input_lck); 205 proto_delayed_attach(entry); 206 } 207 208 /* 209 * Move everything from the lock protected list to the thread 210 * specific list. 211 */ 212 for (i = 0; proto_total_waiting != 0 && i < PROTO_HASH_SLOTS; i++) { 213 for (entry = proto_hash[i]; 214 entry != NULL && proto_total_waiting; entry = entry->next) { 215 if (entry->inject_first != NULL) { 216 lck_mtx_lock_spin(&inp->input_lck); 217 inp->input_waiting &= ~DLIL_PROTO_WAITING; 218 219 packet_list = entry->inject_first; 220 221 entry->inject_first = NULL; 222 entry->inject_last = NULL; 223 proto_total_waiting--; 224 225 lck_mtx_unlock(&inp->input_lck); 226 227 if (entry->domain != NULL && !(entry->domain-> 228 dom_flags & DOM_REENTRANT)) { 229 lck_mtx_lock(entry->domain->dom_mtx); 230 locked = 1; 231 } 232 233 if (entry->chain) { 234 entry->input(entry->protocol, 235 packet_list); 236 } else { 237 mbuf_t packet; 238 239 for (packet = packet_list; 240 packet != NULL; 241 packet = packet_list) { 242 packet_list = 243 mbuf_nextpkt(packet); 244 mbuf_setnextpkt(packet, NULL); 245 entry->input(entry->protocol, 246 packet); 247 } 248 } 249 if (locked) { 250 locked = 0; 251 lck_mtx_unlock(entry->domain->dom_mtx); 252 } 253 } 254 } 255 } 256} 257 258errno_t 259proto_input(protocol_family_t protocol, mbuf_t packet_list) 260{ 261 struct proto_input_entry *entry; 262 errno_t locked = 0, result = 0; 263 264 for (entry = proto_hash[proto_hash_value(protocol)]; entry != NULL; 265 entry = entry->next) { 266 if (entry->protocol == protocol) 267 break; 268 } 269 270 if (entry->domain && !(entry->domain->dom_flags & DOM_REENTRANT)) { 271 lck_mtx_lock(entry->domain->dom_mtx); 272 locked = 1; 273 } 274 275 if (entry->chain) { 276 entry->input(entry->protocol, packet_list); 277 } else { 278 mbuf_t packet; 279 280 for (packet = packet_list; packet != NULL; 281 packet = packet_list) { 282 packet_list = mbuf_nextpkt(packet); 283 mbuf_setnextpkt(packet, NULL); 284 entry->input(entry->protocol, packet); 285 } 286 } 287 288 if (locked) { 289 lck_mtx_unlock(entry->domain->dom_mtx); 290 } 291 return (result); 292} 293 294errno_t 295proto_inject(protocol_family_t protocol, mbuf_t packet_list) 296{ 297 struct proto_input_entry *entry; 298 mbuf_t last_packet; 299 int hash_slot = proto_hash_value(protocol); 300 struct dlil_threading_info *inp = dlil_main_input_thread; 301 302 for (last_packet = packet_list; mbuf_nextpkt(last_packet) != NULL; 303 last_packet = mbuf_nextpkt(last_packet)) 304 /* find the last packet */; 305 306 for (entry = proto_hash[hash_slot]; entry != NULL; 307 entry = entry->next) { 308 if (entry->protocol == protocol) 309 break; 310 } 311 312 if (entry != NULL) { 313 lck_mtx_lock(&inp->input_lck); 314 if (entry->inject_first == NULL) { 315 proto_total_waiting++; 316 inp->input_waiting |= DLIL_PROTO_WAITING; 317 entry->inject_first = packet_list; 318 } else { 319 mbuf_setnextpkt(entry->inject_last, packet_list); 320 } 321 entry->inject_last = last_packet; 322 if ((inp->input_waiting & DLIL_INPUT_RUNNING) == 0) { 323 wakeup((caddr_t)&inp->input_waiting); 324 } 325 lck_mtx_unlock(&inp->input_lck); 326 } else { 327 return (ENOENT); 328 } 329 330 return (0); 331} 332 333static struct proto_family_str * 334proto_plumber_find(protocol_family_t proto_family, ifnet_family_t if_family) 335{ 336 struct proto_family_str *mod = NULL; 337 338 TAILQ_FOREACH(mod, &proto_family_head, proto_fam_next) { 339 if ((mod->proto_family == (proto_family & 0xffff)) && 340 (mod->if_family == (if_family & 0xffff))) 341 break; 342 } 343 344 return (mod); 345} 346 347errno_t 348proto_register_plumber(protocol_family_t protocol_family, 349 ifnet_family_t interface_family, proto_plumb_handler attach, 350 proto_unplumb_handler detach) 351{ 352 struct proto_family_str *proto_family; 353 354 if (attach == NULL) 355 return (EINVAL); 356 357 lck_mtx_lock(proto_family_mutex); 358 359 TAILQ_FOREACH(proto_family, &proto_family_head, proto_fam_next) { 360 if (proto_family->proto_family == protocol_family && 361 proto_family->if_family == interface_family) { 362 lck_mtx_unlock(proto_family_mutex); 363 return (EEXIST); 364 } 365 } 366 367 proto_family = (struct proto_family_str *) 368 _MALLOC(sizeof (struct proto_family_str), M_IFADDR, M_WAITOK); 369 if (!proto_family) { 370 lck_mtx_unlock(proto_family_mutex); 371 return (ENOMEM); 372 } 373 374 bzero(proto_family, sizeof (struct proto_family_str)); 375 proto_family->proto_family = protocol_family; 376 proto_family->if_family = interface_family & 0xffff; 377 proto_family->attach_proto = attach; 378 proto_family->detach_proto = detach; 379 380 TAILQ_INSERT_TAIL(&proto_family_head, proto_family, proto_fam_next); 381 lck_mtx_unlock(proto_family_mutex); 382 return (0); 383} 384 385void 386proto_unregister_plumber(protocol_family_t protocol_family, 387 ifnet_family_t interface_family) 388{ 389 struct proto_family_str *proto_family; 390 391 lck_mtx_lock(proto_family_mutex); 392 393 proto_family = proto_plumber_find(protocol_family, interface_family); 394 if (proto_family == NULL) { 395 lck_mtx_unlock(proto_family_mutex); 396 return; 397 } 398 399 TAILQ_REMOVE(&proto_family_head, proto_family, proto_fam_next); 400 FREE(proto_family, M_IFADDR); 401 402 lck_mtx_unlock(proto_family_mutex); 403} 404 405__private_extern__ errno_t 406proto_plumb(protocol_family_t protocol_family, ifnet_t ifp) 407{ 408 struct proto_family_str *proto_family; 409 int ret = 0; 410 411 lck_mtx_lock(proto_family_mutex); 412 proto_family = proto_plumber_find(protocol_family, ifp->if_family); 413 if (proto_family == NULL) { 414 lck_mtx_unlock(proto_family_mutex); 415 return (ENXIO); 416 } 417 418 ret = proto_family->attach_proto(ifp, protocol_family); 419 420 lck_mtx_unlock(proto_family_mutex); 421 return (ret); 422} 423 424 425__private_extern__ errno_t 426proto_unplumb(protocol_family_t protocol_family, ifnet_t ifp) 427{ 428 struct proto_family_str *proto_family; 429 int ret = 0; 430 431 lck_mtx_lock(proto_family_mutex); 432 433 proto_family = proto_plumber_find(protocol_family, ifp->if_family); 434 if (proto_family != NULL && proto_family->detach_proto) 435 proto_family->detach_proto(ifp, protocol_family); 436 else 437 ret = ifnet_detach_protocol(ifp, protocol_family); 438 439 lck_mtx_unlock(proto_family_mutex); 440 return (ret); 441} 442