pfil.c revision 1.30
1/* $NetBSD: pfil.c,v 1.30 2017/01/04 13:03:41 ryo 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.30 2017/01/04 13:03:41 ryo 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/* Func is either pfil_func_t or pfil_ifunc_t. */ 44typedef void (*pfil_polyfunc_t)(void); 45 46typedef struct { 47 pfil_polyfunc_t pfil_func; 48 void * pfil_arg; 49} pfil_hook_t; 50 51typedef struct { 52 pfil_hook_t hooks[MAX_HOOKS]; 53 u_int nhooks; 54} pfil_list_t; 55 56CTASSERT(PFIL_IN == 1); 57CTASSERT(PFIL_OUT == 2); 58 59struct pfil_head { 60 pfil_list_t ph_in; 61 pfil_list_t ph_out; 62 pfil_list_t ph_ifaddr; 63 pfil_list_t ph_ifevent; 64 int ph_type; 65 void * ph_key; 66 LIST_ENTRY(pfil_head) ph_list; 67}; 68 69static const int pfil_flag_cases[] = { 70 PFIL_IN, PFIL_OUT, PFIL_IFADDR, PFIL_IFNET 71}; 72 73static LIST_HEAD(, pfil_head) pfil_head_list __read_mostly = 74 LIST_HEAD_INITIALIZER(&pfil_head_list); 75 76/* 77 * pfil_head_create: create and register a packet filter head. 78 */ 79pfil_head_t * 80pfil_head_create(int type, void *key) 81{ 82 pfil_head_t *ph; 83 84 if (pfil_head_get(type, key)) { 85 return NULL; 86 } 87 ph = kmem_zalloc(sizeof(pfil_head_t), KM_SLEEP); 88 ph->ph_type = type; 89 ph->ph_key = key; 90 91 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list); 92 return ph; 93} 94 95/* 96 * pfil_head_destroy: remove and destroy a packet filter head. 97 */ 98void 99pfil_head_destroy(pfil_head_t *pfh) 100{ 101 LIST_REMOVE(pfh, ph_list); 102 kmem_free(pfh, sizeof(pfil_head_t)); 103} 104 105/* 106 * pfil_head_get: returns the packer filter head for a given key. 107 */ 108pfil_head_t * 109pfil_head_get(int type, void *key) 110{ 111 pfil_head_t *ph; 112 113 LIST_FOREACH(ph, &pfil_head_list, ph_list) { 114 if (ph->ph_type == type && ph->ph_key == key) 115 break; 116 } 117 return ph; 118} 119 120static pfil_list_t * 121pfil_hook_get(int dir, pfil_head_t *ph) 122{ 123 switch (dir) { 124 case PFIL_IN: 125 return &ph->ph_in; 126 case PFIL_OUT: 127 return &ph->ph_out; 128 case PFIL_IFADDR: 129 return &ph->ph_ifaddr; 130 case PFIL_IFNET: 131 return &ph->ph_ifevent; 132 } 133 return NULL; 134} 135 136static int 137pfil_list_add(pfil_list_t *phlist, pfil_polyfunc_t func, void *arg, int flags) 138{ 139 const u_int nhooks = phlist->nhooks; 140 pfil_hook_t *pfh; 141 142 /* Check if we have a free slot. */ 143 if (nhooks == MAX_HOOKS) { 144 return ENOSPC; 145 } 146 KASSERT(nhooks < MAX_HOOKS); 147 148 /* Make sure the hook is not already added. */ 149 for (u_int i = 0; i < nhooks; i++) { 150 pfh = &phlist->hooks[i]; 151 if (pfh->pfil_func == func && pfh->pfil_arg == arg) 152 return EEXIST; 153 } 154 155 /* 156 * Finally, add the hook. Note: for PFIL_IN we insert the hooks in 157 * reverse order of the PFIL_OUT so that the same path is followed 158 * in or out of the kernel. 159 */ 160 if (flags & PFIL_IN) { 161 /* XXX: May want to revisit this later; */ 162 size_t len = sizeof(pfil_hook_t) * nhooks; 163 pfh = &phlist->hooks[0]; 164 memmove(&phlist->hooks[1], pfh, len); 165 } else { 166 pfh = &phlist->hooks[nhooks]; 167 } 168 phlist->nhooks++; 169 170 pfh->pfil_func = func; 171 pfh->pfil_arg = arg; 172 return 0; 173} 174 175/* 176 * pfil_add_hook: add a function (hook) to the packet filter head. 177 * The possible flags are: 178 * 179 * PFIL_IN call on incoming packets 180 * PFIL_OUT call on outgoing packets 181 * PFIL_ALL call on all of the above 182 */ 183int 184pfil_add_hook(pfil_func_t func, void *arg, int flags, pfil_head_t *ph) 185{ 186 int error = 0; 187 188 KASSERT(func != NULL); 189 KASSERT((flags & ~PFIL_ALL) == 0); 190 191 for (u_int i = 0; i < __arraycount(pfil_flag_cases); i++) { 192 const int fcase = pfil_flag_cases[i]; 193 pfil_list_t *phlist; 194 195 if ((flags & fcase) == 0) { 196 continue; 197 } 198 phlist = pfil_hook_get(fcase, ph); 199 error = pfil_list_add(phlist, (pfil_polyfunc_t)func, arg, flags); 200 if (error) { 201 break; 202 } 203 } 204 if (error) { 205 pfil_remove_hook(func, arg, flags, ph); 206 } 207 return error; 208} 209 210/* 211 * pfil_add_hook: add an interface-event function (hook) to the packet 212 * filter head. The possible flags are: 213 * 214 * PFIL_IFADDR call on interface reconfig (mbuf is ioctl #) 215 * PFIL_IFNET call on interface attach/detach (mbuf is PFIL_IFNET_*) 216 */ 217int 218pfil_add_ihook(pfil_ifunc_t func, void *arg, int flags, pfil_head_t *ph) 219{ 220 pfil_list_t *phlist; 221 222 KASSERT(flags == PFIL_IFADDR || flags == PFIL_IFNET); 223 phlist = pfil_hook_get(flags, ph); 224 return pfil_list_add(phlist, (pfil_polyfunc_t)func, arg, flags); 225} 226 227/* 228 * pfil_list_remove: remove the hook from a specified list. 229 */ 230static int 231pfil_list_remove(pfil_list_t *phlist, pfil_polyfunc_t func, void *arg) 232{ 233 const u_int nhooks = phlist->nhooks; 234 235 for (u_int i = 0; i < nhooks; i++) { 236 pfil_hook_t *last, *pfh = &phlist->hooks[i]; 237 238 if (pfh->pfil_func != func || pfh->pfil_arg != arg) { 239 continue; 240 } 241 if ((last = &phlist->hooks[nhooks - 1]) != pfh) { 242 memcpy(pfh, last, sizeof(pfil_hook_t)); 243 } 244 phlist->nhooks--; 245 return 0; 246 } 247 return ENOENT; 248} 249 250/* 251 * pfil_remove_hook: remove the hook from the packet filter head. 252 */ 253int 254pfil_remove_hook(pfil_func_t func, void *arg, int flags, pfil_head_t *ph) 255{ 256 for (u_int i = 0; i < __arraycount(pfil_flag_cases); i++) { 257 const int fcase = pfil_flag_cases[i]; 258 pfil_list_t *pflist; 259 260 if ((flags & fcase) == 0) { 261 continue; 262 } 263 pflist = pfil_hook_get(fcase, ph); 264 (void)pfil_list_remove(pflist, (pfil_polyfunc_t)func, arg); 265 } 266 return 0; 267} 268 269int 270pfil_remove_ihook(pfil_ifunc_t func, void *arg, int flags, pfil_head_t *ph) 271{ 272 pfil_list_t *pflist; 273 274 KASSERT(flags == PFIL_IFADDR || flags == PFIL_IFNET); 275 pflist = pfil_hook_get(flags, ph); 276 (void)pfil_list_remove(pflist, (pfil_polyfunc_t)func, arg); 277 return 0; 278} 279 280/* 281 * pfil_run_hooks: run the specified packet filter hooks. 282 */ 283int 284pfil_run_hooks(pfil_head_t *ph, struct mbuf **mp, ifnet_t *ifp, int dir) 285{ 286 struct mbuf *m = mp ? *mp : NULL; 287 pfil_list_t *phlist; 288 int ret = 0; 289 290 KASSERT((dir & ~PFIL_ALL) == 0); 291 if (__predict_false((phlist = pfil_hook_get(dir, ph)) == NULL)) { 292 return ret; 293 } 294 295 for (u_int i = 0; i < phlist->nhooks; i++) { 296 pfil_hook_t *pfh = &phlist->hooks[i]; 297 pfil_func_t func = (pfil_func_t)pfh->pfil_func; 298 299 ret = (*func)(pfh->pfil_arg, &m, ifp, dir); 300 if (m == NULL || ret) 301 break; 302 } 303 304 if (mp) { 305 *mp = m; 306 } 307 return ret; 308} 309 310void 311pfil_run_addrhooks(pfil_head_t *ph, u_long cmd, struct ifaddr *ifa) 312{ 313 pfil_list_t *phlist = &ph->ph_ifaddr; 314 315 for (u_int i = 0; i < phlist->nhooks; i++) { 316 pfil_hook_t *pfh = &phlist->hooks[i]; 317 pfil_ifunc_t func = (pfil_ifunc_t)pfh->pfil_func; 318 (*func)(pfh->pfil_arg, cmd, (void *)ifa); 319 } 320} 321 322void 323pfil_run_ifhooks(pfil_head_t *ph, u_long cmd, struct ifnet *ifp) 324{ 325 pfil_list_t *phlist = &ph->ph_ifevent; 326 327 for (u_int i = 0; i < phlist->nhooks; i++) { 328 pfil_hook_t *pfh = &phlist->hooks[i]; 329 pfil_ifunc_t func = (pfil_ifunc_t)pfh->pfil_func; 330 (*func)(pfh->pfil_arg, cmd, (void *)ifp); 331 } 332} 333