uipc_accf.c revision 184205
11553Srgrimes/*- 21553Srgrimes * Copyright (c) 2000 Paycounter, Inc. 31553Srgrimes * Copyright (c) 2005 Robert N. M. Watson 41553Srgrimes * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org> 51553Srgrimes * All rights reserved. 61553Srgrimes * 71553Srgrimes * Redistribution and use in source and binary forms, with or without 81553Srgrimes * modification, are permitted provided that the following conditions 91553Srgrimes * are met: 101553Srgrimes * 1. Redistributions of source code must retain the above copyright 111553Srgrimes * notice, this list of conditions and the following disclaimer. 121553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 131553Srgrimes * notice, this list of conditions and the following disclaimer in the 141553Srgrimes * documentation and/or other materials provided with the distribution. 151553Srgrimes * 161553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 171553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 181553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 191553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 201553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 211553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 221553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 231553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 241553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261553Srgrimes * SUCH DAMAGE. 271553Srgrimes */ 281553Srgrimes 291553Srgrimes#include <sys/cdefs.h> 301553Srgrimes__FBSDID("$FreeBSD: head/sys/kern/uipc_accf.c 184205 2008-10-23 15:53:51Z des $"); 311553Srgrimes 321553Srgrimes#define ACCEPT_FILTER_MOD 33114601Sobrien 341553Srgrimes#include "opt_param.h" 3513977Sphk#include <sys/param.h> 361553Srgrimes#include <sys/systm.h> 371553Srgrimes#include <sys/domain.h> 381553Srgrimes#include <sys/kernel.h> 391553Srgrimes#include <sys/lock.h> 401553Srgrimes#include <sys/malloc.h> 4113977Sphk#include <sys/mbuf.h> 42114601Sobrien#include <sys/module.h> 4329060Scharnier#include <sys/mutex.h> 44114601Sobrien#include <sys/protosw.h> 45114601Sobrien#include <sys/sysctl.h> 461553Srgrimes#include <sys/socket.h> 471553Srgrimes#include <sys/socketvar.h> 481553Srgrimes#include <sys/queue.h> 491553Srgrimes 501553Srgrimesstatic struct mtx accept_filter_mtx; 511553SrgrimesMTX_SYSINIT(accept_filter, &accept_filter_mtx, "accept_filter_mtx", 521553Srgrimes MTX_DEF); 531553Srgrimes#define ACCEPT_FILTER_LOCK() mtx_lock(&accept_filter_mtx) 541553Srgrimes#define ACCEPT_FILTER_UNLOCK() mtx_unlock(&accept_filter_mtx) 5513977Sphk 561553Srgrimesstatic SLIST_HEAD(, accept_filter) accept_filtlsthd = 5713977Sphk SLIST_HEAD_INITIALIZER(&accept_filtlsthd); 5820287Swollman 591553SrgrimesMALLOC_DEFINE(M_ACCF, "accf", "accept filter data"); 601553Srgrimes 611553Srgrimesstatic int unloadable = 0; 621553Srgrimes 6396235SwpaulSYSCTL_DECL(_net_inet); /* XXX: some header should do this for me */ 6496202SkbyancSYSCTL_NODE(_net_inet, OID_AUTO, accf, CTLFLAG_RW, 0, "Accept filters"); 651553SrgrimesSYSCTL_INT(_net_inet_accf, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0, 661553Srgrimes "Allow unload of accept filters (not recommended)"); 671553Srgrimes 681553Srgrimes/* 691553Srgrimes * Must be passed a malloc'd structure so we don't explode if the kld is 701553Srgrimes * unloaded, we leak the struct on deallocation to deal with this, but if a 71108314Sru * filter is loaded with the same name as a leaked one we re-use the entry. 7229060Scharnier */ 7329060Scharnierint 741553Srgrimesaccept_filt_add(struct accept_filter *filt) 751553Srgrimes{ 7629060Scharnier struct accept_filter *p; 771553Srgrimes 7813977Sphk ACCEPT_FILTER_LOCK(); 7993590Smike SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 8029060Scharnier if (strcmp(p->accf_name, filt->accf_name) == 0) { 8113977Sphk if (p->accf_callback != NULL) { 821553Srgrimes ACCEPT_FILTER_UNLOCK(); 83128181Sluigi return (EEXIST); 84128192Sluigi } else { 8513977Sphk p->accf_callback = filt->accf_callback; 86128181Sluigi ACCEPT_FILTER_UNLOCK(); 87128181Sluigi free(filt, M_ACCF); 88128181Sluigi return (0); 89128181Sluigi } 90128192Sluigi } 91128181Sluigi 92128181Sluigi if (p == NULL) 93147172Sru SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next); 94128181Sluigi ACCEPT_FILTER_UNLOCK(); 95128192Sluigi return (0); 96147172Sru} 97147172Sru 98147172Sruint 99147172Sruaccept_filt_del(char *name) 100128181Sluigi{ 10131145Sjulian struct accept_filter *p; 102117729Syar 1031553Srgrimes p = accept_filt_get(name); 104203919Sru if (p == NULL) 105203919Sru return (ENOENT); 106128192Sluigi 10731145Sjulian p->accf_callback = NULL; 10831145Sjulian return (0); 10931145Sjulian} 11031145Sjulian 11131145Sjulianstruct accept_filter * 11231145Sjulianaccept_filt_get(char *name) 11331145Sjulian{ 11431145Sjulian struct accept_filter *p; 11531145Sjulian 11613977Sphk ACCEPT_FILTER_LOCK(); 11777870Sru SLIST_FOREACH(p, &accept_filtlsthd, accf_next) 1181553Srgrimes if (strcmp(p->accf_name, name) == 0) 11931145Sjulian break; 12031145Sjulian ACCEPT_FILTER_UNLOCK(); 121128181Sluigi 1221553Srgrimes return (p); 123117729Syar} 124196216Sremko 1251553Srgrimesint 12631145Sjulianaccept_filt_generic_mod_event(module_t mod, int event, void *data) 12731145Sjulian{ 1281553Srgrimes struct accept_filter *p; 12931145Sjulian struct accept_filter *accfp = (struct accept_filter *) data; 13031145Sjulian int error; 1311553Srgrimes 1321553Srgrimes switch (event) { 13331145Sjulian case MOD_LOAD: 13413977Sphk p = malloc(sizeof(*p), M_ACCF, 13531145Sjulian M_WAITOK); 13631145Sjulian bcopy(accfp, p, sizeof(*p)); 1371553Srgrimes error = accept_filt_add(p); 13831145Sjulian break; 13931145Sjulian 1409874Sjkh case MOD_UNLOAD: 14131145Sjulian /* 14231145Sjulian * Do not support unloading yet. we don't keep track of 143117729Syar * refcounts and unloading an accept filter callback and then 144117729Syar * having it called is a bad thing. A simple fix would be to 145117729Syar * track the refcount in the struct accept_filter. 1461553Srgrimes */ 1471553Srgrimes if (unloadable != 0) { 1481553Srgrimes error = accept_filt_del(accfp->accf_name); 1491553Srgrimes } else 15031145Sjulian error = EOPNOTSUPP; 15131145Sjulian break; 15231145Sjulian 15331145Sjulian case MOD_SHUTDOWN: 15431145Sjulian error = 0; 155117729Syar break; 156154162Sbrooks 157117729Syar default: 158117729Syar error = EOPNOTSUPP; 159117729Syar break; 160117729Syar } 161117729Syar 162117729Syar return (error); 163117729Syar} 164117729Syar 16531145Sjulianint 16631145Sjuliando_getopt_accept_filter(struct socket *so, struct sockopt *sopt) 16731145Sjulian{ 16831145Sjulian struct accept_filter_arg *afap; 16931145Sjulian int error; 17031145Sjulian 17131145Sjulian error = 0; 17231145Sjulian afap = malloc(sizeof(*afap), M_TEMP, 17331145Sjulian M_WAITOK | M_ZERO); 174147172Sru SOCK_LOCK(so); 17531145Sjulian if ((so->so_options & SO_ACCEPTCONN) == 0) { 17631145Sjulian error = EINVAL; 17731145Sjulian goto out; 17831145Sjulian } 17977870Sru if ((so->so_options & SO_ACCEPTFILTER) == 0) { 18031145Sjulian error = EINVAL; 18131145Sjulian goto out; 182147172Sru } 18331145Sjulian strcpy(afap->af_name, so->so_accf->so_accept_filter->accf_name); 18431145Sjulian if (so->so_accf->so_accept_filter_str != NULL) 18531145Sjulian strcpy(afap->af_arg, so->so_accf->so_accept_filter_str); 18631145Sjulianout: 18731145Sjulian SOCK_UNLOCK(so); 18831145Sjulian if (error == 0) 18931145Sjulian error = sooptcopyout(sopt, afap, sizeof(*afap)); 19031145Sjulian free(afap, M_TEMP); 191128192Sluigi return (error); 192128192Sluigi} 193128192Sluigi 194128192Sluigiint 195128192Sluigido_setopt_accept_filter(struct socket *so, struct sockopt *sopt) 19631145Sjulian{ 197128192Sluigi struct accept_filter_arg *afap; 19831145Sjulian struct accept_filter *afp; 19931145Sjulian struct so_accf *newaf; 20031145Sjulian int error = 0; 20131145Sjulian 20231145Sjulian /* 20331145Sjulian * Handle the simple delete case first. 20431145Sjulian */ 20531145Sjulian if (sopt == NULL || sopt->sopt_val == NULL) { 20631145Sjulian SOCK_LOCK(so); 207147172Sru if ((so->so_options & SO_ACCEPTCONN) == 0) { 2081553Srgrimes SOCK_UNLOCK(so); 2091553Srgrimes return (EINVAL); 2101553Srgrimes } 2111553Srgrimes if (so->so_accf != NULL) { 2121553Srgrimes struct so_accf *af = so->so_accf; 213128181Sluigi if (af->so_accept_filter != NULL && 21413977Sphk af->so_accept_filter->accf_destroy != NULL) { 2151553Srgrimes af->so_accept_filter->accf_destroy(so); 2161553Srgrimes } 2171553Srgrimes if (af->so_accept_filter_str != NULL) 218109413Sru free(af->so_accept_filter_str, M_ACCF); 2191553Srgrimes free(af, M_ACCF); 22029060Scharnier so->so_accf = NULL; 221128054Smux } 2221553Srgrimes so->so_options &= ~SO_ACCEPTFILTER; 2231553Srgrimes SOCK_UNLOCK(so); 2241553Srgrimes return (0); 2251553Srgrimes } 2261553Srgrimes 2271553Srgrimes /* 228167260Skevlo * Pre-allocate any memory we may need later to avoid blocking at 229109413Sru * untimely moments. This does not optimize for invalid arguments. 230109413Sru */ 231109413Sru afap = malloc(sizeof(*afap), M_TEMP, 232111910Sru M_WAITOK); 233108314Sru error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap); 234109413Sru afap->af_name[sizeof(afap->af_name)-1] = '\0'; 23556134Sjkh afap->af_arg[sizeof(afap->af_arg)-1] = '\0'; 2361553Srgrimes if (error) { 23729060Scharnier free(afap, M_TEMP); 2381553Srgrimes return (error); 2391553Srgrimes } 2401553Srgrimes afp = accept_filt_get(afap->af_name); 2411553Srgrimes if (afp == NULL) { 2421553Srgrimes free(afap, M_TEMP); 2431553Srgrimes return (ENOENT); 2441553Srgrimes } 2451553Srgrimes /* 2461553Srgrimes * Allocate the new accept filter instance storage. We may 2471553Srgrimes * have to free it again later if we fail to attach it. If 2481553Srgrimes * attached properly, 'newaf' is NULLed to avoid a free() 249128192Sluigi * while in use. 250128192Sluigi */ 251128192Sluigi newaf = malloc(sizeof(*newaf), M_ACCF, M_WAITOK | 252128192Sluigi M_ZERO); 253128192Sluigi if (afp->accf_create != NULL && afap->af_name[0] != '\0') { 254128192Sluigi int len = strlen(afap->af_name) + 1; 255128192Sluigi newaf->so_accept_filter_str = malloc(len, M_ACCF, 256128192Sluigi M_WAITOK); 257128192Sluigi strcpy(newaf->so_accept_filter_str, afap->af_name); 258128192Sluigi } 259128192Sluigi 260128192Sluigi /* 261128192Sluigi * Require a listen socket; don't try to replace an existing filter 262128192Sluigi * without first removing it. 263128192Sluigi */ 264128192Sluigi SOCK_LOCK(so); 265128192Sluigi if (((so->so_options & SO_ACCEPTCONN) == 0) || 266147172Sru (so->so_accf != NULL)) { 267128192Sluigi error = EINVAL; 268128192Sluigi goto out; 269128192Sluigi } 270128192Sluigi 271147172Sru /* 272128192Sluigi * Invoke the accf_create() method of the filter if required. The 273128192Sluigi * socket mutex is held over this call, so create methods for filters 274128192Sluigi * can't block. 275147172Sru */ 276128192Sluigi if (afp->accf_create != NULL) { 277128192Sluigi newaf->so_accept_filter_arg = 278128192Sluigi afp->accf_create(so, afap->af_arg); 279128192Sluigi if (newaf->so_accept_filter_arg == NULL) { 280147172Sru error = EINVAL; 281128192Sluigi goto out; 282128192Sluigi } 283128192Sluigi } 284128192Sluigi newaf->so_accept_filter = afp; 285128192Sluigi so->so_accf = newaf; 286128192Sluigi so->so_options |= SO_ACCEPTFILTER; 287128192Sluigi newaf = NULL; 288151989Sthompsaout: 289147172Sru SOCK_UNLOCK(so); 290147172Sru if (newaf != NULL) { 291147172Sru if (newaf->so_accept_filter_str != NULL) 292128192Sluigi free(newaf->so_accept_filter_str, M_ACCF); 293128192Sluigi free(newaf, M_ACCF); 294128192Sluigi } 295128192Sluigi if (afap != NULL) 2968857Srgrimes free(afap, M_TEMP); 2971553Srgrimes return (error); 298128192Sluigi} 29913977Sphk