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
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
74#define	PROTO_HASH_SLOTS	5
75
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);
83
84static int
85proto_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
101proto_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
120proto_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
159proto_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
173static void
174proto_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
204proto_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
272errno_t
273proto_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
308errno_t
309proto_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
347static struct proto_family_str *
348proto_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
361errno_t
362proto_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
399void
400proto_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
420proto_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
440proto_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}
456