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