pfil.c revision 254771
1/* $FreeBSD: head/sys/net/pfil.c 254771 2013-08-24 10:36:33Z andre $ */ 2/* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */ 3 4/*- 5 * Copyright (c) 1996 Matthew R. Green 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/param.h> 33#include <sys/kernel.h> 34#include <sys/errno.h> 35#include <sys/lock.h> 36#include <sys/malloc.h> 37#include <sys/rmlock.h> 38#include <sys/socket.h> 39#include <sys/socketvar.h> 40#include <sys/systm.h> 41#include <sys/condvar.h> 42#include <sys/lock.h> 43#include <sys/mutex.h> 44#include <sys/proc.h> 45#include <sys/queue.h> 46 47#include <net/if.h> 48#include <net/pfil.h> 49 50static struct mtx pfil_global_lock; 51 52MTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock", 53 MTX_DEF); 54 55static struct packet_filter_hook *pfil_hook_get(int, struct pfil_head *); 56static int pfil_list_add(pfil_list_t *, struct packet_filter_hook *, int); 57static int pfil_list_remove(pfil_list_t *, pfil_func_t, void *); 58 59LIST_HEAD(pfilheadhead, pfil_head); 60VNET_DEFINE(struct pfilheadhead, pfil_head_list); 61#define V_pfil_head_list VNET(pfil_head_list) 62VNET_DEFINE(struct rmlock, pfil_lock); 63#define V_pfil_lock VNET(pfil_lock) 64 65/* 66 * pfil_run_hooks() runs the specified packet filter hooks. 67 */ 68int 69pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 70 int dir, struct inpcb *inp) 71{ 72 struct rm_priotracker rmpt; 73 struct packet_filter_hook *pfh; 74 struct mbuf *m = *mp; 75 int rv = 0; 76 77 PFIL_RLOCK(ph, &rmpt); 78 KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0")); 79 for (pfh = pfil_hook_get(dir, ph); pfh != NULL; 80 pfh = TAILQ_NEXT(pfh, pfil_link)) { 81 if (pfh->pfil_func != NULL) { 82 rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir, 83 inp); 84 if (rv != 0 || m == NULL) 85 break; 86 } 87 } 88 PFIL_RUNLOCK(ph, &rmpt); 89 *mp = m; 90 return (rv); 91} 92 93static struct packet_filter_hook * 94pfil_hook_get(int dir, struct pfil_head *ph) 95{ 96 97 if (dir == PFIL_IN) 98 return (TAILQ_FIRST(&ph->ph_in)); 99 else if (dir == PFIL_OUT) 100 return (TAILQ_FIRST(&ph->ph_out)); 101 else 102 return (NULL); 103} 104 105/* 106 * pfil_try_rlock() acquires rm reader lock for specified head 107 * if this is immediately possible. 108 */ 109int 110pfil_try_rlock(struct pfil_head *ph, struct rm_priotracker *tracker) 111{ 112 113 return (PFIL_TRY_RLOCK(ph, tracker)); 114} 115 116/* 117 * pfil_rlock() acquires rm reader lock for specified head. 118 */ 119void 120pfil_rlock(struct pfil_head *ph, struct rm_priotracker *tracker) 121{ 122 123 PFIL_RLOCK(ph, tracker); 124} 125 126/* 127 * pfil_runlock() releases reader lock for specified head. 128 */ 129void 130pfil_runlock(struct pfil_head *ph, struct rm_priotracker *tracker) 131{ 132 133 PFIL_RUNLOCK(ph, tracker); 134} 135 136/* 137 * pfil_wlock() acquires writer lock for specified head. 138 */ 139void 140pfil_wlock(struct pfil_head *ph) 141{ 142 143 PFIL_WLOCK(ph); 144} 145 146/* 147 * pfil_wunlock() releases writer lock for specified head. 148 */ 149void 150pfil_wunlock(struct pfil_head *ph) 151{ 152 153 PFIL_WUNLOCK(ph); 154} 155 156/* 157 * pfil_wowned() returns a non-zero value if the current thread owns 158 * an exclusive lock. 159 */ 160int 161pfil_wowned(struct pfil_head *ph) 162{ 163 164 return (PFIL_WOWNED(ph)); 165} 166/* 167 * pfil_head_register() registers a pfil_head with the packet filter hook 168 * mechanism. 169 */ 170int 171pfil_head_register(struct pfil_head *ph) 172{ 173 struct pfil_head *lph; 174 175 PFIL_LIST_LOCK(); 176 LIST_FOREACH(lph, &V_pfil_head_list, ph_list) { 177 if (ph->ph_type == lph->ph_type && 178 ph->ph_un.phu_val == lph->ph_un.phu_val) { 179 PFIL_LIST_UNLOCK(); 180 return (EEXIST); 181 } 182 } 183 PFIL_LOCK_INIT(ph); 184 ph->ph_nhooks = 0; 185 TAILQ_INIT(&ph->ph_in); 186 TAILQ_INIT(&ph->ph_out); 187 LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list); 188 PFIL_LIST_UNLOCK(); 189 return (0); 190} 191 192/* 193 * pfil_head_unregister() removes a pfil_head from the packet filter hook 194 * mechanism. The producer of the hook promises that all outstanding 195 * invocations of the hook have completed before it unregisters the hook. 196 */ 197int 198pfil_head_unregister(struct pfil_head *ph) 199{ 200 struct packet_filter_hook *pfh, *pfnext; 201 202 PFIL_LIST_LOCK(); 203 LIST_REMOVE(ph, ph_list); 204 PFIL_LIST_UNLOCK(); 205 TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_link, pfnext) 206 free(pfh, M_IFADDR); 207 TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_link, pfnext) 208 free(pfh, M_IFADDR); 209 PFIL_LOCK_DESTROY(ph); 210 return (0); 211} 212 213/* 214 * pfil_head_get() returns the pfil_head for a given key/dlt. 215 */ 216struct pfil_head * 217pfil_head_get(int type, u_long val) 218{ 219 struct pfil_head *ph; 220 221 PFIL_LIST_LOCK(); 222 LIST_FOREACH(ph, &V_pfil_head_list, ph_list) 223 if (ph->ph_type == type && ph->ph_un.phu_val == val) 224 break; 225 PFIL_LIST_UNLOCK(); 226 return (ph); 227} 228 229/* 230 * pfil_add_hook() adds a function to the packet filter hook. the 231 * flags are: 232 * PFIL_IN call me on incoming packets 233 * PFIL_OUT call me on outgoing packets 234 * PFIL_ALL call me on all of the above 235 * PFIL_WAITOK OK to call malloc with M_WAITOK. 236 */ 237int 238pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 239{ 240 struct packet_filter_hook *pfh1 = NULL; 241 struct packet_filter_hook *pfh2 = NULL; 242 int err; 243 244 if (flags & PFIL_IN) { 245 pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 246 M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 247 if (pfh1 == NULL) { 248 err = ENOMEM; 249 goto error; 250 } 251 } 252 if (flags & PFIL_OUT) { 253 pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 254 M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 255 if (pfh2 == NULL) { 256 err = ENOMEM; 257 goto error; 258 } 259 } 260 PFIL_WLOCK(ph); 261 if (flags & PFIL_IN) { 262 pfh1->pfil_func = func; 263 pfh1->pfil_arg = arg; 264 err = pfil_list_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT); 265 if (err) 266 goto locked_error; 267 ph->ph_nhooks++; 268 } 269 if (flags & PFIL_OUT) { 270 pfh2->pfil_func = func; 271 pfh2->pfil_arg = arg; 272 err = pfil_list_add(&ph->ph_out, pfh2, flags & ~PFIL_IN); 273 if (err) { 274 if (flags & PFIL_IN) 275 pfil_list_remove(&ph->ph_in, func, arg); 276 goto locked_error; 277 } 278 ph->ph_nhooks++; 279 } 280 PFIL_WUNLOCK(ph); 281 return (0); 282locked_error: 283 PFIL_WUNLOCK(ph); 284error: 285 if (pfh1 != NULL) 286 free(pfh1, M_IFADDR); 287 if (pfh2 != NULL) 288 free(pfh2, M_IFADDR); 289 return (err); 290} 291 292/* 293 * pfil_remove_hook removes a specific function from the packet filter hook 294 * list. 295 */ 296int 297pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 298{ 299 int err = 0; 300 301 PFIL_WLOCK(ph); 302 if (flags & PFIL_IN) { 303 err = pfil_list_remove(&ph->ph_in, func, arg); 304 if (err == 0) 305 ph->ph_nhooks--; 306 } 307 if ((err == 0) && (flags & PFIL_OUT)) { 308 err = pfil_list_remove(&ph->ph_out, func, arg); 309 if (err == 0) 310 ph->ph_nhooks--; 311 } 312 PFIL_WUNLOCK(ph); 313 return (err); 314} 315 316static int 317pfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags) 318{ 319 struct packet_filter_hook *pfh; 320 321 /* 322 * First make sure the hook is not already there. 323 */ 324 TAILQ_FOREACH(pfh, list, pfil_link) 325 if (pfh->pfil_func == pfh1->pfil_func && 326 pfh->pfil_arg == pfh1->pfil_arg) 327 return (EEXIST); 328 329 /* 330 * Insert the input list in reverse order of the output list so that 331 * the same path is followed in or out of the kernel. 332 */ 333 if (flags & PFIL_IN) 334 TAILQ_INSERT_HEAD(list, pfh1, pfil_link); 335 else 336 TAILQ_INSERT_TAIL(list, pfh1, pfil_link); 337 return (0); 338} 339 340/* 341 * pfil_list_remove is an internal function that takes a function off the 342 * specified list. 343 */ 344static int 345pfil_list_remove(pfil_list_t *list, pfil_func_t func, void *arg) 346{ 347 struct packet_filter_hook *pfh; 348 349 TAILQ_FOREACH(pfh, list, pfil_link) 350 if (pfh->pfil_func == func && pfh->pfil_arg == arg) { 351 TAILQ_REMOVE(list, pfh, pfil_link); 352 free(pfh, M_IFADDR); 353 return (0); 354 } 355 return (ENOENT); 356} 357 358/* 359 * Stuff that must be initialized for every instance (including the first of 360 * course). 361 */ 362static int 363vnet_pfil_init(const void *unused) 364{ 365 366 LIST_INIT(&V_pfil_head_list); 367 PFIL_LOCK_INIT_REAL(&V_pfil_lock, "shared"); 368 return (0); 369} 370 371/* 372 * Called for the removal of each instance. 373 */ 374static int 375vnet_pfil_uninit(const void *unused) 376{ 377 378 /* XXX should panic if list is not empty */ 379 PFIL_LOCK_DESTROY_REAL(&V_pfil_lock); 380 return (0); 381} 382 383/* Define startup order. */ 384#define PFIL_SYSINIT_ORDER SI_SUB_PROTO_BEGIN 385#define PFIL_MODEVENT_ORDER (SI_ORDER_FIRST) /* On boot slot in here. */ 386#define PFIL_VNET_ORDER (PFIL_MODEVENT_ORDER + 2) /* Later still. */ 387 388/* 389 * Starting up. 390 * 391 * VNET_SYSINIT is called for each existing vnet and each new vnet. 392 */ 393VNET_SYSINIT(vnet_pfil_init, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER, 394 vnet_pfil_init, NULL); 395 396/* 397 * Closing up shop. These are done in REVERSE ORDER. Not called on reboot. 398 * 399 * VNET_SYSUNINIT is called for each exiting vnet as it exits. 400 */ 401VNET_SYSUNINIT(vnet_pfil_uninit, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER, 402 vnet_pfil_uninit, NULL); 403