1 /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/errno.h> 33 #include <sys/malloc.h> 34 #include <sys/socket.h> 35 #include <sys/socketvar.h> 36 #include <sys/systm.h> 37 #include <sys/proc.h> 38 #include <sys/queue.h> 39 #include <sys/sysctl.h> 40 41 #include <net/if.h> 42 #include <net/if_var.h> 43 #include <net/pfil.h> 44 #include <net/netmsg2.h> 45 #include <net/netisr2.h> 46 47 #define PFIL_CFGPORT netisr_cpuport(0) 48 49 /* 50 * The packet filter hooks are designed for anything to call them to 51 * possibly intercept the packet. 52 */ 53 struct packet_filter_hook { 54 TAILQ_ENTRY(packet_filter_hook) pfil_link; 55 pfil_func_t pfil_func; 56 void *pfil_arg; 57 int pfil_flags; 58 }; 59 60 struct netmsg_pfil { 61 struct netmsg_base base; 62 pfil_func_t pfil_func; 63 void *pfil_arg; 64 int pfil_flags; 65 struct pfil_head *pfil_ph; 66 }; 67 68 static LIST_HEAD(, pfil_head) pfil_head_list = 69 LIST_HEAD_INITIALIZER(&pfil_head_list); 70 71 static pfil_list_t *pfil_list_alloc(void); 72 static void pfil_list_free(pfil_list_t *); 73 static void pfil_list_dup(const pfil_list_t *, pfil_list_t *, 74 const struct packet_filter_hook *); 75 static void pfil_list_add(pfil_list_t *, pfil_func_t, void *, int); 76 static struct packet_filter_hook * 77 pfil_list_find(const pfil_list_t *, pfil_func_t, 78 const void *); 79 80 static void pfil_remove_hook_dispatch(netmsg_t); 81 static void pfil_add_hook_dispatch(netmsg_t); 82 83 int filters_default_to_accept = 0; 84 SYSCTL_INT(_net, OID_AUTO, filters_default_to_accept, CTLFLAG_RW, 85 &filters_default_to_accept, 0, 86 "cause ipfw* modules to not block by default"); 87 TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept); 88 89 /* 90 * pfil_run_hooks() runs the specified packet filter hooks. 91 */ 92 int 93 pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 94 int dir) 95 { 96 struct packet_filter_hook *pfh; 97 struct mbuf *m = *mp; 98 pfil_list_t *list; 99 int rv = 0; 100 101 if (dir == PFIL_IN) 102 list = ph->ph_in; 103 else if (dir == PFIL_OUT) 104 list = ph->ph_out; 105 else 106 return 0; /* XXX panic? */ 107 108 /* Make sure 'list' is really used. */ 109 cpu_ccfence(); 110 TAILQ_FOREACH(pfh, list, pfil_link) { 111 if (pfh->pfil_func != NULL) { 112 rv = pfh->pfil_func(pfh->pfil_arg, &m, ifp, dir); 113 if (rv != 0 || m == NULL) 114 break; 115 } 116 } 117 118 *mp = m; 119 return (rv); 120 } 121 122 /* 123 * pfil_head_register() registers a pfil_head with the packet filter 124 * hook mechanism. 125 */ 126 int 127 pfil_head_register(struct pfil_head *ph) 128 { 129 struct pfil_head *lph; 130 131 LIST_FOREACH(lph, &pfil_head_list, ph_list) { 132 if (ph->ph_type == lph->ph_type && 133 ph->ph_un.phu_val == lph->ph_un.phu_val) 134 return EEXIST; 135 } 136 137 ph->ph_in = pfil_list_alloc(); 138 ph->ph_out = pfil_list_alloc(); 139 ph->ph_hashooks = 0; 140 141 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list); 142 143 return (0); 144 } 145 146 /* 147 * pfil_head_unregister() removes a pfil_head from the packet filter 148 * hook mechanism. 149 */ 150 int 151 pfil_head_unregister(struct pfil_head *pfh) 152 { 153 LIST_REMOVE(pfh, ph_list); 154 return (0); 155 } 156 157 /* 158 * pfil_head_get() returns the pfil_head for a given key/dlt. 159 */ 160 struct pfil_head * 161 pfil_head_get(int type, u_long val) 162 { 163 struct pfil_head *ph; 164 165 LIST_FOREACH(ph, &pfil_head_list, ph_list) { 166 if (ph->ph_type == type && ph->ph_un.phu_val == val) 167 break; 168 } 169 return (ph); 170 } 171 172 static void 173 pfil_add_hook_dispatch(netmsg_t nmsg) 174 { 175 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg; 176 pfil_func_t func = pfilmsg->pfil_func; 177 void *arg = pfilmsg->pfil_arg; 178 int flags = pfilmsg->pfil_flags; 179 struct pfil_head *ph = pfilmsg->pfil_ph; 180 const struct packet_filter_hook *pfh; 181 pfil_list_t *list_in = NULL, *list_out = NULL; 182 pfil_list_t *old_list_in = NULL, *old_list_out = NULL; 183 int err = 0; 184 185 /* This probably should not happen ... */ 186 if ((flags & (PFIL_IN | PFIL_OUT)) == 0) 187 goto reply; /* XXX panic? */ 188 189 /* 190 * If pfil hooks exist on any of the requested lists, 191 * then bail out. 192 */ 193 if (flags & PFIL_IN) { 194 pfh = pfil_list_find(ph->ph_in, func, arg); 195 if (pfh != NULL) { 196 err = EEXIST; 197 goto reply; 198 } 199 } 200 if (flags & PFIL_OUT) { 201 pfh = pfil_list_find(ph->ph_out, func, arg); 202 if (pfh != NULL) { 203 err = EEXIST; 204 goto reply; 205 } 206 } 207 208 /* 209 * Duplicate the requested lists, install new hooks 210 * on the duplication 211 */ 212 if (flags & PFIL_IN) { 213 list_in = pfil_list_alloc(); 214 pfil_list_dup(ph->ph_in, list_in, NULL); 215 pfil_list_add(list_in, func, arg, flags & ~PFIL_OUT); 216 } 217 if (flags & PFIL_OUT) { 218 list_out = pfil_list_alloc(); 219 pfil_list_dup(ph->ph_out, list_out, NULL); 220 pfil_list_add(list_out, func, arg, flags & ~PFIL_IN); 221 } 222 223 /* 224 * Switch list pointers, but keep the old ones 225 */ 226 if (list_in != NULL) { 227 old_list_in = ph->ph_in; 228 ph->ph_in = list_in; 229 } 230 if (list_out != NULL) { 231 old_list_out = ph->ph_out; 232 ph->ph_out = list_out; 233 } 234 235 /* 236 * Wait until everyone has finished the old lists iteration 237 */ 238 netmsg_service_sync(); 239 ph->ph_hashooks = 1; 240 241 /* 242 * Now it is safe to free the old lists, since no one sees it 243 */ 244 if (old_list_in != NULL) 245 pfil_list_free(old_list_in); 246 if (old_list_out != NULL) 247 pfil_list_free(old_list_out); 248 reply: 249 lwkt_replymsg(&nmsg->base.lmsg, err); 250 } 251 252 /* 253 * pfil_add_hook() adds a function to the packet filter hook. the 254 * flags are: 255 * PFIL_IN call me on incoming packets 256 * PFIL_OUT call me on outgoing packets 257 * PFIL_ALL call me on all of the above 258 */ 259 int 260 pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 261 { 262 struct netmsg_pfil pfilmsg; 263 netmsg_base_t nmsg; 264 int error; 265 266 nmsg = &pfilmsg.base; 267 netmsg_init(nmsg, NULL, &curthread->td_msgport, 268 0, pfil_add_hook_dispatch); 269 pfilmsg.pfil_func = func; 270 pfilmsg.pfil_arg = arg; 271 pfilmsg.pfil_flags = flags; 272 pfilmsg.pfil_ph = ph; 273 274 error = lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0); 275 return error; 276 } 277 278 static void 279 pfil_remove_hook_dispatch(netmsg_t nmsg) 280 { 281 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg; 282 pfil_func_t func = pfilmsg->pfil_func; 283 void *arg = pfilmsg->pfil_arg; 284 int flags = pfilmsg->pfil_flags; 285 struct pfil_head *ph = pfilmsg->pfil_ph; 286 struct packet_filter_hook *skip_in = NULL, *skip_out = NULL; 287 pfil_list_t *list_in = NULL, *list_out = NULL; 288 pfil_list_t *old_list_in = NULL, *old_list_out = NULL; 289 int err = 0; 290 291 /* This probably should not happen ... */ 292 if ((flags & (PFIL_IN | PFIL_OUT)) == 0) 293 goto reply; /* XXX panic? */ 294 295 /* 296 * The pfil hook should exist on all requested lists, 297 * if not just bail out 298 */ 299 if (flags & PFIL_IN) { 300 skip_in = pfil_list_find(ph->ph_in, func, arg); 301 if (!skip_in) { 302 err = ENOENT; 303 goto reply; 304 } 305 } 306 if (flags & PFIL_OUT) { 307 skip_out = pfil_list_find(ph->ph_out, func, arg); 308 if (!skip_out) { 309 err = ENOENT; 310 goto reply; 311 } 312 } 313 314 /* 315 * Duplicate the requested lists, but the pfil hook to 316 * be deleted is not copied 317 */ 318 if (flags & PFIL_IN) { 319 KKASSERT(skip_in != NULL); 320 list_in = pfil_list_alloc(); 321 pfil_list_dup(ph->ph_in, list_in, skip_in); 322 } 323 if (flags & PFIL_OUT) { 324 KKASSERT(skip_out != NULL); 325 list_out = pfil_list_alloc(); 326 pfil_list_dup(ph->ph_out, list_out, skip_out); 327 } 328 329 /* 330 * Switch list pointers, but keep the old ones 331 */ 332 if (list_in != NULL) { 333 old_list_in = ph->ph_in; 334 ph->ph_in = list_in; 335 } 336 if (list_out != NULL) { 337 old_list_out = ph->ph_out; 338 ph->ph_out = list_out; 339 } 340 341 /* 342 * Wait until everyone has finished the old lists iteration 343 */ 344 if (TAILQ_EMPTY(ph->ph_in) && TAILQ_EMPTY(ph->ph_out)) 345 ph->ph_hashooks = 0; 346 netmsg_service_sync(); 347 348 /* 349 * Now it is safe to free the old lists, since no one sees it 350 */ 351 if (old_list_in != NULL) 352 pfil_list_free(old_list_in); 353 if (old_list_out != NULL) 354 pfil_list_free(old_list_out); 355 reply: 356 lwkt_replymsg(&nmsg->base.lmsg, err); 357 } 358 359 /* 360 * pfil_remove_hook removes a specific function from the packet filter 361 * hook list. 362 */ 363 int 364 pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph) 365 { 366 struct netmsg_pfil pfilmsg; 367 netmsg_base_t nmsg; 368 369 nmsg = &pfilmsg.base; 370 netmsg_init(nmsg, NULL, &curthread->td_msgport, 371 0, pfil_remove_hook_dispatch); 372 pfilmsg.pfil_func = func; 373 pfilmsg.pfil_arg = arg; 374 pfilmsg.pfil_flags = flags; 375 pfilmsg.pfil_ph = ph; 376 377 return lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0); 378 } 379 380 static void 381 pfil_list_add(pfil_list_t *list, pfil_func_t func, void *arg, int flags) 382 { 383 struct packet_filter_hook *pfh; 384 385 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT); 386 387 pfh = kmalloc(sizeof(*pfh), M_IFADDR, M_WAITOK); 388 389 pfh->pfil_func = func; 390 pfh->pfil_arg = arg; 391 pfh->pfil_flags = flags; 392 393 /* 394 * Insert the input list in reverse order of the output list 395 * so that the same path is followed in or out of the kernel. 396 */ 397 if (flags & PFIL_IN) 398 TAILQ_INSERT_HEAD(list, pfh, pfil_link); 399 else 400 TAILQ_INSERT_TAIL(list, pfh, pfil_link); 401 } 402 403 static void 404 pfil_list_dup(const pfil_list_t *from, pfil_list_t *to, 405 const struct packet_filter_hook *skip) 406 { 407 struct packet_filter_hook *pfh_to, *pfh_from; 408 409 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT); 410 KKASSERT(TAILQ_EMPTY(to)); 411 412 TAILQ_FOREACH(pfh_from, from, pfil_link) { 413 if (pfh_from == skip) 414 continue; 415 416 pfh_to = kmalloc(sizeof(*pfh_to), M_IFADDR, M_WAITOK); 417 bcopy(pfh_from, pfh_to, sizeof(*pfh_to)); 418 419 TAILQ_INSERT_TAIL(to, pfh_to, pfil_link); 420 } 421 } 422 423 static pfil_list_t * 424 pfil_list_alloc(void) 425 { 426 pfil_list_t *list; 427 428 list = kmalloc(sizeof(*list), M_IFADDR, M_WAITOK); 429 TAILQ_INIT(list); 430 return list; 431 } 432 433 static void 434 pfil_list_free(pfil_list_t *list) 435 { 436 struct packet_filter_hook *pfh; 437 438 while ((pfh = TAILQ_FIRST(list)) != NULL) { 439 TAILQ_REMOVE(list, pfh, pfil_link); 440 kfree(pfh, M_IFADDR); 441 } 442 kfree(list, M_IFADDR); 443 } 444 445 static struct packet_filter_hook * 446 pfil_list_find(const pfil_list_t *list, pfil_func_t func, const void *arg) 447 { 448 struct packet_filter_hook *pfh; 449 450 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT); 451 452 TAILQ_FOREACH(pfh, list, pfil_link) { 453 if (pfh->pfil_func == func && pfh->pfil_arg == arg) 454 return pfh; 455 } 456 return NULL; 457 } 458