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