1e9c7bebfSDarren Reed /* $FreeBSD$ */ 2134ea224SSam Leffler /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */ 3e9c7bebfSDarren Reed 4c398230bSWarner Losh /*- 5e9c7bebfSDarren Reed * Copyright (c) 1996 Matthew R. Green 6e9c7bebfSDarren Reed * All rights reserved. 7e9c7bebfSDarren Reed * 8e9c7bebfSDarren Reed * Redistribution and use in source and binary forms, with or without 9e9c7bebfSDarren Reed * modification, are permitted provided that the following conditions 10e9c7bebfSDarren Reed * are met: 11e9c7bebfSDarren Reed * 1. Redistributions of source code must retain the above copyright 12e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer. 13e9c7bebfSDarren Reed * 2. Redistributions in binary form must reproduce the above copyright 14e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer in the 15e9c7bebfSDarren Reed * documentation and/or other materials provided with the distribution. 16e9c7bebfSDarren Reed * 3. The name of the author may not be used to endorse or promote products 17e9c7bebfSDarren Reed * derived from this software without specific prior written permission. 18e9c7bebfSDarren Reed * 19e9c7bebfSDarren Reed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20e9c7bebfSDarren Reed * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21e9c7bebfSDarren Reed * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22e9c7bebfSDarren Reed * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23e9c7bebfSDarren Reed * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24e9c7bebfSDarren Reed * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25e9c7bebfSDarren Reed * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26e9c7bebfSDarren Reed * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27e9c7bebfSDarren Reed * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28e9c7bebfSDarren Reed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29e9c7bebfSDarren Reed * SUCH DAMAGE. 30e9c7bebfSDarren Reed */ 31e9c7bebfSDarren Reed 32e9c7bebfSDarren Reed #include <sys/param.h> 33134ea224SSam Leffler #include <sys/kernel.h> 34e9c7bebfSDarren Reed #include <sys/errno.h> 35604afec4SChristian S.J. Peron #include <sys/lock.h> 36e9c7bebfSDarren Reed #include <sys/malloc.h> 371030a1a9SMax Laier #include <sys/rmlock.h> 38e9c7bebfSDarren Reed #include <sys/socket.h> 39e9c7bebfSDarren Reed #include <sys/socketvar.h> 40e9c7bebfSDarren Reed #include <sys/systm.h> 41134ea224SSam Leffler #include <sys/condvar.h> 42134ea224SSam Leffler #include <sys/lock.h> 43134ea224SSam Leffler #include <sys/mutex.h> 44134ea224SSam Leffler #include <sys/proc.h> 45e9c7bebfSDarren Reed #include <sys/queue.h> 46e9c7bebfSDarren Reed 47e9c7bebfSDarren Reed #include <net/if.h> 48e9c7bebfSDarren Reed #include <net/pfil.h> 49e9c7bebfSDarren Reed 50134ea224SSam Leffler static struct mtx pfil_global_lock; 51e9c7bebfSDarren Reed 52134ea224SSam Leffler MTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock", MTX_DEF); 53134ea224SSam Leffler 54134ea224SSam Leffler static int pfil_list_add(pfil_list_t *, struct packet_filter_hook *, int); 55134ea224SSam Leffler 56134ea224SSam Leffler static int pfil_list_remove(pfil_list_t *, 57d6a8d588SMax Laier int (*)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *), void *); 58134ea224SSam Leffler 590b4b0b0fSJulian Elischer LIST_HEAD(pfilheadhead, pfil_head); 600b4b0b0fSJulian Elischer VNET_DEFINE(struct pfilheadhead, pfil_head_list); 610b4b0b0fSJulian Elischer #define V_pfil_head_list VNET(pfil_head_list) 62134ea224SSam Leffler 63134ea224SSam Leffler /* 64134ea224SSam Leffler * pfil_run_hooks() runs the specified packet filter hooks. 65134ea224SSam Leffler */ 66134ea224SSam Leffler int 67134ea224SSam Leffler pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 68d6a8d588SMax Laier int dir, struct inpcb *inp) 69134ea224SSam Leffler { 701030a1a9SMax Laier struct rm_priotracker rmpt; 71134ea224SSam Leffler struct packet_filter_hook *pfh; 72134ea224SSam Leffler struct mbuf *m = *mp; 73134ea224SSam Leffler int rv = 0; 74134ea224SSam Leffler 751030a1a9SMax Laier PFIL_RLOCK(ph, &rmpt); 76604afec4SChristian S.J. Peron KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0")); 77134ea224SSam Leffler for (pfh = pfil_hook_get(dir, ph); pfh != NULL; 78134ea224SSam Leffler pfh = TAILQ_NEXT(pfh, pfil_link)) { 79134ea224SSam Leffler if (pfh->pfil_func != NULL) { 80d6a8d588SMax Laier rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir, inp); 81134ea224SSam Leffler if (rv != 0 || m == NULL) 82134ea224SSam Leffler break; 83134ea224SSam Leffler } 84134ea224SSam Leffler } 851030a1a9SMax Laier PFIL_RUNLOCK(ph, &rmpt); 86134ea224SSam Leffler 87134ea224SSam Leffler *mp = m; 88134ea224SSam Leffler return (rv); 89134ea224SSam Leffler } 90134ea224SSam Leffler 91134ea224SSam Leffler /* 92134ea224SSam Leffler * pfil_head_register() registers a pfil_head with the packet filter 93134ea224SSam Leffler * hook mechanism. 94134ea224SSam Leffler */ 95134ea224SSam Leffler int 96134ea224SSam Leffler pfil_head_register(struct pfil_head *ph) 97134ea224SSam Leffler { 98134ea224SSam Leffler struct pfil_head *lph; 99134ea224SSam Leffler 100134ea224SSam Leffler PFIL_LIST_LOCK(); 1010b4b0b0fSJulian Elischer LIST_FOREACH(lph, &V_pfil_head_list, ph_list) { 102134ea224SSam Leffler if (ph->ph_type == lph->ph_type && 103134ea224SSam Leffler ph->ph_un.phu_val == lph->ph_un.phu_val) { 104134ea224SSam Leffler PFIL_LIST_UNLOCK(); 105134ea224SSam Leffler return EEXIST; 106134ea224SSam Leffler } 107d2c205d5SRobert Watson } 1081030a1a9SMax Laier PFIL_LOCK_INIT(ph); 109604afec4SChristian S.J. Peron ph->ph_nhooks = 0; 110e9c7bebfSDarren Reed TAILQ_INIT(&ph->ph_in); 111e9c7bebfSDarren Reed TAILQ_INIT(&ph->ph_out); 1120b4b0b0fSJulian Elischer LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list); 113134ea224SSam Leffler PFIL_LIST_UNLOCK(); 114134ea224SSam Leffler return (0); 115134ea224SSam Leffler } 116134ea224SSam Leffler 117134ea224SSam Leffler /* 118d2c205d5SRobert Watson * pfil_head_unregister() removes a pfil_head from the packet filter hook 119d2c205d5SRobert Watson * mechanism. The producer of the hook promises that all outstanding 120d2c205d5SRobert Watson * invocations of the hook have completed before it unregisters the hook. 121134ea224SSam Leffler */ 122134ea224SSam Leffler int 123134ea224SSam Leffler pfil_head_unregister(struct pfil_head *ph) 124134ea224SSam Leffler { 125134ea224SSam Leffler struct packet_filter_hook *pfh, *pfnext; 126134ea224SSam Leffler 127134ea224SSam Leffler PFIL_LIST_LOCK(); 128134ea224SSam Leffler LIST_REMOVE(ph, ph_list); 129134ea224SSam Leffler PFIL_LIST_UNLOCK(); 130134ea224SSam Leffler TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_link, pfnext) 131134ea224SSam Leffler free(pfh, M_IFADDR); 132134ea224SSam Leffler TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_link, pfnext) 133134ea224SSam Leffler free(pfh, M_IFADDR); 1341030a1a9SMax Laier PFIL_LOCK_DESTROY(ph); 135134ea224SSam Leffler return (0); 136134ea224SSam Leffler } 137134ea224SSam Leffler 138134ea224SSam Leffler /* 139134ea224SSam Leffler * pfil_head_get() returns the pfil_head for a given key/dlt. 140134ea224SSam Leffler */ 141134ea224SSam Leffler struct pfil_head * 142134ea224SSam Leffler pfil_head_get(int type, u_long val) 143134ea224SSam Leffler { 144134ea224SSam Leffler struct pfil_head *ph; 145134ea224SSam Leffler 146134ea224SSam Leffler PFIL_LIST_LOCK(); 1470b4b0b0fSJulian Elischer LIST_FOREACH(ph, &V_pfil_head_list, ph_list) 148134ea224SSam Leffler if (ph->ph_type == type && ph->ph_un.phu_val == val) 149134ea224SSam Leffler break; 150134ea224SSam Leffler PFIL_LIST_UNLOCK(); 151134ea224SSam Leffler 152134ea224SSam Leffler return (ph); 153e9c7bebfSDarren Reed } 154e9c7bebfSDarren Reed 155e9c7bebfSDarren Reed /* 156e9c7bebfSDarren Reed * pfil_add_hook() adds a function to the packet filter hook. the 157e9c7bebfSDarren Reed * flags are: 158e9c7bebfSDarren Reed * PFIL_IN call me on incoming packets 159e9c7bebfSDarren Reed * PFIL_OUT call me on outgoing packets 160e9c7bebfSDarren Reed * PFIL_ALL call me on all of the above 161a163d034SWarner Losh * PFIL_WAITOK OK to call malloc with M_WAITOK. 162e9c7bebfSDarren Reed */ 163e9c7bebfSDarren Reed int 164d2c205d5SRobert Watson pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, 165d2c205d5SRobert Watson struct inpcb *), void *arg, int flags, struct pfil_head *ph) 166e9c7bebfSDarren Reed { 167134ea224SSam Leffler struct packet_filter_hook *pfh1 = NULL; 168134ea224SSam Leffler struct packet_filter_hook *pfh2 = NULL; 169134ea224SSam Leffler int err; 170e9c7bebfSDarren Reed 171134ea224SSam Leffler if (flags & PFIL_IN) { 172134ea224SSam Leffler pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 173134ea224SSam Leffler M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 174134ea224SSam Leffler if (pfh1 == NULL) { 175134ea224SSam Leffler err = ENOMEM; 176134ea224SSam Leffler goto error; 177134ea224SSam Leffler } 178134ea224SSam Leffler } 179134ea224SSam Leffler if (flags & PFIL_OUT) { 180134ea224SSam Leffler pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 181134ea224SSam Leffler M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 182134ea224SSam Leffler if (pfh2 == NULL) { 183134ea224SSam Leffler err = ENOMEM; 184134ea224SSam Leffler goto error; 185134ea224SSam Leffler } 186134ea224SSam Leffler } 187134ea224SSam Leffler PFIL_WLOCK(ph); 188134ea224SSam Leffler if (flags & PFIL_IN) { 189134ea224SSam Leffler pfh1->pfil_func = func; 190134ea224SSam Leffler pfh1->pfil_arg = arg; 191134ea224SSam Leffler err = pfil_list_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT); 192134ea224SSam Leffler if (err) 193d2c205d5SRobert Watson goto locked_error; 194604afec4SChristian S.J. Peron ph->ph_nhooks++; 195134ea224SSam Leffler } 196134ea224SSam Leffler if (flags & PFIL_OUT) { 197134ea224SSam Leffler pfh2->pfil_func = func; 198134ea224SSam Leffler pfh2->pfil_arg = arg; 199134ea224SSam Leffler err = pfil_list_add(&ph->ph_out, pfh2, flags & ~PFIL_IN); 200e9c7bebfSDarren Reed if (err) { 201e9c7bebfSDarren Reed if (flags & PFIL_IN) 202134ea224SSam Leffler pfil_list_remove(&ph->ph_in, func, arg); 203d2c205d5SRobert Watson goto locked_error; 204134ea224SSam Leffler } 205604afec4SChristian S.J. Peron ph->ph_nhooks++; 206134ea224SSam Leffler } 207134ea224SSam Leffler PFIL_WUNLOCK(ph); 208134ea224SSam Leffler return 0; 209d2c205d5SRobert Watson locked_error: 210134ea224SSam Leffler PFIL_WUNLOCK(ph); 211134ea224SSam Leffler error: 212134ea224SSam Leffler if (pfh1 != NULL) 213134ea224SSam Leffler free(pfh1, M_IFADDR); 214134ea224SSam Leffler if (pfh2 != NULL) 215134ea224SSam Leffler free(pfh2, M_IFADDR); 216e9c7bebfSDarren Reed return err; 217e9c7bebfSDarren Reed } 218e9c7bebfSDarren Reed 219e9c7bebfSDarren Reed /* 220e9c7bebfSDarren Reed * pfil_remove_hook removes a specific function from the packet filter 221e9c7bebfSDarren Reed * hook list. 222e9c7bebfSDarren Reed */ 223e9c7bebfSDarren Reed int 224d6a8d588SMax Laier pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *), 225134ea224SSam Leffler void *arg, int flags, struct pfil_head *ph) 226e9c7bebfSDarren Reed { 227e9c7bebfSDarren Reed int err = 0; 228e9c7bebfSDarren Reed 229134ea224SSam Leffler PFIL_WLOCK(ph); 230e9c7bebfSDarren Reed 231604afec4SChristian S.J. Peron if (flags & PFIL_IN) { 232134ea224SSam Leffler err = pfil_list_remove(&ph->ph_in, func, arg); 233604afec4SChristian S.J. Peron if (err == 0) 234604afec4SChristian S.J. Peron ph->ph_nhooks--; 235604afec4SChristian S.J. Peron } 236604afec4SChristian S.J. Peron if ((err == 0) && (flags & PFIL_OUT)) { 237134ea224SSam Leffler err = pfil_list_remove(&ph->ph_out, func, arg); 238604afec4SChristian S.J. Peron if (err == 0) 239604afec4SChristian S.J. Peron ph->ph_nhooks--; 240604afec4SChristian S.J. Peron } 241134ea224SSam Leffler PFIL_WUNLOCK(ph); 242134ea224SSam Leffler 243e9c7bebfSDarren Reed return err; 244e9c7bebfSDarren Reed } 245e9c7bebfSDarren Reed 246134ea224SSam Leffler static int 247134ea224SSam Leffler pfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags) 248134ea224SSam Leffler { 249134ea224SSam Leffler struct packet_filter_hook *pfh; 250134ea224SSam Leffler 251134ea224SSam Leffler /* 252134ea224SSam Leffler * First make sure the hook is not already there. 253134ea224SSam Leffler */ 254134ea224SSam Leffler TAILQ_FOREACH(pfh, list, pfil_link) 255134ea224SSam Leffler if (pfh->pfil_func == pfh1->pfil_func && 256134ea224SSam Leffler pfh->pfil_arg == pfh1->pfil_arg) 257134ea224SSam Leffler return EEXIST; 258134ea224SSam Leffler /* 259134ea224SSam Leffler * insert the input list in reverse order of the output list 260134ea224SSam Leffler * so that the same path is followed in or out of the kernel. 261134ea224SSam Leffler */ 262134ea224SSam Leffler if (flags & PFIL_IN) 263134ea224SSam Leffler TAILQ_INSERT_HEAD(list, pfh1, pfil_link); 264134ea224SSam Leffler else 265134ea224SSam Leffler TAILQ_INSERT_TAIL(list, pfh1, pfil_link); 266134ea224SSam Leffler 267134ea224SSam Leffler return 0; 268134ea224SSam Leffler } 269134ea224SSam Leffler 270e9c7bebfSDarren Reed /* 271e9c7bebfSDarren Reed * pfil_list_remove is an internal function that takes a function off the 272e9c7bebfSDarren Reed * specified list. 273e9c7bebfSDarren Reed */ 274e9c7bebfSDarren Reed static int 275134ea224SSam Leffler pfil_list_remove(pfil_list_t *list, 276d6a8d588SMax Laier int (*func)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *), void *arg) 277e9c7bebfSDarren Reed { 278e9c7bebfSDarren Reed struct packet_filter_hook *pfh; 279e9c7bebfSDarren Reed 280fc2ffbe6SPoul-Henning Kamp TAILQ_FOREACH(pfh, list, pfil_link) 281134ea224SSam Leffler if (pfh->pfil_func == func && pfh->pfil_arg == arg) { 282e9c7bebfSDarren Reed TAILQ_REMOVE(list, pfh, pfil_link); 283e9c7bebfSDarren Reed free(pfh, M_IFADDR); 284e9c7bebfSDarren Reed return 0; 285e9c7bebfSDarren Reed } 286e9c7bebfSDarren Reed return ENOENT; 287e9c7bebfSDarren Reed } 2880b4b0b0fSJulian Elischer 2890b4b0b0fSJulian Elischer /**************** 2900b4b0b0fSJulian Elischer * Stuff that must be initialized for every instance 2910b4b0b0fSJulian Elischer * (including the first of course). 2920b4b0b0fSJulian Elischer */ 2930b4b0b0fSJulian Elischer static int 2940b4b0b0fSJulian Elischer vnet_pfil_init(const void *unused) 2950b4b0b0fSJulian Elischer { 2960b4b0b0fSJulian Elischer LIST_INIT(&V_pfil_head_list); 2970b4b0b0fSJulian Elischer return (0); 2980b4b0b0fSJulian Elischer } 2990b4b0b0fSJulian Elischer 3000b4b0b0fSJulian Elischer /*********************** 3010b4b0b0fSJulian Elischer * Called for the removal of each instance. 3020b4b0b0fSJulian Elischer */ 3030b4b0b0fSJulian Elischer static int 3040b4b0b0fSJulian Elischer vnet_pfil_uninit(const void *unused) 3050b4b0b0fSJulian Elischer { 3060b4b0b0fSJulian Elischer /* XXX should panic if list is not empty */ 3070b4b0b0fSJulian Elischer return 0; 3080b4b0b0fSJulian Elischer } 3090b4b0b0fSJulian Elischer 3100b4b0b0fSJulian Elischer /* Define startup order. */ 3110b4b0b0fSJulian Elischer #define PFIL_SYSINIT_ORDER SI_SUB_PROTO_BEGIN 3120b4b0b0fSJulian Elischer #define PFIL_MODEVENT_ORDER (SI_ORDER_FIRST) /* On boot slot in here. */ 3130b4b0b0fSJulian Elischer #define PFIL_VNET_ORDER (PFIL_MODEVENT_ORDER + 2) /* Later still. */ 3140b4b0b0fSJulian Elischer 3150b4b0b0fSJulian Elischer /* 3160b4b0b0fSJulian Elischer * Starting up. 3170b4b0b0fSJulian Elischer * VNET_SYSINIT is called for each existing vnet and each new vnet. 3180b4b0b0fSJulian Elischer */ 3190b4b0b0fSJulian Elischer VNET_SYSINIT(vnet_pfil_init, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER, 3200b4b0b0fSJulian Elischer vnet_pfil_init, NULL); 3210b4b0b0fSJulian Elischer 3220b4b0b0fSJulian Elischer /* 3230b4b0b0fSJulian Elischer * Closing up shop. These are done in REVERSE ORDER, 3240b4b0b0fSJulian Elischer * Not called on reboot. 3250b4b0b0fSJulian Elischer * VNET_SYSUNINIT is called for each exiting vnet as it exits. 3260b4b0b0fSJulian Elischer */ 3270b4b0b0fSJulian Elischer VNET_SYSUNINIT(vnet_pfil_uninit, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER, 3280b4b0b0fSJulian Elischer vnet_pfil_uninit, NULL); 3290b4b0b0fSJulian Elischer 330