]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/pfil.c
ping(8): Fix a mandoc related issue
[FreeBSD/FreeBSD.git] / 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 /*
202  * pfil_head_register() registers a pfil_head with the packet filter hook
203  * mechanism.
204  */
205 pfil_head_t
206 pfil_head_register(struct pfil_head_args *pa)
207 {
208         struct pfil_head *head, *list;
209
210         MPASS(pa->pa_version == PFIL_VERSION);
211
212         head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK);
213
214         head->head_nhooksin = head->head_nhooksout = 0;
215         head->head_flags = pa->pa_flags;
216         head->head_type = pa->pa_type;
217         head->head_name = pa->pa_headname;
218         CK_STAILQ_INIT(&head->head_in);
219         CK_STAILQ_INIT(&head->head_out);
220
221         PFIL_LOCK();
222         LIST_FOREACH(list, &V_pfil_head_list, head_list)
223                 if (strcmp(pa->pa_headname, list->head_name) == 0) {
224                         printf("pfil: duplicate head \"%s\"\n",
225                             pa->pa_headname);
226                 }
227         LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list);
228         PFIL_UNLOCK();
229
230         return (head);
231 }
232
233 /*
234  * pfil_head_unregister() removes a pfil_head from the packet filter hook
235  * mechanism.  The producer of the hook promises that all outstanding
236  * invocations of the hook have completed before it unregisters the hook.
237  */
238 void
239 pfil_head_unregister(pfil_head_t ph)
240 {
241         struct pfil_link *link, *next;
242
243         PFIL_LOCK();
244         LIST_REMOVE(ph, head_list);
245
246         CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) {
247                 link->link_hook->hook_links--;
248                 free(link, M_PFIL);
249         }
250         CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) {
251                 link->link_hook->hook_links--;
252                 free(link, M_PFIL);
253         }
254         PFIL_UNLOCK();
255 }
256
257 pfil_hook_t
258 pfil_add_hook(struct pfil_hook_args *pa)
259 {
260         struct pfil_hook *hook, *list;
261
262         MPASS(pa->pa_version == PFIL_VERSION);
263
264         hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO);
265         hook->hook_func = pa->pa_func;
266         hook->hook_ruleset = pa->pa_ruleset;
267         hook->hook_flags = pa->pa_flags;
268         hook->hook_type = pa->pa_type;
269         hook->hook_modname = pa->pa_modname;
270         hook->hook_rulname = pa->pa_rulname;
271
272         PFIL_LOCK();
273         LIST_FOREACH(list, &V_pfil_hook_list, hook_list)
274                 if (strcmp(pa->pa_modname, list->hook_modname) == 0 &&
275                     strcmp(pa->pa_rulname, list->hook_rulname) == 0) {
276                         printf("pfil: duplicate hook \"%s:%s\"\n",
277                             pa->pa_modname, pa->pa_rulname);
278                 }
279         LIST_INSERT_HEAD(&V_pfil_hook_list, hook, hook_list);
280         PFIL_UNLOCK();
281
282         return (hook);
283 }
284
285 static int
286 pfil_unlink(struct pfil_link_args *pa, pfil_head_t head, pfil_hook_t hook)
287 {
288         struct pfil_link *in, *out;
289
290         PFIL_LOCK_ASSERT();
291
292         if (pa->pa_flags & PFIL_IN) {
293                 in = pfil_link_remove(&head->head_in, hook);
294                 if (in != NULL) {
295                         head->head_nhooksin--;
296                         hook->hook_links--;
297                 }
298         } else
299                 in = NULL;
300         if (pa->pa_flags & PFIL_OUT) {
301                 out = pfil_link_remove(&head->head_out, hook);
302                 if (out != NULL) {
303                         head->head_nhooksout--;
304                         hook->hook_links--;
305                 }
306         } else
307                 out = NULL;
308         PFIL_UNLOCK();
309
310         if (in != NULL)
311                 NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
312         if (out != NULL)
313                 NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
314
315         if (in == NULL && out == NULL)
316                 return (ENOENT);
317         else
318                 return (0);
319 }
320
321 int
322 pfil_link(struct pfil_link_args *pa)
323 {
324         struct pfil_link *in, *out, *link;
325         struct pfil_head *head;
326         struct pfil_hook *hook;
327         int error;
328
329         MPASS(pa->pa_version == PFIL_VERSION);
330
331         if ((pa->pa_flags & (PFIL_IN | PFIL_UNLINK)) == PFIL_IN)
332                 in = malloc(sizeof(*in), M_PFIL, M_WAITOK | M_ZERO);
333         else
334                 in = NULL;
335         if ((pa->pa_flags & (PFIL_OUT | PFIL_UNLINK)) == PFIL_OUT)
336                 out = malloc(sizeof(*out), M_PFIL, M_WAITOK | M_ZERO);
337         else
338                 out = NULL;
339
340         PFIL_LOCK();
341         if (pa->pa_flags & PFIL_HEADPTR)
342                 head = pa->pa_head;
343         else
344                 LIST_FOREACH(head, &V_pfil_head_list, head_list)
345                         if (strcmp(pa->pa_headname, head->head_name) == 0)
346                                 break;
347         if (pa->pa_flags & PFIL_HOOKPTR)
348                 hook = pa->pa_hook;
349         else
350                 LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
351                         if (strcmp(pa->pa_modname, hook->hook_modname) == 0 &&
352                             strcmp(pa->pa_rulname, hook->hook_rulname) == 0)
353                                 break;
354         if (head == NULL || hook == NULL) {
355                 error = ENOENT;
356                 goto fail;
357         }
358
359         if (pa->pa_flags & PFIL_UNLINK)
360                 return (pfil_unlink(pa, head, hook));
361
362         if (head->head_type != hook->hook_type ||
363             ((hook->hook_flags & pa->pa_flags) & ~head->head_flags)) {
364                 error = EINVAL;
365                 goto fail;
366         }
367
368         if (pa->pa_flags & PFIL_IN)
369                 CK_STAILQ_FOREACH(link, &head->head_in, link_chain)
370                         if (link->link_hook == hook) {
371                                 error = EEXIST;
372                                 goto fail;
373                         }
374         if (pa->pa_flags & PFIL_OUT)
375                 CK_STAILQ_FOREACH(link, &head->head_out, link_chain)
376                         if (link->link_hook == hook) {
377                                 error = EEXIST;
378                                 goto fail;
379                         }
380
381         if (pa->pa_flags & PFIL_IN) {
382                 in->link_hook = hook;
383                 in->link_func = hook->hook_func;
384                 in->link_flags = hook->hook_flags;
385                 in->link_ruleset = hook->hook_ruleset;
386                 if (pa->pa_flags & PFIL_APPEND)
387                         CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain);
388                 else
389                         CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain);
390                 hook->hook_links++;
391                 head->head_nhooksin++;
392         }
393         if (pa->pa_flags & PFIL_OUT) {
394                 out->link_hook = hook;
395                 out->link_func = hook->hook_func;
396                 out->link_flags = hook->hook_flags;
397                 out->link_ruleset = hook->hook_ruleset;
398                 if (pa->pa_flags & PFIL_APPEND)
399                         CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain);
400                 else
401                         CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain);
402                 hook->hook_links++;
403                 head->head_nhooksout++;
404         }
405         PFIL_UNLOCK();
406
407         return (0);
408
409 fail:
410         PFIL_UNLOCK();
411         free(in, M_PFIL);
412         free(out, M_PFIL);
413         return (error);
414 }
415
416 static void
417 pfil_link_free(epoch_context_t ctx)
418 {
419         struct pfil_link *link;
420
421         link = __containerof(ctx, struct pfil_link, link_epoch_ctx);
422         free(link, M_PFIL);
423 }
424
425 /*
426  * pfil_remove_hook removes a filter from all filtering points.
427  */
428 void
429 pfil_remove_hook(pfil_hook_t hook)
430 {
431         struct pfil_head *head;
432         struct pfil_link *in, *out;
433
434         PFIL_LOCK();
435         LIST_FOREACH(head, &V_pfil_head_list, head_list) {
436 retry:
437                 in = pfil_link_remove(&head->head_in, hook);
438                 if (in != NULL) {
439                         head->head_nhooksin--;
440                         hook->hook_links--;
441                         NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
442                 }
443                 out = pfil_link_remove(&head->head_out, hook);
444                 if (out != NULL) {
445                         head->head_nhooksout--;
446                         hook->hook_links--;
447                         NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
448                 }
449                 if (in != NULL || out != NULL)
450                         /* What if some stupid admin put same filter twice? */
451                         goto retry;
452         }
453         LIST_REMOVE(hook, hook_list);
454         PFIL_UNLOCK();
455         MPASS(hook->hook_links == 0);
456         free(hook, M_PFIL);
457 }
458
459 /*
460  * Internal: Remove a pfil hook from a hook chain.
461  */
462 static struct pfil_link *
463 pfil_link_remove(pfil_chain_t *chain, pfil_hook_t hook)
464 {
465         struct pfil_link *link;
466
467         PFIL_LOCK_ASSERT();
468
469         CK_STAILQ_FOREACH(link, chain, link_chain)
470                 if (link->link_hook == hook) {
471                         CK_STAILQ_REMOVE(chain, link, pfil_link, link_chain);
472                         return (link);
473                 }
474
475         return (NULL);
476 }
477
478 static void
479 pfil_init(const void *unused __unused)
480 {
481         struct make_dev_args args;
482         int error;
483
484         make_dev_args_init(&args);
485         args.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
486         args.mda_devsw = &pfil_cdevsw;
487         args.mda_uid = UID_ROOT;
488         args.mda_gid = GID_WHEEL;
489         args.mda_mode = 0600;
490         error = make_dev_s(&args, &pfil_dev, PFILDEV);
491         KASSERT(error == 0, ("%s: failed to create dev: %d", __func__, error));
492 }
493 /*
494  * Make sure the pfil bits are first before any possible subsystem which
495  * might piggyback on the SI_SUB_PROTO_PFIL.
496  */
497 SYSINIT(pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, pfil_init, NULL);
498
499 /*
500  * User control interface.
501  */
502 static int pfilioc_listheads(struct pfilioc_list *);
503 static int pfilioc_listhooks(struct pfilioc_list *);
504 static int pfilioc_link(struct pfilioc_link *);
505
506 static int
507 pfil_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
508     struct thread *td)
509 {
510         int error;
511
512         CURVNET_SET(TD_TO_VNET(td));
513         error = 0;
514         switch (cmd) {
515         case PFILIOC_LISTHEADS:
516                 error = pfilioc_listheads((struct pfilioc_list *)addr);
517                 break;
518         case PFILIOC_LISTHOOKS:
519                 error = pfilioc_listhooks((struct pfilioc_list *)addr);
520                 break;
521         case PFILIOC_LINK:
522                 error = pfilioc_link((struct pfilioc_link *)addr);
523                 break;
524         default:
525                 error = EINVAL;
526                 break;
527         }
528         CURVNET_RESTORE();
529         return (error);
530 }
531
532 static int
533 pfilioc_listheads(struct pfilioc_list *req)
534 {
535         struct pfil_head *head;
536         struct pfil_link *link;
537         struct pfilioc_head *iohead;
538         struct pfilioc_hook *iohook;
539         u_int nheads, nhooks, hd, hk;
540         int error;
541
542         PFIL_LOCK();
543 restart:
544         nheads = nhooks = 0;
545         LIST_FOREACH(head, &V_pfil_head_list, head_list) {
546                 nheads++;
547                 nhooks += head->head_nhooksin + head->head_nhooksout;
548         }
549         PFIL_UNLOCK();
550
551         if (req->pio_nheads < nheads || req->pio_nhooks < nhooks) {
552                 req->pio_nheads = nheads;
553                 req->pio_nhooks = nhooks;
554                 return (0);
555         }
556
557         iohead = malloc(sizeof(*iohead) * nheads, M_TEMP, M_WAITOK);
558         iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
559
560         hd = hk = 0;
561         PFIL_LOCK();
562         LIST_FOREACH(head, &V_pfil_head_list, head_list) {
563                 if (hd + 1 > nheads ||
564                     hk + head->head_nhooksin + head->head_nhooksout > nhooks) {
565                         /* Configuration changed during malloc(). */
566                         free(iohead, M_TEMP);
567                         free(iohook, M_TEMP);
568                         goto restart;
569                 }
570                 strlcpy(iohead[hd].pio_name, head->head_name,
571                         sizeof(iohead[0].pio_name));
572                 iohead[hd].pio_nhooksin = head->head_nhooksin;
573                 iohead[hd].pio_nhooksout = head->head_nhooksout;
574                 iohead[hd].pio_type = head->head_type;
575                 CK_STAILQ_FOREACH(link, &head->head_in, link_chain) {
576                         strlcpy(iohook[hk].pio_module,
577                             link->link_hook->hook_modname,
578                             sizeof(iohook[0].pio_module));
579                         strlcpy(iohook[hk].pio_ruleset,
580                             link->link_hook->hook_rulname,
581                             sizeof(iohook[0].pio_ruleset));
582                         hk++;
583                 }
584                 CK_STAILQ_FOREACH(link, &head->head_out, link_chain) {
585                         strlcpy(iohook[hk].pio_module,
586                             link->link_hook->hook_modname,
587                             sizeof(iohook[0].pio_module));
588                         strlcpy(iohook[hk].pio_ruleset,
589                             link->link_hook->hook_rulname,
590                             sizeof(iohook[0].pio_ruleset));
591                         hk++;
592                 }
593                 hd++;
594         }
595         PFIL_UNLOCK();
596
597         error = copyout(iohead, req->pio_heads,
598             sizeof(*iohead) * min(hd, req->pio_nheads));
599         if (error == 0)
600                 error = copyout(iohook, req->pio_hooks,
601                     sizeof(*iohook) * min(req->pio_nhooks, hk));
602
603         req->pio_nheads = hd;
604         req->pio_nhooks = hk;
605
606         free(iohead, M_TEMP);
607         free(iohook, M_TEMP);
608
609         return (error);
610 }
611
612 static int
613 pfilioc_listhooks(struct pfilioc_list *req)
614 {
615         struct pfil_hook *hook;
616         struct pfilioc_hook *iohook;
617         u_int nhooks, hk;
618         int error;
619
620         PFIL_LOCK();
621 restart:
622         nhooks = 0;
623         LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
624                 nhooks++;
625         PFIL_UNLOCK();
626
627         if (req->pio_nhooks < nhooks) {
628                 req->pio_nhooks = nhooks;
629                 return (0);
630         }
631
632         iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
633
634         hk = 0;
635         PFIL_LOCK();
636         LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) {
637                 if (hk + 1 > nhooks) {
638                         /* Configuration changed during malloc(). */
639                         free(iohook, M_TEMP);
640                         goto restart;
641                 }
642                 strlcpy(iohook[hk].pio_module, hook->hook_modname,
643                     sizeof(iohook[0].pio_module));
644                 strlcpy(iohook[hk].pio_ruleset, hook->hook_rulname,
645                     sizeof(iohook[0].pio_ruleset));
646                 iohook[hk].pio_type = hook->hook_type;
647                 iohook[hk].pio_flags = hook->hook_flags;
648                 hk++;
649         }
650         PFIL_UNLOCK();
651
652         error = copyout(iohook, req->pio_hooks,
653             sizeof(*iohook) * min(req->pio_nhooks, hk));
654         req->pio_nhooks = hk;
655         free(iohook, M_TEMP);
656
657         return (error);
658 }
659
660 static int
661 pfilioc_link(struct pfilioc_link *req)
662 {
663         struct pfil_link_args args;
664
665         if (req->pio_flags & ~(PFIL_IN | PFIL_OUT | PFIL_UNLINK | PFIL_APPEND))
666                 return (EINVAL);
667
668         args.pa_version = PFIL_VERSION;
669         args.pa_flags = req->pio_flags;
670         args.pa_headname = req->pio_name;
671         args.pa_modname = req->pio_module;
672         args.pa_rulname = req->pio_ruleset;
673
674         return (pfil_link(&args));
675 }