]>
Commit | Line | Data |
---|---|---|
91447636 | 1 | /* |
5ba3f43e | 2 | * Copyright (c) 2003-2017 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
39236c6e | 5 | * |
2d21ac55 A |
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. | |
39236c6e | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
39236c6e | 17 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
39236c6e | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
91447636 A |
27 | */ |
28 | ||
29 | #include <sys/kpi_socketfilter.h> | |
30 | ||
31 | #include <sys/socket.h> | |
32 | #include <sys/param.h> | |
33 | #include <sys/errno.h> | |
34 | #include <sys/malloc.h> | |
35 | #include <sys/protosw.h> | |
316670eb | 36 | #include <sys/domain.h> |
6d2010ae | 37 | #include <sys/proc.h> |
91447636 | 38 | #include <kern/locks.h> |
6d2010ae A |
39 | #include <kern/thread.h> |
40 | #include <kern/debug.h> | |
91447636 | 41 | #include <net/kext_net.h> |
316670eb | 42 | #include <net/if.h> |
5ba3f43e | 43 | #include <net/net_api_stats.h> |
316670eb A |
44 | #include <netinet/in_var.h> |
45 | #include <netinet/ip.h> | |
46 | #include <netinet/ip_var.h> | |
47 | #include <netinet/tcp.h> | |
48 | #include <netinet/tcp_var.h> | |
49 | #include <netinet/udp.h> | |
50 | #include <netinet/udp_var.h> | |
91447636 | 51 | |
c910b4d9 | 52 | #include <libkern/libkern.h> |
6d2010ae | 53 | #include <libkern/OSAtomic.h> |
0a7de745 | 54 | #include <os/refcnt.h> |
c910b4d9 | 55 | |
5ba3f43e | 56 | #include <stdbool.h> |
2d21ac55 A |
57 | #include <string.h> |
58 | ||
0a7de745 A |
59 | #define SFEF_ATTACHED 0x1 /* SFE is on socket list */ |
60 | #define SFEF_NODETACH 0x2 /* Detach should not be called */ | |
61 | #define SFEF_NOSOCKET 0x4 /* Socket is gone */ | |
6d2010ae A |
62 | |
63 | struct socket_filter_entry { | |
0a7de745 A |
64 | struct socket_filter_entry *sfe_next_onsocket; |
65 | struct socket_filter_entry *sfe_next_onfilter; | |
66 | struct socket_filter_entry *sfe_next_oncleanup; | |
39236c6e | 67 | |
0a7de745 A |
68 | struct socket_filter *sfe_filter; |
69 | struct socket *sfe_socket; | |
70 | void *sfe_cookie; | |
39236c6e | 71 | |
0a7de745 A |
72 | uint32_t sfe_flags; |
73 | int32_t sfe_refcount; | |
6d2010ae A |
74 | }; |
75 | ||
76 | struct socket_filter { | |
0a7de745 A |
77 | TAILQ_ENTRY(socket_filter) sf_protosw_next; |
78 | TAILQ_ENTRY(socket_filter) sf_global_next; | |
79 | struct socket_filter_entry *sf_entry_head; | |
39236c6e | 80 | |
0a7de745 A |
81 | struct protosw *sf_proto; |
82 | struct sflt_filter sf_filter; | |
83 | struct os_refcnt sf_refcount; | |
6d2010ae A |
84 | }; |
85 | ||
86 | TAILQ_HEAD(socket_filter_list, socket_filter); | |
87 | ||
0a7de745 A |
88 | static struct socket_filter_list sock_filter_head; |
89 | static lck_rw_t *sock_filter_lock = NULL; | |
90 | static lck_mtx_t *sock_filter_cleanup_lock = NULL; | |
91 | static struct socket_filter_entry *sock_filter_cleanup_entries = NULL; | |
92 | static thread_t sock_filter_cleanup_thread = NULL; | |
91447636 | 93 | |
6d2010ae A |
94 | static void sflt_cleanup_thread(void *, wait_result_t); |
95 | static void sflt_detach_locked(struct socket_filter_entry *entry); | |
96 | ||
5ba3f43e A |
97 | #undef sflt_register |
98 | static errno_t sflt_register_common(const struct sflt_filter *filter, int domain, | |
99 | int type, int protocol, bool is_internal); | |
100 | errno_t sflt_register(const struct sflt_filter *filter, int domain, | |
101 | int type, int protocol); | |
102 | ||
103 | ||
6d2010ae | 104 | #pragma mark -- Internal State Management -- |
3a60a9f5 | 105 | |
743345f9 A |
106 | __private_extern__ int |
107 | sflt_permission_check(struct inpcb *inp) | |
108 | { | |
cb323159 A |
109 | /* Only IPv4 or IPv6 sockets can bypass filters */ |
110 | if (!(inp->inp_vflag & INP_IPV4) && | |
111 | !(inp->inp_vflag & INP_IPV6)) { | |
0a7de745 | 112 | return 0; |
743345f9 A |
113 | } |
114 | /* Sockets that have this entitlement bypass socket filters. */ | |
115 | if (INP_INTCOPROC_ALLOWED(inp)) { | |
0a7de745 | 116 | return 1; |
743345f9 | 117 | } |
cb323159 | 118 | /* Sockets bound to an intcoproc interface bypass socket filters. */ |
743345f9 A |
119 | if ((inp->inp_flags & INP_BOUND_IF) && |
120 | IFNET_IS_INTCOPROC(inp->inp_boundifp)) { | |
0a7de745 | 121 | return 1; |
743345f9 | 122 | } |
cb323159 A |
123 | #if NECP |
124 | /* | |
125 | * Make sure that the NECP policy is populated. | |
126 | * If result is not populated, the policy ID will be | |
127 | * NECP_KERNEL_POLICY_ID_NONE. Note that if the result | |
128 | * is populated, but there was no match, it will be | |
129 | * NECP_KERNEL_POLICY_ID_NO_MATCH. | |
130 | * Do not call inp_update_necp_policy() to avoid scoping | |
131 | * a socket prior to calls to bind(). | |
132 | */ | |
133 | if (inp->inp_policyresult.policy_id == NECP_KERNEL_POLICY_ID_NONE) { | |
134 | necp_socket_find_policy_match(inp, NULL, NULL, 0); | |
135 | } | |
136 | ||
137 | /* If the filter unit is marked to be "no filter", bypass filters */ | |
138 | if (inp->inp_policyresult.results.filter_control_unit == | |
139 | NECP_FILTER_UNIT_NO_FILTER) { | |
140 | return 1; | |
141 | } | |
142 | #endif /* NECP */ | |
0a7de745 | 143 | return 0; |
743345f9 A |
144 | } |
145 | ||
91447636 A |
146 | __private_extern__ void |
147 | sflt_init(void) | |
148 | { | |
0a7de745 A |
149 | lck_grp_attr_t *grp_attrib = NULL; |
150 | lck_attr_t *lck_attrib = NULL; | |
151 | lck_grp_t *lck_group = NULL; | |
39236c6e | 152 | |
91447636 | 153 | TAILQ_INIT(&sock_filter_head); |
39236c6e | 154 | |
6d2010ae | 155 | /* Allocate a rw lock */ |
91447636 | 156 | grp_attrib = lck_grp_attr_alloc_init(); |
91447636 A |
157 | lck_group = lck_grp_alloc_init("socket filter lock", grp_attrib); |
158 | lck_grp_attr_free(grp_attrib); | |
159 | lck_attrib = lck_attr_alloc_init(); | |
6d2010ae A |
160 | sock_filter_lock = lck_rw_alloc_init(lck_group, lck_attrib); |
161 | sock_filter_cleanup_lock = lck_mtx_alloc_init(lck_group, lck_attrib); | |
91447636 A |
162 | lck_grp_free(lck_group); |
163 | lck_attr_free(lck_attrib); | |
164 | } | |
165 | ||
6d2010ae | 166 | static void |
0a7de745 | 167 | sflt_retain_locked(struct socket_filter *filter) |
91447636 | 168 | { |
0a7de745 | 169 | os_ref_retain_locked(&filter->sf_refcount); |
6d2010ae A |
170 | } |
171 | ||
172 | static void | |
39236c6e | 173 | sflt_release_locked(struct socket_filter *filter) |
6d2010ae | 174 | { |
0a7de745 | 175 | if (os_ref_release_locked(&filter->sf_refcount) == 0) { |
39236c6e | 176 | /* Call the unregistered function */ |
6d2010ae A |
177 | if (filter->sf_filter.sf_unregistered) { |
178 | lck_rw_unlock_exclusive(sock_filter_lock); | |
39236c6e | 179 | filter->sf_filter.sf_unregistered( |
0a7de745 | 180 | filter->sf_filter.sf_handle); |
6d2010ae A |
181 | lck_rw_lock_exclusive(sock_filter_lock); |
182 | } | |
39236c6e A |
183 | |
184 | /* Free the entry */ | |
6d2010ae A |
185 | FREE(filter, M_IFADDR); |
186 | } | |
187 | } | |
188 | ||
189 | static void | |
39236c6e | 190 | sflt_entry_retain(struct socket_filter_entry *entry) |
6d2010ae | 191 | { |
39236c6e | 192 | if (OSIncrementAtomic(&entry->sfe_refcount) <= 0) { |
6d2010ae | 193 | panic("sflt_entry_retain - sfe_refcount <= 0\n"); |
39236c6e A |
194 | /* NOTREACHED */ |
195 | } | |
6d2010ae A |
196 | } |
197 | ||
198 | static void | |
39236c6e | 199 | sflt_entry_release(struct socket_filter_entry *entry) |
6d2010ae A |
200 | { |
201 | SInt32 old = OSDecrementAtomic(&entry->sfe_refcount); | |
202 | if (old == 1) { | |
39236c6e A |
203 | /* That was the last reference */ |
204 | ||
205 | /* Take the cleanup lock */ | |
6d2010ae | 206 | lck_mtx_lock(sock_filter_cleanup_lock); |
39236c6e A |
207 | |
208 | /* Put this item on the cleanup list */ | |
6d2010ae A |
209 | entry->sfe_next_oncleanup = sock_filter_cleanup_entries; |
210 | sock_filter_cleanup_entries = entry; | |
39236c6e A |
211 | |
212 | /* If the item is the first item in the list */ | |
6d2010ae A |
213 | if (entry->sfe_next_oncleanup == NULL) { |
214 | if (sock_filter_cleanup_thread == NULL) { | |
39236c6e A |
215 | /* Create a thread */ |
216 | kernel_thread_start(sflt_cleanup_thread, | |
217 | NULL, &sock_filter_cleanup_thread); | |
6d2010ae | 218 | } else { |
39236c6e | 219 | /* Wakeup the thread */ |
6d2010ae A |
220 | wakeup(&sock_filter_cleanup_entries); |
221 | } | |
222 | } | |
39236c6e A |
223 | |
224 | /* Drop the cleanup lock */ | |
6d2010ae | 225 | lck_mtx_unlock(sock_filter_cleanup_lock); |
39236c6e A |
226 | } else if (old <= 0) { |
227 | panic("sflt_entry_release - sfe_refcount (%d) <= 0\n", | |
228 | (int)old); | |
229 | /* NOTREACHED */ | |
6d2010ae A |
230 | } |
231 | } | |
232 | ||
39037602 | 233 | __attribute__((noreturn)) |
6d2010ae | 234 | static void |
39236c6e | 235 | sflt_cleanup_thread(void *blah, wait_result_t blah2) |
6d2010ae | 236 | { |
39236c6e | 237 | #pragma unused(blah, blah2) |
6d2010ae A |
238 | while (1) { |
239 | lck_mtx_lock(sock_filter_cleanup_lock); | |
240 | while (sock_filter_cleanup_entries == NULL) { | |
39236c6e A |
241 | /* Sleep until we've got something better to do */ |
242 | msleep(&sock_filter_cleanup_entries, | |
243 | sock_filter_cleanup_lock, PWAIT, | |
244 | "sflt_cleanup", NULL); | |
6d2010ae | 245 | } |
39236c6e A |
246 | |
247 | /* Pull the current list of dead items */ | |
248 | struct socket_filter_entry *dead = sock_filter_cleanup_entries; | |
6d2010ae | 249 | sock_filter_cleanup_entries = NULL; |
39236c6e A |
250 | |
251 | /* Drop the lock */ | |
6d2010ae | 252 | lck_mtx_unlock(sock_filter_cleanup_lock); |
39236c6e A |
253 | |
254 | /* Take the socket filter lock */ | |
6d2010ae | 255 | lck_rw_lock_exclusive(sock_filter_lock); |
39236c6e A |
256 | |
257 | /* Cleanup every dead item */ | |
0a7de745 | 258 | struct socket_filter_entry *entry; |
6d2010ae | 259 | for (entry = dead; entry; entry = dead) { |
0a7de745 | 260 | struct socket_filter_entry **nextpp; |
39236c6e | 261 | |
6d2010ae | 262 | dead = entry->sfe_next_oncleanup; |
39236c6e A |
263 | |
264 | /* Call detach function if necessary - drop the lock */ | |
6d2010ae | 265 | if ((entry->sfe_flags & SFEF_NODETACH) == 0 && |
39236c6e | 266 | entry->sfe_filter->sf_filter.sf_detach) { |
6d2010ae A |
267 | entry->sfe_flags |= SFEF_NODETACH; |
268 | lck_rw_unlock_exclusive(sock_filter_lock); | |
39236c6e A |
269 | |
270 | /* | |
271 | * Warning - passing a potentially | |
272 | * dead socket may be bad | |
273 | */ | |
0a7de745 A |
274 | entry->sfe_filter->sf_filter.sf_detach( |
275 | entry->sfe_cookie, entry->sfe_socket); | |
39236c6e | 276 | |
6d2010ae A |
277 | lck_rw_lock_exclusive(sock_filter_lock); |
278 | } | |
39236c6e A |
279 | |
280 | /* | |
281 | * Pull entry off the socket list -- | |
282 | * if the socket still exists | |
283 | */ | |
6d2010ae | 284 | if ((entry->sfe_flags & SFEF_NOSOCKET) == 0) { |
39236c6e A |
285 | for (nextpp = &entry->sfe_socket->so_filt; |
286 | *nextpp; | |
287 | nextpp = &(*nextpp)->sfe_next_onsocket) { | |
6d2010ae | 288 | if (*nextpp == entry) { |
39236c6e A |
289 | *nextpp = |
290 | entry->sfe_next_onsocket; | |
6d2010ae A |
291 | break; |
292 | } | |
293 | } | |
294 | } | |
39236c6e A |
295 | |
296 | /* Pull entry off the filter list */ | |
297 | for (nextpp = &entry->sfe_filter->sf_entry_head; | |
298 | *nextpp; nextpp = &(*nextpp)->sfe_next_onfilter) { | |
6d2010ae A |
299 | if (*nextpp == entry) { |
300 | *nextpp = entry->sfe_next_onfilter; | |
301 | break; | |
302 | } | |
303 | } | |
39236c6e A |
304 | |
305 | /* | |
306 | * Release the filter -- may drop lock, but that's okay | |
307 | */ | |
6d2010ae A |
308 | sflt_release_locked(entry->sfe_filter); |
309 | entry->sfe_socket = NULL; | |
310 | entry->sfe_filter = NULL; | |
311 | FREE(entry, M_IFADDR); | |
312 | } | |
39236c6e A |
313 | |
314 | /* Drop the socket filter lock */ | |
6d2010ae A |
315 | lck_rw_unlock_exclusive(sock_filter_lock); |
316 | } | |
39236c6e | 317 | /* NOTREACHED */ |
6d2010ae A |
318 | } |
319 | ||
320 | static int | |
39236c6e A |
321 | sflt_attach_locked(struct socket *so, struct socket_filter *filter, |
322 | int socklocked) | |
6d2010ae A |
323 | { |
324 | int error = 0; | |
325 | struct socket_filter_entry *entry = NULL; | |
39236c6e | 326 | |
0a7de745 A |
327 | if (sflt_permission_check(sotoinpcb(so))) { |
328 | return 0; | |
329 | } | |
743345f9 | 330 | |
0a7de745 A |
331 | if (filter == NULL) { |
332 | return ENOENT; | |
333 | } | |
316670eb | 334 | |
39236c6e | 335 | for (entry = so->so_filt; entry; entry = entry->sfe_next_onfilter) { |
316670eb | 336 | if (entry->sfe_filter->sf_filter.sf_handle == |
0a7de745 A |
337 | filter->sf_filter.sf_handle) { |
338 | return EEXIST; | |
339 | } | |
39236c6e | 340 | } |
316670eb | 341 | /* allocate the socket filter entry */ |
0a7de745 | 342 | MALLOC(entry, struct socket_filter_entry *, sizeof(*entry), M_IFADDR, |
316670eb | 343 | M_WAITOK); |
0a7de745 A |
344 | if (entry == NULL) { |
345 | return ENOMEM; | |
346 | } | |
39236c6e | 347 | |
316670eb A |
348 | /* Initialize the socket filter entry */ |
349 | entry->sfe_cookie = NULL; | |
350 | entry->sfe_flags = SFEF_ATTACHED; | |
39236c6e A |
351 | entry->sfe_refcount = 1; /* corresponds to SFEF_ATTACHED flag set */ |
352 | ||
316670eb A |
353 | /* Put the entry in the filter list */ |
354 | sflt_retain_locked(filter); | |
355 | entry->sfe_filter = filter; | |
356 | entry->sfe_next_onfilter = filter->sf_entry_head; | |
357 | filter->sf_entry_head = entry; | |
39236c6e | 358 | |
316670eb A |
359 | /* Put the entry on the socket filter list */ |
360 | entry->sfe_socket = so; | |
361 | entry->sfe_next_onsocket = so->so_filt; | |
362 | so->so_filt = entry; | |
363 | ||
364 | if (entry->sfe_filter->sf_filter.sf_attach) { | |
39236c6e | 365 | /* Retain the entry while we call attach */ |
316670eb | 366 | sflt_entry_retain(entry); |
39236c6e A |
367 | |
368 | /* | |
369 | * Release the filter lock -- | |
370 | * callers must be aware we will do this | |
371 | */ | |
316670eb | 372 | lck_rw_unlock_exclusive(sock_filter_lock); |
39236c6e A |
373 | |
374 | /* Unlock the socket */ | |
0a7de745 | 375 | if (socklocked) { |
316670eb | 376 | socket_unlock(so, 0); |
0a7de745 | 377 | } |
39236c6e A |
378 | |
379 | /* It's finally safe to call the filter function */ | |
380 | error = entry->sfe_filter->sf_filter.sf_attach( | |
0a7de745 | 381 | &entry->sfe_cookie, so); |
39236c6e A |
382 | |
383 | /* Lock the socket again */ | |
0a7de745 | 384 | if (socklocked) { |
316670eb | 385 | socket_lock(so, 0); |
0a7de745 | 386 | } |
39236c6e A |
387 | |
388 | /* Lock the filters again */ | |
316670eb | 389 | lck_rw_lock_exclusive(sock_filter_lock); |
39236c6e A |
390 | |
391 | /* | |
392 | * If the attach function returns an error, | |
393 | * this filter must be detached | |
394 | */ | |
316670eb | 395 | if (error) { |
39236c6e A |
396 | /* don't call sf_detach */ |
397 | entry->sfe_flags |= SFEF_NODETACH; | |
316670eb | 398 | sflt_detach_locked(entry); |
91447636 | 399 | } |
39236c6e A |
400 | |
401 | /* Release the retain we held through the attach call */ | |
316670eb | 402 | sflt_entry_release(entry); |
91447636 | 403 | } |
39236c6e | 404 | |
0a7de745 | 405 | return error; |
91447636 A |
406 | } |
407 | ||
6d2010ae | 408 | errno_t |
39236c6e | 409 | sflt_attach_internal(socket_t socket, sflt_handle handle) |
91447636 | 410 | { |
0a7de745 A |
411 | if (socket == NULL || handle == 0) { |
412 | return EINVAL; | |
413 | } | |
39236c6e | 414 | |
6d2010ae | 415 | int result = EINVAL; |
39236c6e | 416 | |
6d2010ae | 417 | lck_rw_lock_exclusive(sock_filter_lock); |
39236c6e | 418 | |
6d2010ae A |
419 | struct socket_filter *filter = NULL; |
420 | TAILQ_FOREACH(filter, &sock_filter_head, sf_global_next) { | |
0a7de745 A |
421 | if (filter->sf_filter.sf_handle == handle) { |
422 | break; | |
423 | } | |
6d2010ae | 424 | } |
39236c6e | 425 | |
6d2010ae A |
426 | if (filter) { |
427 | result = sflt_attach_locked(socket, filter, 1); | |
91447636 | 428 | } |
39236c6e | 429 | |
6d2010ae | 430 | lck_rw_unlock_exclusive(sock_filter_lock); |
39236c6e | 431 | |
0a7de745 | 432 | return result; |
91447636 A |
433 | } |
434 | ||
6d2010ae | 435 | static void |
39236c6e | 436 | sflt_detach_locked(struct socket_filter_entry *entry) |
6d2010ae A |
437 | { |
438 | if ((entry->sfe_flags & SFEF_ATTACHED) != 0) { | |
439 | entry->sfe_flags &= ~SFEF_ATTACHED; | |
440 | sflt_entry_release(entry); | |
441 | } | |
442 | } | |
443 | ||
444 | #pragma mark -- Socket Layer Hooks -- | |
445 | ||
91447636 | 446 | __private_extern__ void |
39236c6e | 447 | sflt_initsock(struct socket *so) |
91447636 | 448 | { |
39236c6e A |
449 | /* |
450 | * Point to the real protosw, as so_proto might have been | |
451 | * pointed to a modified version. | |
452 | */ | |
453 | struct protosw *proto = so->so_proto->pr_protosw; | |
454 | ||
6d2010ae A |
455 | lck_rw_lock_shared(sock_filter_lock); |
456 | if (TAILQ_FIRST(&proto->pr_filter_head) != NULL) { | |
39236c6e | 457 | /* Promote lock to exclusive */ |
0a7de745 | 458 | if (!lck_rw_lock_shared_to_exclusive(sock_filter_lock)) { |
6d2010ae | 459 | lck_rw_lock_exclusive(sock_filter_lock); |
0a7de745 | 460 | } |
39236c6e A |
461 | |
462 | /* | |
463 | * Warning: A filter unregistering will be pulled out of | |
464 | * the list. This could happen while we drop the lock in | |
465 | * sftl_attach_locked or sflt_release_locked. For this | |
466 | * reason we retain a reference on the filter (or next_filter) | |
467 | * while calling this function. This protects us from a panic, | |
468 | * but it could result in a socket being created without all | |
469 | * of the global filters if we're attaching a filter as it | |
470 | * is removed, if that's possible. | |
471 | */ | |
472 | struct socket_filter *filter = | |
473 | TAILQ_FIRST(&proto->pr_filter_head); | |
474 | ||
6d2010ae | 475 | sflt_retain_locked(filter); |
39236c6e A |
476 | |
477 | while (filter) { | |
6d2010ae | 478 | struct socket_filter *filter_next; |
39236c6e A |
479 | /* |
480 | * Warning: sflt_attach_private_locked | |
481 | * will drop the lock | |
482 | */ | |
6d2010ae | 483 | sflt_attach_locked(so, filter, 0); |
39236c6e | 484 | |
6d2010ae | 485 | filter_next = TAILQ_NEXT(filter, sf_protosw_next); |
0a7de745 | 486 | if (filter_next) { |
6d2010ae | 487 | sflt_retain_locked(filter_next); |
0a7de745 | 488 | } |
39236c6e A |
489 | |
490 | /* | |
491 | * Warning: filt_release_locked may remove | |
492 | * the filter from the queue | |
493 | */ | |
6d2010ae A |
494 | sflt_release_locked(filter); |
495 | filter = filter_next; | |
496 | } | |
497 | } | |
498 | lck_rw_done(sock_filter_lock); | |
91447636 A |
499 | } |
500 | ||
6d2010ae A |
501 | /* |
502 | * sflt_termsock | |
503 | * | |
504 | * Detaches all filters from the socket. | |
505 | */ | |
91447636 | 506 | __private_extern__ void |
39236c6e | 507 | sflt_termsock(struct socket *so) |
91447636 | 508 | { |
6d2010ae | 509 | lck_rw_lock_exclusive(sock_filter_lock); |
39236c6e | 510 | |
6d2010ae | 511 | struct socket_filter_entry *entry; |
39236c6e | 512 | |
6d2010ae | 513 | while ((entry = so->so_filt) != NULL) { |
39236c6e | 514 | /* Pull filter off the socket */ |
6d2010ae A |
515 | so->so_filt = entry->sfe_next_onsocket; |
516 | entry->sfe_flags |= SFEF_NOSOCKET; | |
39236c6e A |
517 | |
518 | /* Call detach */ | |
6d2010ae | 519 | sflt_detach_locked(entry); |
39236c6e A |
520 | |
521 | /* | |
522 | * On sflt_termsock, we can't return until the detach function | |
523 | * has been called. Call the detach function - this is gross | |
524 | * because the socket filter entry could be freed when we drop | |
525 | * the lock, so we make copies on the stack and retain | |
526 | * everything we need before dropping the lock. | |
527 | */ | |
6d2010ae | 528 | if ((entry->sfe_flags & SFEF_NODETACH) == 0 && |
39236c6e A |
529 | entry->sfe_filter->sf_filter.sf_detach) { |
530 | void *sfe_cookie = entry->sfe_cookie; | |
531 | struct socket_filter *sfe_filter = entry->sfe_filter; | |
532 | ||
533 | /* Retain the socket filter */ | |
6d2010ae | 534 | sflt_retain_locked(sfe_filter); |
39236c6e A |
535 | |
536 | /* Mark that we've called the detach function */ | |
6d2010ae | 537 | entry->sfe_flags |= SFEF_NODETACH; |
39236c6e A |
538 | |
539 | /* Drop the lock before calling the detach function */ | |
6d2010ae A |
540 | lck_rw_unlock_exclusive(sock_filter_lock); |
541 | sfe_filter->sf_filter.sf_detach(sfe_cookie, so); | |
542 | lck_rw_lock_exclusive(sock_filter_lock); | |
39236c6e A |
543 | |
544 | /* Release the filter */ | |
6d2010ae | 545 | sflt_release_locked(sfe_filter); |
91447636 A |
546 | } |
547 | } | |
39236c6e | 548 | |
6d2010ae | 549 | lck_rw_unlock_exclusive(sock_filter_lock); |
91447636 A |
550 | } |
551 | ||
316670eb A |
552 | |
553 | static void | |
39236c6e A |
554 | sflt_notify_internal(struct socket *so, sflt_event_t event, void *param, |
555 | sflt_handle handle) | |
91447636 | 556 | { |
0a7de745 | 557 | if (so->so_filt == NULL) { |
39236c6e | 558 | return; |
0a7de745 | 559 | } |
39236c6e A |
560 | |
561 | struct socket_filter_entry *entry; | |
562 | int unlocked = 0; | |
563 | ||
6d2010ae A |
564 | lck_rw_lock_shared(sock_filter_lock); |
565 | for (entry = so->so_filt; entry; entry = entry->sfe_next_onsocket) { | |
39236c6e A |
566 | if ((entry->sfe_flags & SFEF_ATTACHED) && |
567 | entry->sfe_filter->sf_filter.sf_notify && | |
568 | ((handle && entry->sfe_filter->sf_filter.sf_handle != | |
569 | handle) || !handle)) { | |
570 | /* | |
571 | * Retain the filter entry and release | |
572 | * the socket filter lock | |
573 | */ | |
6d2010ae A |
574 | sflt_entry_retain(entry); |
575 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
576 | |
577 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
578 | if (unlocked == 0) { |
579 | unlocked = 1; | |
91447636 A |
580 | socket_unlock(so, 0); |
581 | } | |
39236c6e A |
582 | |
583 | /* Finally call the filter */ | |
584 | entry->sfe_filter->sf_filter.sf_notify( | |
0a7de745 | 585 | entry->sfe_cookie, so, event, param); |
39236c6e A |
586 | |
587 | /* | |
588 | * Take the socket filter lock again | |
589 | * and release the entry | |
590 | */ | |
6d2010ae A |
591 | lck_rw_lock_shared(sock_filter_lock); |
592 | sflt_entry_release(entry); | |
91447636 A |
593 | } |
594 | } | |
6d2010ae | 595 | lck_rw_unlock_shared(sock_filter_lock); |
39236c6e | 596 | |
6d2010ae | 597 | if (unlocked != 0) { |
91447636 | 598 | socket_lock(so, 0); |
91447636 A |
599 | } |
600 | } | |
601 | ||
316670eb | 602 | __private_extern__ void |
0a7de745 | 603 | sflt_notify(struct socket *so, sflt_event_t event, void *param) |
316670eb A |
604 | { |
605 | sflt_notify_internal(so, event, param, 0); | |
606 | } | |
607 | ||
608 | static void | |
39236c6e A |
609 | sflt_notify_after_register(struct socket *so, sflt_event_t event, |
610 | sflt_handle handle) | |
316670eb A |
611 | { |
612 | sflt_notify_internal(so, event, NULL, handle); | |
613 | } | |
614 | ||
91447636 | 615 | __private_extern__ int |
39236c6e | 616 | sflt_ioctl(struct socket *so, u_long cmd, caddr_t data) |
91447636 | 617 | { |
0a7de745 A |
618 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
619 | return 0; | |
620 | } | |
39236c6e A |
621 | |
622 | struct socket_filter_entry *entry; | |
623 | int unlocked = 0; | |
624 | int error = 0; | |
625 | ||
6d2010ae A |
626 | lck_rw_lock_shared(sock_filter_lock); |
627 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
628 | entry = entry->sfe_next_onsocket) { |
629 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
630 | entry->sfe_filter->sf_filter.sf_ioctl) { | |
631 | /* | |
632 | * Retain the filter entry and release | |
633 | * the socket filter lock | |
634 | */ | |
6d2010ae A |
635 | sflt_entry_retain(entry); |
636 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
637 | |
638 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae | 639 | if (unlocked == 0) { |
91447636 | 640 | socket_unlock(so, 0); |
6d2010ae | 641 | unlocked = 1; |
91447636 | 642 | } |
39236c6e A |
643 | |
644 | /* Call the filter */ | |
645 | error = entry->sfe_filter->sf_filter.sf_ioctl( | |
0a7de745 | 646 | entry->sfe_cookie, so, cmd, data); |
39236c6e A |
647 | |
648 | /* | |
649 | * Take the socket filter lock again | |
650 | * and release the entry | |
651 | */ | |
6d2010ae A |
652 | lck_rw_lock_shared(sock_filter_lock); |
653 | sflt_entry_release(entry); | |
91447636 A |
654 | } |
655 | } | |
6d2010ae A |
656 | lck_rw_unlock_shared(sock_filter_lock); |
657 | ||
658 | if (unlocked) { | |
91447636 | 659 | socket_lock(so, 0); |
91447636 | 660 | } |
39236c6e | 661 | |
0a7de745 | 662 | return error; |
91447636 A |
663 | } |
664 | ||
91447636 | 665 | __private_extern__ int |
39236c6e | 666 | sflt_bind(struct socket *so, const struct sockaddr *nam) |
91447636 | 667 | { |
0a7de745 A |
668 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
669 | return 0; | |
670 | } | |
39236c6e A |
671 | |
672 | struct socket_filter_entry *entry; | |
673 | int unlocked = 0; | |
674 | int error = 0; | |
675 | ||
6d2010ae A |
676 | lck_rw_lock_shared(sock_filter_lock); |
677 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
678 | entry = entry->sfe_next_onsocket) { |
679 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
680 | entry->sfe_filter->sf_filter.sf_bind) { | |
681 | /* | |
682 | * Retain the filter entry and | |
683 | * release the socket filter lock | |
684 | */ | |
6d2010ae A |
685 | sflt_entry_retain(entry); |
686 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
687 | |
688 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
689 | if (unlocked == 0) { |
690 | socket_unlock(so, 0); | |
691 | unlocked = 1; | |
692 | } | |
39236c6e A |
693 | |
694 | /* Call the filter */ | |
695 | error = entry->sfe_filter->sf_filter.sf_bind( | |
0a7de745 | 696 | entry->sfe_cookie, so, nam); |
39236c6e A |
697 | |
698 | /* | |
699 | * Take the socket filter lock again and | |
700 | * release the entry | |
701 | */ | |
6d2010ae A |
702 | lck_rw_lock_shared(sock_filter_lock); |
703 | sflt_entry_release(entry); | |
91447636 A |
704 | } |
705 | } | |
6d2010ae A |
706 | lck_rw_unlock_shared(sock_filter_lock); |
707 | ||
708 | if (unlocked) { | |
709 | socket_lock(so, 0); | |
710 | } | |
39236c6e | 711 | |
0a7de745 | 712 | return error; |
6d2010ae A |
713 | } |
714 | ||
715 | __private_extern__ int | |
39236c6e | 716 | sflt_listen(struct socket *so) |
6d2010ae | 717 | { |
0a7de745 A |
718 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
719 | return 0; | |
720 | } | |
39236c6e A |
721 | |
722 | struct socket_filter_entry *entry; | |
723 | int unlocked = 0; | |
724 | int error = 0; | |
725 | ||
6d2010ae A |
726 | lck_rw_lock_shared(sock_filter_lock); |
727 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
728 | entry = entry->sfe_next_onsocket) { |
729 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
730 | entry->sfe_filter->sf_filter.sf_listen) { | |
731 | /* | |
732 | * Retain the filter entry and release | |
733 | * the socket filter lock | |
734 | */ | |
6d2010ae A |
735 | sflt_entry_retain(entry); |
736 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
737 | |
738 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
739 | if (unlocked == 0) { |
740 | socket_unlock(so, 0); | |
741 | unlocked = 1; | |
742 | } | |
39236c6e A |
743 | |
744 | /* Call the filter */ | |
745 | error = entry->sfe_filter->sf_filter.sf_listen( | |
0a7de745 | 746 | entry->sfe_cookie, so); |
39236c6e A |
747 | |
748 | /* | |
749 | * Take the socket filter lock again | |
750 | * and release the entry | |
751 | */ | |
6d2010ae A |
752 | lck_rw_lock_shared(sock_filter_lock); |
753 | sflt_entry_release(entry); | |
91447636 A |
754 | } |
755 | } | |
6d2010ae A |
756 | lck_rw_unlock_shared(sock_filter_lock); |
757 | ||
758 | if (unlocked) { | |
759 | socket_lock(so, 0); | |
760 | } | |
39236c6e | 761 | |
0a7de745 | 762 | return error; |
6d2010ae A |
763 | } |
764 | ||
765 | __private_extern__ int | |
39236c6e A |
766 | sflt_accept(struct socket *head, struct socket *so, |
767 | const struct sockaddr *local, const struct sockaddr *remote) | |
6d2010ae | 768 | { |
0a7de745 A |
769 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
770 | return 0; | |
771 | } | |
39236c6e A |
772 | |
773 | struct socket_filter_entry *entry; | |
774 | int unlocked = 0; | |
775 | int error = 0; | |
776 | ||
6d2010ae A |
777 | lck_rw_lock_shared(sock_filter_lock); |
778 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
779 | entry = entry->sfe_next_onsocket) { |
780 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
781 | entry->sfe_filter->sf_filter.sf_accept) { | |
782 | /* | |
783 | * Retain the filter entry and | |
784 | * release the socket filter lock | |
785 | */ | |
6d2010ae A |
786 | sflt_entry_retain(entry); |
787 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
788 | |
789 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
790 | if (unlocked == 0) { |
791 | socket_unlock(so, 0); | |
792 | unlocked = 1; | |
91447636 | 793 | } |
39236c6e A |
794 | |
795 | /* Call the filter */ | |
796 | error = entry->sfe_filter->sf_filter.sf_accept( | |
0a7de745 | 797 | entry->sfe_cookie, head, so, local, remote); |
39236c6e A |
798 | |
799 | /* | |
800 | * Take the socket filter lock again | |
801 | * and release the entry | |
802 | */ | |
6d2010ae A |
803 | lck_rw_lock_shared(sock_filter_lock); |
804 | sflt_entry_release(entry); | |
91447636 A |
805 | } |
806 | } | |
6d2010ae A |
807 | lck_rw_unlock_shared(sock_filter_lock); |
808 | ||
809 | if (unlocked) { | |
810 | socket_lock(so, 0); | |
91447636 | 811 | } |
39236c6e | 812 | |
0a7de745 | 813 | return error; |
6d2010ae A |
814 | } |
815 | ||
816 | __private_extern__ int | |
39236c6e | 817 | sflt_getsockname(struct socket *so, struct sockaddr **local) |
6d2010ae | 818 | { |
0a7de745 A |
819 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
820 | return 0; | |
821 | } | |
39236c6e A |
822 | |
823 | struct socket_filter_entry *entry; | |
824 | int unlocked = 0; | |
825 | int error = 0; | |
826 | ||
6d2010ae A |
827 | lck_rw_lock_shared(sock_filter_lock); |
828 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
829 | entry = entry->sfe_next_onsocket) { |
830 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
831 | entry->sfe_filter->sf_filter.sf_getsockname) { | |
832 | /* | |
833 | * Retain the filter entry and | |
834 | * release the socket filter lock | |
835 | */ | |
6d2010ae A |
836 | sflt_entry_retain(entry); |
837 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
838 | |
839 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
840 | if (unlocked == 0) { |
841 | socket_unlock(so, 0); | |
842 | unlocked = 1; | |
843 | } | |
39236c6e A |
844 | |
845 | /* Call the filter */ | |
846 | error = entry->sfe_filter->sf_filter.sf_getsockname( | |
0a7de745 | 847 | entry->sfe_cookie, so, local); |
39236c6e A |
848 | |
849 | /* | |
850 | * Take the socket filter lock again | |
851 | * and release the entry | |
852 | */ | |
6d2010ae A |
853 | lck_rw_lock_shared(sock_filter_lock); |
854 | sflt_entry_release(entry); | |
855 | } | |
856 | } | |
857 | lck_rw_unlock_shared(sock_filter_lock); | |
858 | ||
859 | if (unlocked) { | |
860 | socket_lock(so, 0); | |
91447636 | 861 | } |
39236c6e | 862 | |
0a7de745 | 863 | return error; |
91447636 A |
864 | } |
865 | ||
6d2010ae | 866 | __private_extern__ int |
39236c6e | 867 | sflt_getpeername(struct socket *so, struct sockaddr **remote) |
6d2010ae | 868 | { |
0a7de745 A |
869 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
870 | return 0; | |
871 | } | |
39236c6e A |
872 | |
873 | struct socket_filter_entry *entry; | |
874 | int unlocked = 0; | |
875 | int error = 0; | |
876 | ||
6d2010ae A |
877 | lck_rw_lock_shared(sock_filter_lock); |
878 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
879 | entry = entry->sfe_next_onsocket) { |
880 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
881 | entry->sfe_filter->sf_filter.sf_getpeername) { | |
882 | /* | |
883 | * Retain the filter entry and release | |
884 | * the socket filter lock | |
885 | */ | |
6d2010ae A |
886 | sflt_entry_retain(entry); |
887 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
888 | |
889 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
890 | if (unlocked == 0) { |
891 | socket_unlock(so, 0); | |
892 | unlocked = 1; | |
893 | } | |
39236c6e A |
894 | |
895 | /* Call the filter */ | |
896 | error = entry->sfe_filter->sf_filter.sf_getpeername( | |
0a7de745 | 897 | entry->sfe_cookie, so, remote); |
39236c6e A |
898 | |
899 | /* | |
900 | * Take the socket filter lock again | |
901 | * and release the entry | |
902 | */ | |
6d2010ae A |
903 | lck_rw_lock_shared(sock_filter_lock); |
904 | sflt_entry_release(entry); | |
905 | } | |
906 | } | |
907 | lck_rw_unlock_shared(sock_filter_lock); | |
91447636 | 908 | |
6d2010ae A |
909 | if (unlocked) { |
910 | socket_lock(so, 0); | |
911 | } | |
39236c6e | 912 | |
0a7de745 | 913 | return error; |
6d2010ae | 914 | } |
91447636 | 915 | |
6d2010ae | 916 | __private_extern__ int |
0a7de745 | 917 | sflt_connectin(struct socket *so, const struct sockaddr *remote) |
91447636 | 918 | { |
0a7de745 A |
919 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
920 | return 0; | |
921 | } | |
39236c6e A |
922 | |
923 | struct socket_filter_entry *entry; | |
924 | int unlocked = 0; | |
925 | int error = 0; | |
926 | ||
6d2010ae A |
927 | lck_rw_lock_shared(sock_filter_lock); |
928 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
929 | entry = entry->sfe_next_onsocket) { |
930 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
931 | entry->sfe_filter->sf_filter.sf_connect_in) { | |
932 | /* | |
933 | * Retain the filter entry and release | |
934 | * the socket filter lock | |
935 | */ | |
6d2010ae A |
936 | sflt_entry_retain(entry); |
937 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
938 | |
939 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
940 | if (unlocked == 0) { |
941 | socket_unlock(so, 0); | |
942 | unlocked = 1; | |
4a3eedf9 | 943 | } |
39236c6e A |
944 | |
945 | /* Call the filter */ | |
946 | error = entry->sfe_filter->sf_filter.sf_connect_in( | |
0a7de745 | 947 | entry->sfe_cookie, so, remote); |
39236c6e A |
948 | |
949 | /* | |
950 | * Take the socket filter lock again | |
951 | * and release the entry | |
952 | */ | |
6d2010ae A |
953 | lck_rw_lock_shared(sock_filter_lock); |
954 | sflt_entry_release(entry); | |
3a60a9f5 | 955 | } |
6d2010ae A |
956 | } |
957 | lck_rw_unlock_shared(sock_filter_lock); | |
958 | ||
959 | if (unlocked) { | |
960 | socket_lock(so, 0); | |
961 | } | |
39236c6e | 962 | |
0a7de745 | 963 | return error; |
6d2010ae A |
964 | } |
965 | ||
490019cf A |
966 | static int |
967 | sflt_connectout_common(struct socket *so, const struct sockaddr *nam) | |
6d2010ae | 968 | { |
39236c6e A |
969 | struct socket_filter_entry *entry; |
970 | int unlocked = 0; | |
971 | int error = 0; | |
972 | ||
6d2010ae A |
973 | lck_rw_lock_shared(sock_filter_lock); |
974 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
975 | entry = entry->sfe_next_onsocket) { |
976 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
977 | entry->sfe_filter->sf_filter.sf_connect_out) { | |
978 | /* | |
979 | * Retain the filter entry and release | |
980 | * the socket filter lock | |
981 | */ | |
6d2010ae A |
982 | sflt_entry_retain(entry); |
983 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
984 | |
985 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
986 | if (unlocked == 0) { |
987 | socket_unlock(so, 0); | |
988 | unlocked = 1; | |
91447636 | 989 | } |
39236c6e A |
990 | |
991 | /* Call the filter */ | |
992 | error = entry->sfe_filter->sf_filter.sf_connect_out( | |
0a7de745 | 993 | entry->sfe_cookie, so, nam); |
39236c6e A |
994 | |
995 | /* | |
996 | * Take the socket filter lock again | |
997 | * and release the entry | |
998 | */ | |
6d2010ae A |
999 | lck_rw_lock_shared(sock_filter_lock); |
1000 | sflt_entry_release(entry); | |
91447636 | 1001 | } |
91447636 | 1002 | } |
6d2010ae A |
1003 | lck_rw_unlock_shared(sock_filter_lock); |
1004 | ||
1005 | if (unlocked) { | |
1006 | socket_lock(so, 0); | |
1007 | } | |
39236c6e | 1008 | |
0a7de745 | 1009 | return error; |
39236c6e A |
1010 | } |
1011 | ||
1012 | __private_extern__ int | |
490019cf | 1013 | sflt_connectout(struct socket *so, const struct sockaddr *nam) |
39236c6e A |
1014 | { |
1015 | char buf[SOCK_MAXADDRLEN]; | |
490019cf A |
1016 | struct sockaddr *sa; |
1017 | int error; | |
1018 | ||
0a7de745 A |
1019 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
1020 | return 0; | |
1021 | } | |
490019cf A |
1022 | |
1023 | /* | |
1024 | * Workaround for rdar://23362120 | |
1025 | * Always pass a buffer that can hold an IPv6 socket address | |
1026 | */ | |
0a7de745 | 1027 | bzero(buf, sizeof(buf)); |
490019cf A |
1028 | bcopy(nam, buf, nam->sa_len); |
1029 | sa = (struct sockaddr *)buf; | |
1030 | ||
1031 | error = sflt_connectout_common(so, sa); | |
0a7de745 A |
1032 | if (error != 0) { |
1033 | return error; | |
1034 | } | |
490019cf | 1035 | |
743345f9 | 1036 | /* |
490019cf A |
1037 | * If the address was modified, copy it back |
1038 | */ | |
1039 | if (bcmp(sa, nam, nam->sa_len) != 0) { | |
1040 | bcopy(sa, (struct sockaddr *)(uintptr_t)nam, nam->sa_len); | |
1041 | } | |
1042 | ||
0a7de745 | 1043 | return 0; |
490019cf A |
1044 | } |
1045 | ||
6d2010ae | 1046 | __private_extern__ int |
39236c6e | 1047 | sflt_setsockopt(struct socket *so, struct sockopt *sopt) |
6d2010ae | 1048 | { |
0a7de745 A |
1049 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
1050 | return 0; | |
1051 | } | |
39236c6e A |
1052 | |
1053 | struct socket_filter_entry *entry; | |
1054 | int unlocked = 0; | |
1055 | int error = 0; | |
1056 | ||
6d2010ae A |
1057 | lck_rw_lock_shared(sock_filter_lock); |
1058 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
1059 | entry = entry->sfe_next_onsocket) { |
1060 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
1061 | entry->sfe_filter->sf_filter.sf_setoption) { | |
1062 | /* | |
1063 | * Retain the filter entry and release | |
1064 | * the socket filter lock | |
1065 | */ | |
6d2010ae A |
1066 | sflt_entry_retain(entry); |
1067 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
1068 | |
1069 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
1070 | if (unlocked == 0) { |
1071 | socket_unlock(so, 0); | |
1072 | unlocked = 1; | |
1073 | } | |
39236c6e A |
1074 | |
1075 | /* Call the filter */ | |
1076 | error = entry->sfe_filter->sf_filter.sf_setoption( | |
0a7de745 | 1077 | entry->sfe_cookie, so, sopt); |
39236c6e A |
1078 | |
1079 | /* | |
1080 | * Take the socket filter lock again | |
1081 | * and release the entry | |
1082 | */ | |
6d2010ae A |
1083 | lck_rw_lock_shared(sock_filter_lock); |
1084 | sflt_entry_release(entry); | |
c910b4d9 | 1085 | } |
6d2010ae A |
1086 | } |
1087 | lck_rw_unlock_shared(sock_filter_lock); | |
1088 | ||
1089 | if (unlocked) { | |
1090 | socket_lock(so, 0); | |
1091 | } | |
39236c6e | 1092 | |
0a7de745 | 1093 | return error; |
6d2010ae A |
1094 | } |
1095 | ||
1096 | __private_extern__ int | |
39236c6e | 1097 | sflt_getsockopt(struct socket *so, struct sockopt *sopt) |
6d2010ae | 1098 | { |
0a7de745 A |
1099 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
1100 | return 0; | |
1101 | } | |
39236c6e A |
1102 | |
1103 | struct socket_filter_entry *entry; | |
1104 | int unlocked = 0; | |
1105 | int error = 0; | |
1106 | ||
6d2010ae A |
1107 | lck_rw_lock_shared(sock_filter_lock); |
1108 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
1109 | entry = entry->sfe_next_onsocket) { |
1110 | if ((entry->sfe_flags & SFEF_ATTACHED) && | |
1111 | entry->sfe_filter->sf_filter.sf_getoption) { | |
1112 | /* | |
1113 | * Retain the filter entry and release | |
1114 | * the socket filter lock | |
1115 | */ | |
6d2010ae A |
1116 | sflt_entry_retain(entry); |
1117 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
1118 | |
1119 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
1120 | if (unlocked == 0) { |
1121 | socket_unlock(so, 0); | |
1122 | unlocked = 1; | |
1123 | } | |
39236c6e A |
1124 | |
1125 | /* Call the filter */ | |
1126 | error = entry->sfe_filter->sf_filter.sf_getoption( | |
0a7de745 | 1127 | entry->sfe_cookie, so, sopt); |
39236c6e A |
1128 | |
1129 | /* | |
1130 | * Take the socket filter lock again | |
1131 | * and release the entry | |
1132 | */ | |
6d2010ae A |
1133 | lck_rw_lock_shared(sock_filter_lock); |
1134 | sflt_entry_release(entry); | |
91447636 A |
1135 | } |
1136 | } | |
6d2010ae A |
1137 | lck_rw_unlock_shared(sock_filter_lock); |
1138 | ||
1139 | if (unlocked) { | |
1140 | socket_lock(so, 0); | |
1141 | } | |
39236c6e | 1142 | |
0a7de745 | 1143 | return error; |
6d2010ae A |
1144 | } |
1145 | ||
1146 | __private_extern__ int | |
39236c6e A |
1147 | sflt_data_out(struct socket *so, const struct sockaddr *to, mbuf_t *data, |
1148 | mbuf_t *control, sflt_data_flag_t flags) | |
6d2010ae | 1149 | { |
0a7de745 A |
1150 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
1151 | return 0; | |
1152 | } | |
39236c6e A |
1153 | |
1154 | struct socket_filter_entry *entry; | |
1155 | int unlocked = 0; | |
1156 | int setsendthread = 0; | |
1157 | int error = 0; | |
1158 | ||
6d2010ae A |
1159 | lck_rw_lock_shared(sock_filter_lock); |
1160 | for (entry = so->so_filt; entry && error == 0; | |
39236c6e A |
1161 | entry = entry->sfe_next_onsocket) { |
1162 | /* skip if this is a subflow socket */ | |
0a7de745 | 1163 | if (so->so_flags & SOF_MP_SUBFLOW) { |
39236c6e | 1164 | continue; |
0a7de745 | 1165 | } |
39236c6e A |
1166 | if ((entry->sfe_flags & SFEF_ATTACHED) && |
1167 | entry->sfe_filter->sf_filter.sf_data_out) { | |
1168 | /* | |
1169 | * Retain the filter entry and | |
1170 | * release the socket filter lock | |
1171 | */ | |
6d2010ae A |
1172 | sflt_entry_retain(entry); |
1173 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
1174 | |
1175 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
1176 | if (unlocked == 0) { |
1177 | if (so->so_send_filt_thread == NULL) { | |
1178 | setsendthread = 1; | |
39236c6e A |
1179 | so->so_send_filt_thread = |
1180 | current_thread(); | |
6d2010ae A |
1181 | } |
1182 | socket_unlock(so, 0); | |
1183 | unlocked = 1; | |
1184 | } | |
39236c6e A |
1185 | |
1186 | /* Call the filter */ | |
1187 | error = entry->sfe_filter->sf_filter.sf_data_out( | |
0a7de745 | 1188 | entry->sfe_cookie, so, to, data, control, flags); |
39236c6e A |
1189 | |
1190 | /* | |
1191 | * Take the socket filter lock again | |
1192 | * and release the entry | |
1193 | */ | |
6d2010ae A |
1194 | lck_rw_lock_shared(sock_filter_lock); |
1195 | sflt_entry_release(entry); | |
1196 | } | |
91447636 | 1197 | } |
6d2010ae | 1198 | lck_rw_unlock_shared(sock_filter_lock); |
3a60a9f5 | 1199 | |
6d2010ae A |
1200 | if (unlocked) { |
1201 | socket_lock(so, 0); | |
0a7de745 | 1202 | if (setsendthread) { |
39236c6e | 1203 | so->so_send_filt_thread = NULL; |
0a7de745 | 1204 | } |
6d2010ae | 1205 | } |
39236c6e | 1206 | |
0a7de745 | 1207 | return error; |
6d2010ae | 1208 | } |
3a60a9f5 | 1209 | |
6d2010ae | 1210 | __private_extern__ int |
39236c6e A |
1211 | sflt_data_in(struct socket *so, const struct sockaddr *from, mbuf_t *data, |
1212 | mbuf_t *control, sflt_data_flag_t flags) | |
6d2010ae | 1213 | { |
0a7de745 A |
1214 | if (so->so_filt == NULL || sflt_permission_check(sotoinpcb(so))) { |
1215 | return 0; | |
1216 | } | |
39236c6e A |
1217 | |
1218 | struct socket_filter_entry *entry; | |
1219 | int error = 0; | |
1220 | int unlocked = 0; | |
1221 | ||
6d2010ae | 1222 | lck_rw_lock_shared(sock_filter_lock); |
39236c6e | 1223 | |
6d2010ae | 1224 | for (entry = so->so_filt; entry && (error == 0); |
39236c6e A |
1225 | entry = entry->sfe_next_onsocket) { |
1226 | /* skip if this is a subflow socket */ | |
0a7de745 | 1227 | if (so->so_flags & SOF_MP_SUBFLOW) { |
39236c6e | 1228 | continue; |
0a7de745 | 1229 | } |
6d2010ae | 1230 | if ((entry->sfe_flags & SFEF_ATTACHED) && |
39236c6e A |
1231 | entry->sfe_filter->sf_filter.sf_data_in) { |
1232 | /* | |
1233 | * Retain the filter entry and | |
1234 | * release the socket filter lock | |
1235 | */ | |
6d2010ae A |
1236 | sflt_entry_retain(entry); |
1237 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e A |
1238 | |
1239 | /* If the socket isn't already unlocked, unlock it */ | |
6d2010ae A |
1240 | if (unlocked == 0) { |
1241 | unlocked = 1; | |
1242 | socket_unlock(so, 0); | |
1243 | } | |
39236c6e A |
1244 | |
1245 | /* Call the filter */ | |
6d2010ae | 1246 | error = entry->sfe_filter->sf_filter.sf_data_in( |
0a7de745 | 1247 | entry->sfe_cookie, so, from, data, control, flags); |
39236c6e A |
1248 | |
1249 | /* | |
1250 | * Take the socket filter lock again | |
1251 | * and release the entry | |
1252 | */ | |
6d2010ae A |
1253 | lck_rw_lock_shared(sock_filter_lock); |
1254 | sflt_entry_release(entry); | |
1255 | } | |
1256 | } | |
1257 | lck_rw_unlock_shared(sock_filter_lock); | |
39236c6e | 1258 | |
6d2010ae A |
1259 | if (unlocked) { |
1260 | socket_lock(so, 0); | |
1261 | } | |
39236c6e | 1262 | |
0a7de745 | 1263 | return error; |
91447636 A |
1264 | } |
1265 | ||
6d2010ae A |
1266 | #pragma mark -- KPI -- |
1267 | ||
91447636 | 1268 | errno_t |
39236c6e | 1269 | sflt_attach(socket_t socket, sflt_handle handle) |
91447636 | 1270 | { |
6d2010ae A |
1271 | socket_lock(socket, 1); |
1272 | errno_t result = sflt_attach_internal(socket, handle); | |
1273 | socket_unlock(socket, 1); | |
0a7de745 | 1274 | return result; |
91447636 A |
1275 | } |
1276 | ||
1277 | errno_t | |
39236c6e | 1278 | sflt_detach(socket_t socket, sflt_handle handle) |
91447636 | 1279 | { |
39236c6e | 1280 | struct socket_filter_entry *entry; |
0a7de745 | 1281 | errno_t result = 0; |
39236c6e | 1282 | |
0a7de745 A |
1283 | if (socket == NULL || handle == 0) { |
1284 | return EINVAL; | |
1285 | } | |
39236c6e | 1286 | |
6d2010ae | 1287 | lck_rw_lock_exclusive(sock_filter_lock); |
39236c6e | 1288 | for (entry = socket->so_filt; entry; entry = entry->sfe_next_onsocket) { |
6d2010ae | 1289 | if (entry->sfe_filter->sf_filter.sf_handle == handle && |
39236c6e | 1290 | (entry->sfe_flags & SFEF_ATTACHED) != 0) { |
91447636 | 1291 | break; |
6d2010ae | 1292 | } |
91447636 | 1293 | } |
39236c6e | 1294 | |
6d2010ae A |
1295 | if (entry != NULL) { |
1296 | sflt_detach_locked(entry); | |
91447636 | 1297 | } |
6d2010ae | 1298 | lck_rw_unlock_exclusive(sock_filter_lock); |
39236c6e | 1299 | |
0a7de745 | 1300 | return result; |
91447636 A |
1301 | } |
1302 | ||
316670eb A |
1303 | struct solist { |
1304 | struct solist *next; | |
1305 | struct socket *so; | |
1306 | }; | |
1307 | ||
5ba3f43e A |
1308 | static errno_t |
1309 | sflt_register_common(const struct sflt_filter *filter, int domain, int type, | |
0a7de745 | 1310 | int protocol, bool is_internal) |
91447636 A |
1311 | { |
1312 | struct socket_filter *sock_filt = NULL; | |
1313 | struct socket_filter *match = NULL; | |
1314 | int error = 0; | |
3e170ce0 | 1315 | struct protosw *pr; |
2d21ac55 | 1316 | unsigned int len; |
316670eb A |
1317 | struct socket *so; |
1318 | struct inpcb *inp; | |
1319 | struct solist *solisthead = NULL, *solist = NULL; | |
2d21ac55 | 1320 | |
0a7de745 A |
1321 | if ((domain != PF_INET) && (domain != PF_INET6)) { |
1322 | return ENOTSUP; | |
1323 | } | |
3e170ce0 A |
1324 | |
1325 | pr = pffindproto(domain, protocol, type); | |
0a7de745 A |
1326 | if (pr == NULL) { |
1327 | return ENOENT; | |
1328 | } | |
2d21ac55 A |
1329 | |
1330 | if (filter->sf_attach == NULL || filter->sf_detach == NULL || | |
0a7de745 A |
1331 | filter->sf_handle == 0 || filter->sf_name == NULL) { |
1332 | return EINVAL; | |
1333 | } | |
91447636 A |
1334 | |
1335 | /* Allocate the socket filter */ | |
0a7de745 | 1336 | MALLOC(sock_filt, struct socket_filter *, sizeof(*sock_filt), |
2d21ac55 | 1337 | M_IFADDR, M_WAITOK); |
91447636 | 1338 | if (sock_filt == NULL) { |
0a7de745 | 1339 | return ENOBUFS; |
91447636 | 1340 | } |
2d21ac55 | 1341 | |
0a7de745 | 1342 | bzero(sock_filt, sizeof(*sock_filt)); |
2d21ac55 A |
1343 | |
1344 | /* Legacy sflt_filter length; current structure minus extended */ | |
0a7de745 | 1345 | len = sizeof(*filter) - sizeof(struct sflt_filter_ext); |
2d21ac55 A |
1346 | /* |
1347 | * Include extended fields if filter defines SFLT_EXTENDED. | |
1348 | * We've zeroed out our internal sflt_filter placeholder, | |
1349 | * so any unused portion would have been taken care of. | |
1350 | */ | |
1351 | if (filter->sf_flags & SFLT_EXTENDED) { | |
1352 | unsigned int ext_len = filter->sf_len; | |
1353 | ||
0a7de745 A |
1354 | if (ext_len > sizeof(struct sflt_filter_ext)) { |
1355 | ext_len = sizeof(struct sflt_filter_ext); | |
1356 | } | |
2d21ac55 A |
1357 | |
1358 | len += ext_len; | |
1359 | } | |
1360 | bcopy(filter, &sock_filt->sf_filter, len); | |
1361 | ||
6d2010ae | 1362 | lck_rw_lock_exclusive(sock_filter_lock); |
91447636 A |
1363 | /* Look for an existing entry */ |
1364 | TAILQ_FOREACH(match, &sock_filter_head, sf_global_next) { | |
2d21ac55 A |
1365 | if (match->sf_filter.sf_handle == |
1366 | sock_filt->sf_filter.sf_handle) { | |
91447636 A |
1367 | break; |
1368 | } | |
1369 | } | |
39236c6e | 1370 | |
91447636 A |
1371 | /* Add the entry only if there was no existing entry */ |
1372 | if (match == NULL) { | |
1373 | TAILQ_INSERT_TAIL(&sock_filter_head, sock_filt, sf_global_next); | |
1374 | if ((sock_filt->sf_filter.sf_flags & SFLT_GLOBAL) != 0) { | |
2d21ac55 A |
1375 | TAILQ_INSERT_TAIL(&pr->pr_filter_head, sock_filt, |
1376 | sf_protosw_next); | |
91447636 A |
1377 | sock_filt->sf_proto = pr; |
1378 | } | |
0a7de745 | 1379 | os_ref_init(&sock_filt->sf_refcount, NULL); |
5ba3f43e A |
1380 | |
1381 | OSIncrementAtomic64(&net_api_stats.nas_sfltr_register_count); | |
1382 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_sfltr_register_total); | |
1383 | if (is_internal) { | |
1384 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_sfltr_register_os_total); | |
1385 | } | |
91447636 | 1386 | } |
6d2010ae | 1387 | lck_rw_unlock_exclusive(sock_filter_lock); |
316670eb | 1388 | |
91447636 A |
1389 | if (match != NULL) { |
1390 | FREE(sock_filt, M_IFADDR); | |
0a7de745 | 1391 | return EEXIST; |
91447636 | 1392 | } |
2d21ac55 | 1393 | |
0a7de745 A |
1394 | if (!(filter->sf_flags & SFLT_EXTENDED_REGISTRY)) { |
1395 | return error; | |
1396 | } | |
316670eb A |
1397 | |
1398 | /* | |
1399 | * Setup the filter on the TCP and UDP sockets already created. | |
1400 | */ | |
0a7de745 A |
1401 | #define SOLIST_ADD(_so) do { \ |
1402 | solist->next = solisthead; \ | |
1403 | sock_retain((_so)); \ | |
1404 | solist->so = (_so); \ | |
1405 | solisthead = solist; \ | |
316670eb A |
1406 | } while (0) |
1407 | if (protocol == IPPROTO_TCP) { | |
39236c6e A |
1408 | lck_rw_lock_shared(tcbinfo.ipi_lock); |
1409 | LIST_FOREACH(inp, tcbinfo.ipi_listhead, inp_list) { | |
316670eb | 1410 | so = inp->inp_socket; |
39236c6e A |
1411 | if (so == NULL || (so->so_state & SS_DEFUNCT) || |
1412 | (!(so->so_flags & SOF_MP_SUBFLOW) && | |
1413 | (so->so_state & SS_NOFDREF)) || | |
1414 | !SOCK_CHECK_DOM(so, domain) || | |
0a7de745 | 1415 | !SOCK_CHECK_TYPE(so, type)) { |
316670eb | 1416 | continue; |
0a7de745 A |
1417 | } |
1418 | MALLOC(solist, struct solist *, sizeof(*solist), | |
316670eb | 1419 | M_IFADDR, M_NOWAIT); |
0a7de745 | 1420 | if (!solist) { |
316670eb | 1421 | continue; |
0a7de745 | 1422 | } |
316670eb A |
1423 | SOLIST_ADD(so); |
1424 | } | |
39236c6e | 1425 | lck_rw_done(tcbinfo.ipi_lock); |
316670eb | 1426 | } else if (protocol == IPPROTO_UDP) { |
39236c6e A |
1427 | lck_rw_lock_shared(udbinfo.ipi_lock); |
1428 | LIST_FOREACH(inp, udbinfo.ipi_listhead, inp_list) { | |
316670eb | 1429 | so = inp->inp_socket; |
39236c6e A |
1430 | if (so == NULL || (so->so_state & SS_DEFUNCT) || |
1431 | (!(so->so_flags & SOF_MP_SUBFLOW) && | |
1432 | (so->so_state & SS_NOFDREF)) || | |
1433 | !SOCK_CHECK_DOM(so, domain) || | |
0a7de745 | 1434 | !SOCK_CHECK_TYPE(so, type)) { |
316670eb | 1435 | continue; |
0a7de745 A |
1436 | } |
1437 | MALLOC(solist, struct solist *, sizeof(*solist), | |
316670eb | 1438 | M_IFADDR, M_NOWAIT); |
0a7de745 | 1439 | if (!solist) { |
316670eb | 1440 | continue; |
0a7de745 | 1441 | } |
316670eb A |
1442 | SOLIST_ADD(so); |
1443 | } | |
39236c6e | 1444 | lck_rw_done(udbinfo.ipi_lock); |
316670eb A |
1445 | } |
1446 | /* XXX it's possible to walk the raw socket list as well */ | |
1447 | #undef SOLIST_ADD | |
1448 | ||
1449 | while (solisthead) { | |
1450 | sflt_handle handle = filter->sf_handle; | |
1451 | ||
1452 | so = solisthead->so; | |
fe8ab488 | 1453 | socket_lock(so, 0); |
316670eb | 1454 | sflt_initsock(so); |
0a7de745 | 1455 | if (so->so_state & SS_ISCONNECTING) { |
316670eb A |
1456 | sflt_notify_after_register(so, sock_evt_connecting, |
1457 | handle); | |
0a7de745 | 1458 | } else if (so->so_state & SS_ISCONNECTED) { |
316670eb A |
1459 | sflt_notify_after_register(so, sock_evt_connected, |
1460 | handle); | |
0a7de745 A |
1461 | } else if ((so->so_state & |
1462 | (SS_ISDISCONNECTING | SS_CANTRCVMORE | SS_CANTSENDMORE)) == | |
1463 | (SS_ISDISCONNECTING | SS_CANTRCVMORE | SS_CANTSENDMORE)) { | |
316670eb A |
1464 | sflt_notify_after_register(so, sock_evt_disconnecting, |
1465 | handle); | |
0a7de745 A |
1466 | } else if ((so->so_state & |
1467 | (SS_CANTRCVMORE | SS_CANTSENDMORE | SS_ISDISCONNECTED)) == | |
1468 | (SS_CANTRCVMORE | SS_CANTSENDMORE | SS_ISDISCONNECTED)) { | |
316670eb A |
1469 | sflt_notify_after_register(so, sock_evt_disconnected, |
1470 | handle); | |
0a7de745 | 1471 | } else if (so->so_state & SS_CANTSENDMORE) { |
316670eb A |
1472 | sflt_notify_after_register(so, sock_evt_cantsendmore, |
1473 | handle); | |
0a7de745 | 1474 | } else if (so->so_state & SS_CANTRCVMORE) { |
316670eb A |
1475 | sflt_notify_after_register(so, sock_evt_cantrecvmore, |
1476 | handle); | |
0a7de745 | 1477 | } |
fe8ab488 | 1478 | socket_unlock(so, 0); |
316670eb A |
1479 | /* XXX no easy way to post the sock_evt_closing event */ |
1480 | sock_release(so); | |
1481 | solist = solisthead; | |
1482 | solisthead = solisthead->next; | |
1483 | FREE(solist, M_IFADDR); | |
1484 | } | |
1485 | ||
0a7de745 | 1486 | return error; |
91447636 A |
1487 | } |
1488 | ||
5ba3f43e A |
1489 | errno_t |
1490 | sflt_register_internal(const struct sflt_filter *filter, int domain, int type, | |
0a7de745 | 1491 | int protocol) |
5ba3f43e | 1492 | { |
0a7de745 | 1493 | return sflt_register_common(filter, domain, type, protocol, true); |
5ba3f43e A |
1494 | } |
1495 | ||
1496 | errno_t | |
1497 | sflt_register(const struct sflt_filter *filter, int domain, int type, | |
0a7de745 | 1498 | int protocol) |
5ba3f43e | 1499 | { |
0a7de745 | 1500 | return sflt_register_common(filter, domain, type, protocol, false); |
5ba3f43e A |
1501 | } |
1502 | ||
91447636 | 1503 | errno_t |
39236c6e | 1504 | sflt_unregister(sflt_handle handle) |
91447636 A |
1505 | { |
1506 | struct socket_filter *filter; | |
6d2010ae | 1507 | lck_rw_lock_exclusive(sock_filter_lock); |
39236c6e | 1508 | |
6d2010ae | 1509 | /* Find the entry by the handle */ |
91447636 | 1510 | TAILQ_FOREACH(filter, &sock_filter_head, sf_global_next) { |
0a7de745 | 1511 | if (filter->sf_filter.sf_handle == handle) { |
91447636 | 1512 | break; |
0a7de745 | 1513 | } |
91447636 | 1514 | } |
39236c6e | 1515 | |
91447636 | 1516 | if (filter) { |
5ba3f43e A |
1517 | VERIFY(OSDecrementAtomic64(&net_api_stats.nas_sfltr_register_count) > 0); |
1518 | ||
39236c6e | 1519 | /* Remove it from the global list */ |
91447636 | 1520 | TAILQ_REMOVE(&sock_filter_head, filter, sf_global_next); |
39236c6e A |
1521 | |
1522 | /* Remove it from the protosw list */ | |
91447636 | 1523 | if ((filter->sf_filter.sf_flags & SFLT_GLOBAL) != 0) { |
39236c6e A |
1524 | TAILQ_REMOVE(&filter->sf_proto->pr_filter_head, |
1525 | filter, sf_protosw_next); | |
91447636 | 1526 | } |
39236c6e A |
1527 | |
1528 | /* Detach from any sockets */ | |
6d2010ae | 1529 | struct socket_filter_entry *entry = NULL; |
39236c6e A |
1530 | |
1531 | for (entry = filter->sf_entry_head; entry; | |
1532 | entry = entry->sfe_next_onfilter) { | |
6d2010ae | 1533 | sflt_detach_locked(entry); |
3a60a9f5 | 1534 | } |
39236c6e A |
1535 | |
1536 | /* Release the filter */ | |
6d2010ae | 1537 | sflt_release_locked(filter); |
91447636 | 1538 | } |
39236c6e | 1539 | |
6d2010ae | 1540 | lck_rw_unlock_exclusive(sock_filter_lock); |
39236c6e | 1541 | |
0a7de745 A |
1542 | if (filter == NULL) { |
1543 | return ENOENT; | |
1544 | } | |
39236c6e | 1545 | |
0a7de745 | 1546 | return 0; |
91447636 A |
1547 | } |
1548 | ||
1549 | errno_t | |
39236c6e A |
1550 | sock_inject_data_in(socket_t so, const struct sockaddr *from, mbuf_t data, |
1551 | mbuf_t control, sflt_data_flag_t flags) | |
91447636 A |
1552 | { |
1553 | int error = 0; | |
39236c6e | 1554 | |
0a7de745 A |
1555 | if (so == NULL || data == NULL) { |
1556 | return EINVAL; | |
1557 | } | |
39236c6e | 1558 | |
91447636 | 1559 | if (flags & sock_data_filt_flag_oob) { |
0a7de745 | 1560 | return ENOTSUP; |
91447636 | 1561 | } |
39236c6e | 1562 | |
91447636 | 1563 | socket_lock(so, 1); |
39236c6e A |
1564 | |
1565 | /* reject if this is a subflow socket */ | |
1566 | if (so->so_flags & SOF_MP_SUBFLOW) { | |
1567 | error = ENOTSUP; | |
1568 | goto done; | |
1569 | } | |
1570 | ||
91447636 | 1571 | if (from) { |
39236c6e | 1572 | if (sbappendaddr(&so->so_rcv, |
0a7de745 | 1573 | (struct sockaddr *)(uintptr_t)from, data, control, NULL)) { |
91447636 | 1574 | sorwakeup(so); |
0a7de745 | 1575 | } |
91447636 A |
1576 | goto done; |
1577 | } | |
39236c6e | 1578 | |
91447636 | 1579 | if (control) { |
0a7de745 | 1580 | if (sbappendcontrol(&so->so_rcv, data, control, NULL)) { |
91447636 | 1581 | sorwakeup(so); |
0a7de745 | 1582 | } |
91447636 A |
1583 | goto done; |
1584 | } | |
39236c6e | 1585 | |
91447636 A |
1586 | if (flags & sock_data_filt_flag_record) { |
1587 | if (control || from) { | |
1588 | error = EINVAL; | |
1589 | goto done; | |
1590 | } | |
0a7de745 | 1591 | if (sbappendrecord(&so->so_rcv, (struct mbuf *)data)) { |
91447636 | 1592 | sorwakeup(so); |
0a7de745 | 1593 | } |
91447636 A |
1594 | goto done; |
1595 | } | |
39236c6e | 1596 | |
0a7de745 | 1597 | if (sbappend(&so->so_rcv, data)) { |
91447636 | 1598 | sorwakeup(so); |
0a7de745 | 1599 | } |
91447636 A |
1600 | done: |
1601 | socket_unlock(so, 1); | |
0a7de745 | 1602 | return error; |
91447636 A |
1603 | } |
1604 | ||
1605 | errno_t | |
39236c6e A |
1606 | sock_inject_data_out(socket_t so, const struct sockaddr *to, mbuf_t data, |
1607 | mbuf_t control, sflt_data_flag_t flags) | |
91447636 | 1608 | { |
39236c6e A |
1609 | int sosendflags = 0; |
1610 | ||
1611 | /* reject if this is a subflow socket */ | |
0a7de745 A |
1612 | if (so->so_flags & SOF_MP_SUBFLOW) { |
1613 | return ENOTSUP; | |
1614 | } | |
39236c6e | 1615 | |
0a7de745 | 1616 | if (flags & sock_data_filt_flag_oob) { |
39236c6e | 1617 | sosendflags = MSG_OOB; |
0a7de745 A |
1618 | } |
1619 | return sosend(so, (struct sockaddr *)(uintptr_t)to, NULL, | |
1620 | data, control, sosendflags); | |
91447636 A |
1621 | } |
1622 | ||
1623 | sockopt_dir | |
39236c6e | 1624 | sockopt_direction(sockopt_t sopt) |
91447636 | 1625 | { |
0a7de745 | 1626 | return (sopt->sopt_dir == SOPT_GET) ? sockopt_get : sockopt_set; |
91447636 A |
1627 | } |
1628 | ||
1629 | int | |
39236c6e | 1630 | sockopt_level(sockopt_t sopt) |
91447636 | 1631 | { |
0a7de745 | 1632 | return sopt->sopt_level; |
91447636 A |
1633 | } |
1634 | ||
1635 | int | |
39236c6e | 1636 | sockopt_name(sockopt_t sopt) |
91447636 | 1637 | { |
0a7de745 | 1638 | return sopt->sopt_name; |
91447636 A |
1639 | } |
1640 | ||
1641 | size_t | |
39236c6e | 1642 | sockopt_valsize(sockopt_t sopt) |
91447636 | 1643 | { |
0a7de745 | 1644 | return sopt->sopt_valsize; |
91447636 A |
1645 | } |
1646 | ||
1647 | errno_t | |
39236c6e | 1648 | sockopt_copyin(sockopt_t sopt, void *data, size_t len) |
91447636 | 1649 | { |
0a7de745 | 1650 | return sooptcopyin(sopt, data, len, len); |
91447636 A |
1651 | } |
1652 | ||
1653 | errno_t | |
39236c6e | 1654 | sockopt_copyout(sockopt_t sopt, void *data, size_t len) |
91447636 | 1655 | { |
0a7de745 | 1656 | return sooptcopyout(sopt, data, len); |
91447636 | 1657 | } |