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