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