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