uipc_accf.c revision 143461
1/*- 2 * Copyright (c) 2000 Paycounter, Inc. 3 * Copyright (c) 2005 Robert N. M. Watson 4 * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org> 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 AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, 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__FBSDID("$FreeBSD: head/sys/kern/uipc_accf.c 143461 2005-03-12 12:27:47Z rwatson $"); 31 32#define ACCEPT_FILTER_MOD 33 34#include "opt_param.h" 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/domain.h> 38#include <sys/kernel.h> 39#include <sys/lock.h> 40#include <sys/malloc.h> 41#include <sys/mbuf.h> 42#include <sys/module.h> 43#include <sys/mutex.h> 44#include <sys/protosw.h> 45#include <sys/sysctl.h> 46#include <sys/socket.h> 47#include <sys/socketvar.h> 48#include <sys/queue.h> 49 50static struct mtx accept_filter_mtx; 51MTX_SYSINIT(accept_filter, &accept_filter_mtx, "accept_filter_mtx", 52 MTX_DEF); 53#define ACCEPT_FILTER_LOCK() mtx_lock(&accept_filter_mtx) 54#define ACCEPT_FILTER_UNLOCK() mtx_unlock(&accept_filter_mtx) 55 56static SLIST_HEAD(, accept_filter) accept_filtlsthd = 57 SLIST_HEAD_INITIALIZER(&accept_filtlsthd); 58 59MALLOC_DEFINE(M_ACCF, "accf", "accept filter data"); 60 61static int unloadable = 0; 62 63SYSCTL_DECL(_net_inet); /* XXX: some header should do this for me */ 64SYSCTL_NODE(_net_inet, OID_AUTO, accf, CTLFLAG_RW, 0, "Accept filters"); 65SYSCTL_INT(_net_inet_accf, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0, 66 "Allow unload of accept filters (not recommended)"); 67 68/* 69 * Must be passed a malloc'd structure so we don't explode if the kld is 70 * unloaded, we leak the struct on deallocation to deal with this, but if a 71 * filter is loaded with the same name as a leaked one we re-use the entry. 72 */ 73int 74accept_filt_add(struct accept_filter *filt) 75{ 76 struct accept_filter *p; 77 78 ACCEPT_FILTER_LOCK(); 79 SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 80 if (strcmp(p->accf_name, filt->accf_name) == 0) { 81 if (p->accf_callback != NULL) { 82 ACCEPT_FILTER_UNLOCK(); 83 return (EEXIST); 84 } else { 85 p->accf_callback = filt->accf_callback; 86 ACCEPT_FILTER_UNLOCK(); 87 FREE(filt, M_ACCF); 88 return (0); 89 } 90 } 91 92 if (p == NULL) 93 SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next); 94 ACCEPT_FILTER_UNLOCK(); 95 return (0); 96} 97 98int 99accept_filt_del(char *name) 100{ 101 struct accept_filter *p; 102 103 p = accept_filt_get(name); 104 if (p == NULL) 105 return (ENOENT); 106 107 p->accf_callback = NULL; 108 return (0); 109} 110 111struct accept_filter * 112accept_filt_get(char *name) 113{ 114 struct accept_filter *p; 115 116 ACCEPT_FILTER_LOCK(); 117 SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 118 if (strcmp(p->accf_name, name) == 0) 119 break; 120 ACCEPT_FILTER_UNLOCK(); 121 122 return (p); 123} 124 125int 126accept_filt_generic_mod_event(module_t mod, int event, void *data) 127{ 128 struct accept_filter *p; 129 struct accept_filter *accfp = (struct accept_filter *) data; 130 int error; 131 132 switch (event) { 133 case MOD_LOAD: 134 MALLOC(p, struct accept_filter *, sizeof(*p), M_ACCF, 135 M_WAITOK); 136 bcopy(accfp, p, sizeof(*p)); 137 error = accept_filt_add(p); 138 break; 139 140 case MOD_UNLOAD: 141 /* 142 * Do not support unloading yet. we don't keep track of 143 * refcounts and unloading an accept filter callback and then 144 * having it called is a bad thing. A simple fix would be to 145 * track the refcount in the struct accept_filter. 146 */ 147 if (unloadable != 0) { 148 error = accept_filt_del(accfp->accf_name); 149 } else 150 error = EOPNOTSUPP; 151 break; 152 153 case MOD_SHUTDOWN: 154 error = 0; 155 break; 156 157 default: 158 error = EOPNOTSUPP; 159 break; 160 } 161 162 return (error); 163} 164 165int 166do_setopt_accept_filter(struct socket *so, struct sockopt *sopt) 167{ 168 struct accept_filter_arg *afap; 169 struct accept_filter *afp; 170 struct so_accf *newaf; 171 int error = 0; 172 173 /* 174 * Handle the simple delete case first. 175 */ 176 if (sopt == NULL) { 177 SOCK_LOCK(so); 178 if ((so->so_options & SO_ACCEPTCONN) == 0) { 179 SOCK_UNLOCK(so); 180 return (EINVAL); 181 } 182 if (so->so_accf != NULL) { 183 struct so_accf *af = so->so_accf; 184 if (af->so_accept_filter != NULL && 185 af->so_accept_filter->accf_destroy != NULL) { 186 af->so_accept_filter->accf_destroy(so); 187 } 188 if (af->so_accept_filter_str != NULL) 189 FREE(af->so_accept_filter_str, M_ACCF); 190 FREE(af, M_ACCF); 191 so->so_accf = NULL; 192 } 193 so->so_options &= ~SO_ACCEPTFILTER; 194 SOCK_UNLOCK(so); 195 return (0); 196 } 197 198 /* 199 * Pre-allocate any memory we may need later to avoid blocking at 200 * untimely moments. This does not optimize for invalid arguments. 201 */ 202 MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, 203 M_WAITOK); 204 error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); 205 afap->af_name[sizeof(afap->af_name)-1] = '\0'; 206 afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; 207 if (error) { 208 FREE(afap, M_TEMP); 209 return (error); 210 } 211 afp = accept_filt_get(afap->af_name); 212 if (afp == NULL) { 213 FREE(afap, M_TEMP); 214 return (ENOENT); 215 } 216 /* 217 * Allocate the new accept filter instance storage. We may 218 * have to free it again later if we fail to attach it. If 219 * attached properly, 'newaf' is NULLed to avoid a free() 220 * while in use. 221 */ 222 MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK | 223 M_ZERO); 224 if (afp->accf_create != NULL && afap->af_name[0] != '\0') { 225 int len = strlen(afap->af_name) + 1; 226 MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF, 227 M_WAITOK); 228 strcpy(newaf->so_accept_filter_str, afap->af_name); 229 } 230 231 /* 232 * Require a listen socket; don't try to replace an existing filter 233 * without first removing it. 234 */ 235 SOCK_LOCK(so); 236 if (((so->so_options & SO_ACCEPTCONN) == 0) || 237 (so->so_accf != NULL)) { 238 error = EINVAL; 239 goto out; 240 } 241 242 /* 243 * Invoke the accf_create() method of the filter if required. The 244 * socket mutex is held over this call, so create methods for filters 245 * can't block. 246 */ 247 if (afp->accf_create != NULL) { 248 newaf->so_accept_filter_arg = 249 afp->accf_create(so, afap->af_arg); 250 if (newaf->so_accept_filter_arg == NULL) { 251 error = EINVAL; 252 goto out; 253 } 254 } 255 newaf->so_accept_filter = afp; 256 so->so_accf = newaf; 257 so->so_options |= SO_ACCEPTFILTER; 258 newaf = NULL; 259out: 260 SOCK_UNLOCK(so); 261 if (newaf != NULL) { 262 if (newaf->so_accept_filter_str != NULL) 263 FREE(newaf->so_accept_filter_str, M_ACCF); 264 FREE(newaf, M_ACCF); 265 } 266 if (afap != NULL) 267 FREE(afap, M_TEMP); 268 return (error); 269} 270