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 * 7e9c7bebfSDarren Reed * Copyright (c) 1996 Matthew R. Green 8e9c7bebfSDarren Reed * All rights reserved. 9e9c7bebfSDarren Reed * 10e9c7bebfSDarren Reed * Redistribution and use in source and binary forms, with or without 11e9c7bebfSDarren Reed * modification, are permitted provided that the following conditions 12e9c7bebfSDarren Reed * are met: 13e9c7bebfSDarren Reed * 1. Redistributions of source code must retain the above copyright 14e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer. 15e9c7bebfSDarren Reed * 2. Redistributions in binary form must reproduce the above copyright 16e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer in the 17e9c7bebfSDarren Reed * documentation and/or other materials provided with the distribution. 18e9c7bebfSDarren Reed * 3. The name of the author may not be used to endorse or promote products 19e9c7bebfSDarren Reed * derived from this software without specific prior written permission. 20e9c7bebfSDarren Reed * 21e9c7bebfSDarren Reed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22e9c7bebfSDarren Reed * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23e9c7bebfSDarren Reed * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24e9c7bebfSDarren Reed * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25e9c7bebfSDarren Reed * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26e9c7bebfSDarren Reed * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27e9c7bebfSDarren Reed * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28e9c7bebfSDarren Reed * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29e9c7bebfSDarren Reed * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30e9c7bebfSDarren Reed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31e9c7bebfSDarren Reed * SUCH DAMAGE. 32e9c7bebfSDarren Reed */ 33e9c7bebfSDarren Reed 34e9c7bebfSDarren Reed #include <sys/param.h> 35134ea224SSam Leffler #include <sys/kernel.h> 36e9c7bebfSDarren Reed #include <sys/errno.h> 37604afec4SChristian S.J. Peron #include <sys/lock.h> 38e9c7bebfSDarren Reed #include <sys/malloc.h> 391030a1a9SMax Laier #include <sys/rmlock.h> 40e9c7bebfSDarren Reed #include <sys/socket.h> 41e9c7bebfSDarren Reed #include <sys/socketvar.h> 42e9c7bebfSDarren Reed #include <sys/systm.h> 43134ea224SSam Leffler #include <sys/condvar.h> 44134ea224SSam Leffler #include <sys/lock.h> 45134ea224SSam Leffler #include <sys/mutex.h> 46134ea224SSam Leffler #include <sys/proc.h> 47e9c7bebfSDarren Reed #include <sys/queue.h> 48e9c7bebfSDarren Reed 49e9c7bebfSDarren Reed #include <net/if.h> 5076039bc8SGleb Smirnoff #include <net/if_var.h> 51e9c7bebfSDarren Reed #include <net/pfil.h> 52e9c7bebfSDarren Reed 53134ea224SSam Leffler static struct mtx pfil_global_lock; 54e9c7bebfSDarren Reed 5533c89765SRobert Watson MTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock", 5633c89765SRobert Watson MTX_DEF); 57134ea224SSam Leffler 588da01399SAndre Oppermann static struct packet_filter_hook *pfil_chain_get(int, struct pfil_head *); 598da01399SAndre Oppermann static int pfil_chain_add(pfil_chain_t *, struct packet_filter_hook *, int); 60*effaab88SKristof Provost static int pfil_chain_remove(pfil_chain_t *, void *, void *); 61*effaab88SKristof Provost static int pfil_add_hook_priv(void *, void *, int, struct pfil_head *, bool); 62134ea224SSam Leffler 630b4b0b0fSJulian Elischer LIST_HEAD(pfilheadhead, pfil_head); 640b4b0b0fSJulian Elischer VNET_DEFINE(struct pfilheadhead, pfil_head_list); 650b4b0b0fSJulian Elischer #define V_pfil_head_list VNET(pfil_head_list) 664dab1a18SAlexander V. Chernikov VNET_DEFINE(struct rmlock, pfil_lock); 67134ea224SSam Leffler 68af48c203SAndrey V. Elsukov #define PFIL_LOCK_INIT_REAL(l, t) \ 69af48c203SAndrey V. Elsukov rm_init_flags(l, "PFil " t " rmlock", RM_RECURSE) 70af48c203SAndrey V. Elsukov #define PFIL_LOCK_DESTROY_REAL(l) \ 71af48c203SAndrey V. Elsukov rm_destroy(l) 72af48c203SAndrey V. Elsukov #define PFIL_LOCK_INIT(p) do { \ 73af48c203SAndrey V. Elsukov if ((p)->flags & PFIL_FLAG_PRIVATE_LOCK) { \ 74af48c203SAndrey V. Elsukov PFIL_LOCK_INIT_REAL(&(p)->ph_lock, "private"); \ 75af48c203SAndrey V. Elsukov (p)->ph_plock = &(p)->ph_lock; \ 76af48c203SAndrey V. Elsukov } else \ 77af48c203SAndrey V. Elsukov (p)->ph_plock = &V_pfil_lock; \ 78af48c203SAndrey V. Elsukov } while (0) 79af48c203SAndrey V. Elsukov #define PFIL_LOCK_DESTROY(p) do { \ 80af48c203SAndrey V. Elsukov if ((p)->flags & PFIL_FLAG_PRIVATE_LOCK) \ 81af48c203SAndrey V. Elsukov PFIL_LOCK_DESTROY_REAL((p)->ph_plock); \ 82af48c203SAndrey V. Elsukov } while (0) 83af48c203SAndrey V. Elsukov 84af48c203SAndrey V. Elsukov #define PFIL_TRY_RLOCK(p, t) rm_try_rlock((p)->ph_plock, (t)) 85af48c203SAndrey V. Elsukov #define PFIL_RLOCK(p, t) rm_rlock((p)->ph_plock, (t)) 86af48c203SAndrey V. Elsukov #define PFIL_WLOCK(p) rm_wlock((p)->ph_plock) 87af48c203SAndrey V. Elsukov #define PFIL_RUNLOCK(p, t) rm_runlock((p)->ph_plock, (t)) 88af48c203SAndrey V. Elsukov #define PFIL_WUNLOCK(p) rm_wunlock((p)->ph_plock) 89af48c203SAndrey V. Elsukov #define PFIL_WOWNED(p) rm_wowned((p)->ph_plock) 90af48c203SAndrey V. Elsukov 91af48c203SAndrey V. Elsukov #define PFIL_HEADLIST_LOCK() mtx_lock(&pfil_global_lock) 92af48c203SAndrey V. Elsukov #define PFIL_HEADLIST_UNLOCK() mtx_unlock(&pfil_global_lock) 93af48c203SAndrey V. Elsukov 94134ea224SSam Leffler /* 958da01399SAndre Oppermann * pfil_run_hooks() runs the specified packet filter hook chain. 96134ea224SSam Leffler */ 97134ea224SSam Leffler int 98134ea224SSam Leffler pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 99*effaab88SKristof Provost int dir, int flags, struct inpcb *inp) 100134ea224SSam Leffler { 1011030a1a9SMax Laier struct rm_priotracker rmpt; 102134ea224SSam Leffler struct packet_filter_hook *pfh; 103134ea224SSam Leffler struct mbuf *m = *mp; 104134ea224SSam Leffler int rv = 0; 105134ea224SSam Leffler 1061030a1a9SMax Laier PFIL_RLOCK(ph, &rmpt); 107604afec4SChristian S.J. Peron KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0")); 1088da01399SAndre Oppermann for (pfh = pfil_chain_get(dir, ph); pfh != NULL; 1098da01399SAndre Oppermann pfh = TAILQ_NEXT(pfh, pfil_chain)) { 110*effaab88SKristof Provost if (pfh->pfil_func_flags != NULL) { 111*effaab88SKristof Provost rv = (*pfh->pfil_func_flags)(pfh->pfil_arg, &m, ifp, 112*effaab88SKristof Provost dir, flags, inp); 113*effaab88SKristof Provost if (rv != 0 || m == NULL) 114*effaab88SKristof Provost break; 115*effaab88SKristof Provost } 116134ea224SSam Leffler if (pfh->pfil_func != NULL) { 11733c89765SRobert Watson rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir, 11833c89765SRobert Watson inp); 119134ea224SSam Leffler if (rv != 0 || m == NULL) 120134ea224SSam Leffler break; 121134ea224SSam Leffler } 122134ea224SSam Leffler } 1231030a1a9SMax Laier PFIL_RUNLOCK(ph, &rmpt); 124134ea224SSam Leffler *mp = m; 125134ea224SSam Leffler return (rv); 126134ea224SSam Leffler } 127134ea224SSam Leffler 128887c60fcSAndre Oppermann static struct packet_filter_hook * 1298da01399SAndre Oppermann pfil_chain_get(int dir, struct pfil_head *ph) 130887c60fcSAndre Oppermann { 131887c60fcSAndre Oppermann 132887c60fcSAndre Oppermann if (dir == PFIL_IN) 133887c60fcSAndre Oppermann return (TAILQ_FIRST(&ph->ph_in)); 134887c60fcSAndre Oppermann else if (dir == PFIL_OUT) 135887c60fcSAndre Oppermann return (TAILQ_FIRST(&ph->ph_out)); 136887c60fcSAndre Oppermann else 137887c60fcSAndre Oppermann return (NULL); 138887c60fcSAndre Oppermann } 139887c60fcSAndre Oppermann 140134ea224SSam Leffler /* 1414dab1a18SAlexander V. Chernikov * pfil_try_rlock() acquires rm reader lock for specified head 1425474386bSAndrey V. Elsukov * if this is immediately possible. 1434dab1a18SAlexander V. Chernikov */ 1444dab1a18SAlexander V. Chernikov int 1454dab1a18SAlexander V. Chernikov pfil_try_rlock(struct pfil_head *ph, struct rm_priotracker *tracker) 1464dab1a18SAlexander V. Chernikov { 1475474386bSAndrey V. Elsukov 1485474386bSAndrey V. Elsukov return (PFIL_TRY_RLOCK(ph, tracker)); 1494dab1a18SAlexander V. Chernikov } 1504dab1a18SAlexander V. Chernikov 1514dab1a18SAlexander V. Chernikov /* 1524dab1a18SAlexander V. Chernikov * pfil_rlock() acquires rm reader lock for specified head. 1534dab1a18SAlexander V. Chernikov */ 1544dab1a18SAlexander V. Chernikov void 1554dab1a18SAlexander V. Chernikov pfil_rlock(struct pfil_head *ph, struct rm_priotracker *tracker) 1564dab1a18SAlexander V. Chernikov { 1575474386bSAndrey V. Elsukov 1584dab1a18SAlexander V. Chernikov PFIL_RLOCK(ph, tracker); 1594dab1a18SAlexander V. Chernikov } 1604dab1a18SAlexander V. Chernikov 1614dab1a18SAlexander V. Chernikov /* 1624dab1a18SAlexander V. Chernikov * pfil_runlock() releases reader lock for specified head. 1634dab1a18SAlexander V. Chernikov */ 1644dab1a18SAlexander V. Chernikov void 1654dab1a18SAlexander V. Chernikov pfil_runlock(struct pfil_head *ph, struct rm_priotracker *tracker) 1664dab1a18SAlexander V. Chernikov { 1675474386bSAndrey V. Elsukov 1684dab1a18SAlexander V. Chernikov PFIL_RUNLOCK(ph, tracker); 1694dab1a18SAlexander V. Chernikov } 1704dab1a18SAlexander V. Chernikov 1714dab1a18SAlexander V. Chernikov /* 1724dab1a18SAlexander V. Chernikov * pfil_wlock() acquires writer lock for specified head. 1734dab1a18SAlexander V. Chernikov */ 1744dab1a18SAlexander V. Chernikov void 1754dab1a18SAlexander V. Chernikov pfil_wlock(struct pfil_head *ph) 1764dab1a18SAlexander V. Chernikov { 1775474386bSAndrey V. Elsukov 1784dab1a18SAlexander V. Chernikov PFIL_WLOCK(ph); 1794dab1a18SAlexander V. Chernikov } 1804dab1a18SAlexander V. Chernikov 1814dab1a18SAlexander V. Chernikov /* 1824dab1a18SAlexander V. Chernikov * pfil_wunlock() releases writer lock for specified head. 1834dab1a18SAlexander V. Chernikov */ 1844dab1a18SAlexander V. Chernikov void 1854dab1a18SAlexander V. Chernikov pfil_wunlock(struct pfil_head *ph) 1864dab1a18SAlexander V. Chernikov { 1875474386bSAndrey V. Elsukov 1884dab1a18SAlexander V. Chernikov PFIL_WUNLOCK(ph); 1894dab1a18SAlexander V. Chernikov } 1904dab1a18SAlexander V. Chernikov 1914dab1a18SAlexander V. Chernikov /* 1925474386bSAndrey V. Elsukov * pfil_wowned() returns a non-zero value if the current thread owns 1935474386bSAndrey V. Elsukov * an exclusive lock. 1944dab1a18SAlexander V. Chernikov */ 1954dab1a18SAlexander V. Chernikov int 1964dab1a18SAlexander V. Chernikov pfil_wowned(struct pfil_head *ph) 1974dab1a18SAlexander V. Chernikov { 1985474386bSAndrey V. Elsukov 1995474386bSAndrey V. Elsukov return (PFIL_WOWNED(ph)); 2004dab1a18SAlexander V. Chernikov } 2018da01399SAndre Oppermann 2024dab1a18SAlexander V. Chernikov /* 203cee81198SRobert Watson * pfil_head_register() registers a pfil_head with the packet filter hook 204cee81198SRobert Watson * mechanism. 205134ea224SSam Leffler */ 206134ea224SSam Leffler int 207134ea224SSam Leffler pfil_head_register(struct pfil_head *ph) 208134ea224SSam Leffler { 209134ea224SSam Leffler struct pfil_head *lph; 210134ea224SSam Leffler 211737003b3SAndre Oppermann PFIL_HEADLIST_LOCK(); 2120b4b0b0fSJulian Elischer LIST_FOREACH(lph, &V_pfil_head_list, ph_list) { 213134ea224SSam Leffler if (ph->ph_type == lph->ph_type && 214134ea224SSam Leffler ph->ph_un.phu_val == lph->ph_un.phu_val) { 215737003b3SAndre Oppermann PFIL_HEADLIST_UNLOCK(); 216cee81198SRobert Watson return (EEXIST); 217134ea224SSam Leffler } 218d2c205d5SRobert Watson } 2191030a1a9SMax Laier PFIL_LOCK_INIT(ph); 220604afec4SChristian S.J. Peron ph->ph_nhooks = 0; 221e9c7bebfSDarren Reed TAILQ_INIT(&ph->ph_in); 222e9c7bebfSDarren Reed TAILQ_INIT(&ph->ph_out); 2230b4b0b0fSJulian Elischer LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list); 224737003b3SAndre Oppermann PFIL_HEADLIST_UNLOCK(); 225134ea224SSam Leffler return (0); 226134ea224SSam Leffler } 227134ea224SSam Leffler 228134ea224SSam Leffler /* 229d2c205d5SRobert Watson * pfil_head_unregister() removes a pfil_head from the packet filter hook 230d2c205d5SRobert Watson * mechanism. The producer of the hook promises that all outstanding 231d2c205d5SRobert Watson * invocations of the hook have completed before it unregisters the hook. 232134ea224SSam Leffler */ 233134ea224SSam Leffler int 234134ea224SSam Leffler pfil_head_unregister(struct pfil_head *ph) 235134ea224SSam Leffler { 236134ea224SSam Leffler struct packet_filter_hook *pfh, *pfnext; 237134ea224SSam Leffler 238737003b3SAndre Oppermann PFIL_HEADLIST_LOCK(); 239134ea224SSam Leffler LIST_REMOVE(ph, ph_list); 240737003b3SAndre Oppermann PFIL_HEADLIST_UNLOCK(); 2418da01399SAndre Oppermann TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_chain, pfnext) 242134ea224SSam Leffler free(pfh, M_IFADDR); 2438da01399SAndre Oppermann TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_chain, pfnext) 244134ea224SSam Leffler free(pfh, M_IFADDR); 2451030a1a9SMax Laier PFIL_LOCK_DESTROY(ph); 246134ea224SSam Leffler return (0); 247134ea224SSam Leffler } 248134ea224SSam Leffler 249134ea224SSam Leffler /* 250134ea224SSam Leffler * pfil_head_get() returns the pfil_head for a given key/dlt. 251134ea224SSam Leffler */ 252134ea224SSam Leffler struct pfil_head * 253134ea224SSam Leffler pfil_head_get(int type, u_long val) 254134ea224SSam Leffler { 255134ea224SSam Leffler struct pfil_head *ph; 256134ea224SSam Leffler 257737003b3SAndre Oppermann PFIL_HEADLIST_LOCK(); 2580b4b0b0fSJulian Elischer LIST_FOREACH(ph, &V_pfil_head_list, ph_list) 259134ea224SSam Leffler if (ph->ph_type == type && ph->ph_un.phu_val == val) 260134ea224SSam Leffler break; 261737003b3SAndre Oppermann PFIL_HEADLIST_UNLOCK(); 262134ea224SSam Leffler return (ph); 263e9c7bebfSDarren Reed } 264e9c7bebfSDarren Reed 265e9c7bebfSDarren Reed /* 266*effaab88SKristof Provost * pfil_add_hook_flags() adds a function to the packet filter hook. the 267*effaab88SKristof Provost * flags are: 268*effaab88SKristof Provost * PFIL_IN call me on incoming packets 269*effaab88SKristof Provost * PFIL_OUT call me on outgoing packets 270*effaab88SKristof Provost * PFIL_ALL call me on all of the above 271*effaab88SKristof Provost * PFIL_WAITOK OK to call malloc with M_WAITOK. 272*effaab88SKristof Provost */ 273*effaab88SKristof Provost int 274*effaab88SKristof Provost pfil_add_hook_flags(pfil_func_flags_t func, void *arg, int flags, 275*effaab88SKristof Provost struct pfil_head *ph) 276*effaab88SKristof Provost { 277*effaab88SKristof Provost return (pfil_add_hook_priv(func, arg, flags, ph, true)); 278*effaab88SKristof Provost } 279*effaab88SKristof Provost 280*effaab88SKristof Provost /* 281e9c7bebfSDarren Reed * pfil_add_hook() adds a function to the packet filter hook. the 282e9c7bebfSDarren Reed * flags are: 283e9c7bebfSDarren Reed * PFIL_IN call me on incoming packets 284e9c7bebfSDarren Reed * PFIL_OUT call me on outgoing packets 285e9c7bebfSDarren Reed * PFIL_ALL call me on all of the above 286a163d034SWarner Losh * PFIL_WAITOK OK to call malloc with M_WAITOK. 287e9c7bebfSDarren Reed */ 288e9c7bebfSDarren Reed int 28925da5060SAndre Oppermann pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 290e9c7bebfSDarren Reed { 291*effaab88SKristof Provost return (pfil_add_hook_priv(func, arg, flags, ph, false)); 292*effaab88SKristof Provost } 293*effaab88SKristof Provost 294*effaab88SKristof Provost static int 295*effaab88SKristof Provost pfil_add_hook_priv(void *func, void *arg, int flags, 296*effaab88SKristof Provost struct pfil_head *ph, bool hasflags) 297*effaab88SKristof Provost { 298134ea224SSam Leffler struct packet_filter_hook *pfh1 = NULL; 299134ea224SSam Leffler struct packet_filter_hook *pfh2 = NULL; 300134ea224SSam Leffler int err; 301e9c7bebfSDarren Reed 302134ea224SSam Leffler if (flags & PFIL_IN) { 303134ea224SSam Leffler pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 304134ea224SSam Leffler M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 305134ea224SSam Leffler if (pfh1 == NULL) { 306134ea224SSam Leffler err = ENOMEM; 307134ea224SSam Leffler goto error; 308134ea224SSam Leffler } 309134ea224SSam Leffler } 310134ea224SSam Leffler if (flags & PFIL_OUT) { 311134ea224SSam Leffler pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 312134ea224SSam Leffler M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 313134ea224SSam Leffler if (pfh2 == NULL) { 314134ea224SSam Leffler err = ENOMEM; 315134ea224SSam Leffler goto error; 316134ea224SSam Leffler } 317134ea224SSam Leffler } 318134ea224SSam Leffler PFIL_WLOCK(ph); 319134ea224SSam Leffler if (flags & PFIL_IN) { 320*effaab88SKristof Provost pfh1->pfil_func_flags = hasflags ? func : NULL; 321*effaab88SKristof Provost pfh1->pfil_func = hasflags ? NULL : func; 322134ea224SSam Leffler pfh1->pfil_arg = arg; 3238da01399SAndre Oppermann err = pfil_chain_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT); 324134ea224SSam Leffler if (err) 325d2c205d5SRobert Watson goto locked_error; 326604afec4SChristian S.J. Peron ph->ph_nhooks++; 327134ea224SSam Leffler } 328134ea224SSam Leffler if (flags & PFIL_OUT) { 329*effaab88SKristof Provost pfh2->pfil_func_flags = hasflags ? func : NULL; 330*effaab88SKristof Provost pfh2->pfil_func = hasflags ? NULL : func; 331134ea224SSam Leffler pfh2->pfil_arg = arg; 3328da01399SAndre Oppermann err = pfil_chain_add(&ph->ph_out, pfh2, flags & ~PFIL_IN); 333e9c7bebfSDarren Reed if (err) { 334e9c7bebfSDarren Reed if (flags & PFIL_IN) 3358da01399SAndre Oppermann pfil_chain_remove(&ph->ph_in, func, arg); 336d2c205d5SRobert Watson goto locked_error; 337134ea224SSam Leffler } 338604afec4SChristian S.J. Peron ph->ph_nhooks++; 339134ea224SSam Leffler } 340134ea224SSam Leffler PFIL_WUNLOCK(ph); 341cee81198SRobert Watson return (0); 342d2c205d5SRobert Watson locked_error: 343134ea224SSam Leffler PFIL_WUNLOCK(ph); 344134ea224SSam Leffler error: 345134ea224SSam Leffler if (pfh1 != NULL) 346134ea224SSam Leffler free(pfh1, M_IFADDR); 347134ea224SSam Leffler if (pfh2 != NULL) 348134ea224SSam Leffler free(pfh2, M_IFADDR); 349cee81198SRobert Watson return (err); 350e9c7bebfSDarren Reed } 351e9c7bebfSDarren Reed 352e9c7bebfSDarren Reed /* 353*effaab88SKristof Provost * pfil_remove_hook_flags removes a specific function from the packet filter hook 354*effaab88SKristof Provost * chain. 355*effaab88SKristof Provost */ 356*effaab88SKristof Provost int 357*effaab88SKristof Provost pfil_remove_hook_flags(pfil_func_flags_t func, void *arg, int flags, 358*effaab88SKristof Provost struct pfil_head *ph) 359*effaab88SKristof Provost { 360*effaab88SKristof Provost return (pfil_remove_hook((pfil_func_t)func, arg, flags, ph)); 361*effaab88SKristof Provost } 362*effaab88SKristof Provost 363*effaab88SKristof Provost /* 364cee81198SRobert Watson * pfil_remove_hook removes a specific function from the packet filter hook 3658da01399SAndre Oppermann * chain. 366e9c7bebfSDarren Reed */ 367e9c7bebfSDarren Reed int 36825da5060SAndre Oppermann pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 369e9c7bebfSDarren Reed { 370e9c7bebfSDarren Reed int err = 0; 371e9c7bebfSDarren Reed 372134ea224SSam Leffler PFIL_WLOCK(ph); 373604afec4SChristian S.J. Peron if (flags & PFIL_IN) { 3748da01399SAndre Oppermann err = pfil_chain_remove(&ph->ph_in, func, arg); 375604afec4SChristian S.J. Peron if (err == 0) 376604afec4SChristian S.J. Peron ph->ph_nhooks--; 377604afec4SChristian S.J. Peron } 378604afec4SChristian S.J. Peron if ((err == 0) && (flags & PFIL_OUT)) { 3798da01399SAndre Oppermann err = pfil_chain_remove(&ph->ph_out, func, arg); 380604afec4SChristian S.J. Peron if (err == 0) 381604afec4SChristian S.J. Peron ph->ph_nhooks--; 382604afec4SChristian S.J. Peron } 383134ea224SSam Leffler PFIL_WUNLOCK(ph); 384cee81198SRobert Watson return (err); 385e9c7bebfSDarren Reed } 386e9c7bebfSDarren Reed 3878da01399SAndre Oppermann /* 3888da01399SAndre Oppermann * Internal: Add a new pfil hook into a hook chain. 3898da01399SAndre Oppermann */ 390134ea224SSam Leffler static int 3918da01399SAndre Oppermann pfil_chain_add(pfil_chain_t *chain, struct packet_filter_hook *pfh1, int flags) 392134ea224SSam Leffler { 393134ea224SSam Leffler struct packet_filter_hook *pfh; 394134ea224SSam Leffler 395134ea224SSam Leffler /* 396134ea224SSam Leffler * First make sure the hook is not already there. 397134ea224SSam Leffler */ 3988da01399SAndre Oppermann TAILQ_FOREACH(pfh, chain, pfil_chain) 399*effaab88SKristof Provost if (((pfh->pfil_func != NULL && pfh->pfil_func == pfh1->pfil_func) || 400*effaab88SKristof Provost (pfh->pfil_func_flags != NULL && 401*effaab88SKristof Provost pfh->pfil_func_flags == pfh1->pfil_func_flags)) && 402134ea224SSam Leffler pfh->pfil_arg == pfh1->pfil_arg) 403cee81198SRobert Watson return (EEXIST); 404cee81198SRobert Watson 405134ea224SSam Leffler /* 406cee81198SRobert Watson * Insert the input list in reverse order of the output list so that 407cee81198SRobert Watson * the same path is followed in or out of the kernel. 408134ea224SSam Leffler */ 409134ea224SSam Leffler if (flags & PFIL_IN) 4108da01399SAndre Oppermann TAILQ_INSERT_HEAD(chain, pfh1, pfil_chain); 411134ea224SSam Leffler else 4128da01399SAndre Oppermann TAILQ_INSERT_TAIL(chain, pfh1, pfil_chain); 413cee81198SRobert Watson return (0); 414134ea224SSam Leffler } 415134ea224SSam Leffler 416e9c7bebfSDarren Reed /* 4178da01399SAndre Oppermann * Internal: Remove a pfil hook from a hook chain. 418e9c7bebfSDarren Reed */ 419e9c7bebfSDarren Reed static int 420*effaab88SKristof Provost pfil_chain_remove(pfil_chain_t *chain, void *func, void *arg) 421e9c7bebfSDarren Reed { 422e9c7bebfSDarren Reed struct packet_filter_hook *pfh; 423e9c7bebfSDarren Reed 4248da01399SAndre Oppermann TAILQ_FOREACH(pfh, chain, pfil_chain) 425*effaab88SKristof Provost if ((pfh->pfil_func == func || pfh->pfil_func_flags == func) && 426*effaab88SKristof Provost pfh->pfil_arg == arg) { 4278da01399SAndre Oppermann TAILQ_REMOVE(chain, pfh, pfil_chain); 428e9c7bebfSDarren Reed free(pfh, M_IFADDR); 429cee81198SRobert Watson return (0); 430e9c7bebfSDarren Reed } 431cee81198SRobert Watson return (ENOENT); 432e9c7bebfSDarren Reed } 4330b4b0b0fSJulian Elischer 434cee81198SRobert Watson /* 435cee81198SRobert Watson * Stuff that must be initialized for every instance (including the first of 436cee81198SRobert Watson * course). 4370b4b0b0fSJulian Elischer */ 438b9dbac48SBjoern A. Zeeb static void 439b9dbac48SBjoern A. Zeeb vnet_pfil_init(const void *unused __unused) 4400b4b0b0fSJulian Elischer { 441cee81198SRobert Watson 4420b4b0b0fSJulian Elischer LIST_INIT(&V_pfil_head_list); 4434dab1a18SAlexander V. Chernikov PFIL_LOCK_INIT_REAL(&V_pfil_lock, "shared"); 4440b4b0b0fSJulian Elischer } 4450b4b0b0fSJulian Elischer 446cee81198SRobert Watson /* 4470b4b0b0fSJulian Elischer * Called for the removal of each instance. 4480b4b0b0fSJulian Elischer */ 449b9dbac48SBjoern A. Zeeb static void 450b9dbac48SBjoern A. Zeeb vnet_pfil_uninit(const void *unused __unused) 4510b4b0b0fSJulian Elischer { 452cee81198SRobert Watson 4538da01399SAndre Oppermann KASSERT(LIST_EMPTY(&V_pfil_head_list), 4548da01399SAndre Oppermann ("%s: pfil_head_list %p not empty", __func__, &V_pfil_head_list)); 4554dab1a18SAlexander V. Chernikov PFIL_LOCK_DESTROY_REAL(&V_pfil_lock); 4560b4b0b0fSJulian Elischer } 4570b4b0b0fSJulian Elischer 4580b4b0b0fSJulian Elischer /* 4590b4b0b0fSJulian Elischer * Starting up. 460cee81198SRobert Watson * 4610b4b0b0fSJulian Elischer * VNET_SYSINIT is called for each existing vnet and each new vnet. 46289856f7eSBjoern A. Zeeb * Make sure the pfil bits are first before any possible subsystem which 46389856f7eSBjoern A. Zeeb * might piggyback on the SI_SUB_PROTO_PFIL. 4640b4b0b0fSJulian Elischer */ 46589856f7eSBjoern A. Zeeb VNET_SYSINIT(vnet_pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, 4660b4b0b0fSJulian Elischer vnet_pfil_init, NULL); 4670b4b0b0fSJulian Elischer 4680b4b0b0fSJulian Elischer /* 469cee81198SRobert Watson * Closing up shop. These are done in REVERSE ORDER. Not called on reboot. 470cee81198SRobert Watson * 4710b4b0b0fSJulian Elischer * VNET_SYSUNINIT is called for each exiting vnet as it exits. 4720b4b0b0fSJulian Elischer */ 47389856f7eSBjoern A. Zeeb VNET_SYSUNINIT(vnet_pfil_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, 4740b4b0b0fSJulian Elischer vnet_pfil_uninit, NULL); 475