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