1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2000 Paycounter, Inc. 5 * Copyright (c) 2005 Robert N. M. Watson 6 * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, 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/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #define ACCEPT_FILTER_MOD 35 36 #include "opt_param.h" 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/domain.h> 40 #include <sys/kernel.h> 41 #include <sys/lock.h> 42 #include <sys/malloc.h> 43 #include <sys/mbuf.h> 44 #include <sys/module.h> 45 #include <sys/mutex.h> 46 #include <sys/protosw.h> 47 #include <sys/sysctl.h> 48 #include <sys/socket.h> 49 #include <sys/socketvar.h> 50 #include <sys/queue.h> 51 52 static struct mtx accept_filter_mtx; 53 MTX_SYSINIT(accept_filter, &accept_filter_mtx, "accept_filter_mtx", 54 MTX_DEF); 55 #define ACCEPT_FILTER_LOCK() mtx_lock(&accept_filter_mtx) 56 #define ACCEPT_FILTER_UNLOCK() mtx_unlock(&accept_filter_mtx) 57 58 static SLIST_HEAD(, accept_filter) accept_filtlsthd = 59 SLIST_HEAD_INITIALIZER(accept_filtlsthd); 60 61 MALLOC_DEFINE(M_ACCF, "accf", "accept filter data"); 62 63 static int unloadable = 0; 64 65 SYSCTL_NODE(_net, OID_AUTO, accf, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 66 "Accept filters"); 67 SYSCTL_INT(_net_accf, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0, 68 "Allow unload of accept filters (not recommended)"); 69 70 /* 71 * Must be passed a malloc'd structure so we don't explode if the kld is 72 * unloaded, we leak the struct on deallocation to deal with this, but if a 73 * filter is loaded with the same name as a leaked one we re-use the entry. 74 */ 75 int 76 accept_filt_add(struct accept_filter *filt) 77 { 78 struct accept_filter *p; 79 80 ACCEPT_FILTER_LOCK(); 81 SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 82 if (strcmp(p->accf_name, filt->accf_name) == 0) { 83 if (p->accf_callback != NULL) { 84 ACCEPT_FILTER_UNLOCK(); 85 return (EEXIST); 86 } else { 87 p->accf_callback = filt->accf_callback; 88 ACCEPT_FILTER_UNLOCK(); 89 free(filt, M_ACCF); 90 return (0); 91 } 92 } 93 94 if (p == NULL) 95 SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next); 96 ACCEPT_FILTER_UNLOCK(); 97 return (0); 98 } 99 100 int 101 accept_filt_del(char *name) 102 { 103 struct accept_filter *p; 104 105 p = accept_filt_get(name); 106 if (p == NULL) 107 return (ENOENT); 108 109 p->accf_callback = NULL; 110 return (0); 111 } 112 113 struct accept_filter * 114 accept_filt_get(char *name) 115 { 116 struct accept_filter *p; 117 118 ACCEPT_FILTER_LOCK(); 119 SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 120 if (strcmp(p->accf_name, name) == 0) 121 break; 122 ACCEPT_FILTER_UNLOCK(); 123 124 return (p); 125 } 126 127 int 128 accept_filt_generic_mod_event(module_t mod, int event, void *data) 129 { 130 struct accept_filter *p; 131 struct accept_filter *accfp = (struct accept_filter *) data; 132 int error; 133 134 switch (event) { 135 case MOD_LOAD: 136 p = malloc(sizeof(*p), M_ACCF, M_WAITOK); 137 bcopy(accfp, p, sizeof(*p)); 138 error = accept_filt_add(p); 139 break; 140 141 case MOD_UNLOAD: 142 /* 143 * Do not support unloading yet. we don't keep track of 144 * refcounts and unloading an accept filter callback and then 145 * having it called is a bad thing. A simple fix would be to 146 * track the refcount in the struct accept_filter. 147 */ 148 if (unloadable != 0) { 149 error = accept_filt_del(accfp->accf_name); 150 } else 151 error = EOPNOTSUPP; 152 break; 153 154 case MOD_SHUTDOWN: 155 error = 0; 156 break; 157 158 default: 159 error = EOPNOTSUPP; 160 break; 161 } 162 163 return (error); 164 } 165 166 int 167 accept_filt_getopt(struct socket *so, struct sockopt *sopt) 168 { 169 struct accept_filter_arg *afap; 170 int error; 171 172 error = 0; 173 afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK | M_ZERO); 174 SOCK_LOCK(so); 175 if (!SOLISTENING(so)) { 176 error = EINVAL; 177 goto out; 178 } 179 if (so->sol_accept_filter == NULL) { 180 error = EINVAL; 181 goto out; 182 } 183 strcpy(afap->af_name, so->sol_accept_filter->accf_name); 184 if (so->sol_accept_filter_str != NULL) 185 strcpy(afap->af_arg, so->sol_accept_filter_str); 186 out: 187 SOCK_UNLOCK(so); 188 if (error == 0) 189 error = sooptcopyout(sopt, afap, sizeof(*afap)); 190 free(afap, M_TEMP); 191 return (error); 192 } 193 194 int 195 accept_filt_setopt(struct socket *so, struct sockopt *sopt) 196 { 197 struct accept_filter_arg *afap; 198 struct accept_filter *afp; 199 char *accept_filter_str = NULL; 200 void *accept_filter_arg = NULL; 201 int error; 202 203 /* 204 * Handle the simple delete case first. 205 */ 206 if (sopt == NULL || sopt->sopt_val == NULL) { 207 struct socket *sp, *sp1; 208 int wakeup; 209 210 SOCK_LOCK(so); 211 if (!SOLISTENING(so)) { 212 SOCK_UNLOCK(so); 213 return (EINVAL); 214 } 215 if (so->sol_accept_filter == NULL) { 216 SOCK_UNLOCK(so); 217 return (0); 218 } 219 if (so->sol_accept_filter->accf_destroy != NULL) 220 so->sol_accept_filter->accf_destroy(so); 221 if (so->sol_accept_filter_str != NULL) 222 free(so->sol_accept_filter_str, M_ACCF); 223 so->sol_accept_filter = NULL; 224 so->sol_accept_filter_arg = NULL; 225 so->sol_accept_filter_str = NULL; 226 so->so_options &= ~SO_ACCEPTFILTER; 227 228 /* 229 * Move from incomplete queue to complete only those 230 * connections, that are blocked by us. 231 */ 232 wakeup = 0; 233 TAILQ_FOREACH_SAFE(sp, &so->sol_incomp, so_list, sp1) { 234 SOCK_LOCK(sp); 235 if (sp->so_options & SO_ACCEPTFILTER) { 236 TAILQ_REMOVE(&so->sol_incomp, sp, so_list); 237 TAILQ_INSERT_TAIL(&so->sol_comp, sp, so_list); 238 sp->so_qstate = SQ_COMP; 239 sp->so_options &= ~SO_ACCEPTFILTER; 240 so->sol_incqlen--; 241 so->sol_qlen++; 242 wakeup = 1; 243 } 244 SOCK_UNLOCK(sp); 245 } 246 if (wakeup) 247 solisten_wakeup(so); /* unlocks */ 248 else 249 SOLISTEN_UNLOCK(so); 250 return (0); 251 } 252 253 /* 254 * Pre-allocate any memory we may need later to avoid blocking at 255 * untimely moments. This does not optimize for invalid arguments. 256 */ 257 afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK); 258 error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); 259 afap->af_name[sizeof(afap->af_name)-1] = '\0'; 260 afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; 261 if (error) { 262 free(afap, M_TEMP); 263 return (error); 264 } 265 afp = accept_filt_get(afap->af_name); 266 if (afp == NULL) { 267 free(afap, M_TEMP); 268 return (ENOENT); 269 } 270 if (afp->accf_create != NULL && afap->af_name[0] != '\0') { 271 size_t len = strlen(afap->af_name) + 1; 272 accept_filter_str = malloc(len, M_ACCF, M_WAITOK); 273 strcpy(accept_filter_str, afap->af_name); 274 } 275 276 /* 277 * Require a listen socket; don't try to replace an existing filter 278 * without first removing it. 279 */ 280 SOCK_LOCK(so); 281 if (!SOLISTENING(so) || so->sol_accept_filter != NULL) { 282 error = EINVAL; 283 goto out; 284 } 285 286 /* 287 * Invoke the accf_create() method of the filter if required. The 288 * socket mutex is held over this call, so create methods for filters 289 * can't block. 290 */ 291 if (afp->accf_create != NULL) { 292 accept_filter_arg = afp->accf_create(so, afap->af_arg); 293 if (accept_filter_arg == NULL) { 294 error = EINVAL; 295 goto out; 296 } 297 } 298 so->sol_accept_filter = afp; 299 so->sol_accept_filter_arg = accept_filter_arg; 300 so->sol_accept_filter_str = accept_filter_str; 301 accept_filter_str = NULL; 302 so->so_options |= SO_ACCEPTFILTER; 303 out: 304 SOCK_UNLOCK(so); 305 if (accept_filter_str != NULL) 306 free(accept_filter_str, M_ACCF); 307 free(afap, M_TEMP); 308 return (error); 309 } 310