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