]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/kpi_protocol.c
xnu-792.12.6.tar.gz
[apple/xnu.git] / bsd / net / kpi_protocol.c
1 /*
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 #include "kpi_protocol.h"
32
33 #include <sys/param.h>
34 #include <sys/malloc.h>
35 #include <sys/socket.h>
36 #include <sys/systm.h>
37 #include <sys/kpi_mbuf.h>
38 #include <sys/domain.h>
39 #include <net/if.h>
40 #include <net/dlil.h>
41 #include <libkern/OSAtomic.h>
42
43 void proto_kpi_init(void);
44 void proto_input_run(void);
45
46 typedef int (*attach_t)(struct ifnet *ifp, u_long protocol_family);
47 typedef int (*detach_t)(struct ifnet *ifp, u_long protocol_family);
48
49 /****************************************************************************/
50 /* WARNING: Big assumption made here - there can be only one input thread */
51 struct proto_input_entry {
52 struct proto_input_entry *next;
53 int detach;
54 struct domain *domain;
55
56 protocol_family_t protocol;
57 proto_input_handler input;
58 proto_input_detached_handler detached;
59
60 mbuf_t first_packet;
61 mbuf_t last_packet;
62 };
63
64 #define PROTO_HASH_SLOTS 5
65
66 static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS];
67 static struct proto_input_entry *proto_input_add_list;
68 static lck_mtx_t *proto_input_lock = 0;
69 __private_extern__ u_int32_t inject_buckets = 0;
70
71 extern thread_t dlil_input_thread_ptr;
72 extern int dlil_input_thread_wakeup;
73
74 static int
75 proto_hash_value(
76 protocol_family_t protocol)
77 {
78 switch(protocol) {
79 case PF_INET:
80 return 0;
81 case PF_INET6:
82 return 1;
83 case PF_APPLETALK:
84 return 2;
85 case PF_VLAN:
86 return 3;
87 }
88 return 4;
89 }
90
91 __private_extern__ void
92 proto_kpi_init(void)
93 {
94 lck_grp_attr_t *grp_attrib = 0;
95 lck_attr_t *lck_attrib = 0;
96 lck_grp_t *lck_group = 0;
97
98 /* Allocate a mtx lock */
99 grp_attrib = lck_grp_attr_alloc_init();
100 lck_grp_attr_setdefault(grp_attrib);
101 lck_group = lck_grp_alloc_init("protocol kpi", grp_attrib);
102 lck_grp_attr_free(grp_attrib);
103 lck_attrib = lck_attr_alloc_init();
104 lck_attr_setdefault(lck_attrib);
105 proto_input_lock = lck_mtx_alloc_init(lck_group, lck_attrib);
106 lck_grp_free(lck_group);
107 lck_attr_free(lck_attrib);
108 }
109
110 __private_extern__ errno_t
111 proto_register_input(
112 protocol_family_t protocol,
113 proto_input_handler input,
114 proto_input_detached_handler detached)
115 {
116
117 struct proto_input_entry *entry;
118
119 entry = _MALLOC(sizeof(*entry), M_IFADDR, M_WAITOK);
120
121 if (entry == NULL)
122 return ENOMEM;
123
124 bzero(entry, sizeof(*entry));
125 entry->protocol = protocol;
126 entry->input = input;
127 entry->detached = detached;
128
129 {
130 struct domain *dp = domains;
131 extern lck_mtx_t *domain_proto_mtx;
132
133 lck_mtx_assert(domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED);
134 lck_mtx_lock(domain_proto_mtx);
135 while (dp && dp->dom_family != protocol)
136 dp = dp->dom_next;
137 entry->domain = dp;
138 lck_mtx_unlock(domain_proto_mtx);
139 }
140
141
142 do {
143 entry->next = proto_input_add_list;
144 } while(!OSCompareAndSwap((UInt32)entry->next, (UInt32)entry, (UInt32*)&proto_input_add_list));
145
146 wakeup((caddr_t)&dlil_input_thread_wakeup);
147
148 return 0;
149 }
150
151
152 __private_extern__ void
153 proto_unregister_input(
154 protocol_family_t protocol)
155 {
156 struct proto_input_entry *entry = NULL;
157
158 for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next)
159 if (entry->protocol == protocol)
160 break;
161
162 if (entry)
163 entry->detach = 1;
164 }
165
166
167 static void
168 proto_delayed_attach(
169 struct proto_input_entry *entry)
170 {
171 struct proto_input_entry *next_entry;
172 for (next_entry = entry->next; entry; entry = next_entry) {
173 struct proto_input_entry *exist;
174 int hash_slot;
175
176 hash_slot = proto_hash_value(entry->protocol);
177 next_entry = entry->next;
178
179 for (exist = proto_hash[hash_slot]; exist; exist = exist->next)
180 if (exist->protocol == entry->protocol)
181 break;
182
183 /* If the entry already exists, call detached and dispose */
184 if (exist) {
185 if (entry->detached)
186 entry->detached(entry->protocol);
187 FREE(entry, M_IFADDR);
188 }
189 else {
190 entry->next = proto_hash[hash_slot];
191 proto_hash[hash_slot] = entry;
192 }
193 }
194 }
195
196 static void
197 proto_delayed_inject(
198 struct proto_input_entry *entry)
199 {
200 mbuf_t packet_list;
201 mbuf_t packet;
202 int locked = 0;
203
204 lck_mtx_lock(proto_input_lock);
205 packet_list = entry->first_packet;
206 entry->first_packet = entry->last_packet = 0;
207 lck_mtx_unlock(proto_input_lock);
208
209 if (packet_list == NULL)
210 return;
211
212 if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) {
213 lck_mtx_lock(entry->domain->dom_mtx);
214 locked = 1;
215 }
216
217 for (packet = packet_list; packet; packet = packet_list) {
218 packet_list = mbuf_nextpkt(packet);
219 mbuf_setnextpkt(packet, NULL);
220 entry->input(entry->protocol, packet);
221 }
222
223 if (locked) {
224 lck_mtx_unlock(entry->domain->dom_mtx);
225 }
226 }
227
228 /* This function must be called from a single dlil input thread */
229 __private_extern__ void
230 proto_input_run(void)
231 {
232 struct proto_input_entry *entry;
233 u_int32_t inject;
234 int i;
235
236 if (current_thread() != dlil_input_thread_ptr)
237 panic("proto_input_run called from a thread other than dlil_input_thread!\n");
238
239 do {
240 entry = proto_input_add_list;
241 } while (entry && !OSCompareAndSwap((UInt32)entry, 0, (UInt32*)&proto_input_add_list));
242
243 if (entry)
244 proto_delayed_attach(entry);
245
246 do {
247 inject = inject_buckets;
248 } while (inject && !OSCompareAndSwap(inject, 0, (UInt32*)&inject_buckets));
249
250 if (inject) {
251 for (i = 0; i < PROTO_HASH_SLOTS; i++) {
252 if ((inject & (1L << i)) != 0) {
253 for (entry = proto_hash[i]; entry; entry = entry->next) {
254 if (entry->first_packet) {
255 proto_delayed_inject(entry);
256 }
257 }
258 }
259 }
260 }
261 }
262
263 errno_t
264 proto_input(
265 protocol_family_t protocol,
266 mbuf_t packet_list)
267 {
268 struct proto_input_entry *entry;
269
270 if (current_thread() != dlil_input_thread_ptr)
271 panic("proto_input called from a thread other than dlil_input_thread!\n");
272
273 for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next) {
274 if (entry->protocol == protocol)
275 break;
276 }
277
278 if (entry) {
279 mbuf_t packet;
280 #if DIRECT_PROTO_INPUT
281 // See <rdar://problem/3687868> for why this is disabled
282 // We need to release the dlil lock before taking the protocol lock
283 for (packet = packet_list; packet; packet = packet_list) {
284 packet_list = mbuf_nextpkt(packet);
285 mbuf_setnextpkt(packet, NULL);
286 entry->input(entry->protocol, packet);
287 }
288 #else
289 mbuf_t last_packet;
290 int hash_slot = proto_hash_value(protocol);
291
292 for (last_packet = packet_list; mbuf_nextpkt(last_packet);
293 last_packet = mbuf_nextpkt(last_packet))
294 /* find the last packet */;
295
296 lck_mtx_lock(proto_input_lock);
297 if (entry->first_packet == NULL) {
298 entry->first_packet = packet_list;
299 }
300 else {
301 mbuf_setnextpkt(entry->last_packet, packet_list);
302 }
303 entry->last_packet = last_packet;
304 lck_mtx_unlock(proto_input_lock);
305
306 OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets);
307 #endif
308 }
309 else
310 {
311 return ENOENT;
312 }
313
314 return 0;
315 }
316
317 errno_t
318 proto_inject(
319 protocol_family_t protocol,
320 mbuf_t packet_list)
321 {
322 struct proto_input_entry *entry;
323 mbuf_t last_packet;
324 int hash_slot = proto_hash_value(protocol);
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) {
336 lck_mtx_lock(proto_input_lock);
337 if (entry->first_packet == NULL) {
338 entry->first_packet = packet_list;
339 }
340 else {
341 mbuf_setnextpkt(entry->last_packet, packet_list);
342 }
343 entry->last_packet = last_packet;
344 lck_mtx_unlock(proto_input_lock);
345
346 OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets);
347
348 wakeup((caddr_t)&dlil_input_thread_wakeup);
349 }
350 else
351 {
352 return ENOENT;
353 }
354
355 return 0;
356 }
357
358 errno_t
359 proto_register_plumber(
360 protocol_family_t proto_fam,
361 ifnet_family_t if_fam,
362 proto_plumb_handler plumb,
363 proto_unplumb_handler unplumb)
364 {
365 return dlil_reg_proto_module(proto_fam, if_fam, (attach_t)plumb, (detach_t)unplumb);
366 }
367
368 void
369 proto_unregister_plumber(
370 protocol_family_t proto_fam,
371 ifnet_family_t if_fam)
372 {
373 (void)dlil_dereg_proto_module(proto_fam, if_fam);
374 }