FreeBSD/Linux Kernel Cross Reference
sys/net/pfil.c
1 /* $FreeBSD$ */
2 /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */
3
4 /*-
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
8 * Copyright (c) 1996 Matthew R. Green
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/param.h>
36 #include <sys/conf.h>
37 #include <sys/kernel.h>
38 #include <sys/epoch.h>
39 #include <sys/errno.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <sys/systm.h>
45 #include <sys/lock.h>
46 #include <sys/mutex.h>
47 #include <sys/proc.h>
48 #include <sys/queue.h>
49 #include <sys/ucred.h>
50 #include <sys/jail.h>
51
52 #include <net/if.h>
53 #include <net/if_var.h>
54 #include <net/pfil.h>
55
56 static MALLOC_DEFINE(M_PFIL, "pfil", "pfil(9) packet filter hooks");
57
58 static int pfil_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
59 static struct cdevsw pfil_cdevsw = {
60 .d_ioctl = pfil_ioctl,
61 .d_name = PFILDEV,
62 .d_version = D_VERSION,
63 };
64 static struct cdev *pfil_dev;
65
66 static struct mtx pfil_lock;
67 MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF);
68 #define PFIL_LOCK() mtx_lock(&pfil_lock)
69 #define PFIL_UNLOCK() mtx_unlock(&pfil_lock)
70 #define PFIL_LOCK_ASSERT() mtx_assert(&pfil_lock, MA_OWNED)
71
72 struct pfil_hook {
73 pfil_func_t hook_func;
74 void *hook_ruleset;
75 int hook_flags;
76 int hook_links;
77 enum pfil_types hook_type;
78 const char *hook_modname;
79 const char *hook_rulname;
80 LIST_ENTRY(pfil_hook) hook_list;
81 };
82
83 struct pfil_link {
84 CK_STAILQ_ENTRY(pfil_link) link_chain;
85 pfil_func_t link_func;
86 void *link_ruleset;
87 int link_flags;
88 struct pfil_hook *link_hook;
89 struct epoch_context link_epoch_ctx;
90 };
91
92 typedef CK_STAILQ_HEAD(pfil_chain, pfil_link) pfil_chain_t;
93 struct pfil_head {
94 int head_nhooksin;
95 int head_nhooksout;
96 pfil_chain_t head_in;
97 pfil_chain_t head_out;
98 int head_flags;
99 enum pfil_types head_type;
100 LIST_ENTRY(pfil_head) head_list;
101 const char *head_name;
102 };
103
104 LIST_HEAD(pfilheadhead, pfil_head);
105 VNET_DEFINE_STATIC(struct pfilheadhead, pfil_head_list) =
106 LIST_HEAD_INITIALIZER(pfil_head_list);
107 #define V_pfil_head_list VNET(pfil_head_list)
108
109 LIST_HEAD(pfilhookhead, pfil_hook);
110 VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) =
111 LIST_HEAD_INITIALIZER(pfil_hook_list);
112 #define V_pfil_hook_list VNET(pfil_hook_list)
113
114 static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t );
115 static void pfil_link_free(epoch_context_t);
116
117 int
118 pfil_realloc(pfil_packet_t *p, int flags, struct ifnet *ifp)
119 {
120 struct mbuf *m;
121
122 MPASS(flags & PFIL_MEMPTR);
123
124 if ((m = m_devget(p->mem, PFIL_LENGTH(flags), 0, ifp, NULL)) == NULL)
125 return (ENOMEM);
126 *p = pfil_packet_align(*p);
127 *p->m = m;
128
129 return (0);
130 }
131
132 static __noinline int
133 pfil_fake_mbuf(pfil_func_t func, pfil_packet_t *p, struct ifnet *ifp, int flags,
134 void *ruleset, struct inpcb *inp)
135 {
136 struct mbuf m, *mp;
137 pfil_return_t rv;
138
139 (void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR);
140 m_extadd(&m, p->mem, PFIL_LENGTH(flags), NULL, NULL, NULL, 0,
141 EXT_RXRING);
142 m.m_len = m.m_pkthdr.len = PFIL_LENGTH(flags);
143 mp = &m;
144 flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
145
146 rv = func(&mp, ifp, flags, ruleset, inp);
147 if (rv == PFIL_PASS && mp != &m) {
148 /*
149 * Firewalls that need pfil_fake_mbuf() most likely don't
150 * know they need return PFIL_REALLOCED.
151 */
152 rv = PFIL_REALLOCED;
153 *p = pfil_packet_align(*p);
154 *p->m = mp;
155 }
156
157 return (rv);
158 }
159
160 /*
161 * pfil_run_hooks() runs the specified packet filter hook chain.
162 */
163 int
164 pfil_run_hooks(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
165 int flags, struct inpcb *inp)
166 {
167 pfil_chain_t *pch;
168 struct pfil_link *link;
169 pfil_return_t rv;
170 bool realloc = false;
171
172 NET_EPOCH_ASSERT();
173
174 if (PFIL_DIR(flags) == PFIL_IN)
175 pch = &head->head_in;
176 else if (__predict_true(PFIL_DIR(flags) == PFIL_OUT))
177 pch = &head->head_out;
178 else
179 panic("%s: bogus flags %d", __func__, flags);
180
181 rv = PFIL_PASS;
182 CK_STAILQ_FOREACH(link, pch, link_chain) {
183 if ((flags & PFIL_MEMPTR) && !(link->link_flags & PFIL_MEMPTR))
184 rv = pfil_fake_mbuf(link->link_func, &p, ifp, flags,
185 link->link_ruleset, inp);
186 else
187 rv = (*link->link_func)(p, ifp, flags,
188 link->link_ruleset, inp);
189 if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
190 break;
191 else if (rv == PFIL_REALLOCED) {
192 flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
193 realloc = true;
194 }
195 }
196 if (realloc && rv == PFIL_PASS)
197 rv = PFIL_REALLOCED;
198 return (rv);
199 }
200
201 static __always_inline int
202 pfil_mbuf_common(pfil_chain_t *pch, pfil_packet_t p, struct ifnet *ifp,
203 int flags, struct inpcb *inp)
204 {
205 struct pfil_link *link;
206 pfil_return_t rv;
207
208 NET_EPOCH_ASSERT();
209 KASSERT(flags == PFIL_IN || flags == PFIL_OUT,
210 ("%s: unsupported flags %d", __func__, flags));
211
212 rv = PFIL_PASS;
213 CK_STAILQ_FOREACH(link, pch, link_chain) {
214 rv = (*link->link_func)(p, ifp, flags, link->link_ruleset, inp);
215 if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
216 break;
217 }
218 return (rv);
219 }
220
221 int
222 pfil_mbuf_in(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
223 struct inpcb *inp)
224 {
225
226 return (pfil_mbuf_common(&head->head_in, p, ifp, PFIL_IN, inp));
227 }
228
229 int
230 pfil_mbuf_out(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
231 struct inpcb *inp)
232 {
233
234 return (pfil_mbuf_common(&head->head_out, p, ifp, PFIL_OUT, inp));
235 }
236
237 /*
238 * pfil_head_register() registers a pfil_head with the packet filter hook
239 * mechanism.
240 */
241 pfil_head_t
242 pfil_head_register(struct pfil_head_args *pa)
243 {
244 struct pfil_head *head, *list;
245
246 MPASS(pa->pa_version == PFIL_VERSION);
247
248 head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK);
249
250 head->head_nhooksin = head->head_nhooksout = 0;
251 head->head_flags = pa->pa_flags;
252 head->head_type = pa->pa_type;
253 head->head_name = pa->pa_headname;
254 CK_STAILQ_INIT(&head->head_in);
255 CK_STAILQ_INIT(&head->head_out);
256
257 PFIL_LOCK();
258 LIST_FOREACH(list, &V_pfil_head_list, head_list)
259 if (strcmp(pa->pa_headname, list->head_name) == 0) {
260 printf("pfil: duplicate head \"%s\"\n",
261 pa->pa_headname);
262 }
263 LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list);
264 PFIL_UNLOCK();
265
266 return (head);
267 }
268
269 /*
270 * pfil_head_unregister() removes a pfil_head from the packet filter hook
271 * mechanism. The producer of the hook promises that all outstanding
272 * invocations of the hook have completed before it unregisters the hook.
273 */
274 void
275 pfil_head_unregister(pfil_head_t ph)
276 {
277 struct pfil_link *link, *next;
278
279 PFIL_LOCK();
280 LIST_REMOVE(ph, head_list);
281
282 CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) {
283 link->link_hook->hook_links--;
284 free(link, M_PFIL);
285 }
286 CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) {
287 link->link_hook->hook_links--;
288 free(link, M_PFIL);
289 }
290 PFIL_UNLOCK();
291 }
292
293 pfil_hook_t
294 pfil_add_hook(struct pfil_hook_args *pa)
295 {
296 struct pfil_hook *hook, *list;
297
298 MPASS(pa->pa_version == PFIL_VERSION);
299
300 hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO);
301 hook->hook_func = pa->pa_func;
302 hook->hook_ruleset = pa->pa_ruleset;
303 hook->hook_flags = pa->pa_flags;
304 hook->hook_type = pa->pa_type;
305 hook->hook_modname = pa->pa_modname;
306 hook->hook_rulname = pa->pa_rulname;
307
308 PFIL_LOCK();
309 LIST_FOREACH(list, &V_pfil_hook_list, hook_list)
310 if (strcmp(pa->pa_modname, list->hook_modname) == 0 &&
311 strcmp(pa->pa_rulname, list->hook_rulname) == 0) {
312 printf("pfil: duplicate hook \"%s:%s\"\n",
313 pa->pa_modname, pa->pa_rulname);
314 }
315 LIST_INSERT_HEAD(&V_pfil_hook_list, hook, hook_list);
316 PFIL_UNLOCK();
317
318 return (hook);
319 }
320
321 static int
322 pfil_unlink(struct pfil_link_args *pa, pfil_head_t head, pfil_hook_t hook)
323 {
324 struct pfil_link *in, *out;
325
326 PFIL_LOCK_ASSERT();
327
328 if (pa->pa_flags & PFIL_IN) {
329 in = pfil_link_remove(&head->head_in, hook);
330 if (in != NULL) {
331 head->head_nhooksin--;
332 hook->hook_links--;
333 }
334 } else
335 in = NULL;
336 if (pa->pa_flags & PFIL_OUT) {
337 out = pfil_link_remove(&head->head_out, hook);
338 if (out != NULL) {
339 head->head_nhooksout--;
340 hook->hook_links--;
341 }
342 } else
343 out = NULL;
344 PFIL_UNLOCK();
345
346 if (in != NULL)
347 NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
348 if (out != NULL)
349 NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
350
351 if (in == NULL && out == NULL)
352 return (ENOENT);
353 else
354 return (0);
355 }
356
357 int
358 pfil_link(struct pfil_link_args *pa)
359 {
360 struct pfil_link *in, *out, *link;
361 struct pfil_head *head;
362 struct pfil_hook *hook;
363 int error;
364
365 MPASS(pa->pa_version == PFIL_VERSION);
366
367 if ((pa->pa_flags & (PFIL_IN | PFIL_UNLINK)) == PFIL_IN)
368 in = malloc(sizeof(*in), M_PFIL, M_WAITOK | M_ZERO);
369 else
370 in = NULL;
371 if ((pa->pa_flags & (PFIL_OUT | PFIL_UNLINK)) == PFIL_OUT)
372 out = malloc(sizeof(*out), M_PFIL, M_WAITOK | M_ZERO);
373 else
374 out = NULL;
375
376 PFIL_LOCK();
377 if (pa->pa_flags & PFIL_HEADPTR)
378 head = pa->pa_head;
379 else
380 LIST_FOREACH(head, &V_pfil_head_list, head_list)
381 if (strcmp(pa->pa_headname, head->head_name) == 0)
382 break;
383 if (pa->pa_flags & PFIL_HOOKPTR)
384 hook = pa->pa_hook;
385 else
386 LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
387 if (strcmp(pa->pa_modname, hook->hook_modname) == 0 &&
388 strcmp(pa->pa_rulname, hook->hook_rulname) == 0)
389 break;
390 if (head == NULL || hook == NULL) {
391 error = ENOENT;
392 goto fail;
393 }
394
395 if (pa->pa_flags & PFIL_UNLINK)
396 return (pfil_unlink(pa, head, hook));
397
398 if (head->head_type != hook->hook_type ||
399 ((hook->hook_flags & pa->pa_flags) & ~head->head_flags)) {
400 error = EINVAL;
401 goto fail;
402 }
403
404 if (pa->pa_flags & PFIL_IN)
405 CK_STAILQ_FOREACH(link, &head->head_in, link_chain)
406 if (link->link_hook == hook) {
407 error = EEXIST;
408 goto fail;
409 }
410 if (pa->pa_flags & PFIL_OUT)
411 CK_STAILQ_FOREACH(link, &head->head_out, link_chain)
412 if (link->link_hook == hook) {
413 error = EEXIST;
414 goto fail;
415 }
416
417 if (pa->pa_flags & PFIL_IN) {
418 in->link_hook = hook;
419 in->link_func = hook->hook_func;
420 in->link_flags = hook->hook_flags;
421 in->link_ruleset = hook->hook_ruleset;
422 if (pa->pa_flags & PFIL_APPEND)
423 CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain);
424 else
425 CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain);
426 hook->hook_links++;
427 head->head_nhooksin++;
428 }
429 if (pa->pa_flags & PFIL_OUT) {
430 out->link_hook = hook;
431 out->link_func = hook->hook_func;
432 out->link_flags = hook->hook_flags;
433 out->link_ruleset = hook->hook_ruleset;
434 if (pa->pa_flags & PFIL_APPEND)
435 CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain);
436 else
437 CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain);
438 hook->hook_links++;
439 head->head_nhooksout++;
440 }
441 PFIL_UNLOCK();
442
443 return (0);
444
445 fail:
446 PFIL_UNLOCK();
447 free(in, M_PFIL);
448 free(out, M_PFIL);
449 return (error);
450 }
451
452 static void
453 pfil_link_free(epoch_context_t ctx)
454 {
455 struct pfil_link *link;
456
457 link = __containerof(ctx, struct pfil_link, link_epoch_ctx);
458 free(link, M_PFIL);
459 }
460
461 /*
462 * pfil_remove_hook removes a filter from all filtering points.
463 */
464 void
465 pfil_remove_hook(pfil_hook_t hook)
466 {
467 struct pfil_head *head;
468 struct pfil_link *in, *out;
469
470 PFIL_LOCK();
471 LIST_FOREACH(head, &V_pfil_head_list, head_list) {
472 retry:
473 in = pfil_link_remove(&head->head_in, hook);
474 if (in != NULL) {
475 head->head_nhooksin--;
476 hook->hook_links--;
477 NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
478 }
479 out = pfil_link_remove(&head->head_out, hook);
480 if (out != NULL) {
481 head->head_nhooksout--;
482 hook->hook_links--;
483 NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
484 }
485 if (in != NULL || out != NULL)
486 /* What if some stupid admin put same filter twice? */
487 goto retry;
488 }
489 LIST_REMOVE(hook, hook_list);
490 PFIL_UNLOCK();
491 MPASS(hook->hook_links == 0);
492 free(hook, M_PFIL);
493 }
494
495 /*
496 * Internal: Remove a pfil hook from a hook chain.
497 */
498 static struct pfil_link *
499 pfil_link_remove(pfil_chain_t *chain, pfil_hook_t hook)
500 {
501 struct pfil_link *link;
502
503 PFIL_LOCK_ASSERT();
504
505 CK_STAILQ_FOREACH(link, chain, link_chain)
506 if (link->link_hook == hook) {
507 CK_STAILQ_REMOVE(chain, link, pfil_link, link_chain);
508 return (link);
509 }
510
511 return (NULL);
512 }
513
514 static void
515 pfil_init(const void *unused __unused)
516 {
517 struct make_dev_args args;
518 int error __diagused;
519
520 make_dev_args_init(&args);
521 args.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
522 args.mda_devsw = &pfil_cdevsw;
523 args.mda_uid = UID_ROOT;
524 args.mda_gid = GID_WHEEL;
525 args.mda_mode = 0600;
526 error = make_dev_s(&args, &pfil_dev, PFILDEV);
527 KASSERT(error == 0, ("%s: failed to create dev: %d", __func__, error));
528 }
529 /*
530 * Make sure the pfil bits are first before any possible subsystem which
531 * might piggyback on the SI_SUB_PROTO_PFIL.
532 */
533 SYSINIT(pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, pfil_init, NULL);
534
535 /*
536 * User control interface.
537 */
538 static int pfilioc_listheads(struct pfilioc_list *);
539 static int pfilioc_listhooks(struct pfilioc_list *);
540 static int pfilioc_link(struct pfilioc_link *);
541
542 static int
543 pfil_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
544 struct thread *td)
545 {
546 int error;
547
548 CURVNET_SET(TD_TO_VNET(td));
549 error = 0;
550 switch (cmd) {
551 case PFILIOC_LISTHEADS:
552 error = pfilioc_listheads((struct pfilioc_list *)addr);
553 break;
554 case PFILIOC_LISTHOOKS:
555 error = pfilioc_listhooks((struct pfilioc_list *)addr);
556 break;
557 case PFILIOC_LINK:
558 error = pfilioc_link((struct pfilioc_link *)addr);
559 break;
560 default:
561 error = EINVAL;
562 break;
563 }
564 CURVNET_RESTORE();
565 return (error);
566 }
567
568 static int
569 pfilioc_listheads(struct pfilioc_list *req)
570 {
571 struct pfil_head *head;
572 struct pfil_link *link;
573 struct pfilioc_head *iohead;
574 struct pfilioc_hook *iohook;
575 u_int nheads, nhooks, hd, hk;
576 int error;
577
578 PFIL_LOCK();
579 restart:
580 nheads = nhooks = 0;
581 LIST_FOREACH(head, &V_pfil_head_list, head_list) {
582 nheads++;
583 nhooks += head->head_nhooksin + head->head_nhooksout;
584 }
585 PFIL_UNLOCK();
586
587 if (req->pio_nheads < nheads || req->pio_nhooks < nhooks) {
588 req->pio_nheads = nheads;
589 req->pio_nhooks = nhooks;
590 return (0);
591 }
592
593 iohead = malloc(sizeof(*iohead) * nheads, M_TEMP, M_WAITOK);
594 iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
595
596 hd = hk = 0;
597 PFIL_LOCK();
598 LIST_FOREACH(head, &V_pfil_head_list, head_list) {
599 if (hd + 1 > nheads ||
600 hk + head->head_nhooksin + head->head_nhooksout > nhooks) {
601 /* Configuration changed during malloc(). */
602 free(iohead, M_TEMP);
603 free(iohook, M_TEMP);
604 goto restart;
605 }
606 strlcpy(iohead[hd].pio_name, head->head_name,
607 sizeof(iohead[0].pio_name));
608 iohead[hd].pio_nhooksin = head->head_nhooksin;
609 iohead[hd].pio_nhooksout = head->head_nhooksout;
610 iohead[hd].pio_type = head->head_type;
611 CK_STAILQ_FOREACH(link, &head->head_in, link_chain) {
612 strlcpy(iohook[hk].pio_module,
613 link->link_hook->hook_modname,
614 sizeof(iohook[0].pio_module));
615 strlcpy(iohook[hk].pio_ruleset,
616 link->link_hook->hook_rulname,
617 sizeof(iohook[0].pio_ruleset));
618 hk++;
619 }
620 CK_STAILQ_FOREACH(link, &head->head_out, link_chain) {
621 strlcpy(iohook[hk].pio_module,
622 link->link_hook->hook_modname,
623 sizeof(iohook[0].pio_module));
624 strlcpy(iohook[hk].pio_ruleset,
625 link->link_hook->hook_rulname,
626 sizeof(iohook[0].pio_ruleset));
627 hk++;
628 }
629 hd++;
630 }
631 PFIL_UNLOCK();
632
633 error = copyout(iohead, req->pio_heads,
634 sizeof(*iohead) * min(hd, req->pio_nheads));
635 if (error == 0)
636 error = copyout(iohook, req->pio_hooks,
637 sizeof(*iohook) * min(req->pio_nhooks, hk));
638
639 req->pio_nheads = hd;
640 req->pio_nhooks = hk;
641
642 free(iohead, M_TEMP);
643 free(iohook, M_TEMP);
644
645 return (error);
646 }
647
648 static int
649 pfilioc_listhooks(struct pfilioc_list *req)
650 {
651 struct pfil_hook *hook;
652 struct pfilioc_hook *iohook;
653 u_int nhooks, hk;
654 int error;
655
656 PFIL_LOCK();
657 restart:
658 nhooks = 0;
659 LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
660 nhooks++;
661 PFIL_UNLOCK();
662
663 if (req->pio_nhooks < nhooks) {
664 req->pio_nhooks = nhooks;
665 return (0);
666 }
667
668 iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
669
670 hk = 0;
671 PFIL_LOCK();
672 LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) {
673 if (hk + 1 > nhooks) {
674 /* Configuration changed during malloc(). */
675 free(iohook, M_TEMP);
676 goto restart;
677 }
678 strlcpy(iohook[hk].pio_module, hook->hook_modname,
679 sizeof(iohook[0].pio_module));
680 strlcpy(iohook[hk].pio_ruleset, hook->hook_rulname,
681 sizeof(iohook[0].pio_ruleset));
682 iohook[hk].pio_type = hook->hook_type;
683 iohook[hk].pio_flags = hook->hook_flags;
684 hk++;
685 }
686 PFIL_UNLOCK();
687
688 error = copyout(iohook, req->pio_hooks,
689 sizeof(*iohook) * min(req->pio_nhooks, hk));
690 req->pio_nhooks = hk;
691 free(iohook, M_TEMP);
692
693 return (error);
694 }
695
696 static int
697 pfilioc_link(struct pfilioc_link *req)
698 {
699 struct pfil_link_args args;
700
701 if (req->pio_flags & ~(PFIL_IN | PFIL_OUT | PFIL_UNLINK | PFIL_APPEND))
702 return (EINVAL);
703
704 args.pa_version = PFIL_VERSION;
705 args.pa_flags = req->pio_flags;
706 args.pa_headname = req->pio_name;
707 args.pa_modname = req->pio_module;
708 args.pa_rulname = req->pio_ruleset;
709
710 return (pfil_link(&args));
711 }
Cache object: 828239f8663bfd521c5f1a5e6f3ee1c3
|