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