1 /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Copyright (c) 2019 Gleb Smirnoff <[email protected]>
7 * Copyright (c) 1996 Matthew R. Green
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include <sys/param.h>
35 #include <sys/conf.h>
36 #include <sys/kernel.h>
37 #include <sys/epoch.h>
38 #include <sys/errno.h>
39 #include <sys/lock.h>
40 #include <sys/malloc.h>
41 #include <sys/socket.h>
42 #include <sys/socketvar.h>
43 #include <sys/systm.h>
44 #include <sys/lock.h>
45 #include <sys/mutex.h>
46 #include <sys/proc.h>
47 #include <sys/queue.h>
48 #include <sys/ucred.h>
49 #include <sys/jail.h>
50
51 #include <net/if.h>
52 #include <net/if_var.h>
53 #include <net/pfil.h>
54
55 static MALLOC_DEFINE(M_PFIL, "pfil", "pfil(9) packet filter hooks");
56
57 static int pfil_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
58 static struct cdevsw pfil_cdevsw = {
59 .d_ioctl = pfil_ioctl,
60 .d_name = PFILDEV,
61 .d_version = D_VERSION,
62 };
63 static struct cdev *pfil_dev;
64
65 static struct mtx pfil_lock;
66 MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF);
67 #define PFIL_LOCK() mtx_lock(&pfil_lock)
68 #define PFIL_UNLOCK() mtx_unlock(&pfil_lock)
69 #define PFIL_LOCK_ASSERT() mtx_assert(&pfil_lock, MA_OWNED)
70
71 struct pfil_hook {
72 pfil_mbuf_chk_t hook_mbuf_chk;
73 pfil_mem_chk_t hook_mem_chk;
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_mbuf_chk_t link_mbuf_chk;
86 pfil_mem_chk_t link_mem_chk;
87 void *link_ruleset;
88 int link_flags;
89 struct pfil_hook *link_hook;
90 struct epoch_context link_epoch_ctx;
91 };
92
93 typedef CK_STAILQ_HEAD(pfil_chain, pfil_link) pfil_chain_t;
94 struct pfil_head {
95 int head_nhooksin;
96 int head_nhooksout;
97 pfil_chain_t head_in;
98 pfil_chain_t head_out;
99 int head_flags;
100 enum pfil_types head_type;
101 LIST_ENTRY(pfil_head) head_list;
102 const char *head_name;
103 };
104
105 LIST_HEAD(pfilheadhead, pfil_head);
106 VNET_DEFINE_STATIC(struct pfilheadhead, pfil_head_list) =
107 LIST_HEAD_INITIALIZER(pfil_head_list);
108 #define V_pfil_head_list VNET(pfil_head_list)
109
110 LIST_HEAD(pfilhookhead, pfil_hook);
111 VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) =
112 LIST_HEAD_INITIALIZER(pfil_hook_list);
113 #define V_pfil_hook_list VNET(pfil_hook_list)
114
115 static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t );
116 static void pfil_link_free(epoch_context_t);
117
118 /*
119 * To couple a filtering point that provides memory pointer with a filter that
120 * works on mbufs only.
121 */
122 static __noinline int
pfil_fake_mbuf(pfil_mbuf_chk_t func,void * mem,u_int len,struct ifnet * ifp,int flags,void * ruleset,struct mbuf ** mp)123 pfil_fake_mbuf(pfil_mbuf_chk_t func, void *mem, u_int len, struct ifnet *ifp,
124 int flags, void *ruleset, struct mbuf **mp)
125 {
126 struct mbuf m;
127 pfil_return_t rv;
128
129 (void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR);
130 m_extadd(&m, mem, len, NULL, NULL, NULL, 0, EXT_RXRING);
131 m.m_len = m.m_pkthdr.len = len;
132 *mp = &m;
133
134 rv = func(mp, ifp, flags, ruleset, NULL);
135 if (rv == PFIL_PASS && *mp != &m) {
136 /*
137 * Firewalls that need pfil_fake_mbuf() most likely don't
138 * know they need return PFIL_REALLOCED.
139 */
140 rv = PFIL_REALLOCED;
141 }
142
143 return (rv);
144 }
145
146 static __always_inline int
pfil_mem_common(pfil_chain_t * pch,void * mem,u_int len,int flags,struct ifnet * ifp,struct mbuf ** m)147 pfil_mem_common(pfil_chain_t *pch, void *mem, u_int len, int flags,
148 struct ifnet *ifp, struct mbuf **m)
149 {
150 struct pfil_link *link;
151 pfil_return_t rv;
152 bool realloc = false;
153
154 NET_EPOCH_ASSERT();
155 KASSERT(flags == PFIL_IN || flags == PFIL_OUT,
156 ("%s: unsupported flags %d", __func__, flags));
157
158 rv = PFIL_PASS;
159 CK_STAILQ_FOREACH(link, pch, link_chain) {
160 if (__predict_true(link->link_mem_chk != NULL && !realloc))
161 rv = link->link_mem_chk(mem, len, flags, ifp,
162 link->link_ruleset, m);
163 else if (!realloc)
164 rv = pfil_fake_mbuf(link->link_mbuf_chk, mem, len, ifp,
165 flags, link->link_ruleset, m);
166 else
167 rv = link->link_mbuf_chk(m, ifp, flags,
168 link->link_ruleset, NULL);
169
170 if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
171 break;
172 else if (rv == PFIL_REALLOCED)
173 realloc = true;
174 }
175 if (realloc && rv == PFIL_PASS)
176 rv = PFIL_REALLOCED;
177 return (rv);
178 }
179
180 int
pfil_mem_in(struct pfil_head * head,void * mem,u_int len,struct ifnet * ifp,struct mbuf ** m)181 pfil_mem_in(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
182 struct mbuf **m)
183 {
184
185 return (pfil_mem_common(&head->head_in, mem, len, PFIL_IN, ifp, m));
186 }
187
188 int
pfil_mem_out(struct pfil_head * head,void * mem,u_int len,struct ifnet * ifp,struct mbuf ** m)189 pfil_mem_out(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
190 struct mbuf **m)
191 {
192
193 return (pfil_mem_common(&head->head_out, mem, len, PFIL_OUT, ifp, m));
194 }
195
196 static __always_inline int
pfil_mbuf_common(pfil_chain_t * pch,struct mbuf ** m,struct ifnet * ifp,int flags,struct inpcb * inp)197 pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp,
198 int flags, struct inpcb *inp)
199 {
200 struct pfil_link *link;
201 pfil_return_t rv;
202
203 NET_EPOCH_ASSERT();
204 KASSERT((flags & ~(PFIL_IN|PFIL_OUT|PFIL_FWD)) == 0,
205 ("%s: unsupported flags %#x", __func__, flags));
206 KASSERT((flags & ~PFIL_FWD) == PFIL_IN ||
207 (flags & ~PFIL_FWD) == PFIL_OUT,
208 ("%s: conflicting directions %#x", __func__, flags));
209
210 rv = PFIL_PASS;
211 CK_STAILQ_FOREACH(link, pch, link_chain) {
212 rv = link->link_mbuf_chk(m, ifp, flags, link->link_ruleset,
213 inp);
214 if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
215 break;
216 }
217 return (rv);
218 }
219
220 int
pfil_mbuf_in(struct pfil_head * head,struct mbuf ** m,struct ifnet * ifp,struct inpcb * inp)221 pfil_mbuf_in(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
222 struct inpcb *inp)
223 {
224
225 return (pfil_mbuf_common(&head->head_in, m, ifp, PFIL_IN, inp));
226 }
227
228 int
pfil_mbuf_out(struct pfil_head * head,struct mbuf ** m,struct ifnet * ifp,struct inpcb * inp)229 pfil_mbuf_out(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
230 struct inpcb *inp)
231 {
232
233 return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT, inp));
234 }
235
236 int
pfil_mbuf_fwd(struct pfil_head * head,struct mbuf ** m,struct ifnet * ifp,struct inpcb * inp)237 pfil_mbuf_fwd(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
238 struct inpcb *inp)
239 {
240
241 return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT | PFIL_FWD, inp));
242 }
243
244 /*
245 * pfil_head_register() registers a pfil_head with the packet filter hook
246 * mechanism.
247 */
248 pfil_head_t
pfil_head_register(struct pfil_head_args * pa)249 pfil_head_register(struct pfil_head_args *pa)
250 {
251 struct pfil_head *head, *list;
252
253 MPASS(pa->pa_version == PFIL_VERSION);
254
255 head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK);
256
257 head->head_nhooksin = head->head_nhooksout = 0;
258 head->head_flags = pa->pa_flags;
259 head->head_type = pa->pa_type;
260 head->head_name = pa->pa_headname;
261 CK_STAILQ_INIT(&head->head_in);
262 CK_STAILQ_INIT(&head->head_out);
263
264 PFIL_LOCK();
265 LIST_FOREACH(list, &V_pfil_head_list, head_list)
266 if (strcmp(pa->pa_headname, list->head_name) == 0) {
267 printf("pfil: duplicate head \"%s\"\n",
268 pa->pa_headname);
269 }
270 LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list);
271 PFIL_UNLOCK();
272
273 return (head);
274 }
275
276 /*
277 * pfil_head_unregister() removes a pfil_head from the packet filter hook
278 * mechanism. The producer of the hook promises that all outstanding
279 * invocations of the hook have completed before it unregisters the hook.
280 */
281 void
pfil_head_unregister(pfil_head_t ph)282 pfil_head_unregister(pfil_head_t ph)
283 {
284 struct pfil_link *link, *next;
285
286 PFIL_LOCK();
287 LIST_REMOVE(ph, head_list);
288
289 CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) {
290 link->link_hook->hook_links--;
291 free(link, M_PFIL);
292 }
293 CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) {
294 link->link_hook->hook_links--;
295 free(link, M_PFIL);
296 }
297 PFIL_UNLOCK();
298 free(ph, M_PFIL);
299 }
300
301 pfil_hook_t
pfil_add_hook(struct pfil_hook_args * pa)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
pfil_unlink(struct pfil_link_args * pa,pfil_head_t head,pfil_hook_t hook)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
pfil_link(struct pfil_link_args * pa)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
pfil_link_free(epoch_context_t ctx)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
pfil_remove_hook(pfil_hook_t hook)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 *
pfil_link_remove(pfil_chain_t * chain,pfil_hook_t hook)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
pfil_init(const void * unused __unused)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
pfil_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flags,struct thread * td)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
pfilioc_listheads(struct pfilioc_list * req)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
pfilioc_listhooks(struct pfilioc_list * req)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
pfilioc_link(struct pfilioc_link * req)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 }
723