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 } 723