]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/pfil.c
iflib(9): Remove support for cloning pseudo interfaces
[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_mbuf_chk_t  hook_mbuf_chk;
74         pfil_mem_chk_t   hook_mem_chk;
75         void            *hook_ruleset;
76         int              hook_flags;
77         int              hook_links;
78         enum pfil_types  hook_type;
79         const char      *hook_modname;
80         const char      *hook_rulname;
81         LIST_ENTRY(pfil_hook) hook_list;
82 };
83
84 struct pfil_link {
85         CK_STAILQ_ENTRY(pfil_link) link_chain;
86         pfil_mbuf_chk_t          link_mbuf_chk;
87         pfil_mem_chk_t           link_mem_chk;
88         void                    *link_ruleset;
89         int                      link_flags;
90         struct pfil_hook        *link_hook;
91         struct epoch_context     link_epoch_ctx;
92 };
93
94 typedef CK_STAILQ_HEAD(pfil_chain, pfil_link)   pfil_chain_t;
95 struct pfil_head {
96         int              head_nhooksin;
97         int              head_nhooksout;
98         pfil_chain_t     head_in;
99         pfil_chain_t     head_out;
100         int              head_flags;
101         enum pfil_types  head_type;
102         LIST_ENTRY(pfil_head) head_list;
103         const char      *head_name;
104 };
105
106 LIST_HEAD(pfilheadhead, pfil_head);
107 VNET_DEFINE_STATIC(struct pfilheadhead, pfil_head_list) =
108     LIST_HEAD_INITIALIZER(pfil_head_list);
109 #define V_pfil_head_list        VNET(pfil_head_list)
110
111 LIST_HEAD(pfilhookhead, pfil_hook);
112 VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) =
113     LIST_HEAD_INITIALIZER(pfil_hook_list);
114 #define V_pfil_hook_list        VNET(pfil_hook_list)
115
116 static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t );
117 static void pfil_link_free(epoch_context_t);
118
119 /*
120  * To couple a filtering point that provides memory pointer with a filter that
121  * works on mbufs only.
122  */
123 static __noinline int
124 pfil_fake_mbuf(pfil_mbuf_chk_t func, void *mem, u_int len, struct ifnet *ifp,
125     int flags, void *ruleset, struct mbuf **mp)
126 {
127         struct mbuf m;
128         pfil_return_t rv;
129
130         (void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR);
131         m_extadd(&m, mem, len, NULL, NULL, NULL, 0, EXT_RXRING);
132         m.m_len = m.m_pkthdr.len = len;
133         *mp = &m;
134
135         rv = func(mp, ifp, flags, ruleset, NULL);
136         if (rv == PFIL_PASS && *mp != &m) {
137                 /*
138                  * Firewalls that need pfil_fake_mbuf() most likely don't
139                  * know they need return PFIL_REALLOCED.
140                  */
141                 rv = PFIL_REALLOCED;
142         }
143
144         return (rv);
145 }
146
147 static __always_inline int
148 pfil_mem_common(pfil_chain_t *pch, void *mem, u_int len, int flags,
149     struct ifnet *ifp, struct mbuf **m)
150 {
151         struct pfil_link *link;
152         pfil_return_t rv;
153         bool realloc = false;
154
155         NET_EPOCH_ASSERT();
156         KASSERT(flags == PFIL_IN || flags == PFIL_OUT,
157             ("%s: unsupported flags %d", __func__, flags));
158
159         rv = PFIL_PASS;
160         CK_STAILQ_FOREACH(link, pch, link_chain) {
161                 if (__predict_true(link->link_mem_chk != NULL && !realloc))
162                         rv = link->link_mem_chk(mem, len, flags, ifp,
163                             link->link_ruleset, m);
164                 else if (!realloc)
165                         rv = pfil_fake_mbuf(link->link_mbuf_chk, mem, len, ifp,
166                             flags, link->link_ruleset, m);
167                 else
168                         rv = link->link_mbuf_chk(m, ifp, flags,
169                             link->link_ruleset, NULL);
170
171                 if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
172                         break;
173                 else if (rv == PFIL_REALLOCED)
174                         realloc = true;
175         }
176         if (realloc && rv == PFIL_PASS)
177                 rv = PFIL_REALLOCED;
178         return (rv);
179 }
180
181 int
182 pfil_mem_in(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
183     struct mbuf **m)
184 {
185
186         return (pfil_mem_common(&head->head_in, mem, len, PFIL_IN, ifp, m));
187 }
188
189 int
190 pfil_mem_out(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
191     struct mbuf **m)
192 {
193
194         return (pfil_mem_common(&head->head_out, mem, len, PFIL_OUT, ifp, m));
195 }
196
197 static __always_inline int
198 pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp,
199     int flags, struct inpcb *inp)
200 {
201         struct pfil_link *link;
202         pfil_return_t rv;
203
204         NET_EPOCH_ASSERT();
205         KASSERT((flags & ~(PFIL_IN|PFIL_OUT|PFIL_FWD)) == 0,
206             ("%s: unsupported flags %#x", __func__, flags));
207         KASSERT((flags & ~PFIL_FWD) == PFIL_IN ||
208             (flags & ~PFIL_FWD) == PFIL_OUT,
209             ("%s: conflicting directions %#x", __func__, flags));
210
211         rv = PFIL_PASS;
212         CK_STAILQ_FOREACH(link, pch, link_chain) {
213                 rv = link->link_mbuf_chk(m, ifp, flags, link->link_ruleset,
214                     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, struct mbuf **m, struct ifnet *ifp,
223    struct inpcb *inp)
224 {
225
226         return (pfil_mbuf_common(&head->head_in, m, ifp, PFIL_IN, inp));
227 }
228
229 int
230 pfil_mbuf_out(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
231     struct inpcb *inp)
232 {
233
234         return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT, inp));
235 }
236
237 int
238 pfil_mbuf_fwd(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
239     struct inpcb *inp)
240 {
241
242         return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT | PFIL_FWD, inp));
243 }
244
245 /*
246  * pfil_head_register() registers a pfil_head with the packet filter hook
247  * mechanism.
248  */
249 pfil_head_t
250 pfil_head_register(struct pfil_head_args *pa)
251 {
252         struct pfil_head *head, *list;
253
254         MPASS(pa->pa_version == PFIL_VERSION);
255
256         head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK);
257
258         head->head_nhooksin = head->head_nhooksout = 0;
259         head->head_flags = pa->pa_flags;
260         head->head_type = pa->pa_type;
261         head->head_name = pa->pa_headname;
262         CK_STAILQ_INIT(&head->head_in);
263         CK_STAILQ_INIT(&head->head_out);
264
265         PFIL_LOCK();
266         LIST_FOREACH(list, &V_pfil_head_list, head_list)
267                 if (strcmp(pa->pa_headname, list->head_name) == 0) {
268                         printf("pfil: duplicate head \"%s\"\n",
269                             pa->pa_headname);
270                 }
271         LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list);
272         PFIL_UNLOCK();
273
274         return (head);
275 }
276
277 /*
278  * pfil_head_unregister() removes a pfil_head from the packet filter hook
279  * mechanism.  The producer of the hook promises that all outstanding
280  * invocations of the hook have completed before it unregisters the hook.
281  */
282 void
283 pfil_head_unregister(pfil_head_t ph)
284 {
285         struct pfil_link *link, *next;
286
287         PFIL_LOCK();
288         LIST_REMOVE(ph, head_list);
289
290         CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) {
291                 link->link_hook->hook_links--;
292                 free(link, M_PFIL);
293         }
294         CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) {
295                 link->link_hook->hook_links--;
296                 free(link, M_PFIL);
297         }
298         PFIL_UNLOCK();
299 }
300
301 pfil_hook_t
302 pfil_add_hook(struct pfil_hook_args *pa)
303 {
304         struct pfil_hook *hook, *list;
305
306         MPASS(pa->pa_version == PFIL_VERSION);
307
308         hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO);
309         hook->hook_mbuf_chk = pa->pa_mbuf_chk;
310         hook->hook_mem_chk = pa->pa_mem_chk;
311         hook->hook_ruleset = pa->pa_ruleset;
312         hook->hook_flags = pa->pa_flags;
313         hook->hook_type = pa->pa_type;
314         hook->hook_modname = pa->pa_modname;
315         hook->hook_rulname = pa->pa_rulname;
316
317         PFIL_LOCK();
318         LIST_FOREACH(list, &V_pfil_hook_list, hook_list)
319                 if (strcmp(pa->pa_modname, list->hook_modname) == 0 &&
320                     strcmp(pa->pa_rulname, list->hook_rulname) == 0) {
321                         printf("pfil: duplicate hook \"%s:%s\"\n",
322                             pa->pa_modname, pa->pa_rulname);
323                 }
324         LIST_INSERT_HEAD(&V_pfil_hook_list, hook, hook_list);
325         PFIL_UNLOCK();
326
327         return (hook);
328 }
329
330 static int
331 pfil_unlink(struct pfil_link_args *pa, pfil_head_t head, pfil_hook_t hook)
332 {
333         struct pfil_link *in, *out;
334
335         PFIL_LOCK_ASSERT();
336
337         if (pa->pa_flags & PFIL_IN) {
338                 in = pfil_link_remove(&head->head_in, hook);
339                 if (in != NULL) {
340                         head->head_nhooksin--;
341                         hook->hook_links--;
342                 }
343         } else
344                 in = NULL;
345         if (pa->pa_flags & PFIL_OUT) {
346                 out = pfil_link_remove(&head->head_out, hook);
347                 if (out != NULL) {
348                         head->head_nhooksout--;
349                         hook->hook_links--;
350                 }
351         } else
352                 out = NULL;
353         PFIL_UNLOCK();
354
355         if (in != NULL)
356                 NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
357         if (out != NULL)
358                 NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
359
360         if (in == NULL && out == NULL)
361                 return (ENOENT);
362         else
363                 return (0);
364 }
365
366 int
367 pfil_link(struct pfil_link_args *pa)
368 {
369         struct pfil_link *in, *out, *link;
370         struct pfil_head *head;
371         struct pfil_hook *hook;
372         int error;
373
374         MPASS(pa->pa_version == PFIL_VERSION);
375
376         if ((pa->pa_flags & (PFIL_IN | PFIL_UNLINK)) == PFIL_IN)
377                 in = malloc(sizeof(*in), M_PFIL, M_WAITOK | M_ZERO);
378         else
379                 in = NULL;
380         if ((pa->pa_flags & (PFIL_OUT | PFIL_UNLINK)) == PFIL_OUT)
381                 out = malloc(sizeof(*out), M_PFIL, M_WAITOK | M_ZERO);
382         else
383                 out = NULL;
384
385         PFIL_LOCK();
386         if (pa->pa_flags & PFIL_HEADPTR)
387                 head = pa->pa_head;
388         else
389                 LIST_FOREACH(head, &V_pfil_head_list, head_list)
390                         if (strcmp(pa->pa_headname, head->head_name) == 0)
391                                 break;
392         if (pa->pa_flags & PFIL_HOOKPTR)
393                 hook = pa->pa_hook;
394         else
395                 LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
396                         if (strcmp(pa->pa_modname, hook->hook_modname) == 0 &&
397                             strcmp(pa->pa_rulname, hook->hook_rulname) == 0)
398                                 break;
399         if (head == NULL || hook == NULL) {
400                 error = ENOENT;
401                 goto fail;
402         }
403
404         if (pa->pa_flags & PFIL_UNLINK)
405                 return (pfil_unlink(pa, head, hook));
406
407         if (head->head_type != hook->hook_type ||
408             ((hook->hook_flags & pa->pa_flags) & ~head->head_flags)) {
409                 error = EINVAL;
410                 goto fail;
411         }
412
413         if (pa->pa_flags & PFIL_IN)
414                 CK_STAILQ_FOREACH(link, &head->head_in, link_chain)
415                         if (link->link_hook == hook) {
416                                 error = EEXIST;
417                                 goto fail;
418                         }
419         if (pa->pa_flags & PFIL_OUT)
420                 CK_STAILQ_FOREACH(link, &head->head_out, link_chain)
421                         if (link->link_hook == hook) {
422                                 error = EEXIST;
423                                 goto fail;
424                         }
425
426         if (pa->pa_flags & PFIL_IN) {
427                 in->link_hook = hook;
428                 in->link_mbuf_chk = hook->hook_mbuf_chk;
429                 in->link_mem_chk = hook->hook_mem_chk;
430                 in->link_flags = hook->hook_flags;
431                 in->link_ruleset = hook->hook_ruleset;
432                 if (pa->pa_flags & PFIL_APPEND)
433                         CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain);
434                 else
435                         CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain);
436                 hook->hook_links++;
437                 head->head_nhooksin++;
438         }
439         if (pa->pa_flags & PFIL_OUT) {
440                 out->link_hook = hook;
441                 out->link_mbuf_chk = hook->hook_mbuf_chk;
442                 out->link_mem_chk = hook->hook_mem_chk;
443                 out->link_flags = hook->hook_flags;
444                 out->link_ruleset = hook->hook_ruleset;
445                 if (pa->pa_flags & PFIL_APPEND)
446                         CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain);
447                 else
448                         CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain);
449                 hook->hook_links++;
450                 head->head_nhooksout++;
451         }
452         PFIL_UNLOCK();
453
454         return (0);
455
456 fail:
457         PFIL_UNLOCK();
458         free(in, M_PFIL);
459         free(out, M_PFIL);
460         return (error);
461 }
462
463 static void
464 pfil_link_free(epoch_context_t ctx)
465 {
466         struct pfil_link *link;
467
468         link = __containerof(ctx, struct pfil_link, link_epoch_ctx);
469         free(link, M_PFIL);
470 }
471
472 /*
473  * pfil_remove_hook removes a filter from all filtering points.
474  */
475 void
476 pfil_remove_hook(pfil_hook_t hook)
477 {
478         struct pfil_head *head;
479         struct pfil_link *in, *out;
480
481         PFIL_LOCK();
482         LIST_FOREACH(head, &V_pfil_head_list, head_list) {
483 retry:
484                 in = pfil_link_remove(&head->head_in, hook);
485                 if (in != NULL) {
486                         head->head_nhooksin--;
487                         hook->hook_links--;
488                         NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
489                 }
490                 out = pfil_link_remove(&head->head_out, hook);
491                 if (out != NULL) {
492                         head->head_nhooksout--;
493                         hook->hook_links--;
494                         NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
495                 }
496                 if (in != NULL || out != NULL)
497                         /* What if some stupid admin put same filter twice? */
498                         goto retry;
499         }
500         LIST_REMOVE(hook, hook_list);
501         PFIL_UNLOCK();
502         MPASS(hook->hook_links == 0);
503         free(hook, M_PFIL);
504 }
505
506 /*
507  * Internal: Remove a pfil hook from a hook chain.
508  */
509 static struct pfil_link *
510 pfil_link_remove(pfil_chain_t *chain, pfil_hook_t hook)
511 {
512         struct pfil_link *link;
513
514         PFIL_LOCK_ASSERT();
515
516         CK_STAILQ_FOREACH(link, chain, link_chain)
517                 if (link->link_hook == hook) {
518                         CK_STAILQ_REMOVE(chain, link, pfil_link, link_chain);
519                         return (link);
520                 }
521
522         return (NULL);
523 }
524
525 static void
526 pfil_init(const void *unused __unused)
527 {
528         struct make_dev_args args;
529         int error __diagused;
530
531         make_dev_args_init(&args);
532         args.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
533         args.mda_devsw = &pfil_cdevsw;
534         args.mda_uid = UID_ROOT;
535         args.mda_gid = GID_WHEEL;
536         args.mda_mode = 0600;
537         error = make_dev_s(&args, &pfil_dev, PFILDEV);
538         KASSERT(error == 0, ("%s: failed to create dev: %d", __func__, error));
539 }
540 /*
541  * Make sure the pfil bits are first before any possible subsystem which
542  * might piggyback on the SI_SUB_PROTO_PFIL.
543  */
544 SYSINIT(pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, pfil_init, NULL);
545
546 /*
547  * User control interface.
548  */
549 static int pfilioc_listheads(struct pfilioc_list *);
550 static int pfilioc_listhooks(struct pfilioc_list *);
551 static int pfilioc_link(struct pfilioc_link *);
552
553 static int
554 pfil_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
555     struct thread *td)
556 {
557         int error;
558
559         CURVNET_SET(TD_TO_VNET(td));
560         error = 0;
561         switch (cmd) {
562         case PFILIOC_LISTHEADS:
563                 error = pfilioc_listheads((struct pfilioc_list *)addr);
564                 break;
565         case PFILIOC_LISTHOOKS:
566                 error = pfilioc_listhooks((struct pfilioc_list *)addr);
567                 break;
568         case PFILIOC_LINK:
569                 error = pfilioc_link((struct pfilioc_link *)addr);
570                 break;
571         default:
572                 error = EINVAL;
573                 break;
574         }
575         CURVNET_RESTORE();
576         return (error);
577 }
578
579 static int
580 pfilioc_listheads(struct pfilioc_list *req)
581 {
582         struct pfil_head *head;
583         struct pfil_link *link;
584         struct pfilioc_head *iohead;
585         struct pfilioc_hook *iohook;
586         u_int nheads, nhooks, hd, hk;
587         int error;
588
589         PFIL_LOCK();
590 restart:
591         nheads = nhooks = 0;
592         LIST_FOREACH(head, &V_pfil_head_list, head_list) {
593                 nheads++;
594                 nhooks += head->head_nhooksin + head->head_nhooksout;
595         }
596         PFIL_UNLOCK();
597
598         if (req->pio_nheads < nheads || req->pio_nhooks < nhooks) {
599                 req->pio_nheads = nheads;
600                 req->pio_nhooks = nhooks;
601                 return (0);
602         }
603
604         iohead = malloc(sizeof(*iohead) * nheads, M_TEMP, M_WAITOK);
605         iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
606
607         hd = hk = 0;
608         PFIL_LOCK();
609         LIST_FOREACH(head, &V_pfil_head_list, head_list) {
610                 if (hd + 1 > nheads ||
611                     hk + head->head_nhooksin + head->head_nhooksout > nhooks) {
612                         /* Configuration changed during malloc(). */
613                         free(iohead, M_TEMP);
614                         free(iohook, M_TEMP);
615                         goto restart;
616                 }
617                 strlcpy(iohead[hd].pio_name, head->head_name,
618                         sizeof(iohead[0].pio_name));
619                 iohead[hd].pio_nhooksin = head->head_nhooksin;
620                 iohead[hd].pio_nhooksout = head->head_nhooksout;
621                 iohead[hd].pio_type = head->head_type;
622                 CK_STAILQ_FOREACH(link, &head->head_in, link_chain) {
623                         strlcpy(iohook[hk].pio_module,
624                             link->link_hook->hook_modname,
625                             sizeof(iohook[0].pio_module));
626                         strlcpy(iohook[hk].pio_ruleset,
627                             link->link_hook->hook_rulname,
628                             sizeof(iohook[0].pio_ruleset));
629                         hk++;
630                 }
631                 CK_STAILQ_FOREACH(link, &head->head_out, link_chain) {
632                         strlcpy(iohook[hk].pio_module,
633                             link->link_hook->hook_modname,
634                             sizeof(iohook[0].pio_module));
635                         strlcpy(iohook[hk].pio_ruleset,
636                             link->link_hook->hook_rulname,
637                             sizeof(iohook[0].pio_ruleset));
638                         hk++;
639                 }
640                 hd++;
641         }
642         PFIL_UNLOCK();
643
644         error = copyout(iohead, req->pio_heads,
645             sizeof(*iohead) * min(hd, req->pio_nheads));
646         if (error == 0)
647                 error = copyout(iohook, req->pio_hooks,
648                     sizeof(*iohook) * min(req->pio_nhooks, hk));
649
650         req->pio_nheads = hd;
651         req->pio_nhooks = hk;
652
653         free(iohead, M_TEMP);
654         free(iohook, M_TEMP);
655
656         return (error);
657 }
658
659 static int
660 pfilioc_listhooks(struct pfilioc_list *req)
661 {
662         struct pfil_hook *hook;
663         struct pfilioc_hook *iohook;
664         u_int nhooks, hk;
665         int error;
666
667         PFIL_LOCK();
668 restart:
669         nhooks = 0;
670         LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
671                 nhooks++;
672         PFIL_UNLOCK();
673
674         if (req->pio_nhooks < nhooks) {
675                 req->pio_nhooks = nhooks;
676                 return (0);
677         }
678
679         iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
680
681         hk = 0;
682         PFIL_LOCK();
683         LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) {
684                 if (hk + 1 > nhooks) {
685                         /* Configuration changed during malloc(). */
686                         free(iohook, M_TEMP);
687                         goto restart;
688                 }
689                 strlcpy(iohook[hk].pio_module, hook->hook_modname,
690                     sizeof(iohook[0].pio_module));
691                 strlcpy(iohook[hk].pio_ruleset, hook->hook_rulname,
692                     sizeof(iohook[0].pio_ruleset));
693                 iohook[hk].pio_type = hook->hook_type;
694                 iohook[hk].pio_flags = hook->hook_flags;
695                 hk++;
696         }
697         PFIL_UNLOCK();
698
699         error = copyout(iohook, req->pio_hooks,
700             sizeof(*iohook) * min(req->pio_nhooks, hk));
701         req->pio_nhooks = hk;
702         free(iohook, M_TEMP);
703
704         return (error);
705 }
706
707 static int
708 pfilioc_link(struct pfilioc_link *req)
709 {
710         struct pfil_link_args args;
711
712         if (req->pio_flags & ~(PFIL_IN | PFIL_OUT | PFIL_UNLINK | PFIL_APPEND))
713                 return (EINVAL);
714
715         args.pa_version = PFIL_VERSION;
716         args.pa_flags = req->pio_flags;
717         args.pa_headname = req->pio_name;
718         args.pa_modname = req->pio_module;
719         args.pa_rulname = req->pio_ruleset;
720
721         return (pfil_link(&args));
722 }