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