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