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