xref: /xnu-8792.81.2/bsd/net/kpi_protocol.c (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
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 
41 void proto_input_run(void);
42 
43 typedef int (*attach_t)(struct ifnet *ifp, uint32_t protocol_family);
44 typedef int (*detach_t)(struct ifnet *ifp, uint32_t protocol_family);
45 
46 struct 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 
66 struct 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 static LCK_GRP_DECLARE(proto_family_grp, "protocol kpi");
75 static LCK_MTX_DECLARE(proto_family_mutex, &proto_family_grp);
76 
77 static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS];
78 static int proto_total_waiting = 0;
79 static struct proto_input_entry *proto_input_add_list = NULL;
80 static TAILQ_HEAD(, proto_family_str) proto_family_head =
81     TAILQ_HEAD_INITIALIZER(proto_family_head);
82 
83 __private_extern__ errno_t
proto_register_input(protocol_family_t protocol,proto_input_handler input,proto_input_detached_handler detached,int chains)84 proto_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(&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((caddr_t)&inp->dlth_flags);
123 	}
124 	lck_mtx_unlock(&inp->dlth_lock);
125 
126 	return 0;
127 }
128 
129 __private_extern__ void
proto_unregister_input(protocol_family_t protocol)130 proto_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 
146 static void
proto_delayed_attach(struct proto_input_entry * entry)147 proto_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
proto_input_run(void)179 proto_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(&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(&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(&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(&inp->dlth_lock);
215 
216 				if (entry->domain != NULL && !(entry->domain->
217 				    dom_flags & DOM_REENTRANT)) {
218 					lck_mtx_lock(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(packet);
233 						mbuf_setnextpkt(packet, NULL);
234 						entry->input(entry->protocol,
235 						    packet);
236 					}
237 				}
238 				if (locked) {
239 					locked = 0;
240 					lck_mtx_unlock(entry->domain->dom_mtx);
241 				}
242 			}
243 		}
244 	}
245 }
246 
247 errno_t
proto_input(protocol_family_t protocol,mbuf_t packet_list)248 proto_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(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(packet);
277 			mbuf_setnextpkt(packet, NULL);
278 			entry->input(entry->protocol, packet);
279 		}
280 	}
281 
282 	if (locked) {
283 		lck_mtx_unlock(entry->domain->dom_mtx);
284 	}
285 	return result;
286 }
287 
288 errno_t
proto_inject(protocol_family_t protocol,mbuf_t packet_list)289 proto_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(last_packet) != NULL;
297 	    last_packet = mbuf_nextpkt(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(&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(entry->inject_last, packet_list);
316 		}
317 		entry->inject_last = last_packet;
318 		if ((inp->dlth_flags & DLIL_INPUT_RUNNING) == 0) {
319 			wakeup((caddr_t)&inp->dlth_flags);
320 		}
321 		lck_mtx_unlock(&inp->dlth_lock);
322 	} else {
323 		return ENOENT;
324 	}
325 
326 	return 0;
327 }
328 
329 static struct proto_family_str *
proto_plumber_find(protocol_family_t proto_family,ifnet_family_t if_family)330 proto_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 
344 errno_t
proto_register_plumber(protocol_family_t protocol_family,ifnet_family_t interface_family,proto_plumb_handler attach,proto_unplumb_handler detach)345 proto_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(&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(&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(&proto_family_mutex);
375 	return 0;
376 }
377 
378 void
proto_unregister_plumber(protocol_family_t protocol_family,ifnet_family_t interface_family)379 proto_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(&proto_family_mutex);
385 
386 	proto_family = proto_plumber_find(protocol_family, interface_family);
387 	if (proto_family == NULL) {
388 		lck_mtx_unlock(&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(&proto_family_mutex);
396 }
397 
398 __private_extern__ errno_t
proto_plumb(protocol_family_t protocol_family,ifnet_t ifp)399 proto_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(&proto_family_mutex);
405 	proto_family = proto_plumber_find(protocol_family, ifp->if_family);
406 	if (proto_family == NULL) {
407 		lck_mtx_unlock(&proto_family_mutex);
408 		return ENXIO;
409 	}
410 
411 	ret = proto_family->attach_proto(ifp, protocol_family);
412 
413 	lck_mtx_unlock(&proto_family_mutex);
414 	return ret;
415 }
416 
417 
418 __private_extern__ errno_t
proto_unplumb(protocol_family_t protocol_family,ifnet_t ifp)419 proto_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(&proto_family_mutex);
425 
426 	proto_family = proto_plumber_find(protocol_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(ifp, protocol_family);
431 	}
432 
433 	lck_mtx_unlock(&proto_family_mutex);
434 	return ret;
435 }
436