uipc_accf.c revision 143427
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 143427 2005-03-11 21:37:45Z 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 newaf = NULL; 199 afap = NULL; 200 201 /* 202 * XXXRW: Configuring accept filters should be an atomic test-and-set 203 * operation to prevent races during setup and attach. There may be 204 * more general issues of racing and ordering here that are not yet 205 * addressed by locking. 206 */ 207 /* do not set/remove accept filters on non listen sockets */ 208 SOCK_LOCK(so); 209 if ((so->so_options & SO_ACCEPTCONN) == 0) { 210 SOCK_UNLOCK(so); 211 return (EINVAL); 212 } 213 SOCK_UNLOCK(so); 214 215 /*- 216 * Adding a filter. 217 * 218 * Do memory allocation, copyin, and filter lookup now while we're 219 * not holding any locks. Avoids sleeping with a mutex, as well as 220 * introducing a lock order between accept filter locks and socket 221 * locks here. 222 */ 223 MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, 224 M_WAITOK); 225 /* don't put large objects on the kernel stack */ 226 error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); 227 afap->af_name[sizeof(afap->af_name)-1] = '\0'; 228 afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; 229 if (error) { 230 FREE(afap, M_TEMP); 231 return (error); 232 } 233 afp = accept_filt_get(afap->af_name); 234 if (afp == NULL) { 235 FREE(afap, M_TEMP); 236 return (ENOENT); 237 } 238 239 /* 240 * Allocate the new accept filter instance storage. We may have to 241 * free it again later if we fail to attach it. If attached 242 * properly, 'newaf' is NULLed to avoid a free() while in use. 243 */ 244 MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK | 245 M_ZERO); 246 if (afp->accf_create != NULL && afap->af_name[0] != '\0') { 247 int len = strlen(afap->af_name) + 1; 248 MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF, 249 M_WAITOK); 250 strcpy(newaf->so_accept_filter_str, afap->af_name); 251 } 252 253 SOCK_LOCK(so); 254 /* must remove previous filter first */ 255 if (so->so_accf != NULL) { 256 error = EINVAL; 257 goto out; 258 } 259 /* 260 * Invoke the accf_create() method of the filter if required. 261 * XXXRW: the socket mutex is held over this call, so the create 262 * method cannot block. This may be something we have to change, but 263 * it would require addressing possible races. 264 */ 265 if (afp->accf_create != NULL) { 266 newaf->so_accept_filter_arg = 267 afp->accf_create(so, afap->af_arg); 268 if (newaf->so_accept_filter_arg == NULL) { 269 error = EINVAL; 270 goto out; 271 } 272 } 273 newaf->so_accept_filter = afp; 274 so->so_accf = newaf; 275 so->so_options |= SO_ACCEPTFILTER; 276 newaf = NULL; 277out: 278 SOCK_UNLOCK(so); 279 if (newaf != NULL) { 280 if (newaf->so_accept_filter_str != NULL) 281 FREE(newaf->so_accept_filter_str, M_ACCF); 282 FREE(newaf, M_ACCF); 283 } 284 if (afap != NULL) 285 FREE(afap, M_TEMP); 286 return (error); 287} 288