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