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