pfil.c revision 198233
1120386Ssam/* $FreeBSD: head/sys/net/pfil.c 198233 2009-10-19 15:19:14Z rwatson $ */ 2120386Ssam/* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */ 360317Sdarrenr 4139823Simp/*- 560317Sdarrenr * Copyright (c) 1996 Matthew R. Green 660317Sdarrenr * All rights reserved. 760317Sdarrenr * 860317Sdarrenr * Redistribution and use in source and binary forms, with or without 960317Sdarrenr * modification, are permitted provided that the following conditions 1060317Sdarrenr * are met: 1160317Sdarrenr * 1. Redistributions of source code must retain the above copyright 1260317Sdarrenr * notice, this list of conditions and the following disclaimer. 1360317Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright 1460317Sdarrenr * notice, this list of conditions and the following disclaimer in the 1560317Sdarrenr * documentation and/or other materials provided with the distribution. 1660317Sdarrenr * 3. The name of the author may not be used to endorse or promote products 1760317Sdarrenr * derived from this software without specific prior written permission. 1860317Sdarrenr * 1960317Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2060317Sdarrenr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2160317Sdarrenr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2260317Sdarrenr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2360317Sdarrenr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2460317Sdarrenr * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2560317Sdarrenr * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2660317Sdarrenr * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2760317Sdarrenr * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2860317Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2960317Sdarrenr * SUCH DAMAGE. 3060317Sdarrenr */ 3160317Sdarrenr 3260317Sdarrenr#include <sys/param.h> 33120386Ssam#include <sys/kernel.h> 3460317Sdarrenr#include <sys/errno.h> 35155201Scsjp#include <sys/lock.h> 3660317Sdarrenr#include <sys/malloc.h> 37173904Smlaier#include <sys/rmlock.h> 3860317Sdarrenr#include <sys/socket.h> 3960317Sdarrenr#include <sys/socketvar.h> 4060317Sdarrenr#include <sys/systm.h> 41120386Ssam#include <sys/condvar.h> 42120386Ssam#include <sys/lock.h> 43120386Ssam#include <sys/mutex.h> 44120386Ssam#include <sys/proc.h> 4560317Sdarrenr#include <sys/queue.h> 4660317Sdarrenr 4760317Sdarrenr#include <net/if.h> 4860317Sdarrenr#include <net/pfil.h> 4960317Sdarrenr 50120386Ssamstatic struct mtx pfil_global_lock; 51120386Ssam 52198198SrwatsonMTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock", 53198198Srwatson MTX_DEF); 54120386Ssam 55120386Ssamstatic int pfil_list_add(pfil_list_t *, struct packet_filter_hook *, int); 56120386Ssam 5760317Sdarrenrstatic int pfil_list_remove(pfil_list_t *, 58198198Srwatson int (*)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *), 59198198Srwatson void *); 6060317Sdarrenr 61197952SjulianLIST_HEAD(pfilheadhead, pfil_head); 62197952SjulianVNET_DEFINE(struct pfilheadhead, pfil_head_list); 63197952Sjulian#define V_pfil_head_list VNET(pfil_head_list) 64120386Ssam 65120386Ssam/* 66120386Ssam * pfil_run_hooks() runs the specified packet filter hooks. 67120386Ssam */ 68120386Ssamint 69120386Ssampfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 70135920Smlaier int dir, struct inpcb *inp) 71120386Ssam{ 72173904Smlaier struct rm_priotracker rmpt; 73120386Ssam struct packet_filter_hook *pfh; 74120386Ssam struct mbuf *m = *mp; 75120386Ssam int rv = 0; 76120386Ssam 77173904Smlaier PFIL_RLOCK(ph, &rmpt); 78155201Scsjp KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0")); 79120386Ssam for (pfh = pfil_hook_get(dir, ph); pfh != NULL; 80120386Ssam pfh = TAILQ_NEXT(pfh, pfil_link)) { 81120386Ssam if (pfh->pfil_func != NULL) { 82198198Srwatson rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir, 83198198Srwatson inp); 84120386Ssam if (rv != 0 || m == NULL) 85120386Ssam break; 86120386Ssam } 87120386Ssam } 88173904Smlaier PFIL_RUNLOCK(ph, &rmpt); 89120386Ssam *mp = m; 90120386Ssam return (rv); 91120386Ssam} 92120386Ssam 93120386Ssam/* 94198233Srwatson * pfil_head_register() registers a pfil_head with the packet filter hook 95198233Srwatson * mechanism. 96120386Ssam */ 97120386Ssamint 98120386Ssampfil_head_register(struct pfil_head *ph) 99120386Ssam{ 100120386Ssam struct pfil_head *lph; 101120386Ssam 102120386Ssam PFIL_LIST_LOCK(); 103197952Sjulian LIST_FOREACH(lph, &V_pfil_head_list, ph_list) { 104120386Ssam if (ph->ph_type == lph->ph_type && 105120386Ssam ph->ph_un.phu_val == lph->ph_un.phu_val) { 106120386Ssam PFIL_LIST_UNLOCK(); 107198233Srwatson return (EEXIST); 108120386Ssam } 109186187Srwatson } 110173904Smlaier PFIL_LOCK_INIT(ph); 111155201Scsjp ph->ph_nhooks = 0; 11260317Sdarrenr TAILQ_INIT(&ph->ph_in); 11360317Sdarrenr TAILQ_INIT(&ph->ph_out); 114197952Sjulian LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list); 115120386Ssam PFIL_LIST_UNLOCK(); 116120386Ssam return (0); 11760317Sdarrenr} 11860317Sdarrenr 11960317Sdarrenr/* 120186187Srwatson * pfil_head_unregister() removes a pfil_head from the packet filter hook 121186187Srwatson * mechanism. The producer of the hook promises that all outstanding 122186187Srwatson * invocations of the hook have completed before it unregisters the hook. 123120386Ssam */ 124120386Ssamint 125120386Ssampfil_head_unregister(struct pfil_head *ph) 126120386Ssam{ 127120386Ssam struct packet_filter_hook *pfh, *pfnext; 128120386Ssam 129120386Ssam PFIL_LIST_LOCK(); 130120386Ssam LIST_REMOVE(ph, ph_list); 131120386Ssam PFIL_LIST_UNLOCK(); 132120386Ssam TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_link, pfnext) 133120386Ssam free(pfh, M_IFADDR); 134120386Ssam TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_link, pfnext) 135120386Ssam free(pfh, M_IFADDR); 136173904Smlaier PFIL_LOCK_DESTROY(ph); 137120386Ssam return (0); 138120386Ssam} 139120386Ssam 140120386Ssam/* 141120386Ssam * pfil_head_get() returns the pfil_head for a given key/dlt. 142120386Ssam */ 143120386Ssamstruct pfil_head * 144120386Ssampfil_head_get(int type, u_long val) 145120386Ssam{ 146120386Ssam struct pfil_head *ph; 147120386Ssam 148120386Ssam PFIL_LIST_LOCK(); 149197952Sjulian LIST_FOREACH(ph, &V_pfil_head_list, ph_list) 150120386Ssam if (ph->ph_type == type && ph->ph_un.phu_val == val) 151120386Ssam break; 152120386Ssam PFIL_LIST_UNLOCK(); 153120386Ssam return (ph); 154120386Ssam} 155120386Ssam 156120386Ssam/* 15760317Sdarrenr * pfil_add_hook() adds a function to the packet filter hook. the 15860317Sdarrenr * flags are: 15960317Sdarrenr * PFIL_IN call me on incoming packets 16060317Sdarrenr * PFIL_OUT call me on outgoing packets 16160317Sdarrenr * PFIL_ALL call me on all of the above 162111119Simp * PFIL_WAITOK OK to call malloc with M_WAITOK. 16360317Sdarrenr */ 16460317Sdarrenrint 165186187Srwatsonpfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, 166186187Srwatson struct inpcb *), void *arg, int flags, struct pfil_head *ph) 16760317Sdarrenr{ 168120386Ssam struct packet_filter_hook *pfh1 = NULL; 169120386Ssam struct packet_filter_hook *pfh2 = NULL; 170120386Ssam int err; 17160317Sdarrenr 172120386Ssam if (flags & PFIL_IN) { 173120386Ssam pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 174120386Ssam M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 175120386Ssam if (pfh1 == NULL) { 176120386Ssam err = ENOMEM; 177120386Ssam goto error; 178120386Ssam } 179120386Ssam } 180120386Ssam if (flags & PFIL_OUT) { 181120386Ssam pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 182120386Ssam M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 183120386Ssam if (pfh2 == NULL) { 184120386Ssam err = ENOMEM; 185120386Ssam goto error; 186120386Ssam } 187120386Ssam } 188155201Scsjp PFIL_WLOCK(ph); 189120386Ssam if (flags & PFIL_IN) { 190120386Ssam pfh1->pfil_func = func; 191120386Ssam pfh1->pfil_arg = arg; 192120386Ssam err = pfil_list_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT); 193120386Ssam if (err) 194186187Srwatson goto locked_error; 195155201Scsjp ph->ph_nhooks++; 196120386Ssam } 197120386Ssam if (flags & PFIL_OUT) { 198120386Ssam pfh2->pfil_func = func; 199120386Ssam pfh2->pfil_arg = arg; 200120386Ssam err = pfil_list_add(&ph->ph_out, pfh2, flags & ~PFIL_IN); 201120386Ssam if (err) { 202120386Ssam if (flags & PFIL_IN) 203120386Ssam pfil_list_remove(&ph->ph_in, func, arg); 204186187Srwatson goto locked_error; 205120386Ssam } 206155201Scsjp ph->ph_nhooks++; 207120386Ssam } 208120386Ssam PFIL_WUNLOCK(ph); 209198233Srwatson return (0); 210186187Srwatsonlocked_error: 211120386Ssam PFIL_WUNLOCK(ph); 212120386Ssamerror: 213120386Ssam if (pfh1 != NULL) 214120386Ssam free(pfh1, M_IFADDR); 215120386Ssam if (pfh2 != NULL) 216120386Ssam free(pfh2, M_IFADDR); 217198233Srwatson return (err); 21860317Sdarrenr} 21960317Sdarrenr 22060317Sdarrenr/* 221198233Srwatson * pfil_remove_hook removes a specific function from the packet filter hook 222198233Srwatson * list. 22360317Sdarrenr */ 22460317Sdarrenrint 225198198Srwatsonpfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int, 226198198Srwatson struct inpcb *), void *arg, int flags, struct pfil_head *ph) 22760317Sdarrenr{ 22860317Sdarrenr int err = 0; 22960317Sdarrenr 230155201Scsjp PFIL_WLOCK(ph); 231155201Scsjp if (flags & PFIL_IN) { 232120386Ssam err = pfil_list_remove(&ph->ph_in, func, arg); 233155201Scsjp if (err == 0) 234155201Scsjp ph->ph_nhooks--; 235155201Scsjp } 236155201Scsjp if ((err == 0) && (flags & PFIL_OUT)) { 237120386Ssam err = pfil_list_remove(&ph->ph_out, func, arg); 238155201Scsjp if (err == 0) 239155201Scsjp ph->ph_nhooks--; 240155201Scsjp } 241120386Ssam PFIL_WUNLOCK(ph); 242198233Srwatson return (err); 24360317Sdarrenr} 24460317Sdarrenr 245120386Ssamstatic int 246120386Ssampfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags) 247120386Ssam{ 248120386Ssam struct packet_filter_hook *pfh; 249120386Ssam 250120386Ssam /* 251120386Ssam * First make sure the hook is not already there. 252120386Ssam */ 253120386Ssam TAILQ_FOREACH(pfh, list, pfil_link) 254120386Ssam if (pfh->pfil_func == pfh1->pfil_func && 255120386Ssam pfh->pfil_arg == pfh1->pfil_arg) 256198233Srwatson return (EEXIST); 257198233Srwatson 258120386Ssam /* 259198233Srwatson * Insert the input list in reverse order of the output list so that 260198233Srwatson * the same path is followed in or out of the kernel. 261120386Ssam */ 262120386Ssam if (flags & PFIL_IN) 263120386Ssam TAILQ_INSERT_HEAD(list, pfh1, pfil_link); 264120386Ssam else 265120386Ssam TAILQ_INSERT_TAIL(list, pfh1, pfil_link); 266198233Srwatson return (0); 267120386Ssam} 268120386Ssam 26960317Sdarrenr/* 27060317Sdarrenr * pfil_list_remove is an internal function that takes a function off the 27160317Sdarrenr * specified list. 27260317Sdarrenr */ 27360317Sdarrenrstatic int 274120386Ssampfil_list_remove(pfil_list_t *list, 275198198Srwatson int (*func)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *), 276198198Srwatson void *arg) 27760317Sdarrenr{ 27860317Sdarrenr struct packet_filter_hook *pfh; 27960317Sdarrenr 28071999Sphk TAILQ_FOREACH(pfh, list, pfil_link) 281120386Ssam if (pfh->pfil_func == func && pfh->pfil_arg == arg) { 28260317Sdarrenr TAILQ_REMOVE(list, pfh, pfil_link); 28360317Sdarrenr free(pfh, M_IFADDR); 284198233Srwatson return (0); 28560317Sdarrenr } 286198233Srwatson return (ENOENT); 28760317Sdarrenr} 288197952Sjulian 289198233Srwatson/* 290198233Srwatson * Stuff that must be initialized for every instance (including the first of 291198233Srwatson * course). 292197952Sjulian */ 293197952Sjulianstatic int 294197952Sjulianvnet_pfil_init(const void *unused) 295197952Sjulian{ 296198233Srwatson 297197952Sjulian LIST_INIT(&V_pfil_head_list); 298197952Sjulian return (0); 299197952Sjulian} 300197952Sjulian 301198233Srwatson/* 302197952Sjulian * Called for the removal of each instance. 303197952Sjulian */ 304197952Sjulianstatic int 305197952Sjulianvnet_pfil_uninit(const void *unused) 306197952Sjulian{ 307198233Srwatson 308197952Sjulian /* XXX should panic if list is not empty */ 309198233Srwatson return (0); 310197952Sjulian} 311197952Sjulian 312197952Sjulian/* Define startup order. */ 313197952Sjulian#define PFIL_SYSINIT_ORDER SI_SUB_PROTO_BEGIN 314197952Sjulian#define PFIL_MODEVENT_ORDER (SI_ORDER_FIRST) /* On boot slot in here. */ 315197952Sjulian#define PFIL_VNET_ORDER (PFIL_MODEVENT_ORDER + 2) /* Later still. */ 316197952Sjulian 317197952Sjulian/* 318198233Srwatson * Starting up. 319198233Srwatson * 320197952Sjulian * VNET_SYSINIT is called for each existing vnet and each new vnet. 321197952Sjulian */ 322197952SjulianVNET_SYSINIT(vnet_pfil_init, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER, 323198233Srwatson vnet_pfil_init, NULL); 324197952Sjulian 325197952Sjulian/* 326198233Srwatson * Closing up shop. These are done in REVERSE ORDER. Not called on reboot. 327198233Srwatson * 328197952Sjulian * VNET_SYSUNINIT is called for each exiting vnet as it exits. 329197952Sjulian */ 330197952SjulianVNET_SYSUNINIT(vnet_pfil_uninit, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER, 331198233Srwatson vnet_pfil_uninit, NULL); 332