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