19454b2d8SWarner Losh /*- 234b94e8bSAlfred Perlstein * Copyright (c) 2000 Paycounter, Inc. 364c23807SRobert Watson * Copyright (c) 2005 Robert N. M. Watson 434b94e8bSAlfred Perlstein * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org> 5a79b7128SAlfred Perlstein * All rights reserved. 6a79b7128SAlfred Perlstein * 7a79b7128SAlfred Perlstein * Redistribution and use in source and binary forms, with or without 8a79b7128SAlfred Perlstein * modification, are permitted provided that the following conditions 9a79b7128SAlfred Perlstein * are met: 10a79b7128SAlfred Perlstein * 1. Redistributions of source code must retain the above copyright 11a79b7128SAlfred Perlstein * notice, this list of conditions and the following disclaimer. 12a79b7128SAlfred Perlstein * 2. Redistributions in binary form must reproduce the above copyright 13a79b7128SAlfred Perlstein * notice, this list of conditions and the following disclaimer in the 14a79b7128SAlfred Perlstein * documentation and/or other materials provided with the distribution. 15a79b7128SAlfred Perlstein * 16a79b7128SAlfred Perlstein * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17a79b7128SAlfred Perlstein * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18a79b7128SAlfred Perlstein * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19a79b7128SAlfred Perlstein * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20a79b7128SAlfred Perlstein * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21a79b7128SAlfred Perlstein * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22a79b7128SAlfred Perlstein * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23a79b7128SAlfred Perlstein * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24a79b7128SAlfred Perlstein * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25a79b7128SAlfred Perlstein * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26a79b7128SAlfred Perlstein * SUCH DAMAGE. 27a79b7128SAlfred Perlstein */ 28a79b7128SAlfred Perlstein 29677b542eSDavid E. O'Brien #include <sys/cdefs.h> 30677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 31677b542eSDavid E. O'Brien 32a79b7128SAlfred Perlstein #define ACCEPT_FILTER_MOD 33a79b7128SAlfred Perlstein 345b86eac4SJesper Skriver #include "opt_param.h" 35a79b7128SAlfred Perlstein #include <sys/param.h> 36a79b7128SAlfred Perlstein #include <sys/systm.h> 37a79b7128SAlfred Perlstein #include <sys/domain.h> 38a79b7128SAlfred Perlstein #include <sys/kernel.h> 39d087080cSRobert Watson #include <sys/lock.h> 40a79b7128SAlfred Perlstein #include <sys/malloc.h> 41a79b7128SAlfred Perlstein #include <sys/mbuf.h> 4241ee9f1cSPoul-Henning Kamp #include <sys/module.h> 43d087080cSRobert Watson #include <sys/mutex.h> 44a79b7128SAlfred Perlstein #include <sys/protosw.h> 4534b94e8bSAlfred Perlstein #include <sys/sysctl.h> 46a79b7128SAlfred Perlstein #include <sys/socket.h> 47a79b7128SAlfred Perlstein #include <sys/socketvar.h> 48a79b7128SAlfred Perlstein #include <sys/queue.h> 49a79b7128SAlfred Perlstein 50d087080cSRobert Watson static struct mtx accept_filter_mtx; 51d087080cSRobert Watson MTX_SYSINIT(accept_filter, &accept_filter_mtx, "accept_filter_mtx", 52d087080cSRobert Watson MTX_DEF); 53d087080cSRobert Watson #define ACCEPT_FILTER_LOCK() mtx_lock(&accept_filter_mtx) 54d087080cSRobert Watson #define ACCEPT_FILTER_UNLOCK() mtx_unlock(&accept_filter_mtx) 55d087080cSRobert Watson 56a79b7128SAlfred Perlstein static SLIST_HEAD(, accept_filter) accept_filtlsthd = 57a79b7128SAlfred Perlstein SLIST_HEAD_INITIALIZER(&accept_filtlsthd); 58a79b7128SAlfred Perlstein 59a79b7128SAlfred Perlstein MALLOC_DEFINE(M_ACCF, "accf", "accept filter data"); 60a79b7128SAlfred Perlstein 6134b94e8bSAlfred Perlstein static int unloadable = 0; 6234b94e8bSAlfred Perlstein 6334b94e8bSAlfred Perlstein SYSCTL_DECL(_net_inet); /* XXX: some header should do this for me */ 6434b94e8bSAlfred Perlstein SYSCTL_NODE(_net_inet, OID_AUTO, accf, CTLFLAG_RW, 0, "Accept filters"); 6534b94e8bSAlfred Perlstein SYSCTL_INT(_net_inet_accf, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0, 6634b94e8bSAlfred Perlstein "Allow unload of accept filters (not recommended)"); 6734b94e8bSAlfred Perlstein 68a79b7128SAlfred Perlstein /* 691ed716a1SRobert Watson * Must be passed a malloc'd structure so we don't explode if the kld is 701ed716a1SRobert Watson * unloaded, we leak the struct on deallocation to deal with this, but if a 711ed716a1SRobert Watson * filter is loaded with the same name as a leaked one we re-use the entry. 72a79b7128SAlfred Perlstein */ 73a79b7128SAlfred Perlstein int 74a79b7128SAlfred Perlstein accept_filt_add(struct accept_filter *filt) 75a79b7128SAlfred Perlstein { 76a79b7128SAlfred Perlstein struct accept_filter *p; 77a79b7128SAlfred Perlstein 78d087080cSRobert Watson ACCEPT_FILTER_LOCK(); 79a79b7128SAlfred Perlstein SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 80a79b7128SAlfred Perlstein if (strcmp(p->accf_name, filt->accf_name) == 0) { 81a79b7128SAlfred Perlstein if (p->accf_callback != NULL) { 82d087080cSRobert Watson ACCEPT_FILTER_UNLOCK(); 83a79b7128SAlfred Perlstein return (EEXIST); 84a79b7128SAlfred Perlstein } else { 85a79b7128SAlfred Perlstein p->accf_callback = filt->accf_callback; 86d087080cSRobert Watson ACCEPT_FILTER_UNLOCK(); 87a79b7128SAlfred Perlstein FREE(filt, M_ACCF); 88a79b7128SAlfred Perlstein return (0); 89a79b7128SAlfred Perlstein } 90a79b7128SAlfred Perlstein } 91a79b7128SAlfred Perlstein 92a79b7128SAlfred Perlstein if (p == NULL) 93a79b7128SAlfred Perlstein SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next); 94d087080cSRobert Watson ACCEPT_FILTER_UNLOCK(); 95a79b7128SAlfred Perlstein return (0); 96a79b7128SAlfred Perlstein } 97a79b7128SAlfred Perlstein 98a79b7128SAlfred Perlstein int 99a79b7128SAlfred Perlstein accept_filt_del(char *name) 100a79b7128SAlfred Perlstein { 101a79b7128SAlfred Perlstein struct accept_filter *p; 102a79b7128SAlfred Perlstein 103a79b7128SAlfred Perlstein p = accept_filt_get(name); 104a79b7128SAlfred Perlstein if (p == NULL) 105a79b7128SAlfred Perlstein return (ENOENT); 106a79b7128SAlfred Perlstein 107a79b7128SAlfred Perlstein p->accf_callback = NULL; 108a79b7128SAlfred Perlstein return (0); 109a79b7128SAlfred Perlstein } 110a79b7128SAlfred Perlstein 111a79b7128SAlfred Perlstein struct accept_filter * 112a79b7128SAlfred Perlstein accept_filt_get(char *name) 113a79b7128SAlfred Perlstein { 114a79b7128SAlfred Perlstein struct accept_filter *p; 115a79b7128SAlfred Perlstein 116d087080cSRobert Watson ACCEPT_FILTER_LOCK(); 117a79b7128SAlfred Perlstein SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 118a79b7128SAlfred Perlstein if (strcmp(p->accf_name, name) == 0) 119d087080cSRobert Watson break; 120d087080cSRobert Watson ACCEPT_FILTER_UNLOCK(); 121a79b7128SAlfred Perlstein 122d087080cSRobert Watson return (p); 123a79b7128SAlfred Perlstein } 124a79b7128SAlfred Perlstein 125a79b7128SAlfred Perlstein int 126a79b7128SAlfred Perlstein accept_filt_generic_mod_event(module_t mod, int event, void *data) 127a79b7128SAlfred Perlstein { 128a79b7128SAlfred Perlstein struct accept_filter *p; 129a79b7128SAlfred Perlstein struct accept_filter *accfp = (struct accept_filter *) data; 130d087080cSRobert Watson int error; 131a79b7128SAlfred Perlstein 132a79b7128SAlfred Perlstein switch (event) { 133a79b7128SAlfred Perlstein case MOD_LOAD: 1341ed716a1SRobert Watson MALLOC(p, struct accept_filter *, sizeof(*p), M_ACCF, 1351ed716a1SRobert Watson M_WAITOK); 136a79b7128SAlfred Perlstein bcopy(accfp, p, sizeof(*p)); 137a79b7128SAlfred Perlstein error = accept_filt_add(p); 138a79b7128SAlfred Perlstein break; 139a79b7128SAlfred Perlstein 140a79b7128SAlfred Perlstein case MOD_UNLOAD: 14185f5e7f0SAlfred Perlstein /* 1421ed716a1SRobert Watson * Do not support unloading yet. we don't keep track of 1431ed716a1SRobert Watson * refcounts and unloading an accept filter callback and then 1441ed716a1SRobert Watson * having it called is a bad thing. A simple fix would be to 1451ed716a1SRobert Watson * track the refcount in the struct accept_filter. 14685f5e7f0SAlfred Perlstein */ 14734b94e8bSAlfred Perlstein if (unloadable != 0) { 148a79b7128SAlfred Perlstein error = accept_filt_del(accfp->accf_name); 14934b94e8bSAlfred Perlstein } else 15085f5e7f0SAlfred Perlstein error = EOPNOTSUPP; 151a79b7128SAlfred Perlstein break; 152a79b7128SAlfred Perlstein 153a79b7128SAlfred Perlstein case MOD_SHUTDOWN: 154a79b7128SAlfred Perlstein error = 0; 155a79b7128SAlfred Perlstein break; 156a79b7128SAlfred Perlstein 157a79b7128SAlfred Perlstein default: 158a79b7128SAlfred Perlstein error = EOPNOTSUPP; 159a79b7128SAlfred Perlstein break; 160a79b7128SAlfred Perlstein } 161a79b7128SAlfred Perlstein 162a79b7128SAlfred Perlstein return (error); 163a79b7128SAlfred Perlstein } 16478e43644SRobert Watson 16578e43644SRobert Watson int 166a59f81d2SRobert Watson do_getopt_accept_filter(struct socket *so, struct sockopt *sopt) 167a59f81d2SRobert Watson { 168a59f81d2SRobert Watson struct accept_filter_arg *afap; 169a59f81d2SRobert Watson int error; 170a59f81d2SRobert Watson 171a59f81d2SRobert Watson error = 0; 172a59f81d2SRobert Watson MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, 173a59f81d2SRobert Watson M_WAITOK | M_ZERO); 174a59f81d2SRobert Watson SOCK_LOCK(so); 175a59f81d2SRobert Watson if ((so->so_options & SO_ACCEPTCONN) == 0) { 176a59f81d2SRobert Watson error = EINVAL; 177a59f81d2SRobert Watson goto out; 178a59f81d2SRobert Watson } 179a59f81d2SRobert Watson if ((so->so_options & SO_ACCEPTFILTER) == 0) 180a59f81d2SRobert Watson goto out; 181a59f81d2SRobert Watson strcpy(afap->af_name, so->so_accf->so_accept_filter->accf_name); 182a59f81d2SRobert Watson if (so->so_accf->so_accept_filter_str != NULL) 183a59f81d2SRobert Watson strcpy(afap->af_arg, so->so_accf->so_accept_filter_str); 184a59f81d2SRobert Watson out: 185a59f81d2SRobert Watson SOCK_UNLOCK(so); 186a59f81d2SRobert Watson if (error == 0) 187a59f81d2SRobert Watson error = sooptcopyout(sopt, afap, sizeof(*afap)); 188a59f81d2SRobert Watson FREE(afap, M_TEMP); 189a59f81d2SRobert Watson return (error); 190a59f81d2SRobert Watson } 191a59f81d2SRobert Watson 192a59f81d2SRobert Watson int 19311d06c4bSRobert Watson do_setopt_accept_filter(struct socket *so, struct sockopt *sopt) 19478e43644SRobert Watson { 19578e43644SRobert Watson struct accept_filter_arg *afap; 19678e43644SRobert Watson struct accept_filter *afp; 19778e43644SRobert Watson struct so_accf *newaf; 19878e43644SRobert Watson int error = 0; 19978e43644SRobert Watson 20064c23807SRobert Watson /* 20164c23807SRobert Watson * Handle the simple delete case first. 20264c23807SRobert Watson */ 20364c23807SRobert Watson if (sopt == NULL) { 20464c23807SRobert Watson SOCK_LOCK(so); 20564c23807SRobert Watson if ((so->so_options & SO_ACCEPTCONN) == 0) { 20664c23807SRobert Watson SOCK_UNLOCK(so); 20764c23807SRobert Watson return (EINVAL); 20864c23807SRobert Watson } 20964c23807SRobert Watson if (so->so_accf != NULL) { 21064c23807SRobert Watson struct so_accf *af = so->so_accf; 21164c23807SRobert Watson if (af->so_accept_filter != NULL && 21264c23807SRobert Watson af->so_accept_filter->accf_destroy != NULL) { 21364c23807SRobert Watson af->so_accept_filter->accf_destroy(so); 21464c23807SRobert Watson } 21564c23807SRobert Watson if (af->so_accept_filter_str != NULL) 21664c23807SRobert Watson FREE(af->so_accept_filter_str, M_ACCF); 21764c23807SRobert Watson FREE(af, M_ACCF); 21864c23807SRobert Watson so->so_accf = NULL; 21964c23807SRobert Watson } 22064c23807SRobert Watson so->so_options &= ~SO_ACCEPTFILTER; 22164c23807SRobert Watson SOCK_UNLOCK(so); 22264c23807SRobert Watson return (0); 22364c23807SRobert Watson } 22464c23807SRobert Watson 22578e43644SRobert Watson /* 22692081a83SRobert Watson * Pre-allocate any memory we may need later to avoid blocking at 22792081a83SRobert Watson * untimely moments. This does not optimize for invalid arguments. 22878e43644SRobert Watson */ 22978e43644SRobert Watson MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP, 23078e43644SRobert Watson M_WAITOK); 23178e43644SRobert Watson error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); 23278e43644SRobert Watson afap->af_name[sizeof(afap->af_name)-1] = '\0'; 23378e43644SRobert Watson afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; 23478e43644SRobert Watson if (error) { 23578e43644SRobert Watson FREE(afap, M_TEMP); 23678e43644SRobert Watson return (error); 23778e43644SRobert Watson } 23878e43644SRobert Watson afp = accept_filt_get(afap->af_name); 23978e43644SRobert Watson if (afp == NULL) { 24078e43644SRobert Watson FREE(afap, M_TEMP); 24178e43644SRobert Watson return (ENOENT); 24278e43644SRobert Watson } 24378e43644SRobert Watson /* 24492081a83SRobert Watson * Allocate the new accept filter instance storage. We may 24592081a83SRobert Watson * have to free it again later if we fail to attach it. If 24692081a83SRobert Watson * attached properly, 'newaf' is NULLed to avoid a free() 24792081a83SRobert Watson * while in use. 24878e43644SRobert Watson */ 24978e43644SRobert Watson MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK | 25078e43644SRobert Watson M_ZERO); 25178e43644SRobert Watson if (afp->accf_create != NULL && afap->af_name[0] != '\0') { 25278e43644SRobert Watson int len = strlen(afap->af_name) + 1; 25378e43644SRobert Watson MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF, 25478e43644SRobert Watson M_WAITOK); 25578e43644SRobert Watson strcpy(newaf->so_accept_filter_str, afap->af_name); 25678e43644SRobert Watson } 25778e43644SRobert Watson 25892081a83SRobert Watson /* 25992081a83SRobert Watson * Require a listen socket; don't try to replace an existing filter 26092081a83SRobert Watson * without first removing it. 26192081a83SRobert Watson */ 26278e43644SRobert Watson SOCK_LOCK(so); 26392081a83SRobert Watson if (((so->so_options & SO_ACCEPTCONN) == 0) || 26492081a83SRobert Watson (so->so_accf != NULL)) { 26578e43644SRobert Watson error = EINVAL; 26678e43644SRobert Watson goto out; 26778e43644SRobert Watson } 26892081a83SRobert Watson 26978e43644SRobert Watson /* 27092081a83SRobert Watson * Invoke the accf_create() method of the filter if required. The 27192081a83SRobert Watson * socket mutex is held over this call, so create methods for filters 27292081a83SRobert Watson * can't block. 27378e43644SRobert Watson */ 27478e43644SRobert Watson if (afp->accf_create != NULL) { 27578e43644SRobert Watson newaf->so_accept_filter_arg = 27678e43644SRobert Watson afp->accf_create(so, afap->af_arg); 27778e43644SRobert Watson if (newaf->so_accept_filter_arg == NULL) { 27878e43644SRobert Watson error = EINVAL; 27978e43644SRobert Watson goto out; 28078e43644SRobert Watson } 28178e43644SRobert Watson } 28278e43644SRobert Watson newaf->so_accept_filter = afp; 28378e43644SRobert Watson so->so_accf = newaf; 28478e43644SRobert Watson so->so_options |= SO_ACCEPTFILTER; 28578e43644SRobert Watson newaf = NULL; 28678e43644SRobert Watson out: 28778e43644SRobert Watson SOCK_UNLOCK(so); 28878e43644SRobert Watson if (newaf != NULL) { 28978e43644SRobert Watson if (newaf->so_accept_filter_str != NULL) 29078e43644SRobert Watson FREE(newaf->so_accept_filter_str, M_ACCF); 29178e43644SRobert Watson FREE(newaf, M_ACCF); 29278e43644SRobert Watson } 29378e43644SRobert Watson if (afap != NULL) 29478e43644SRobert Watson FREE(afap, M_TEMP); 29578e43644SRobert Watson return (error); 29678e43644SRobert Watson } 297