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