]>
Commit | Line | Data |
---|---|---|
91447636 | 1 | /* |
6d2010ae | 2 | * Copyright (c) 2004-2010 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
91447636 | 5 | * |
2d21ac55 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. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 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 | ||
91447636 A |
41 | void proto_input_run(void); |
42 | ||
b0d623f7 A |
43 | typedef int (*attach_t)(struct ifnet *ifp, uint32_t protocol_family); |
44 | typedef int (*detach_t)(struct ifnet *ifp, uint32_t protocol_family); | |
91447636 | 45 | |
91447636 A |
46 | struct proto_input_entry { |
47 | struct proto_input_entry *next; | |
48 | int detach; | |
49 | struct domain *domain; | |
2d21ac55 A |
50 | int hash; |
51 | int chain; | |
91447636 A |
52 | |
53 | protocol_family_t protocol; | |
54 | proto_input_handler input; | |
55 | proto_input_detached_handler detached; | |
56 | ||
2d21ac55 A |
57 | mbuf_t inject_first; |
58 | mbuf_t inject_last; | |
59 | ||
60 | struct proto_input_entry *input_next; | |
61 | mbuf_t input_first; | |
62 | mbuf_t input_last; | |
63 | }; | |
64 | ||
65 | ||
66 | struct proto_family_str { | |
67 | TAILQ_ENTRY(proto_family_str) proto_fam_next; | |
68 | protocol_family_t proto_family; | |
69 | ifnet_family_t if_family; | |
70 | proto_plumb_handler attach_proto; | |
71 | proto_unplumb_handler detach_proto; | |
91447636 A |
72 | }; |
73 | ||
74 | #define PROTO_HASH_SLOTS 5 | |
75 | ||
2d21ac55 A |
76 | static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS]; |
77 | static int proto_total_waiting = 0; | |
78 | static struct proto_input_entry *proto_input_add_list = NULL; | |
79 | static lck_mtx_t *proto_family_mutex = 0; | |
80 | static TAILQ_HEAD(, proto_family_str) proto_family_head = | |
81 | TAILQ_HEAD_INITIALIZER(proto_family_head); | |
91447636 | 82 | |
2d21ac55 A |
83 | extern lck_mtx_t *domain_proto_mtx; |
84 | extern struct dlil_threading_info *dlil_lo_thread_ptr; | |
91447636 A |
85 | |
86 | static int | |
87 | proto_hash_value( | |
88 | protocol_family_t protocol) | |
89 | { | |
90 | switch(protocol) { | |
91 | case PF_INET: | |
92 | return 0; | |
93 | case PF_INET6: | |
94 | return 1; | |
95 | case PF_APPLETALK: | |
96 | return 2; | |
97 | case PF_VLAN: | |
98 | return 3; | |
99 | } | |
100 | return 4; | |
101 | } | |
102 | ||
103 | __private_extern__ void | |
104 | proto_kpi_init(void) | |
105 | { | |
106 | lck_grp_attr_t *grp_attrib = 0; | |
107 | lck_attr_t *lck_attrib = 0; | |
108 | lck_grp_t *lck_group = 0; | |
109 | ||
110 | /* Allocate a mtx lock */ | |
111 | grp_attrib = lck_grp_attr_alloc_init(); | |
91447636 A |
112 | lck_group = lck_grp_alloc_init("protocol kpi", grp_attrib); |
113 | lck_grp_attr_free(grp_attrib); | |
114 | lck_attrib = lck_attr_alloc_init(); | |
2d21ac55 | 115 | proto_family_mutex = lck_mtx_alloc_init(lck_group, lck_attrib); |
91447636 A |
116 | lck_grp_free(lck_group); |
117 | lck_attr_free(lck_attrib); | |
2d21ac55 A |
118 | |
119 | bzero(proto_hash, sizeof(proto_hash)); | |
91447636 A |
120 | } |
121 | ||
122 | __private_extern__ errno_t | |
123 | proto_register_input( | |
124 | protocol_family_t protocol, | |
125 | proto_input_handler input, | |
2d21ac55 A |
126 | proto_input_detached_handler detached, |
127 | int chains) | |
91447636 A |
128 | { |
129 | ||
130 | struct proto_input_entry *entry; | |
2d21ac55 | 131 | struct dlil_threading_info *thread = dlil_lo_thread_ptr; |
91447636 A |
132 | |
133 | entry = _MALLOC(sizeof(*entry), M_IFADDR, M_WAITOK); | |
134 | ||
135 | if (entry == NULL) | |
136 | return ENOMEM; | |
137 | ||
138 | bzero(entry, sizeof(*entry)); | |
139 | entry->protocol = protocol; | |
140 | entry->input = input; | |
141 | entry->detached = detached; | |
2d21ac55 A |
142 | entry->hash = proto_hash_value(protocol); |
143 | entry->chain = chains; | |
91447636 A |
144 | |
145 | { | |
146 | struct domain *dp = domains; | |
91447636 A |
147 | |
148 | lck_mtx_assert(domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); | |
149 | lck_mtx_lock(domain_proto_mtx); | |
2d21ac55 | 150 | while (dp && (protocol_family_t)dp->dom_family != protocol) |
91447636 A |
151 | dp = dp->dom_next; |
152 | entry->domain = dp; | |
153 | lck_mtx_unlock(domain_proto_mtx); | |
154 | } | |
155 | ||
156 | ||
6d2010ae | 157 | lck_mtx_lock(&thread->input_lck); |
2d21ac55 A |
158 | entry->next = proto_input_add_list; |
159 | proto_input_add_list = entry; | |
91447636 | 160 | |
2d21ac55 A |
161 | thread->input_waiting |= DLIL_PROTO_REGISTER; |
162 | if ((thread->input_waiting & DLIL_INPUT_RUNNING) == 0) | |
163 | wakeup((caddr_t)&thread->input_waiting); | |
6d2010ae | 164 | lck_mtx_unlock(&thread->input_lck); |
91447636 A |
165 | |
166 | return 0; | |
167 | } | |
168 | ||
169 | ||
170 | __private_extern__ void | |
171 | proto_unregister_input( | |
172 | protocol_family_t protocol) | |
173 | { | |
174 | struct proto_input_entry *entry = NULL; | |
175 | ||
176 | for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next) | |
177 | if (entry->protocol == protocol) | |
178 | break; | |
179 | ||
180 | if (entry) | |
181 | entry->detach = 1; | |
182 | } | |
183 | ||
184 | ||
185 | static void | |
186 | proto_delayed_attach( | |
187 | struct proto_input_entry *entry) | |
188 | { | |
189 | struct proto_input_entry *next_entry; | |
190 | for (next_entry = entry->next; entry; entry = next_entry) { | |
191 | struct proto_input_entry *exist; | |
192 | int hash_slot; | |
193 | ||
194 | hash_slot = proto_hash_value(entry->protocol); | |
195 | next_entry = entry->next; | |
196 | ||
197 | for (exist = proto_hash[hash_slot]; exist; exist = exist->next) | |
198 | if (exist->protocol == entry->protocol) | |
199 | break; | |
200 | ||
201 | /* If the entry already exists, call detached and dispose */ | |
202 | if (exist) { | |
203 | if (entry->detached) | |
204 | entry->detached(entry->protocol); | |
205 | FREE(entry, M_IFADDR); | |
206 | } | |
207 | else { | |
208 | entry->next = proto_hash[hash_slot]; | |
209 | proto_hash[hash_slot] = entry; | |
210 | } | |
211 | } | |
212 | } | |
213 | ||
91447636 A |
214 | __private_extern__ void |
215 | proto_input_run(void) | |
216 | { | |
217 | struct proto_input_entry *entry; | |
2d21ac55 A |
218 | struct dlil_threading_info *thread = dlil_lo_thread_ptr; |
219 | mbuf_t packet_list; | |
220 | int i, locked = 0; | |
221 | ||
6d2010ae | 222 | lck_mtx_assert(&thread->input_lck, LCK_MTX_ASSERT_NOTOWNED); |
91447636 | 223 | |
2d21ac55 | 224 | if ((thread->input_waiting & DLIL_PROTO_REGISTER) != 0) { |
6d2010ae | 225 | lck_mtx_lock_spin(&thread->input_lck); |
91447636 | 226 | entry = proto_input_add_list; |
2d21ac55 A |
227 | proto_input_add_list = NULL; |
228 | thread->input_waiting &= ~DLIL_PROTO_REGISTER; | |
6d2010ae | 229 | lck_mtx_unlock(&thread->input_lck); |
91447636 | 230 | proto_delayed_attach(entry); |
2d21ac55 A |
231 | } |
232 | /* | |
233 | Move everything from the lock protected list to the thread | |
234 | specific list. | |
235 | */ | |
236 | for (i = 0; proto_total_waiting != 0 && i < PROTO_HASH_SLOTS; i++) { | |
237 | for (entry = proto_hash[i]; entry && proto_total_waiting; | |
238 | entry = entry->next) { | |
239 | if (entry->inject_first) { | |
6d2010ae | 240 | lck_mtx_lock_spin(&thread->input_lck); |
2d21ac55 A |
241 | thread->input_waiting &= ~DLIL_PROTO_WAITING; |
242 | ||
243 | packet_list = entry->inject_first; | |
244 | ||
245 | entry->inject_first = NULL; | |
246 | entry->inject_last = NULL; | |
247 | proto_total_waiting--; | |
248 | ||
6d2010ae | 249 | lck_mtx_unlock(&thread->input_lck); |
2d21ac55 A |
250 | |
251 | if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) { | |
252 | lck_mtx_lock(entry->domain->dom_mtx); | |
253 | locked = 1; | |
254 | } | |
255 | ||
256 | if (entry->chain) { | |
257 | entry->input(entry->protocol, packet_list); | |
258 | } | |
259 | else { | |
260 | mbuf_t packet; | |
261 | ||
262 | for (packet = packet_list; packet; packet = packet_list) { | |
263 | packet_list = mbuf_nextpkt(packet); | |
264 | mbuf_setnextpkt(packet, NULL); | |
265 | entry->input(entry->protocol, packet); | |
91447636 A |
266 | } |
267 | } | |
2d21ac55 | 268 | if (locked) { |
4a3eedf9 | 269 | locked = 0; |
2d21ac55 A |
270 | lck_mtx_unlock(entry->domain->dom_mtx); |
271 | } | |
91447636 A |
272 | } |
273 | } | |
2d21ac55 A |
274 | } |
275 | ||
91447636 A |
276 | } |
277 | ||
278 | errno_t | |
279 | proto_input( | |
280 | protocol_family_t protocol, | |
281 | mbuf_t packet_list) | |
282 | { | |
2d21ac55 A |
283 | struct proto_input_entry *entry; |
284 | errno_t locked =0, result = 0; | |
285 | ||
286 | for (entry = proto_hash[proto_hash_value(protocol)]; entry; | |
287 | entry = entry->next) { | |
91447636 A |
288 | if (entry->protocol == protocol) |
289 | break; | |
290 | } | |
291 | ||
2d21ac55 A |
292 | if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) { |
293 | lck_mtx_lock(entry->domain->dom_mtx); | |
294 | locked = 1; | |
295 | } | |
296 | ||
297 | if (entry->chain) { | |
298 | entry->input(entry->protocol, packet_list); | |
299 | } | |
300 | else { | |
91447636 | 301 | mbuf_t packet; |
2d21ac55 | 302 | |
91447636 A |
303 | for (packet = packet_list; packet; packet = packet_list) { |
304 | packet_list = mbuf_nextpkt(packet); | |
305 | mbuf_setnextpkt(packet, NULL); | |
306 | entry->input(entry->protocol, packet); | |
307 | } | |
91447636 A |
308 | } |
309 | ||
2d21ac55 A |
310 | if (locked) { |
311 | lck_mtx_unlock(entry->domain->dom_mtx); | |
312 | } | |
313 | return result; | |
91447636 A |
314 | } |
315 | ||
316 | errno_t | |
317 | proto_inject( | |
318 | protocol_family_t protocol, | |
319 | mbuf_t packet_list) | |
320 | { | |
321 | struct proto_input_entry *entry; | |
2d21ac55 A |
322 | mbuf_t last_packet; |
323 | int hash_slot = proto_hash_value(protocol); | |
324 | struct dlil_threading_info *thread = dlil_lo_thread_ptr; | |
91447636 A |
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) { | |
6d2010ae | 336 | lck_mtx_lock(&thread->input_lck); |
2d21ac55 A |
337 | if (entry->inject_first == NULL) { |
338 | proto_total_waiting++; | |
339 | thread->input_waiting |= DLIL_PROTO_WAITING; | |
340 | entry->inject_first = packet_list; | |
91447636 A |
341 | } |
342 | else { | |
2d21ac55 | 343 | mbuf_setnextpkt(entry->inject_last, packet_list); |
91447636 | 344 | } |
2d21ac55 A |
345 | entry->inject_last = last_packet; |
346 | if ((thread->input_waiting & DLIL_INPUT_RUNNING) == 0) { | |
347 | wakeup((caddr_t)&thread->input_waiting); | |
348 | } | |
6d2010ae | 349 | lck_mtx_unlock(&thread->input_lck); |
91447636 A |
350 | } |
351 | else | |
352 | { | |
353 | return ENOENT; | |
354 | } | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
2d21ac55 A |
359 | static struct proto_family_str* |
360 | proto_plumber_find( | |
361 | protocol_family_t proto_family, | |
362 | ifnet_family_t if_family) | |
363 | { | |
364 | struct proto_family_str *mod = NULL; | |
365 | ||
366 | TAILQ_FOREACH(mod, &proto_family_head, proto_fam_next) { | |
367 | if ((mod->proto_family == (proto_family & 0xffff)) | |
368 | && (mod->if_family == (if_family & 0xffff))) | |
369 | break; | |
370 | } | |
371 | ||
372 | return mod; | |
373 | } | |
374 | ||
91447636 A |
375 | errno_t |
376 | proto_register_plumber( | |
2d21ac55 A |
377 | protocol_family_t protocol_family, |
378 | ifnet_family_t interface_family, | |
379 | proto_plumb_handler attach, | |
380 | proto_unplumb_handler detach) | |
91447636 | 381 | { |
2d21ac55 A |
382 | struct proto_family_str *proto_family; |
383 | ||
384 | if (attach == NULL) return EINVAL; | |
385 | ||
386 | lck_mtx_lock(proto_family_mutex); | |
387 | ||
388 | TAILQ_FOREACH(proto_family, &proto_family_head, proto_fam_next) { | |
389 | if (proto_family->proto_family == protocol_family && | |
390 | proto_family->if_family == interface_family) { | |
391 | lck_mtx_unlock(proto_family_mutex); | |
392 | return EEXIST; | |
393 | } | |
394 | } | |
395 | ||
396 | proto_family = (struct proto_family_str *) _MALLOC(sizeof(struct proto_family_str), M_IFADDR, M_WAITOK); | |
397 | if (!proto_family) { | |
398 | lck_mtx_unlock(proto_family_mutex); | |
399 | return ENOMEM; | |
400 | } | |
401 | ||
402 | bzero(proto_family, sizeof(struct proto_family_str)); | |
403 | proto_family->proto_family = protocol_family; | |
404 | proto_family->if_family = interface_family & 0xffff; | |
405 | proto_family->attach_proto = attach; | |
406 | proto_family->detach_proto = detach; | |
407 | ||
408 | TAILQ_INSERT_TAIL(&proto_family_head, proto_family, proto_fam_next); | |
409 | lck_mtx_unlock(proto_family_mutex); | |
410 | return 0; | |
91447636 A |
411 | } |
412 | ||
413 | void | |
414 | proto_unregister_plumber( | |
2d21ac55 A |
415 | protocol_family_t protocol_family, |
416 | ifnet_family_t interface_family) | |
91447636 | 417 | { |
2d21ac55 A |
418 | struct proto_family_str *proto_family; |
419 | ||
420 | lck_mtx_lock(proto_family_mutex); | |
421 | ||
422 | proto_family = proto_plumber_find(protocol_family, interface_family); | |
423 | if (proto_family == 0) { | |
424 | lck_mtx_unlock(proto_family_mutex); | |
425 | return; | |
426 | } | |
427 | ||
428 | TAILQ_REMOVE(&proto_family_head, proto_family, proto_fam_next); | |
429 | FREE(proto_family, M_IFADDR); | |
430 | ||
431 | lck_mtx_unlock(proto_family_mutex); | |
432 | return; | |
433 | } | |
434 | ||
435 | __private_extern__ errno_t | |
436 | proto_plumb( | |
437 | protocol_family_t protocol_family, | |
438 | ifnet_t ifp) | |
439 | { | |
440 | struct proto_family_str *proto_family; | |
441 | int ret = 0; | |
442 | ||
443 | lck_mtx_lock(proto_family_mutex); | |
444 | proto_family = proto_plumber_find(protocol_family, ifp->if_family); | |
445 | if (proto_family == 0) { | |
446 | lck_mtx_unlock(proto_family_mutex); | |
447 | return ENXIO; | |
448 | } | |
449 | ||
450 | ret = proto_family->attach_proto(ifp, protocol_family); | |
451 | ||
452 | lck_mtx_unlock(proto_family_mutex); | |
453 | return ret; | |
454 | } | |
455 | ||
456 | ||
457 | __private_extern__ errno_t | |
458 | proto_unplumb( | |
459 | protocol_family_t protocol_family, | |
460 | ifnet_t ifp) | |
461 | { | |
462 | struct proto_family_str *proto_family; | |
463 | int ret = 0; | |
464 | ||
465 | lck_mtx_lock(proto_family_mutex); | |
466 | ||
467 | proto_family = proto_plumber_find(protocol_family, ifp->if_family); | |
468 | if (proto_family && proto_family->detach_proto) | |
469 | proto_family->detach_proto(ifp, protocol_family); | |
470 | else | |
471 | ret = ifnet_detach_protocol(ifp, protocol_family); | |
472 | ||
473 | lck_mtx_unlock(proto_family_mutex); | |
474 | return ret; | |
91447636 | 475 | } |