1 /* $NetBSD: pfil.c,v 1.28 2013/06/29 21:06:58 rmind Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Mindaugas Rasiukevicius <rmind at NetBSD org> 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 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: pfil.c,v 1.28 2013/06/29 21:06:58 rmind Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/queue.h> 36 #include <sys/kmem.h> 37 38 #include <net/if.h> 39 #include <net/pfil.h> 40 41 #define MAX_HOOKS 8 42 43 typedef struct { 44 pfil_func_t pfil_func; 45 void * pfil_arg; 46 } pfil_hook_t; 47 48 typedef struct { 49 pfil_hook_t hooks[MAX_HOOKS]; 50 u_int nhooks; 51 } pfil_list_t; 52 53 struct pfil_head { 54 pfil_list_t ph_in; 55 pfil_list_t ph_out; 56 pfil_list_t ph_ifaddr; 57 pfil_list_t ph_ifevent; 58 int ph_type; 59 void * ph_key; 60 LIST_ENTRY(pfil_head) ph_list; 61 }; 62 63 static const int pfil_flag_cases[] = { 64 PFIL_IN, PFIL_OUT, PFIL_IFADDR, PFIL_IFNET 65 }; 66 67 static LIST_HEAD(, pfil_head) pfil_head_list __read_mostly = 68 LIST_HEAD_INITIALIZER(&pfil_head_list); 69 70 /* 71 * pfil_head_create: create and register a packet filter head. 72 */ 73 pfil_head_t * 74 pfil_head_create(int type, void *key) 75 { 76 pfil_head_t *ph; 77 78 if (pfil_head_get(type, key)) { 79 return NULL; 80 } 81 ph = kmem_zalloc(sizeof(pfil_head_t), KM_SLEEP); 82 ph->ph_type = type; 83 ph->ph_key = key; 84 85 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list); 86 return ph; 87 } 88 89 /* 90 * pfil_head_destroy: remove and destroy a packet filter head. 91 */ 92 void 93 pfil_head_destroy(pfil_head_t *pfh) 94 { 95 LIST_REMOVE(pfh, ph_list); 96 kmem_free(pfh, sizeof(pfil_head_t)); 97 } 98 99 /* 100 * pfil_head_get: returns the packer filter head for a given key. 101 */ 102 pfil_head_t * 103 pfil_head_get(int type, void *key) 104 { 105 pfil_head_t *ph; 106 107 LIST_FOREACH(ph, &pfil_head_list, ph_list) { 108 if (ph->ph_type == type && ph->ph_key == key) 109 break; 110 } 111 return ph; 112 } 113 114 static pfil_list_t * 115 pfil_hook_get(int dir, pfil_head_t *ph) 116 { 117 switch (dir) { 118 case PFIL_IN: 119 return &ph->ph_in; 120 case PFIL_OUT: 121 return &ph->ph_out; 122 case PFIL_IFADDR: 123 return &ph->ph_ifaddr; 124 case PFIL_IFNET: 125 return &ph->ph_ifevent; 126 } 127 return NULL; 128 } 129 130 static int 131 pfil_list_add(pfil_list_t *phlist, pfil_func_t func, void *arg, int flags) 132 { 133 const u_int nhooks = phlist->nhooks; 134 pfil_hook_t *pfh; 135 136 /* Check if we have a free slot. */ 137 if (nhooks == MAX_HOOKS) { 138 return ENOSPC; 139 } 140 KASSERT(nhooks < MAX_HOOKS); 141 142 /* Make sure the hook is not already added. */ 143 for (u_int i = 0; i < nhooks; i++) { 144 pfh = &phlist->hooks[i]; 145 if (pfh->pfil_func == func && pfh->pfil_arg == arg) 146 return EEXIST; 147 } 148 149 /* 150 * Finally, add the hook. Note: for PFIL_IN we insert the hooks in 151 * reverse order of the PFIL_OUT so that the same path is followed 152 * in or out of the kernel. 153 */ 154 if (flags & PFIL_IN) { 155 /* XXX: May want to revisit this later; */ 156 size_t len = sizeof(pfil_hook_t) * nhooks; 157 pfh = &phlist->hooks[0]; 158 memmove(&phlist->hooks[1], pfh, len); 159 } else { 160 pfh = &phlist->hooks[nhooks]; 161 } 162 phlist->nhooks++; 163 164 pfh->pfil_func = func; 165 pfh->pfil_arg = arg; 166 return 0; 167 } 168 169 /* 170 * pfil_add_hook: add a function (hook) to the packet filter head. 171 * The possible flags are: 172 * 173 * PFIL_IN call on incoming packets 174 * PFIL_OUT call on outgoing packets 175 * PFIL_ALL call on all of the above 176 * PFIL_IFADDR call on interface reconfig (mbuf is ioctl #) 177 * PFIL_IFNET call on interface attach/detach (mbuf is PFIL_IFNET_*) 178 */ 179 int 180 pfil_add_hook(pfil_func_t func, void *arg, int flags, pfil_head_t *ph) 181 { 182 int error = 0; 183 184 KASSERT(func != NULL); 185 186 for (u_int i = 0; i < __arraycount(pfil_flag_cases); i++) { 187 const int fcase = pfil_flag_cases[i]; 188 pfil_list_t *phlist; 189 190 if ((flags & fcase) == 0) { 191 continue; 192 } 193 phlist = pfil_hook_get(fcase, ph); 194 if ((error = pfil_list_add(phlist, func, arg, flags)) != 0) { 195 break; 196 } 197 } 198 if (error) { 199 pfil_remove_hook(func, arg, flags, ph); 200 } 201 return error; 202 } 203 204 /* 205 * pfil_list_remove: remove the hook from a specified list. 206 */ 207 static int 208 pfil_list_remove(pfil_list_t *phlist, pfil_func_t func, void *arg) 209 { 210 const u_int nhooks = phlist->nhooks; 211 212 for (u_int i = 0; i < nhooks; i++) { 213 pfil_hook_t *last, *pfh = &phlist->hooks[i]; 214 215 if (pfh->pfil_func != func || pfh->pfil_arg != arg) { 216 continue; 217 } 218 if ((last = &phlist->hooks[nhooks - 1]) != pfh) { 219 memcpy(pfh, last, sizeof(pfil_hook_t)); 220 } 221 phlist->nhooks--; 222 return 0; 223 } 224 return ENOENT; 225 } 226 227 /* 228 * pfil_remove_hook: remove the hook from the packet filter head. 229 */ 230 int 231 pfil_remove_hook(pfil_func_t func, void *arg, int flags, pfil_head_t *ph) 232 { 233 for (u_int i = 0; i < __arraycount(pfil_flag_cases); i++) { 234 const int fcase = pfil_flag_cases[i]; 235 pfil_list_t *pflist; 236 237 if ((flags & fcase) == 0) { 238 continue; 239 } 240 pflist = pfil_hook_get(fcase, ph); 241 (void)pfil_list_remove(pflist, func, arg); 242 } 243 return 0; 244 } 245 246 /* 247 * pfil_run_hooks: run the specified packet filter hooks. 248 */ 249 int 250 pfil_run_hooks(pfil_head_t *ph, struct mbuf **mp, ifnet_t *ifp, int dir) 251 { 252 const bool pass_mbuf = (dir & PFIL_ALL) != 0 && mp; 253 struct mbuf *m = pass_mbuf ? *mp : NULL; 254 pfil_list_t *phlist; 255 int ret = 0; 256 257 if ((phlist = pfil_hook_get(dir, ph)) == NULL) { 258 return ret; 259 } 260 261 for (u_int i = 0; i < phlist->nhooks; i++) { 262 pfil_hook_t *pfh = &phlist->hooks[i]; 263 pfil_func_t func = pfh->pfil_func; 264 265 if (__predict_true(dir & PFIL_ALL)) { 266 ret = (*func)(pfh->pfil_arg, &m, ifp, dir); 267 if (m == NULL) 268 break; 269 } else { 270 ret = (*func)(pfh->pfil_arg, mp, ifp, dir); 271 } 272 if (ret) 273 break; 274 } 275 276 if (pass_mbuf) { 277 *mp = m; 278 } 279 return ret; 280 } 281