pfil.c revision 1.22
1/* $NetBSD: pfil.c,v 1.22 2004/07/18 11:36:04 yamt 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/cdefs.h> 32__KERNEL_RCSID(0, "$NetBSD: pfil.c,v 1.22 2004/07/18 11:36:04 yamt Exp $"); 33 34#include <sys/param.h> 35#include <sys/errno.h> 36#include <sys/malloc.h> 37#include <sys/socket.h> 38#include <sys/socketvar.h> 39#include <sys/systm.h> 40#include <sys/proc.h> 41#include <sys/queue.h> 42 43#include <net/if.h> 44#include <net/pfil.h> 45 46static int pfil_list_add(pfil_list_t *, 47 int (*)(void *, struct mbuf **, struct ifnet *, int), void *, int); 48 49static int pfil_list_remove(pfil_list_t *, 50 int (*)(void *, struct mbuf **, struct ifnet *, int), void *); 51 52LIST_HEAD(, pfil_head) pfil_head_list = 53 LIST_HEAD_INITIALIZER(&pfil_head_list); 54 55/* 56 * pfil_run_hooks() runs the specified packet filter hooks. 57 */ 58int 59pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 60 int dir) 61{ 62 struct packet_filter_hook *pfh; 63 struct mbuf *m = NULL; 64 int rv = 0; 65 66 if ((dir & PFIL_ALL) && mp) 67 m = *mp; 68 for (pfh = pfil_hook_get(dir, ph); pfh != NULL; 69 pfh = TAILQ_NEXT(pfh, pfil_link)) { 70 if (pfh->pfil_func != NULL) { 71 if (pfh->pfil_flags & PFIL_ALL) { 72 rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, 73 dir); 74 if (rv != 0 || m == NULL) 75 break; 76 } else { 77 rv = (*pfh->pfil_func)(pfh->pfil_arg, mp, ifp, 78 dir); 79 if (rv != 0) 80 break; 81 } 82 } 83 } 84 85 if ((dir & PFIL_ALL) && mp) 86 *mp = m; 87 return (rv); 88} 89 90/* 91 * pfil_head_register() registers a pfil_head with the packet filter 92 * hook mechanism. 93 */ 94int 95pfil_head_register(struct pfil_head *ph) 96{ 97 struct pfil_head *lph; 98 99 for (lph = LIST_FIRST(&pfil_head_list); lph != NULL; 100 lph = LIST_NEXT(lph, ph_list)) { 101 if (ph->ph_type == lph->ph_type && 102 ph->ph_un.phu_val == lph->ph_un.phu_val) 103 return EEXIST; 104 } 105 106 TAILQ_INIT(&ph->ph_in); 107 TAILQ_INIT(&ph->ph_out); 108 TAILQ_INIT(&ph->ph_ifaddr); 109 TAILQ_INIT(&ph->ph_newif); 110 111 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list); 112 113 return (0); 114} 115 116/* 117 * pfil_head_unregister() removes a pfil_head from the packet filter 118 * hook mechanism. 119 */ 120int 121pfil_head_unregister(struct pfil_head *pfh) 122{ 123 124 LIST_REMOVE(pfh, ph_list); 125 return (0); 126} 127 128/* 129 * pfil_head_get() returns the pfil_head for a given key/dlt. 130 */ 131struct pfil_head * 132pfil_head_get(int type, u_long val) 133{ 134 struct pfil_head *ph; 135 136 for (ph = LIST_FIRST(&pfil_head_list); ph != NULL; 137 ph = LIST_NEXT(ph, ph_list)) { 138 if (ph->ph_type == type && 139 ph->ph_un.phu_val == val) 140 break; 141 } 142 143 return (ph); 144} 145 146/* 147 * pfil_add_hook() adds a function to the packet filter hook. the 148 * flags are: 149 * PFIL_IN call me on incoming packets 150 * PFIL_OUT call me on outgoing packets 151 * PFIL_ALL call me on all of the above 152 * PFIL_IFADDR call me on interface reconfig (mbuf ** is ioctl #) 153 * PFIL_NEWIF call me on interface creation (mbuf ** is ignored) 154 * PFIL_WAITOK OK to call malloc with M_WAITOK. 155 */ 156int 157pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), 158 void *arg, int flags, struct pfil_head *ph) 159{ 160 int err = 0; 161 162 if (flags & PFIL_IN) { 163 err = pfil_list_add(&ph->ph_in, func, arg, flags & ~PFIL_OUT); 164 if (err) 165 return err; 166 } 167 if (flags & PFIL_OUT) { 168 err = pfil_list_add(&ph->ph_out, func, arg, flags & ~PFIL_IN); 169 if (err) { 170 if (flags & PFIL_IN) 171 pfil_list_remove(&ph->ph_in, func, arg); 172 return err; 173 } 174 } 175 if (flags & PFIL_IFADDR) { 176 err = pfil_list_add(&ph->ph_ifaddr, func, arg, 177 flags & ~PFIL_IFADDR); 178 if (err) { 179 if (flags & PFIL_IN) 180 pfil_list_remove(&ph->ph_in, func, arg); 181 if (flags & PFIL_OUT) 182 pfil_list_remove(&ph->ph_out, func, arg); 183 return err; 184 } 185 } 186 if (flags & PFIL_NEWIF) { 187 err = pfil_list_add(&ph->ph_newif, func, arg, 188 flags & ~PFIL_NEWIF); 189 if (err) { 190 if (flags & PFIL_IN) 191 pfil_list_remove(&ph->ph_in, func, arg); 192 if (flags & PFIL_OUT) 193 pfil_list_remove(&ph->ph_out, func, arg); 194 if (flags & PFIL_IFADDR) 195 pfil_list_remove(&ph->ph_ifaddr, func, arg); 196 return err; 197 } 198 } 199 return 0; 200} 201 202static int 203pfil_list_add(pfil_list_t *list, 204 int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg, 205 int flags) 206{ 207 struct packet_filter_hook *pfh; 208 209 /* 210 * First make sure the hook is not already there. 211 */ 212 for (pfh = TAILQ_FIRST(list); pfh != NULL; 213 pfh = TAILQ_NEXT(pfh, pfil_link)) { 214 if (pfh->pfil_func == func && 215 pfh->pfil_arg == arg) 216 return EEXIST; 217 } 218 219 pfh = (struct packet_filter_hook *)malloc(sizeof(*pfh), M_IFADDR, 220 (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 221 if (pfh == NULL) 222 return ENOMEM; 223 224 pfh->pfil_func = func; 225 pfh->pfil_arg = arg; 226 pfh->pfil_flags = flags; 227 228 /* 229 * insert the input list in reverse order of the output list 230 * so that the same path is followed in or out of the kernel. 231 */ 232 if (flags & PFIL_IN) 233 TAILQ_INSERT_HEAD(list, pfh, pfil_link); 234 else 235 TAILQ_INSERT_TAIL(list, pfh, pfil_link); 236 237 return 0; 238} 239 240/* 241 * pfil_remove_hook removes a specific function from the packet filter 242 * hook list. 243 */ 244int 245pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), 246 void *arg, int flags, struct pfil_head *ph) 247{ 248 int err = 0; 249 250 if (flags & PFIL_IN) 251 err = pfil_list_remove(&ph->ph_in, func, arg); 252 if ((err == 0) && (flags & PFIL_OUT)) 253 err = pfil_list_remove(&ph->ph_out, func, arg); 254 if ((err == 0) && (flags & PFIL_IFADDR)) 255 err = pfil_list_remove(&ph->ph_ifaddr, func, arg); 256 if ((err == 0) && (flags & PFIL_NEWIF)) 257 err = pfil_list_remove(&ph->ph_newif, func, arg); 258 return err; 259} 260 261/* 262 * pfil_list_remove is an internal function that takes a function off the 263 * specified list. 264 */ 265static int 266pfil_list_remove(pfil_list_t *list, 267 int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg) 268{ 269 struct packet_filter_hook *pfh; 270 271 for (pfh = TAILQ_FIRST(list); pfh != NULL; 272 pfh = TAILQ_NEXT(pfh, pfil_link)) { 273 if (pfh->pfil_func == func && pfh->pfil_arg == arg) { 274 TAILQ_REMOVE(list, pfh, pfil_link); 275 free(pfh, M_IFADDR); 276 return 0; 277 } 278 } 279 return ENOENT; 280} 281