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