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