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/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem 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 46 static int pfil_list_add(pfil_list_t *, 47 int (*)(void *, struct mbuf **, struct ifnet *, int), void *, int); 48 49 static int pfil_list_remove(pfil_list_t *, 50 int (*)(void *, struct mbuf **, struct ifnet *, int), void *); 51 52 LIST_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 */ 58 int 59 pfil_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 = *mp; 64 int rv = 0; 65 66 for (pfh = pfil_hook_get(dir, ph); pfh != NULL; 67 pfh = TAILQ_NEXT(pfh, pfil_link)) { 68 if (pfh->pfil_func != NULL) { 69 rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir); 70 if (rv != 0 || m == NULL) 71 break; 72 } 73 } 74 75 *mp = m; 76 return (rv); 77 } 78 79 /* 80 * pfil_head_register() registers a pfil_head with the packet filter 81 * hook mechanism. 82 */ 83 int 84 pfil_head_register(struct pfil_head *ph) 85 { 86 struct pfil_head *lph; 87 88 for (lph = LIST_FIRST(&pfil_head_list); lph != NULL; 89 lph = LIST_NEXT(lph, ph_list)) { 90 if (ph->ph_type == lph->ph_type && 91 ph->ph_un.phu_val == lph->ph_un.phu_val) 92 return EEXIST; 93 } 94 95 TAILQ_INIT(&ph->ph_in); 96 TAILQ_INIT(&ph->ph_out); 97 98 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list); 99 100 return (0); 101 } 102 103 /* 104 * pfil_head_unregister() removes a pfil_head from the packet filter 105 * hook mechanism. 106 */ 107 int 108 pfil_head_unregister(struct pfil_head *pfh) 109 { 110 111 LIST_REMOVE(pfh, ph_list); 112 return (0); 113 } 114 115 /* 116 * pfil_head_get() returns the pfil_head for a given key/dlt. 117 */ 118 struct pfil_head * 119 pfil_head_get(int type, u_long val) 120 { 121 struct pfil_head *ph; 122 123 for (ph = LIST_FIRST(&pfil_head_list); ph != NULL; 124 ph = LIST_NEXT(ph, ph_list)) { 125 if (ph->ph_type == type && 126 ph->ph_un.phu_val == val) 127 break; 128 } 129 130 return (ph); 131 } 132 133 /* 134 * pfil_add_hook() adds a function to the packet filter hook. the 135 * flags are: 136 * PFIL_IN call me on incoming packets 137 * PFIL_OUT call me on outgoing packets 138 * PFIL_ALL call me on all of the above 139 * PFIL_WAITOK OK to call malloc with M_WAITOK. 140 */ 141 int 142 pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), 143 void *arg, int flags, struct pfil_head *ph) 144 { 145 int err = 0; 146 147 if (flags & PFIL_IN) { 148 err = pfil_list_add(&ph->ph_in, func, arg, flags & ~PFIL_OUT); 149 if (err) 150 return err; 151 } 152 if (flags & PFIL_OUT) { 153 err = pfil_list_add(&ph->ph_out, func, arg, flags & ~PFIL_IN); 154 if (err) { 155 if (flags & PFIL_IN) 156 pfil_list_remove(&ph->ph_in, func, arg); 157 return err; 158 } 159 } 160 return 0; 161 } 162 163 static int 164 pfil_list_add(pfil_list_t *list, 165 int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg, 166 int flags) 167 { 168 struct packet_filter_hook *pfh; 169 170 /* 171 * First make sure the hook is not already there. 172 */ 173 for (pfh = TAILQ_FIRST(list); pfh != NULL; 174 pfh = TAILQ_NEXT(pfh, pfil_link)) { 175 if (pfh->pfil_func == func && 176 pfh->pfil_arg == arg) 177 return EEXIST; 178 } 179 180 pfh = (struct packet_filter_hook *)malloc(sizeof(*pfh), M_IFADDR, 181 (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT); 182 if (pfh == NULL) 183 return ENOMEM; 184 185 pfh->pfil_func = func; 186 pfh->pfil_arg = arg; 187 188 /* 189 * insert the input list in reverse order of the output list 190 * so that the same path is followed in or out of the kernel. 191 */ 192 if (flags & PFIL_IN) 193 TAILQ_INSERT_HEAD(list, pfh, pfil_link); 194 else 195 TAILQ_INSERT_TAIL(list, pfh, pfil_link); 196 197 return 0; 198 } 199 200 /* 201 * pfil_remove_hook removes a specific function from the packet filter 202 * hook list. 203 */ 204 int 205 pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int), 206 void *arg, int flags, struct pfil_head *ph) 207 { 208 int err = 0; 209 210 if (flags & PFIL_IN) 211 err = pfil_list_remove(&ph->ph_in, func, arg); 212 if ((err == 0) && (flags & PFIL_OUT)) 213 err = pfil_list_remove(&ph->ph_out, func, arg); 214 return err; 215 } 216 217 /* 218 * pfil_list_remove is an internal function that takes a function off the 219 * specified list. 220 */ 221 static int 222 pfil_list_remove(pfil_list_t *list, 223 int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg) 224 { 225 struct packet_filter_hook *pfh; 226 227 for (pfh = TAILQ_FIRST(list); pfh != NULL; 228 pfh = TAILQ_NEXT(pfh, pfil_link)) { 229 if (pfh->pfil_func == func && pfh->pfil_arg == arg) { 230 TAILQ_REMOVE(list, pfh, pfil_link); 231 free(pfh, M_IFADDR); 232 return 0; 233 } 234 } 235 return ENOENT; 236 } 237