]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/kpi_protocol.c
xnu-792.22.5.tar.gz
[apple/xnu.git] / bsd / net / kpi_protocol.c
CommitLineData
91447636 1/*
5d5c5d0d
A
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
8f6c56a5 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
91447636 5 *
8f6c56a5
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.
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
8ad349bb 24 * limitations under the License.
8f6c56a5
A
25 *
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
41void proto_kpi_init(void);
42void proto_input_run(void);
43
44typedef int (*attach_t)(struct ifnet *ifp, u_long protocol_family);
45typedef int (*detach_t)(struct ifnet *ifp, u_long protocol_family);
46
47/****************************************************************************/
48/* WARNING: Big assumption made here - there can be only one input thread */
49struct proto_input_entry {
50 struct proto_input_entry *next;
51 int detach;
52 struct domain *domain;
53
54 protocol_family_t protocol;
55 proto_input_handler input;
56 proto_input_detached_handler detached;
57
58 mbuf_t first_packet;
59 mbuf_t last_packet;
60};
61
62#define PROTO_HASH_SLOTS 5
63
64static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS];
65static struct proto_input_entry *proto_input_add_list;
66static lck_mtx_t *proto_input_lock = 0;
b36670ce 67__private_extern__ u_int32_t inject_buckets = 0;
91447636
A
68
69extern thread_t dlil_input_thread_ptr;
70extern int dlil_input_thread_wakeup;
71
72static int
73proto_hash_value(
74 protocol_family_t protocol)
75{
76 switch(protocol) {
77 case PF_INET:
78 return 0;
79 case PF_INET6:
80 return 1;
81 case PF_APPLETALK:
82 return 2;
83 case PF_VLAN:
84 return 3;
85 }
86 return 4;
87}
88
89__private_extern__ void
90proto_kpi_init(void)
91{
92 lck_grp_attr_t *grp_attrib = 0;
93 lck_attr_t *lck_attrib = 0;
94 lck_grp_t *lck_group = 0;
95
96 /* Allocate a mtx lock */
97 grp_attrib = lck_grp_attr_alloc_init();
91447636
A
98 lck_group = lck_grp_alloc_init("protocol kpi", grp_attrib);
99 lck_grp_attr_free(grp_attrib);
100 lck_attrib = lck_attr_alloc_init();
91447636
A
101 proto_input_lock = lck_mtx_alloc_init(lck_group, lck_attrib);
102 lck_grp_free(lck_group);
103 lck_attr_free(lck_attrib);
104}
105
106__private_extern__ errno_t
107proto_register_input(
108 protocol_family_t protocol,
109 proto_input_handler input,
110 proto_input_detached_handler detached)
111{
112
113 struct proto_input_entry *entry;
114
115 entry = _MALLOC(sizeof(*entry), M_IFADDR, M_WAITOK);
116
117 if (entry == NULL)
118 return ENOMEM;
119
120 bzero(entry, sizeof(*entry));
121 entry->protocol = protocol;
122 entry->input = input;
123 entry->detached = detached;
124
125 {
126 struct domain *dp = domains;
127 extern lck_mtx_t *domain_proto_mtx;
128
129 lck_mtx_assert(domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED);
130 lck_mtx_lock(domain_proto_mtx);
131 while (dp && dp->dom_family != protocol)
132 dp = dp->dom_next;
133 entry->domain = dp;
134 lck_mtx_unlock(domain_proto_mtx);
135 }
136
137
138 do {
139 entry->next = proto_input_add_list;
140 } while(!OSCompareAndSwap((UInt32)entry->next, (UInt32)entry, (UInt32*)&proto_input_add_list));
141
142 wakeup((caddr_t)&dlil_input_thread_wakeup);
143
144 return 0;
145}
146
147
148__private_extern__ void
149proto_unregister_input(
150 protocol_family_t protocol)
151{
152 struct proto_input_entry *entry = NULL;
153
154 for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next)
155 if (entry->protocol == protocol)
156 break;
157
158 if (entry)
159 entry->detach = 1;
160}
161
162
163static void
164proto_delayed_attach(
165 struct proto_input_entry *entry)
166{
167 struct proto_input_entry *next_entry;
168 for (next_entry = entry->next; entry; entry = next_entry) {
169 struct proto_input_entry *exist;
170 int hash_slot;
171
172 hash_slot = proto_hash_value(entry->protocol);
173 next_entry = entry->next;
174
175 for (exist = proto_hash[hash_slot]; exist; exist = exist->next)
176 if (exist->protocol == entry->protocol)
177 break;
178
179 /* If the entry already exists, call detached and dispose */
180 if (exist) {
181 if (entry->detached)
182 entry->detached(entry->protocol);
183 FREE(entry, M_IFADDR);
184 }
185 else {
186 entry->next = proto_hash[hash_slot];
187 proto_hash[hash_slot] = entry;
188 }
189 }
190}
191
192static void
193proto_delayed_inject(
194 struct proto_input_entry *entry)
195{
196 mbuf_t packet_list;
197 mbuf_t packet;
198 int locked = 0;
199
200 lck_mtx_lock(proto_input_lock);
201 packet_list = entry->first_packet;
202 entry->first_packet = entry->last_packet = 0;
203 lck_mtx_unlock(proto_input_lock);
204
205 if (packet_list == NULL)
206 return;
207
208 if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) {
209 lck_mtx_lock(entry->domain->dom_mtx);
210 locked = 1;
211 }
212
213 for (packet = packet_list; packet; packet = packet_list) {
214 packet_list = mbuf_nextpkt(packet);
215 mbuf_setnextpkt(packet, NULL);
216 entry->input(entry->protocol, packet);
217 }
218
219 if (locked) {
220 lck_mtx_unlock(entry->domain->dom_mtx);
221 }
222}
223
224/* This function must be called from a single dlil input thread */
225__private_extern__ void
226proto_input_run(void)
227{
228 struct proto_input_entry *entry;
229 u_int32_t inject;
230 int i;
231
232 if (current_thread() != dlil_input_thread_ptr)
233 panic("proto_input_run called from a thread other than dlil_input_thread!\n");
234
235 do {
236 entry = proto_input_add_list;
237 } while (entry && !OSCompareAndSwap((UInt32)entry, 0, (UInt32*)&proto_input_add_list));
238
239 if (entry)
240 proto_delayed_attach(entry);
241
242 do {
243 inject = inject_buckets;
244 } while (inject && !OSCompareAndSwap(inject, 0, (UInt32*)&inject_buckets));
245
246 if (inject) {
247 for (i = 0; i < PROTO_HASH_SLOTS; i++) {
248 if ((inject & (1L << i)) != 0) {
249 for (entry = proto_hash[i]; entry; entry = entry->next) {
250 if (entry->first_packet) {
251 proto_delayed_inject(entry);
252 }
253 }
254 }
255 }
256 }
257}
258
259errno_t
260proto_input(
261 protocol_family_t protocol,
262 mbuf_t packet_list)
263{
264 struct proto_input_entry *entry;
265
266 if (current_thread() != dlil_input_thread_ptr)
267 panic("proto_input called from a thread other than dlil_input_thread!\n");
268
269 for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next) {
270 if (entry->protocol == protocol)
271 break;
272 }
273
274 if (entry) {
275 mbuf_t packet;
276#if DIRECT_PROTO_INPUT
277 // See <rdar://problem/3687868> for why this is disabled
278 // We need to release the dlil lock before taking the protocol lock
279 for (packet = packet_list; packet; packet = packet_list) {
280 packet_list = mbuf_nextpkt(packet);
281 mbuf_setnextpkt(packet, NULL);
282 entry->input(entry->protocol, packet);
283 }
284#else
285 mbuf_t last_packet;
286 int hash_slot = proto_hash_value(protocol);
287
288 for (last_packet = packet_list; mbuf_nextpkt(last_packet);
289 last_packet = mbuf_nextpkt(last_packet))
290 /* find the last packet */;
291
292 lck_mtx_lock(proto_input_lock);
293 if (entry->first_packet == NULL) {
294 entry->first_packet = packet_list;
295 }
296 else {
297 mbuf_setnextpkt(entry->last_packet, packet_list);
298 }
299 entry->last_packet = last_packet;
300 lck_mtx_unlock(proto_input_lock);
301
302 OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets);
303#endif
304 }
305 else
306 {
307 return ENOENT;
308 }
309
310 return 0;
311}
312
313errno_t
314proto_inject(
315 protocol_family_t protocol,
316 mbuf_t packet_list)
317{
318 struct proto_input_entry *entry;
319 mbuf_t last_packet;
320 int hash_slot = proto_hash_value(protocol);
321
322 for (last_packet = packet_list; mbuf_nextpkt(last_packet);
323 last_packet = mbuf_nextpkt(last_packet))
324 /* find the last packet */;
325
326 for (entry = proto_hash[hash_slot]; entry; entry = entry->next) {
327 if (entry->protocol == protocol)
328 break;
329 }
330
331 if (entry) {
332 lck_mtx_lock(proto_input_lock);
333 if (entry->first_packet == NULL) {
334 entry->first_packet = packet_list;
335 }
336 else {
337 mbuf_setnextpkt(entry->last_packet, packet_list);
338 }
339 entry->last_packet = last_packet;
340 lck_mtx_unlock(proto_input_lock);
341
342 OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets);
343
344 wakeup((caddr_t)&dlil_input_thread_wakeup);
345 }
346 else
347 {
348 return ENOENT;
349 }
350
351 return 0;
352}
353
354errno_t
355proto_register_plumber(
356 protocol_family_t proto_fam,
357 ifnet_family_t if_fam,
358 proto_plumb_handler plumb,
359 proto_unplumb_handler unplumb)
360{
361 return dlil_reg_proto_module(proto_fam, if_fam, (attach_t)plumb, (detach_t)unplumb);
362}
363
364void
365proto_unregister_plumber(
366 protocol_family_t proto_fam,
367 ifnet_family_t if_fam)
368{
369 (void)dlil_dereg_proto_module(proto_fam, if_fam);
370}