1 /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */ 2 /* $DragonFly: src/sys/net/pfil.c,v 1.14 2008/09/20 06:08:13 sephe 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/errno.h> 34 #include <sys/malloc.h> 35 #include <sys/socket.h> 36 #include <sys/socketvar.h> 37 #include <sys/systm.h> 38 #include <sys/proc.h> 39 #include <sys/queue.h> 40 #include <sys/sysctl.h> 41 42 #include <net/if.h> 43 #include <net/pfil.h> 44 #include <net/netmsg2.h> 45 #include <net/netisr2.h> 46 #include <sys/mplock2.h> 47 48 #define PFIL_CFGPORT netisr_cpuport(0) 49 50 #define PFIL_GETMPLOCK(pfh) \ 51 do { \ 52 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \ 53 get_mplock(); \ 54 } while (0) 55 56 #define PFIL_RELMPLOCK(pfh) \ 57 do { \ 58 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \ 59 rel_mplock(); \ 60 } while (0) 61 62 /* 63 * The packet filter hooks are designed for anything to call them to 64 * possibly intercept the packet. 65 */ 66 struct packet_filter_hook { 67 TAILQ_ENTRY(packet_filter_hook) pfil_link; 68 pfil_func_t pfil_func; 69 void *pfil_arg; 70 int pfil_flags; 71 }; 72 73 struct netmsg_pfil { 74 struct netmsg_base base; 75 pfil_func_t pfil_func; 76 void *pfil_arg; 77 int pfil_flags; 78 struct pfil_head *pfil_ph; 79 }; 80 81 static LIST_HEAD(, pfil_head) pfil_head_list = 82 LIST_HEAD_INITIALIZER(&pfil_head_list); 83 84 static pfil_list_t *pfil_list_alloc(void); 85 static void pfil_list_free(pfil_list_t *); 86 static void pfil_list_dup(const pfil_list_t *, pfil_list_t *, 87 const struct packet_filter_hook *); 88 static void pfil_list_add(pfil_list_t *, pfil_func_t, void *, int); 89 static struct packet_filter_hook * 90 pfil_list_find(const pfil_list_t *, pfil_func_t, 91 const void *); 92 93 static void pfil_remove_hook_dispatch(netmsg_t); 94 static void pfil_add_hook_dispatch(netmsg_t); 95 96 int filters_default_to_accept = 0; 97 SYSCTL_INT(_net, OID_AUTO, filters_default_to_accept, CTLFLAG_RW, 98 &filters_default_to_accept, 0, 99 "cause ipfw* modules to not block by default"); 100 TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept); 101 102 /* 103 * pfil_run_hooks() runs the specified packet filter hooks. 104 */ 105 int 106 pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 107 int dir) 108 { 109 struct packet_filter_hook *pfh; 110 struct mbuf *m = *mp; 111 pfil_list_t *list; 112 int rv = 0; 113 114 if (dir == PFIL_IN) 115 list = ph->ph_in; 116 else if (dir == PFIL_OUT) 117 list = ph->ph_out; 118 else 119 return 0; /* XXX panic? */ 120 121 TAILQ_FOREACH(pfh, list, pfil_link) { 122 if (pfh->pfil_func != NULL) { 123 PFIL_GETMPLOCK(pfh); 124 rv = pfh->pfil_func(pfh->pfil_arg, &m, ifp, dir); 125 PFIL_RELMPLOCK(pfh); 126 127 if (rv != 0 || m == NULL) 128 break; 129 } 130 } 131 132 *mp = m; 133 return (rv); 134 } 135 136 /* 137 * pfil_head_register() registers a pfil_head with the packet filter 138 * hook mechanism. 139 */ 140 int 141 pfil_head_register(struct pfil_head *ph) 142 { 143 struct pfil_head *lph; 144 145 LIST_FOREACH(lph, &pfil_head_list, ph_list) { 146 if (ph->ph_type == lph->ph_type && 147 ph->ph_un.phu_val == lph->ph_un.phu_val) 148 return EEXIST; 149 } 150 151 ph->ph_in = pfil_list_alloc(); 152 ph->ph_out = pfil_list_alloc(); 153 ph->ph_hashooks = 0; 154 155 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list); 156 157 return (0); 158 } 159 160 /* 161 * pfil_head_unregister() removes a pfil_head from the packet filter 162 * hook mechanism. 163 */ 164 int 165 pfil_head_unregister(struct pfil_head *pfh) 166 { 167 LIST_REMOVE(pfh, ph_list); 168 return (0); 169 } 170 171 /* 172 * pfil_head_get() returns the pfil_head for a given key/dlt. 173 */ 174 struct pfil_head * 175 pfil_head_get(int type, u_long val) 176 { 177 struct pfil_head *ph; 178 179 LIST_FOREACH(ph, &pfil_head_list, ph_list) { 180 if (ph->ph_type == type && ph->ph_un.phu_val == val) 181 break; 182 } 183 return (ph); 184 } 185 186 static void 187 pfil_add_hook_dispatch(netmsg_t nmsg) 188 { 189 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg; 190 pfil_func_t func = pfilmsg->pfil_func; 191 void *arg = pfilmsg->pfil_arg; 192 int flags = pfilmsg->pfil_flags; 193 struct pfil_head *ph = pfilmsg->pfil_ph; 194 const struct packet_filter_hook *pfh; 195 pfil_list_t *list_in = NULL, *list_out = NULL; 196 pfil_list_t *old_list_in = NULL, *old_list_out = NULL; 197 int err = 0; 198 199 /* This probably should not happen ... */ 200 if ((flags & (PFIL_IN | PFIL_OUT)) == 0) 201 goto reply; /* XXX panic? */ 202 203 /* 204 * If pfil hooks exist on any of the requested lists, 205 * then bail out. 206 */ 207 if (flags & PFIL_IN) { 208 pfh = pfil_list_find(ph->ph_in, func, arg); 209 if (pfh != NULL) { 210 err = EEXIST; 211 goto reply; 212 } 213 } 214 if (flags & PFIL_OUT) { 215 pfh = pfil_list_find(ph->ph_out, func, arg); 216 if (pfh != NULL) { 217 err = EEXIST; 218 goto reply; 219 } 220 } 221 222 /* 223 * Duplicate the requested lists, install new hooks 224 * on the duplication 225 */ 226 if (flags & PFIL_IN) { 227 list_in = pfil_list_alloc(); 228 pfil_list_dup(ph->ph_in, list_in, NULL); 229 pfil_list_add(list_in, func, arg, flags & ~PFIL_OUT); 230 } 231 if (flags & PFIL_OUT) { 232 list_out = pfil_list_alloc(); 233 pfil_list_dup(ph->ph_out, list_out, NULL); 234 pfil_list_add(list_out, func, arg, flags & ~PFIL_IN); 235 } 236 237 /* 238 * Switch list pointers, but keep the old ones 239 */ 240 if (list_in != NULL) { 241 old_list_in = ph->ph_in; 242 ph->ph_in = list_in; 243 } 244 if (list_out != NULL) { 245 old_list_out = ph->ph_out; 246 ph->ph_out = list_out; 247 } 248 249 /* 250 * Wait until everyone has finished the old lists iteration 251 */ 252 netmsg_service_sync(); 253 ph->ph_hashooks = 1; 254 255 /* 256 * Now it is safe to free the old lists, since no one sees it 257 */ 258 if (old_list_in != NULL) 259 pfil_list_free(old_list_in); 260 if (old_list_out != NULL) 261 pfil_list_free(old_list_out); 262 reply: 263 lwkt_replymsg(&nmsg->base.lmsg, err); 264 } 265 266 /* 267 * pfil_add_hook() adds a function to the packet filter hook. the 268 * flags are: 269 * PFIL_IN call me on incoming packets 270 * PFIL_OUT call me on outgoing packets 271 * PFIL_ALL call me on all of the above 272 * PFIL_MPSAFE call me without BGL 273 */ 274 int 275 pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 276 { 277 struct netmsg_pfil pfilmsg; 278 netmsg_base_t nmsg; 279 int error; 280 281 nmsg = &pfilmsg.base; 282 netmsg_init(nmsg, NULL, &curthread->td_msgport, 283 0, pfil_add_hook_dispatch); 284 pfilmsg.pfil_func = func; 285 pfilmsg.pfil_arg = arg; 286 pfilmsg.pfil_flags = flags; 287 pfilmsg.pfil_ph = ph; 288 289 error = lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0); 290 return error; 291 } 292 293 static void 294 pfil_remove_hook_dispatch(netmsg_t nmsg) 295 { 296 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg; 297 pfil_func_t func = pfilmsg->pfil_func; 298 void *arg = pfilmsg->pfil_arg; 299 int flags = pfilmsg->pfil_flags; 300 struct pfil_head *ph = pfilmsg->pfil_ph; 301 struct packet_filter_hook *skip_in = NULL, *skip_out = NULL; 302 pfil_list_t *list_in = NULL, *list_out = NULL; 303 pfil_list_t *old_list_in = NULL, *old_list_out = NULL; 304 int err = 0; 305 306 /* This probably should not happen ... */ 307 if ((flags & (PFIL_IN | PFIL_OUT)) == 0) 308 goto reply; /* XXX panic? */ 309 310 /* 311 * The pfil hook should exist on all requested lists, 312 * if not just bail out 313 */ 314 if (flags & PFIL_IN) { 315 skip_in = pfil_list_find(ph->ph_in, func, arg); 316 if (!skip_in) { 317 err = ENOENT; 318 goto reply; 319 } 320 } 321 if (flags & PFIL_OUT) { 322 skip_out = pfil_list_find(ph->ph_out, func, arg); 323 if (!skip_out) { 324 err = ENOENT; 325 goto reply; 326 } 327 } 328 329 /* 330 * Duplicate the requested lists, but the pfil hook to 331 * be deleted is not copied 332 */ 333 if (flags & PFIL_IN) { 334 KKASSERT(skip_in != NULL); 335 list_in = pfil_list_alloc(); 336 pfil_list_dup(ph->ph_in, list_in, skip_in); 337 } 338 if (flags & PFIL_OUT) { 339 KKASSERT(skip_out != NULL); 340 list_out = pfil_list_alloc(); 341 pfil_list_dup(ph->ph_out, list_out, skip_out); 342 } 343 344 /* 345 * Switch list pointers, but keep the old ones 346 */ 347 if (list_in != NULL) { 348 old_list_in = ph->ph_in; 349 ph->ph_in = list_in; 350 } 351 if (list_out != NULL) { 352 old_list_out = ph->ph_out; 353 ph->ph_out = list_out; 354 } 355 356 /* 357 * Wait until everyone has finished the old lists iteration 358 */ 359 if (TAILQ_EMPTY(ph->ph_in) && TAILQ_EMPTY(ph->ph_out)) 360 ph->ph_hashooks = 0; 361 netmsg_service_sync(); 362 363 /* 364 * Now it is safe to free the old lists, since no one sees it 365 */ 366 if (old_list_in != NULL) 367 pfil_list_free(old_list_in); 368 if (old_list_out != NULL) 369 pfil_list_free(old_list_out); 370 reply: 371 lwkt_replymsg(&nmsg->base.lmsg, err); 372 } 373 374 /* 375 * pfil_remove_hook removes a specific function from the packet filter 376 * hook list. 377 */ 378 int 379 pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 380 { 381 struct netmsg_pfil pfilmsg; 382 netmsg_base_t nmsg; 383 384 nmsg = &pfilmsg.base; 385 netmsg_init(nmsg, NULL, &curthread->td_msgport, 386 0, pfil_remove_hook_dispatch); 387 pfilmsg.pfil_func = func; 388 pfilmsg.pfil_arg = arg; 389 pfilmsg.pfil_flags = flags; 390 pfilmsg.pfil_ph = ph; 391 392 return lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0); 393 } 394 395 static void 396 pfil_list_add(pfil_list_t *list, pfil_func_t func, void *arg, int flags) 397 { 398 struct packet_filter_hook *pfh; 399 400 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT); 401 402 pfh = kmalloc(sizeof(*pfh), M_IFADDR, M_WAITOK); 403 404 pfh->pfil_func = func; 405 pfh->pfil_arg = arg; 406 pfh->pfil_flags = flags; 407 408 /* 409 * Insert the input list in reverse order of the output list 410 * so that the same path is followed in or out of the kernel. 411 */ 412 if (flags & PFIL_IN) 413 TAILQ_INSERT_HEAD(list, pfh, pfil_link); 414 else 415 TAILQ_INSERT_TAIL(list, pfh, pfil_link); 416 } 417 418 static void 419 pfil_list_dup(const pfil_list_t *from, pfil_list_t *to, 420 const struct packet_filter_hook *skip) 421 { 422 struct packet_filter_hook *pfh_to, *pfh_from; 423 424 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT); 425 KKASSERT(TAILQ_EMPTY(to)); 426 427 TAILQ_FOREACH(pfh_from, from, pfil_link) { 428 if (pfh_from == skip) 429 continue; 430 431 pfh_to = kmalloc(sizeof(*pfh_to), M_IFADDR, M_WAITOK); 432 bcopy(pfh_from, pfh_to, sizeof(*pfh_to)); 433 434 TAILQ_INSERT_TAIL(to, pfh_to, pfil_link); 435 } 436 } 437 438 static pfil_list_t * 439 pfil_list_alloc(void) 440 { 441 pfil_list_t *list; 442 443 list = kmalloc(sizeof(*list), M_IFADDR, M_WAITOK); 444 TAILQ_INIT(list); 445 return list; 446 } 447 448 static void 449 pfil_list_free(pfil_list_t *list) 450 { 451 struct packet_filter_hook *pfh; 452 453 while ((pfh = TAILQ_FIRST(list)) != NULL) { 454 TAILQ_REMOVE(list, pfh, pfil_link); 455 kfree(pfh, M_IFADDR); 456 } 457 kfree(list, M_IFADDR); 458 } 459 460 static struct packet_filter_hook * 461 pfil_list_find(const pfil_list_t *list, pfil_func_t func, const void *arg) 462 { 463 struct packet_filter_hook *pfh; 464 465 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT); 466 467 TAILQ_FOREACH(pfh, list, pfil_link) { 468 if (pfh->pfil_func == func && pfh->pfil_arg == arg) 469 return pfh; 470 } 471 return NULL; 472 } 473