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