1e9c7bebfSDarren Reed /* $FreeBSD$ */ 2134ea224SSam Leffler /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */ 3e9c7bebfSDarren Reed 4c398230bSWarner Losh /*- 5fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 6fe267a55SPedro F. Giffuni * 7b252313fSGleb Smirnoff * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org> 8e9c7bebfSDarren Reed * Copyright (c) 1996 Matthew R. Green 9e9c7bebfSDarren Reed * All rights reserved. 10e9c7bebfSDarren Reed * 11e9c7bebfSDarren Reed * Redistribution and use in source and binary forms, with or without 12e9c7bebfSDarren Reed * modification, are permitted provided that the following conditions 13e9c7bebfSDarren Reed * are met: 14e9c7bebfSDarren Reed * 1. Redistributions of source code must retain the above copyright 15e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer. 16e9c7bebfSDarren Reed * 2. Redistributions in binary form must reproduce the above copyright 17e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer in the 18e9c7bebfSDarren Reed * documentation and/or other materials provided with the distribution. 19e9c7bebfSDarren Reed * 3. The name of the author may not be used to endorse or promote products 20e9c7bebfSDarren Reed * derived from this software without specific prior written permission. 21e9c7bebfSDarren Reed * 22e9c7bebfSDarren Reed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23e9c7bebfSDarren Reed * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24e9c7bebfSDarren Reed * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25e9c7bebfSDarren Reed * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26e9c7bebfSDarren Reed * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27e9c7bebfSDarren Reed * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28e9c7bebfSDarren Reed * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29e9c7bebfSDarren Reed * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30e9c7bebfSDarren Reed * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31e9c7bebfSDarren Reed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32e9c7bebfSDarren Reed * SUCH DAMAGE. 33e9c7bebfSDarren Reed */ 34e9c7bebfSDarren Reed 35e9c7bebfSDarren Reed #include <sys/param.h> 36b252313fSGleb Smirnoff #include <sys/conf.h> 37134ea224SSam Leffler #include <sys/kernel.h> 38b252313fSGleb Smirnoff #include <sys/epoch.h> 39e9c7bebfSDarren Reed #include <sys/errno.h> 40604afec4SChristian S.J. Peron #include <sys/lock.h> 41e9c7bebfSDarren Reed #include <sys/malloc.h> 42e9c7bebfSDarren Reed #include <sys/socket.h> 43e9c7bebfSDarren Reed #include <sys/socketvar.h> 44e9c7bebfSDarren Reed #include <sys/systm.h> 45134ea224SSam Leffler #include <sys/lock.h> 46134ea224SSam Leffler #include <sys/mutex.h> 47134ea224SSam Leffler #include <sys/proc.h> 48e9c7bebfSDarren Reed #include <sys/queue.h> 493ca1c423SGleb Smirnoff #include <sys/ucred.h> 503ca1c423SGleb Smirnoff #include <sys/jail.h> 51e9c7bebfSDarren Reed 52e9c7bebfSDarren Reed #include <net/if.h> 5376039bc8SGleb Smirnoff #include <net/if_var.h> 54e9c7bebfSDarren Reed #include <net/pfil.h> 55e9c7bebfSDarren Reed 56b252313fSGleb Smirnoff static MALLOC_DEFINE(M_PFIL, "pfil", "pfil(9) packet filter hooks"); 57e9c7bebfSDarren Reed 58b252313fSGleb Smirnoff static int pfil_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *); 59b252313fSGleb Smirnoff static struct cdevsw pfil_cdevsw = { 60b252313fSGleb Smirnoff .d_ioctl = pfil_ioctl, 61b252313fSGleb Smirnoff .d_name = PFILDEV, 62b252313fSGleb Smirnoff .d_version = D_VERSION, 63b252313fSGleb Smirnoff }; 64b252313fSGleb Smirnoff static struct cdev *pfil_dev; 65134ea224SSam Leffler 66b252313fSGleb Smirnoff static struct mtx pfil_lock; 67b252313fSGleb Smirnoff MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF); 68b252313fSGleb Smirnoff #define PFIL_LOCK() mtx_lock(&pfil_lock) 69b252313fSGleb Smirnoff #define PFIL_UNLOCK() mtx_unlock(&pfil_lock) 70b252313fSGleb Smirnoff #define PFIL_LOCK_ASSERT() mtx_assert(&pfil_lock, MA_OWNED) 71b252313fSGleb Smirnoff 72b252313fSGleb Smirnoff struct pfil_hook { 73caf32b26SGleb Smirnoff pfil_mbuf_chk_t hook_mbuf_chk; 74caf32b26SGleb Smirnoff pfil_mem_chk_t hook_mem_chk; 75b252313fSGleb Smirnoff void *hook_ruleset; 76b252313fSGleb Smirnoff int hook_flags; 77b252313fSGleb Smirnoff int hook_links; 78b252313fSGleb Smirnoff enum pfil_types hook_type; 79b252313fSGleb Smirnoff const char *hook_modname; 80b252313fSGleb Smirnoff const char *hook_rulname; 81b252313fSGleb Smirnoff LIST_ENTRY(pfil_hook) hook_list; 82b252313fSGleb Smirnoff }; 83b252313fSGleb Smirnoff 84b252313fSGleb Smirnoff struct pfil_link { 85b252313fSGleb Smirnoff CK_STAILQ_ENTRY(pfil_link) link_chain; 86caf32b26SGleb Smirnoff pfil_mbuf_chk_t link_mbuf_chk; 87caf32b26SGleb Smirnoff pfil_mem_chk_t link_mem_chk; 88b252313fSGleb Smirnoff void *link_ruleset; 89b252313fSGleb Smirnoff int link_flags; 90b252313fSGleb Smirnoff struct pfil_hook *link_hook; 91b252313fSGleb Smirnoff struct epoch_context link_epoch_ctx; 92b252313fSGleb Smirnoff }; 93b252313fSGleb Smirnoff 94b252313fSGleb Smirnoff typedef CK_STAILQ_HEAD(pfil_chain, pfil_link) pfil_chain_t; 95b252313fSGleb Smirnoff struct pfil_head { 96b252313fSGleb Smirnoff int head_nhooksin; 97b252313fSGleb Smirnoff int head_nhooksout; 98b252313fSGleb Smirnoff pfil_chain_t head_in; 99b252313fSGleb Smirnoff pfil_chain_t head_out; 100b252313fSGleb Smirnoff int head_flags; 101b252313fSGleb Smirnoff enum pfil_types head_type; 102b252313fSGleb Smirnoff LIST_ENTRY(pfil_head) head_list; 103b252313fSGleb Smirnoff const char *head_name; 104b252313fSGleb Smirnoff }; 105134ea224SSam Leffler 1060b4b0b0fSJulian Elischer LIST_HEAD(pfilheadhead, pfil_head); 107b252313fSGleb Smirnoff VNET_DEFINE_STATIC(struct pfilheadhead, pfil_head_list) = 108b252313fSGleb Smirnoff LIST_HEAD_INITIALIZER(pfil_head_list); 1090b4b0b0fSJulian Elischer #define V_pfil_head_list VNET(pfil_head_list) 110134ea224SSam Leffler 111b252313fSGleb Smirnoff LIST_HEAD(pfilhookhead, pfil_hook); 112b252313fSGleb Smirnoff VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) = 113b252313fSGleb Smirnoff LIST_HEAD_INITIALIZER(pfil_hook_list); 114b252313fSGleb Smirnoff #define V_pfil_hook_list VNET(pfil_hook_list) 115af48c203SAndrey V. Elsukov 116b252313fSGleb Smirnoff static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t ); 117b252313fSGleb Smirnoff static void pfil_link_free(epoch_context_t); 118af48c203SAndrey V. Elsukov 119caf32b26SGleb Smirnoff /* 120caf32b26SGleb Smirnoff * To couple a filtering point that provides memory pointer with a filter that 121caf32b26SGleb Smirnoff * works on mbufs only. 122caf32b26SGleb Smirnoff */ 123b252313fSGleb Smirnoff static __noinline int 124caf32b26SGleb Smirnoff pfil_fake_mbuf(pfil_mbuf_chk_t func, void *mem, u_int len, struct ifnet *ifp, 125caf32b26SGleb Smirnoff int flags, void *ruleset, struct mbuf **mp) 126b252313fSGleb Smirnoff { 127caf32b26SGleb Smirnoff struct mbuf m; 128b252313fSGleb Smirnoff pfil_return_t rv; 129b252313fSGleb Smirnoff 130b252313fSGleb Smirnoff (void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR); 131caf32b26SGleb Smirnoff m_extadd(&m, mem, len, NULL, NULL, NULL, 0, EXT_RXRING); 132caf32b26SGleb Smirnoff m.m_len = m.m_pkthdr.len = len; 133caf32b26SGleb Smirnoff *mp = &m; 134b252313fSGleb Smirnoff 135caf32b26SGleb Smirnoff rv = func(mp, ifp, flags, ruleset, NULL); 136caf32b26SGleb Smirnoff if (rv == PFIL_PASS && *mp != &m) { 137b252313fSGleb Smirnoff /* 138b252313fSGleb Smirnoff * Firewalls that need pfil_fake_mbuf() most likely don't 139c9341022SGleb Smirnoff * know they need return PFIL_REALLOCED. 140b252313fSGleb Smirnoff */ 141b252313fSGleb Smirnoff rv = PFIL_REALLOCED; 142b252313fSGleb Smirnoff } 143b252313fSGleb Smirnoff 144b252313fSGleb Smirnoff return (rv); 145b252313fSGleb Smirnoff } 146af48c203SAndrey V. Elsukov 147caf32b26SGleb Smirnoff static __always_inline int 148caf32b26SGleb Smirnoff pfil_mem_common(pfil_chain_t *pch, void *mem, u_int len, int flags, 149caf32b26SGleb Smirnoff struct ifnet *ifp, struct mbuf **m) 150134ea224SSam Leffler { 151b252313fSGleb Smirnoff struct pfil_link *link; 152b9fdb4b3SGleb Smirnoff pfil_return_t rv; 153b9fdb4b3SGleb Smirnoff bool realloc = false; 154134ea224SSam Leffler 155e87ad0abSGleb Smirnoff NET_EPOCH_ASSERT(); 156caf32b26SGleb Smirnoff KASSERT(flags == PFIL_IN || flags == PFIL_OUT, 157caf32b26SGleb Smirnoff ("%s: unsupported flags %d", __func__, flags)); 158b252313fSGleb Smirnoff 159b252313fSGleb Smirnoff rv = PFIL_PASS; 160b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, pch, link_chain) { 161caf32b26SGleb Smirnoff if (__predict_true(link->link_mem_chk != NULL && !realloc)) 162caf32b26SGleb Smirnoff rv = link->link_mem_chk(mem, len, flags, ifp, 163caf32b26SGleb Smirnoff link->link_ruleset, m); 164caf32b26SGleb Smirnoff else if (!realloc) 165caf32b26SGleb Smirnoff rv = pfil_fake_mbuf(link->link_mbuf_chk, mem, len, ifp, 166caf32b26SGleb Smirnoff flags, link->link_ruleset, m); 167b252313fSGleb Smirnoff else 168caf32b26SGleb Smirnoff rv = link->link_mbuf_chk(m, ifp, flags, 169caf32b26SGleb Smirnoff link->link_ruleset, NULL); 170caf32b26SGleb Smirnoff 171b9fdb4b3SGleb Smirnoff if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) 172b252313fSGleb Smirnoff break; 173caf32b26SGleb Smirnoff else if (rv == PFIL_REALLOCED) 174b9fdb4b3SGleb Smirnoff realloc = true; 175887c60fcSAndre Oppermann } 176b9fdb4b3SGleb Smirnoff if (realloc && rv == PFIL_PASS) 177b9fdb4b3SGleb Smirnoff rv = PFIL_REALLOCED; 178b9fdb4b3SGleb Smirnoff return (rv); 1794dab1a18SAlexander V. Chernikov } 1808da01399SAndre Oppermann 181caf32b26SGleb Smirnoff int 182caf32b26SGleb Smirnoff pfil_mem_in(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp, 183caf32b26SGleb Smirnoff struct mbuf **m) 184caf32b26SGleb Smirnoff { 185caf32b26SGleb Smirnoff 186caf32b26SGleb Smirnoff return (pfil_mem_common(&head->head_in, mem, len, PFIL_IN, ifp, m)); 187caf32b26SGleb Smirnoff } 188caf32b26SGleb Smirnoff 189caf32b26SGleb Smirnoff int 190caf32b26SGleb Smirnoff pfil_mem_out(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp, 191caf32b26SGleb Smirnoff struct mbuf **m) 192caf32b26SGleb Smirnoff { 193caf32b26SGleb Smirnoff 194caf32b26SGleb Smirnoff return (pfil_mem_common(&head->head_out, mem, len, PFIL_OUT, ifp, m)); 195caf32b26SGleb Smirnoff } 196caf32b26SGleb Smirnoff 1970b70e3e7SMateusz Guzik static __always_inline int 198caf32b26SGleb Smirnoff pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp, 1990b70e3e7SMateusz Guzik int flags, struct inpcb *inp) 2000b70e3e7SMateusz Guzik { 2010b70e3e7SMateusz Guzik struct pfil_link *link; 2020b70e3e7SMateusz Guzik pfil_return_t rv; 2030b70e3e7SMateusz Guzik 2040b70e3e7SMateusz Guzik NET_EPOCH_ASSERT(); 205*b52b61c0SKristof Provost KASSERT((flags & ~(PFIL_IN|PFIL_OUT|PFIL_FWD)) == 0, 206*b52b61c0SKristof Provost ("%s: unsupported flags %#x", __func__, flags)); 207*b52b61c0SKristof Provost KASSERT((flags & ~PFIL_FWD) == PFIL_IN || 208*b52b61c0SKristof Provost (flags & ~PFIL_FWD) == PFIL_OUT, 209*b52b61c0SKristof Provost ("%s: conflicting directions %#x", __func__, flags)); 2100b70e3e7SMateusz Guzik 2110b70e3e7SMateusz Guzik rv = PFIL_PASS; 2120b70e3e7SMateusz Guzik CK_STAILQ_FOREACH(link, pch, link_chain) { 213caf32b26SGleb Smirnoff rv = link->link_mbuf_chk(m, ifp, flags, link->link_ruleset, 214caf32b26SGleb Smirnoff inp); 2150b70e3e7SMateusz Guzik if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) 2160b70e3e7SMateusz Guzik break; 2170b70e3e7SMateusz Guzik } 2180b70e3e7SMateusz Guzik return (rv); 2190b70e3e7SMateusz Guzik } 2200b70e3e7SMateusz Guzik 2210b70e3e7SMateusz Guzik int 222caf32b26SGleb Smirnoff pfil_mbuf_in(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp, 2230b70e3e7SMateusz Guzik struct inpcb *inp) 2240b70e3e7SMateusz Guzik { 2250b70e3e7SMateusz Guzik 226caf32b26SGleb Smirnoff return (pfil_mbuf_common(&head->head_in, m, ifp, PFIL_IN, inp)); 2270b70e3e7SMateusz Guzik } 2280b70e3e7SMateusz Guzik 2290b70e3e7SMateusz Guzik int 230caf32b26SGleb Smirnoff pfil_mbuf_out(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp, 2310b70e3e7SMateusz Guzik struct inpcb *inp) 2320b70e3e7SMateusz Guzik { 2330b70e3e7SMateusz Guzik 234caf32b26SGleb Smirnoff return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT, inp)); 2350b70e3e7SMateusz Guzik } 2360b70e3e7SMateusz Guzik 237*b52b61c0SKristof Provost int 238*b52b61c0SKristof Provost pfil_mbuf_fwd(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp, 239*b52b61c0SKristof Provost struct inpcb *inp) 240*b52b61c0SKristof Provost { 241*b52b61c0SKristof Provost 242*b52b61c0SKristof Provost return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT | PFIL_FWD, inp)); 243*b52b61c0SKristof Provost } 244*b52b61c0SKristof Provost 2454dab1a18SAlexander V. Chernikov /* 246cee81198SRobert Watson * pfil_head_register() registers a pfil_head with the packet filter hook 247cee81198SRobert Watson * mechanism. 248134ea224SSam Leffler */ 249b252313fSGleb Smirnoff pfil_head_t 250b252313fSGleb Smirnoff pfil_head_register(struct pfil_head_args *pa) 251134ea224SSam Leffler { 252b252313fSGleb Smirnoff struct pfil_head *head, *list; 253134ea224SSam Leffler 254b252313fSGleb Smirnoff MPASS(pa->pa_version == PFIL_VERSION); 255b252313fSGleb Smirnoff 256b252313fSGleb Smirnoff head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK); 257b252313fSGleb Smirnoff 258b252313fSGleb Smirnoff head->head_nhooksin = head->head_nhooksout = 0; 259b252313fSGleb Smirnoff head->head_flags = pa->pa_flags; 260b252313fSGleb Smirnoff head->head_type = pa->pa_type; 261b252313fSGleb Smirnoff head->head_name = pa->pa_headname; 262b252313fSGleb Smirnoff CK_STAILQ_INIT(&head->head_in); 263b252313fSGleb Smirnoff CK_STAILQ_INIT(&head->head_out); 264b252313fSGleb Smirnoff 265b252313fSGleb Smirnoff PFIL_LOCK(); 266b252313fSGleb Smirnoff LIST_FOREACH(list, &V_pfil_head_list, head_list) 267b252313fSGleb Smirnoff if (strcmp(pa->pa_headname, list->head_name) == 0) { 268b252313fSGleb Smirnoff printf("pfil: duplicate head \"%s\"\n", 269b252313fSGleb Smirnoff pa->pa_headname); 270134ea224SSam Leffler } 271b252313fSGleb Smirnoff LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list); 272b252313fSGleb Smirnoff PFIL_UNLOCK(); 273b252313fSGleb Smirnoff 274b252313fSGleb Smirnoff return (head); 275134ea224SSam Leffler } 276134ea224SSam Leffler 277134ea224SSam Leffler /* 278d2c205d5SRobert Watson * pfil_head_unregister() removes a pfil_head from the packet filter hook 279d2c205d5SRobert Watson * mechanism. The producer of the hook promises that all outstanding 280d2c205d5SRobert Watson * invocations of the hook have completed before it unregisters the hook. 281134ea224SSam Leffler */ 282b252313fSGleb Smirnoff void 283b252313fSGleb Smirnoff pfil_head_unregister(pfil_head_t ph) 284134ea224SSam Leffler { 285b252313fSGleb Smirnoff struct pfil_link *link, *next; 286134ea224SSam Leffler 287b252313fSGleb Smirnoff PFIL_LOCK(); 288b252313fSGleb Smirnoff LIST_REMOVE(ph, head_list); 289b252313fSGleb Smirnoff 290b252313fSGleb Smirnoff CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) { 291b252313fSGleb Smirnoff link->link_hook->hook_links--; 292b252313fSGleb Smirnoff free(link, M_PFIL); 293b252313fSGleb Smirnoff } 294b252313fSGleb Smirnoff CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) { 295b252313fSGleb Smirnoff link->link_hook->hook_links--; 296b252313fSGleb Smirnoff free(link, M_PFIL); 297b252313fSGleb Smirnoff } 298b252313fSGleb Smirnoff PFIL_UNLOCK(); 299134ea224SSam Leffler } 300134ea224SSam Leffler 301b252313fSGleb Smirnoff pfil_hook_t 302b252313fSGleb Smirnoff pfil_add_hook(struct pfil_hook_args *pa) 303134ea224SSam Leffler { 304b252313fSGleb Smirnoff struct pfil_hook *hook, *list; 305134ea224SSam Leffler 306b252313fSGleb Smirnoff MPASS(pa->pa_version == PFIL_VERSION); 307b252313fSGleb Smirnoff 308b252313fSGleb Smirnoff hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO); 309caf32b26SGleb Smirnoff hook->hook_mbuf_chk = pa->pa_mbuf_chk; 310caf32b26SGleb Smirnoff hook->hook_mem_chk = pa->pa_mem_chk; 311b252313fSGleb Smirnoff hook->hook_ruleset = pa->pa_ruleset; 312b252313fSGleb Smirnoff hook->hook_flags = pa->pa_flags; 313b252313fSGleb Smirnoff hook->hook_type = pa->pa_type; 314b252313fSGleb Smirnoff hook->hook_modname = pa->pa_modname; 315b252313fSGleb Smirnoff hook->hook_rulname = pa->pa_rulname; 316b252313fSGleb Smirnoff 317b252313fSGleb Smirnoff PFIL_LOCK(); 318b252313fSGleb Smirnoff LIST_FOREACH(list, &V_pfil_hook_list, hook_list) 319b252313fSGleb Smirnoff if (strcmp(pa->pa_modname, list->hook_modname) == 0 && 320b252313fSGleb Smirnoff strcmp(pa->pa_rulname, list->hook_rulname) == 0) { 321b252313fSGleb Smirnoff printf("pfil: duplicate hook \"%s:%s\"\n", 322b252313fSGleb Smirnoff pa->pa_modname, pa->pa_rulname); 323e9c7bebfSDarren Reed } 324b252313fSGleb Smirnoff LIST_INSERT_HEAD(&V_pfil_hook_list, hook, hook_list); 325b252313fSGleb Smirnoff PFIL_UNLOCK(); 326e9c7bebfSDarren Reed 327b252313fSGleb Smirnoff return (hook); 328effaab88SKristof Provost } 329effaab88SKristof Provost 330effaab88SKristof Provost static int 331b252313fSGleb Smirnoff pfil_unlink(struct pfil_link_args *pa, pfil_head_t head, pfil_hook_t hook) 332effaab88SKristof Provost { 333b252313fSGleb Smirnoff struct pfil_link *in, *out; 334e9c7bebfSDarren Reed 335b252313fSGleb Smirnoff PFIL_LOCK_ASSERT(); 336e9c7bebfSDarren Reed 337b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_IN) { 338b252313fSGleb Smirnoff in = pfil_link_remove(&head->head_in, hook); 339b252313fSGleb Smirnoff if (in != NULL) { 340b252313fSGleb Smirnoff head->head_nhooksin--; 341b252313fSGleb Smirnoff hook->hook_links--; 342effaab88SKristof Provost } 343b252313fSGleb Smirnoff } else 344b252313fSGleb Smirnoff in = NULL; 345b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_OUT) { 346b252313fSGleb Smirnoff out = pfil_link_remove(&head->head_out, hook); 347b252313fSGleb Smirnoff if (out != NULL) { 348b252313fSGleb Smirnoff head->head_nhooksout--; 349b252313fSGleb Smirnoff hook->hook_links--; 350604afec4SChristian S.J. Peron } 351b252313fSGleb Smirnoff } else 352b252313fSGleb Smirnoff out = NULL; 353b252313fSGleb Smirnoff PFIL_UNLOCK(); 354e9c7bebfSDarren Reed 355b252313fSGleb Smirnoff if (in != NULL) 356e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx); 357b252313fSGleb Smirnoff if (out != NULL) 358e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx); 359134ea224SSam Leffler 360b252313fSGleb Smirnoff if (in == NULL && out == NULL) 361b252313fSGleb Smirnoff return (ENOENT); 362134ea224SSam Leffler else 363cee81198SRobert Watson return (0); 364134ea224SSam Leffler } 365134ea224SSam Leffler 366b252313fSGleb Smirnoff int 367b252313fSGleb Smirnoff pfil_link(struct pfil_link_args *pa) 368b252313fSGleb Smirnoff { 369b252313fSGleb Smirnoff struct pfil_link *in, *out, *link; 370b252313fSGleb Smirnoff struct pfil_head *head; 371b252313fSGleb Smirnoff struct pfil_hook *hook; 372b252313fSGleb Smirnoff int error; 373b252313fSGleb Smirnoff 374b252313fSGleb Smirnoff MPASS(pa->pa_version == PFIL_VERSION); 375b252313fSGleb Smirnoff 376b252313fSGleb Smirnoff if ((pa->pa_flags & (PFIL_IN | PFIL_UNLINK)) == PFIL_IN) 377b252313fSGleb Smirnoff in = malloc(sizeof(*in), M_PFIL, M_WAITOK | M_ZERO); 378b252313fSGleb Smirnoff else 379b252313fSGleb Smirnoff in = NULL; 380b252313fSGleb Smirnoff if ((pa->pa_flags & (PFIL_OUT | PFIL_UNLINK)) == PFIL_OUT) 381b252313fSGleb Smirnoff out = malloc(sizeof(*out), M_PFIL, M_WAITOK | M_ZERO); 382b252313fSGleb Smirnoff else 383b252313fSGleb Smirnoff out = NULL; 384b252313fSGleb Smirnoff 385b252313fSGleb Smirnoff PFIL_LOCK(); 386b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_HEADPTR) 387b252313fSGleb Smirnoff head = pa->pa_head; 388b252313fSGleb Smirnoff else 389b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list) 390b252313fSGleb Smirnoff if (strcmp(pa->pa_headname, head->head_name) == 0) 391b252313fSGleb Smirnoff break; 392b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_HOOKPTR) 393b252313fSGleb Smirnoff hook = pa->pa_hook; 394b252313fSGleb Smirnoff else 395b252313fSGleb Smirnoff LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) 396b252313fSGleb Smirnoff if (strcmp(pa->pa_modname, hook->hook_modname) == 0 && 397b252313fSGleb Smirnoff strcmp(pa->pa_rulname, hook->hook_rulname) == 0) 398b252313fSGleb Smirnoff break; 399b252313fSGleb Smirnoff if (head == NULL || hook == NULL) { 400b252313fSGleb Smirnoff error = ENOENT; 401b252313fSGleb Smirnoff goto fail; 402b252313fSGleb Smirnoff } 403b252313fSGleb Smirnoff 404b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_UNLINK) 405b252313fSGleb Smirnoff return (pfil_unlink(pa, head, hook)); 406b252313fSGleb Smirnoff 407b252313fSGleb Smirnoff if (head->head_type != hook->hook_type || 408b252313fSGleb Smirnoff ((hook->hook_flags & pa->pa_flags) & ~head->head_flags)) { 409b252313fSGleb Smirnoff error = EINVAL; 410b252313fSGleb Smirnoff goto fail; 411b252313fSGleb Smirnoff } 412b252313fSGleb Smirnoff 413b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_IN) 414b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_in, link_chain) 415b252313fSGleb Smirnoff if (link->link_hook == hook) { 416b252313fSGleb Smirnoff error = EEXIST; 417b252313fSGleb Smirnoff goto fail; 418b252313fSGleb Smirnoff } 419b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_OUT) 420b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_out, link_chain) 421b252313fSGleb Smirnoff if (link->link_hook == hook) { 422b252313fSGleb Smirnoff error = EEXIST; 423b252313fSGleb Smirnoff goto fail; 424b252313fSGleb Smirnoff } 425b252313fSGleb Smirnoff 426b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_IN) { 427b252313fSGleb Smirnoff in->link_hook = hook; 428caf32b26SGleb Smirnoff in->link_mbuf_chk = hook->hook_mbuf_chk; 429caf32b26SGleb Smirnoff in->link_mem_chk = hook->hook_mem_chk; 430b252313fSGleb Smirnoff in->link_flags = hook->hook_flags; 431b252313fSGleb Smirnoff in->link_ruleset = hook->hook_ruleset; 432b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_APPEND) 433b252313fSGleb Smirnoff CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain); 434b252313fSGleb Smirnoff else 435b252313fSGleb Smirnoff CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain); 436b252313fSGleb Smirnoff hook->hook_links++; 437b252313fSGleb Smirnoff head->head_nhooksin++; 438b252313fSGleb Smirnoff } 439b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_OUT) { 440b252313fSGleb Smirnoff out->link_hook = hook; 441caf32b26SGleb Smirnoff out->link_mbuf_chk = hook->hook_mbuf_chk; 442caf32b26SGleb Smirnoff out->link_mem_chk = hook->hook_mem_chk; 443b252313fSGleb Smirnoff out->link_flags = hook->hook_flags; 444b252313fSGleb Smirnoff out->link_ruleset = hook->hook_ruleset; 445b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_APPEND) 446b252313fSGleb Smirnoff CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain); 447b252313fSGleb Smirnoff else 448b252313fSGleb Smirnoff CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain); 449b252313fSGleb Smirnoff hook->hook_links++; 450b252313fSGleb Smirnoff head->head_nhooksout++; 451b252313fSGleb Smirnoff } 452b252313fSGleb Smirnoff PFIL_UNLOCK(); 453b252313fSGleb Smirnoff 454b252313fSGleb Smirnoff return (0); 455b252313fSGleb Smirnoff 456b252313fSGleb Smirnoff fail: 457b252313fSGleb Smirnoff PFIL_UNLOCK(); 458b252313fSGleb Smirnoff free(in, M_PFIL); 459b252313fSGleb Smirnoff free(out, M_PFIL); 460b252313fSGleb Smirnoff return (error); 461b252313fSGleb Smirnoff } 462b252313fSGleb Smirnoff 463b252313fSGleb Smirnoff static void 464b252313fSGleb Smirnoff pfil_link_free(epoch_context_t ctx) 465b252313fSGleb Smirnoff { 466b252313fSGleb Smirnoff struct pfil_link *link; 467b252313fSGleb Smirnoff 468b252313fSGleb Smirnoff link = __containerof(ctx, struct pfil_link, link_epoch_ctx); 469b252313fSGleb Smirnoff free(link, M_PFIL); 470b252313fSGleb Smirnoff } 471b252313fSGleb Smirnoff 472b252313fSGleb Smirnoff /* 473b252313fSGleb Smirnoff * pfil_remove_hook removes a filter from all filtering points. 474b252313fSGleb Smirnoff */ 475b252313fSGleb Smirnoff void 476b252313fSGleb Smirnoff pfil_remove_hook(pfil_hook_t hook) 477b252313fSGleb Smirnoff { 478b252313fSGleb Smirnoff struct pfil_head *head; 479b252313fSGleb Smirnoff struct pfil_link *in, *out; 480b252313fSGleb Smirnoff 481b252313fSGleb Smirnoff PFIL_LOCK(); 482b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list) { 483b252313fSGleb Smirnoff retry: 484b252313fSGleb Smirnoff in = pfil_link_remove(&head->head_in, hook); 485b252313fSGleb Smirnoff if (in != NULL) { 486b252313fSGleb Smirnoff head->head_nhooksin--; 487b252313fSGleb Smirnoff hook->hook_links--; 488e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx); 489b252313fSGleb Smirnoff } 490b252313fSGleb Smirnoff out = pfil_link_remove(&head->head_out, hook); 491b252313fSGleb Smirnoff if (out != NULL) { 492b252313fSGleb Smirnoff head->head_nhooksout--; 493b252313fSGleb Smirnoff hook->hook_links--; 494e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx); 495b252313fSGleb Smirnoff } 496b252313fSGleb Smirnoff if (in != NULL || out != NULL) 497b252313fSGleb Smirnoff /* What if some stupid admin put same filter twice? */ 498b252313fSGleb Smirnoff goto retry; 499b252313fSGleb Smirnoff } 500b252313fSGleb Smirnoff LIST_REMOVE(hook, hook_list); 501b252313fSGleb Smirnoff PFIL_UNLOCK(); 502b252313fSGleb Smirnoff MPASS(hook->hook_links == 0); 503b252313fSGleb Smirnoff free(hook, M_PFIL); 504b252313fSGleb Smirnoff } 505b252313fSGleb Smirnoff 506e9c7bebfSDarren Reed /* 5078da01399SAndre Oppermann * Internal: Remove a pfil hook from a hook chain. 508e9c7bebfSDarren Reed */ 509b252313fSGleb Smirnoff static struct pfil_link * 510b252313fSGleb Smirnoff pfil_link_remove(pfil_chain_t *chain, pfil_hook_t hook) 511e9c7bebfSDarren Reed { 512b252313fSGleb Smirnoff struct pfil_link *link; 513e9c7bebfSDarren Reed 514b252313fSGleb Smirnoff PFIL_LOCK_ASSERT(); 515b252313fSGleb Smirnoff 516b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, chain, link_chain) 517b252313fSGleb Smirnoff if (link->link_hook == hook) { 518b252313fSGleb Smirnoff CK_STAILQ_REMOVE(chain, link, pfil_link, link_chain); 519b252313fSGleb Smirnoff return (link); 520e9c7bebfSDarren Reed } 5210b4b0b0fSJulian Elischer 522b252313fSGleb Smirnoff return (NULL); 523b252313fSGleb Smirnoff } 524b252313fSGleb Smirnoff 525b9dbac48SBjoern A. Zeeb static void 526b252313fSGleb Smirnoff pfil_init(const void *unused __unused) 5270b4b0b0fSJulian Elischer { 528b252313fSGleb Smirnoff struct make_dev_args args; 529c681cce9SMateusz Guzik int error __diagused; 530cee81198SRobert Watson 531b252313fSGleb Smirnoff make_dev_args_init(&args); 532b252313fSGleb Smirnoff args.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; 533b252313fSGleb Smirnoff args.mda_devsw = &pfil_cdevsw; 534b252313fSGleb Smirnoff args.mda_uid = UID_ROOT; 535b252313fSGleb Smirnoff args.mda_gid = GID_WHEEL; 536b252313fSGleb Smirnoff args.mda_mode = 0600; 537b252313fSGleb Smirnoff error = make_dev_s(&args, &pfil_dev, PFILDEV); 538b252313fSGleb Smirnoff KASSERT(error == 0, ("%s: failed to create dev: %d", __func__, error)); 5390b4b0b0fSJulian Elischer } 540cee81198SRobert Watson /* 54189856f7eSBjoern A. Zeeb * Make sure the pfil bits are first before any possible subsystem which 54289856f7eSBjoern A. Zeeb * might piggyback on the SI_SUB_PROTO_PFIL. 5430b4b0b0fSJulian Elischer */ 544b252313fSGleb Smirnoff SYSINIT(pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, pfil_init, NULL); 5450b4b0b0fSJulian Elischer 5460b4b0b0fSJulian Elischer /* 547b252313fSGleb Smirnoff * User control interface. 5480b4b0b0fSJulian Elischer */ 549b252313fSGleb Smirnoff static int pfilioc_listheads(struct pfilioc_list *); 550b252313fSGleb Smirnoff static int pfilioc_listhooks(struct pfilioc_list *); 551b252313fSGleb Smirnoff static int pfilioc_link(struct pfilioc_link *); 552b252313fSGleb Smirnoff 553b252313fSGleb Smirnoff static int 554b252313fSGleb Smirnoff pfil_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, 555b252313fSGleb Smirnoff struct thread *td) 556b252313fSGleb Smirnoff { 557b252313fSGleb Smirnoff int error; 558b252313fSGleb Smirnoff 5593ca1c423SGleb Smirnoff CURVNET_SET(TD_TO_VNET(td)); 560b252313fSGleb Smirnoff error = 0; 561b252313fSGleb Smirnoff switch (cmd) { 562b252313fSGleb Smirnoff case PFILIOC_LISTHEADS: 563b252313fSGleb Smirnoff error = pfilioc_listheads((struct pfilioc_list *)addr); 564b252313fSGleb Smirnoff break; 565b252313fSGleb Smirnoff case PFILIOC_LISTHOOKS: 566b252313fSGleb Smirnoff error = pfilioc_listhooks((struct pfilioc_list *)addr); 567b252313fSGleb Smirnoff break; 568b252313fSGleb Smirnoff case PFILIOC_LINK: 569b252313fSGleb Smirnoff error = pfilioc_link((struct pfilioc_link *)addr); 570b252313fSGleb Smirnoff break; 571b252313fSGleb Smirnoff default: 5723ca1c423SGleb Smirnoff error = EINVAL; 5733ca1c423SGleb Smirnoff break; 574b252313fSGleb Smirnoff } 5753ca1c423SGleb Smirnoff CURVNET_RESTORE(); 576b252313fSGleb Smirnoff return (error); 577b252313fSGleb Smirnoff } 578b252313fSGleb Smirnoff 579b252313fSGleb Smirnoff static int 580b252313fSGleb Smirnoff pfilioc_listheads(struct pfilioc_list *req) 581b252313fSGleb Smirnoff { 582b252313fSGleb Smirnoff struct pfil_head *head; 583b252313fSGleb Smirnoff struct pfil_link *link; 584b252313fSGleb Smirnoff struct pfilioc_head *iohead; 585b252313fSGleb Smirnoff struct pfilioc_hook *iohook; 586b252313fSGleb Smirnoff u_int nheads, nhooks, hd, hk; 587b252313fSGleb Smirnoff int error; 588b252313fSGleb Smirnoff 589b252313fSGleb Smirnoff PFIL_LOCK(); 590b252313fSGleb Smirnoff restart: 591b252313fSGleb Smirnoff nheads = nhooks = 0; 592b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list) { 593b252313fSGleb Smirnoff nheads++; 594b252313fSGleb Smirnoff nhooks += head->head_nhooksin + head->head_nhooksout; 595b252313fSGleb Smirnoff } 596b252313fSGleb Smirnoff PFIL_UNLOCK(); 597b252313fSGleb Smirnoff 598b252313fSGleb Smirnoff if (req->pio_nheads < nheads || req->pio_nhooks < nhooks) { 599b252313fSGleb Smirnoff req->pio_nheads = nheads; 600b252313fSGleb Smirnoff req->pio_nhooks = nhooks; 601b252313fSGleb Smirnoff return (0); 602b252313fSGleb Smirnoff } 603b252313fSGleb Smirnoff 604b252313fSGleb Smirnoff iohead = malloc(sizeof(*iohead) * nheads, M_TEMP, M_WAITOK); 605b252313fSGleb Smirnoff iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK); 606b252313fSGleb Smirnoff 607b252313fSGleb Smirnoff hd = hk = 0; 608b252313fSGleb Smirnoff PFIL_LOCK(); 609b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list) { 610b252313fSGleb Smirnoff if (hd + 1 > nheads || 611b252313fSGleb Smirnoff hk + head->head_nhooksin + head->head_nhooksout > nhooks) { 612b252313fSGleb Smirnoff /* Configuration changed during malloc(). */ 613b252313fSGleb Smirnoff free(iohead, M_TEMP); 614b252313fSGleb Smirnoff free(iohook, M_TEMP); 615b252313fSGleb Smirnoff goto restart; 616b252313fSGleb Smirnoff } 617b252313fSGleb Smirnoff strlcpy(iohead[hd].pio_name, head->head_name, 618b252313fSGleb Smirnoff sizeof(iohead[0].pio_name)); 619b252313fSGleb Smirnoff iohead[hd].pio_nhooksin = head->head_nhooksin; 620b252313fSGleb Smirnoff iohead[hd].pio_nhooksout = head->head_nhooksout; 621b252313fSGleb Smirnoff iohead[hd].pio_type = head->head_type; 622b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_in, link_chain) { 623b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_module, 624b252313fSGleb Smirnoff link->link_hook->hook_modname, 625b252313fSGleb Smirnoff sizeof(iohook[0].pio_module)); 626b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_ruleset, 627b252313fSGleb Smirnoff link->link_hook->hook_rulname, 628b252313fSGleb Smirnoff sizeof(iohook[0].pio_ruleset)); 629b252313fSGleb Smirnoff hk++; 630b252313fSGleb Smirnoff } 631b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_out, link_chain) { 632b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_module, 633b252313fSGleb Smirnoff link->link_hook->hook_modname, 634b252313fSGleb Smirnoff sizeof(iohook[0].pio_module)); 635b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_ruleset, 636b252313fSGleb Smirnoff link->link_hook->hook_rulname, 637b252313fSGleb Smirnoff sizeof(iohook[0].pio_ruleset)); 638b252313fSGleb Smirnoff hk++; 639b252313fSGleb Smirnoff } 640b252313fSGleb Smirnoff hd++; 641b252313fSGleb Smirnoff } 642b252313fSGleb Smirnoff PFIL_UNLOCK(); 643b252313fSGleb Smirnoff 644b252313fSGleb Smirnoff error = copyout(iohead, req->pio_heads, 645b252313fSGleb Smirnoff sizeof(*iohead) * min(hd, req->pio_nheads)); 646b252313fSGleb Smirnoff if (error == 0) 647b252313fSGleb Smirnoff error = copyout(iohook, req->pio_hooks, 648b252313fSGleb Smirnoff sizeof(*iohook) * min(req->pio_nhooks, hk)); 649b252313fSGleb Smirnoff 650b252313fSGleb Smirnoff req->pio_nheads = hd; 651b252313fSGleb Smirnoff req->pio_nhooks = hk; 652b252313fSGleb Smirnoff 653b252313fSGleb Smirnoff free(iohead, M_TEMP); 654b252313fSGleb Smirnoff free(iohook, M_TEMP); 655b252313fSGleb Smirnoff 656b252313fSGleb Smirnoff return (error); 657b252313fSGleb Smirnoff } 658b252313fSGleb Smirnoff 659b252313fSGleb Smirnoff static int 660b252313fSGleb Smirnoff pfilioc_listhooks(struct pfilioc_list *req) 661b252313fSGleb Smirnoff { 662b252313fSGleb Smirnoff struct pfil_hook *hook; 663b252313fSGleb Smirnoff struct pfilioc_hook *iohook; 664b252313fSGleb Smirnoff u_int nhooks, hk; 665b252313fSGleb Smirnoff int error; 666b252313fSGleb Smirnoff 667b252313fSGleb Smirnoff PFIL_LOCK(); 668b252313fSGleb Smirnoff restart: 669b252313fSGleb Smirnoff nhooks = 0; 670b252313fSGleb Smirnoff LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) 671b252313fSGleb Smirnoff nhooks++; 672b252313fSGleb Smirnoff PFIL_UNLOCK(); 673b252313fSGleb Smirnoff 674b252313fSGleb Smirnoff if (req->pio_nhooks < nhooks) { 675b252313fSGleb Smirnoff req->pio_nhooks = nhooks; 676b252313fSGleb Smirnoff return (0); 677b252313fSGleb Smirnoff } 678b252313fSGleb Smirnoff 679b252313fSGleb Smirnoff iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK); 680b252313fSGleb Smirnoff 681b252313fSGleb Smirnoff hk = 0; 682b252313fSGleb Smirnoff PFIL_LOCK(); 683b252313fSGleb Smirnoff LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) { 684b252313fSGleb Smirnoff if (hk + 1 > nhooks) { 685b252313fSGleb Smirnoff /* Configuration changed during malloc(). */ 686b252313fSGleb Smirnoff free(iohook, M_TEMP); 687b252313fSGleb Smirnoff goto restart; 688b252313fSGleb Smirnoff } 689b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_module, hook->hook_modname, 690b252313fSGleb Smirnoff sizeof(iohook[0].pio_module)); 691b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_ruleset, hook->hook_rulname, 692b252313fSGleb Smirnoff sizeof(iohook[0].pio_ruleset)); 693b252313fSGleb Smirnoff iohook[hk].pio_type = hook->hook_type; 694b252313fSGleb Smirnoff iohook[hk].pio_flags = hook->hook_flags; 695b252313fSGleb Smirnoff hk++; 696b252313fSGleb Smirnoff } 697b252313fSGleb Smirnoff PFIL_UNLOCK(); 698b252313fSGleb Smirnoff 699b252313fSGleb Smirnoff error = copyout(iohook, req->pio_hooks, 700b252313fSGleb Smirnoff sizeof(*iohook) * min(req->pio_nhooks, hk)); 701b252313fSGleb Smirnoff req->pio_nhooks = hk; 702b252313fSGleb Smirnoff free(iohook, M_TEMP); 703b252313fSGleb Smirnoff 704b252313fSGleb Smirnoff return (error); 705b252313fSGleb Smirnoff } 706b252313fSGleb Smirnoff 707b252313fSGleb Smirnoff static int 708b252313fSGleb Smirnoff pfilioc_link(struct pfilioc_link *req) 709b252313fSGleb Smirnoff { 710b252313fSGleb Smirnoff struct pfil_link_args args; 711b252313fSGleb Smirnoff 712b252313fSGleb Smirnoff if (req->pio_flags & ~(PFIL_IN | PFIL_OUT | PFIL_UNLINK | PFIL_APPEND)) 713b252313fSGleb Smirnoff return (EINVAL); 714b252313fSGleb Smirnoff 715b252313fSGleb Smirnoff args.pa_version = PFIL_VERSION; 716b252313fSGleb Smirnoff args.pa_flags = req->pio_flags; 717b252313fSGleb Smirnoff args.pa_headname = req->pio_name; 718b252313fSGleb Smirnoff args.pa_modname = req->pio_module; 719b252313fSGleb Smirnoff args.pa_rulname = req->pio_ruleset; 720b252313fSGleb Smirnoff 721b252313fSGleb Smirnoff return (pfil_link(&args)); 722b252313fSGleb Smirnoff } 723