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