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