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