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