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