1 /* $NetBSD: pfil.c,v 1.27 2008/06/23 03:13:12 dyoung 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: pfil.c,v 1.27 2008/06/23 03:13:12 dyoung Exp $"); 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 44 static int pfil_list_add(pfil_list_t *, 45 int (*)(void *, struct mbuf **, struct ifnet *, int), void *, int); 46 47 static int pfil_list_remove(pfil_list_t *, 48 int (*)(void *, struct mbuf **, struct ifnet *, int), void *); 49 50 LIST_HEAD(, pfil_head) pfil_head_list = 51 LIST_HEAD_INITIALIZER(&pfil_head_list); 52 53 /* 54 * pfil_run_hooks() runs the specified packet filter hooks. 55 */ 56 int 57 pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp, 58 int dir) 59 { 60 struct packet_filter_hook *pfh; 61 struct mbuf *m = NULL; 62 int rv = 0; 63 64 if ((dir & PFIL_ALL) && mp) 65 m = *mp; 66 for (pfh = pfil_hook_get(dir, ph); pfh != NULL; 67 pfh = TAILQ_NEXT(pfh, pfil_link)) { 68 if (pfh->pfil_func != NULL) { 69 if (pfh->pfil_flags & PFIL_ALL) { 70 rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, 71 dir); 72 if (rv != 0 || m == NULL) 73 break; 74 } else { 75 rv = (*pfh->pfil_func)(pfh->pfil_arg, mp, ifp, 76 dir); 77 if (rv != 0) 78 break; 79 } 80 } 81 } 82 83 if ((dir & PFIL_ALL) && mp) 84 *mp = m; 85 return (rv); 86 } 87 88 /* 89 * pfil_head_register() registers a pfil_head with the packet filter 90 * hook mechanism. 91 */ 92 int 93 pfil_head_register(struct pfil_head *ph) 94 { 95 struct pfil_head *lph; 96 97 LIST_FOREACH(lph, &pfil_head_list, ph_list) { 98 if (ph->ph_type == lph->ph_type && 99 ph->ph_un.phu_val == lph->ph_un.phu_val) 100 return EEXIST; 101 } 102 103 TAILQ_INIT(&ph->ph_in); 104 TAILQ_INIT(&ph->ph_out); 105 TAILQ_INIT(&ph->ph_ifaddr); 106 TAILQ_INIT(&ph->ph_ifnetevent); 107 108 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list); 109 110 return (0); 111 } 112 113 /* 114 * pfil_head_unregister() removes a pfil_head from the packet filter 115 * hook mechanism. 116 */ 117 int 118 pfil_head_unregister(struct pfil_head *pfh) 119 { 120 121 LIST_REMOVE(pfh, ph_list); 122 return (0); 123 } 124 125 /* 126 * pfil_head_get() returns the pfil_head for a given key/dlt. 127 */ 128 struct pfil_head * 129 pfil_head_get(int type, u_long val) 130 { 131 struct pfil_head *ph; 132 133 LIST_FOREACH(ph, &pfil_head_list, ph_list) { 134 if (ph->ph_type == type && ph->ph_un.phu_val == val) 135 break; 136 } 137 138 return (ph); 139 } 140 141 /* 142 * pfil_add_hook() adds a function to the packet filter hook. the 143 * flags are: 144 * PFIL_IN call me on incoming packets 145 * PFIL_OUT call me on outgoing packets 146 * PFIL_ALL call me on all of the above 147 * PFIL_IFADDR call me on interface reconfig (mbuf ** is ioctl #) 148 * PFIL_IFNET call me on interface attach/detach 149 * (mbuf ** is PFIL_IFNET_*) 150 * PFIL_WAITOK OK to call malloc with M_WAITOK. 151 */ 152 int 153 pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), 154 void *arg, int flags, struct pfil_head *ph) 155 { 156 int err = 0; 157 158 if (flags & PFIL_IN) { 159 err = pfil_list_add(&ph->ph_in, func, arg, flags & ~PFIL_OUT); 160 if (err) 161 return err; 162 } 163 if (flags & PFIL_OUT) { 164 err = pfil_list_add(&ph->ph_out, func, arg, flags & ~PFIL_IN); 165 if (err) { 166 if (flags & PFIL_IN) 167 pfil_list_remove(&ph->ph_in, func, arg); 168 return err; 169 } 170 } 171 if (flags & PFIL_IFADDR) { 172 err = pfil_list_add(&ph->ph_ifaddr, func, arg, flags); 173 if (err) { 174 if (flags & PFIL_IN) 175 pfil_list_remove(&ph->ph_in, func, arg); 176 if (flags & PFIL_OUT) 177 pfil_list_remove(&ph->ph_out, func, arg); 178 return err; 179 } 180 } 181 if (flags & PFIL_IFNET) { 182 err = pfil_list_add(&ph->ph_ifnetevent, func, arg, flags); 183 if (err) { 184 if (flags & PFIL_IN) 185 pfil_list_remove(&ph->ph_in, func, arg); 186 if (flags & PFIL_OUT) 187 pfil_list_remove(&ph->ph_out, func, arg); 188 if (flags & PFIL_IFADDR) 189 pfil_list_remove(&ph->ph_ifaddr, func, arg); 190 return err; 191 } 192 } 193 return 0; 194 } 195 196 static int 197 pfil_list_add(pfil_list_t *list, 198 int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg, 199 int flags) 200 { 201 struct packet_filter_hook *pfh; 202 203 /* 204 * First make sure the hook is not already there. 205 */ 206 TAILQ_FOREACH(pfh, list, pfil_link) { 207 if (pfh->pfil_func == func && pfh->pfil_arg == arg) 208 return EEXIST; 209 } 210 211 pfh = (struct packet_filter_hook *)malloc(sizeof(*pfh), M_IFADDR, 212 (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 213 if (pfh == NULL) 214 return ENOMEM; 215 216 pfh->pfil_func = func; 217 pfh->pfil_arg = arg; 218 pfh->pfil_flags = flags; 219 220 /* 221 * insert the input list in reverse order of the output list 222 * so that the same path is followed in or out of the kernel. 223 */ 224 if (flags & PFIL_IN) 225 TAILQ_INSERT_HEAD(list, pfh, pfil_link); 226 else 227 TAILQ_INSERT_TAIL(list, pfh, pfil_link); 228 229 return 0; 230 } 231 232 /* 233 * pfil_remove_hook removes a specific function from the packet filter 234 * hook list. 235 */ 236 int 237 pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), 238 void *arg, int flags, struct pfil_head *ph) 239 { 240 int err = 0; 241 242 if (flags & PFIL_IN) 243 err = pfil_list_remove(&ph->ph_in, func, arg); 244 if ((err == 0) && (flags & PFIL_OUT)) 245 err = pfil_list_remove(&ph->ph_out, func, arg); 246 if ((err == 0) && (flags & PFIL_IFADDR)) 247 err = pfil_list_remove(&ph->ph_ifaddr, func, arg); 248 if ((err == 0) && (flags & PFIL_IFNET)) 249 err = pfil_list_remove(&ph->ph_ifnetevent, func, arg); 250 return err; 251 } 252 253 /* 254 * pfil_list_remove is an internal function that takes a function off the 255 * specified list. 256 */ 257 static int 258 pfil_list_remove(pfil_list_t *list, 259 int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg) 260 { 261 struct packet_filter_hook *pfh; 262 263 TAILQ_FOREACH(pfh, list, pfil_link) { 264 if (pfh->pfil_func == func && pfh->pfil_arg == arg) { 265 TAILQ_REMOVE(list, pfh, pfil_link); 266 free(pfh, M_IFADDR); 267 return 0; 268 } 269 } 270 return ENOENT; 271 } 272