uipc_accf.c revision 142060
1139804Simp/*-
265534Salfred * Copyright (c) 2000 Paycounter, Inc.
365534Salfred * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org>
461837Salfred * All rights reserved.
561837Salfred *
661837Salfred * Redistribution and use in source and binary forms, with or without
761837Salfred * modification, are permitted provided that the following conditions
861837Salfred * are met:
961837Salfred * 1. Redistributions of source code must retain the above copyright
1061837Salfred *    notice, this list of conditions and the following disclaimer.
1161837Salfred * 2. Redistributions in binary form must reproduce the above copyright
1261837Salfred *    notice, this list of conditions and the following disclaimer in the
1361837Salfred *    documentation and/or other materials provided with the distribution.
1461837Salfred *
1561837Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1661837Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1761837Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1861837Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1961837Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2061837Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2161837Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2261837Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2361837Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2461837Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2561837Salfred * SUCH DAMAGE.
2661837Salfred */
2761837Salfred
28116182Sobrien#include <sys/cdefs.h>
29116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/uipc_accf.c 142060 2005-02-18 19:01:22Z rwatson $");
30116182Sobrien
3161837Salfred#define ACCEPT_FILTER_MOD
3261837Salfred
3377598Sjesper#include "opt_param.h"
3461837Salfred#include <sys/param.h>
3561837Salfred#include <sys/systm.h>
3661837Salfred#include <sys/domain.h>
3761837Salfred#include <sys/kernel.h>
38129920Srwatson#include <sys/lock.h>
3961837Salfred#include <sys/malloc.h>
4061837Salfred#include <sys/mbuf.h>
41129876Sphk#include <sys/module.h>
42129920Srwatson#include <sys/mutex.h>
4361837Salfred#include <sys/protosw.h>
4465534Salfred#include <sys/sysctl.h>
4561837Salfred#include <sys/socket.h>
4661837Salfred#include <sys/socketvar.h>
4761837Salfred#include <sys/queue.h>
4861837Salfred
49129920Srwatsonstatic struct mtx accept_filter_mtx;
50129920SrwatsonMTX_SYSINIT(accept_filter, &accept_filter_mtx, "accept_filter_mtx",
51129920Srwatson	MTX_DEF);
52129920Srwatson#define	ACCEPT_FILTER_LOCK()	mtx_lock(&accept_filter_mtx)
53129920Srwatson#define	ACCEPT_FILTER_UNLOCK()	mtx_unlock(&accept_filter_mtx)
54129920Srwatson
5561837Salfredstatic SLIST_HEAD(, accept_filter) accept_filtlsthd =
5661837Salfred	SLIST_HEAD_INITIALIZER(&accept_filtlsthd);
5761837Salfred
5861837SalfredMALLOC_DEFINE(M_ACCF, "accf", "accept filter data");
5961837Salfred
6065534Salfredstatic int unloadable = 0;
6165534Salfred
6265534SalfredSYSCTL_DECL(_net_inet);	/* XXX: some header should do this for me */
6365534SalfredSYSCTL_NODE(_net_inet, OID_AUTO, accf, CTLFLAG_RW, 0, "Accept filters");
6465534SalfredSYSCTL_INT(_net_inet_accf, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0,
6565534Salfred	"Allow unload of accept filters (not recommended)");
6665534Salfred
6761837Salfred/*
68142056Srwatson * Must be passed a malloc'd structure so we don't explode if the kld is
69142056Srwatson * unloaded, we leak the struct on deallocation to deal with this, but if a
70142056Srwatson * filter is loaded with the same name as a leaked one we re-use the entry.
7161837Salfred */
7261837Salfredint
7361837Salfredaccept_filt_add(struct accept_filter *filt)
7461837Salfred{
7561837Salfred	struct accept_filter *p;
7661837Salfred
77129920Srwatson	ACCEPT_FILTER_LOCK();
7861837Salfred	SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
7961837Salfred		if (strcmp(p->accf_name, filt->accf_name) == 0)  {
8061837Salfred			if (p->accf_callback != NULL) {
81129920Srwatson				ACCEPT_FILTER_UNLOCK();
8261837Salfred				return (EEXIST);
8361837Salfred			} else {
8461837Salfred				p->accf_callback = filt->accf_callback;
85129920Srwatson				ACCEPT_FILTER_UNLOCK();
8661837Salfred				FREE(filt, M_ACCF);
8761837Salfred				return (0);
8861837Salfred			}
8961837Salfred		}
9061837Salfred
9161837Salfred	if (p == NULL)
9261837Salfred		SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next);
93129920Srwatson	ACCEPT_FILTER_UNLOCK();
9461837Salfred	return (0);
9561837Salfred}
9661837Salfred
9761837Salfredint
9861837Salfredaccept_filt_del(char *name)
9961837Salfred{
10061837Salfred	struct accept_filter *p;
10161837Salfred
10261837Salfred	p = accept_filt_get(name);
10361837Salfred	if (p == NULL)
10461837Salfred		return (ENOENT);
10561837Salfred
10661837Salfred	p->accf_callback = NULL;
10761837Salfred	return (0);
10861837Salfred}
10961837Salfred
11061837Salfredstruct accept_filter *
11161837Salfredaccept_filt_get(char *name)
11261837Salfred{
11361837Salfred	struct accept_filter *p;
11461837Salfred
115129920Srwatson	ACCEPT_FILTER_LOCK();
11661837Salfred	SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
11761837Salfred		if (strcmp(p->accf_name, name) == 0)
118129920Srwatson			break;
119129920Srwatson	ACCEPT_FILTER_UNLOCK();
12061837Salfred
121129920Srwatson	return (p);
12261837Salfred}
12361837Salfred
12461837Salfredint
12561837Salfredaccept_filt_generic_mod_event(module_t mod, int event, void *data)
12661837Salfred{
12761837Salfred	struct accept_filter *p;
12861837Salfred	struct accept_filter *accfp = (struct accept_filter *) data;
129142060Srwatson	int error;
13061837Salfred
13161837Salfred	switch (event) {
13261837Salfred	case MOD_LOAD:
133142056Srwatson		MALLOC(p, struct accept_filter *, sizeof(*p), M_ACCF,
134142056Srwatson		    M_WAITOK);
13561837Salfred		bcopy(accfp, p, sizeof(*p));
13661837Salfred		error = accept_filt_add(p);
13761837Salfred		break;
13861837Salfred
13961837Salfred	case MOD_UNLOAD:
14063645Salfred		/*
141142056Srwatson		 * Do not support unloading yet. we don't keep track of
142142056Srwatson		 * refcounts and unloading an accept filter callback and then
143142056Srwatson		 * having it called is a bad thing.  A simple fix would be to
144142056Srwatson		 * track the refcount in the struct accept_filter.
14563645Salfred		 */
14665534Salfred		if (unloadable != 0) {
14765534Salfred			error = accept_filt_del(accfp->accf_name);
14865534Salfred		} else
14965534Salfred			error = EOPNOTSUPP;
15061837Salfred		break;
15161837Salfred
15261837Salfred	case MOD_SHUTDOWN:
15361837Salfred		error = 0;
15461837Salfred		break;
15561837Salfred
15661837Salfred	default:
15761837Salfred		error = EOPNOTSUPP;
15861837Salfred		break;
15961837Salfred	}
16061837Salfred
16161837Salfred	return (error);
16261837Salfred}
163142058Srwatson
164142058Srwatsonint
165142060Srwatsondo_setopt_accept_filter(struct socket *so, struct sockopt *sopt)
166142058Srwatson{
167142060Srwatson	struct accept_filter_arg *afap;
168142060Srwatson	struct accept_filter *afp;
169142060Srwatson	struct so_accf *newaf;
170142060Srwatson	int error = 0;
171142058Srwatson
172142058Srwatson	newaf = NULL;
173142058Srwatson	afap = NULL;
174142058Srwatson
175142058Srwatson	/*
176142058Srwatson	 * XXXRW: Configuring accept filters should be an atomic test-and-set
177142058Srwatson	 * operation to prevent races during setup and attach.  There may be
178142058Srwatson	 * more general issues of racing and ordering here that are not yet
179142058Srwatson	 * addressed by locking.
180142058Srwatson	 */
181142058Srwatson	/* do not set/remove accept filters on non listen sockets */
182142058Srwatson	SOCK_LOCK(so);
183142058Srwatson	if ((so->so_options & SO_ACCEPTCONN) == 0) {
184142058Srwatson		SOCK_UNLOCK(so);
185142058Srwatson		return (EINVAL);
186142058Srwatson	}
187142058Srwatson
188142058Srwatson	/* removing the filter */
189142058Srwatson	if (sopt == NULL) {
190142058Srwatson		if (so->so_accf != NULL) {
191142058Srwatson			struct so_accf *af = so->so_accf;
192142058Srwatson			if (af->so_accept_filter != NULL &&
193142058Srwatson				af->so_accept_filter->accf_destroy != NULL) {
194142058Srwatson				af->so_accept_filter->accf_destroy(so);
195142058Srwatson			}
196142058Srwatson			if (af->so_accept_filter_str != NULL) {
197142058Srwatson				FREE(af->so_accept_filter_str, M_ACCF);
198142058Srwatson			}
199142058Srwatson			FREE(af, M_ACCF);
200142058Srwatson			so->so_accf = NULL;
201142058Srwatson		}
202142058Srwatson		so->so_options &= ~SO_ACCEPTFILTER;
203142058Srwatson		SOCK_UNLOCK(so);
204142058Srwatson		return (0);
205142058Srwatson	}
206142058Srwatson	SOCK_UNLOCK(so);
207142058Srwatson
208142058Srwatson	/*-
209142058Srwatson	 * Adding a filter.
210142058Srwatson	 *
211142058Srwatson	 * Do memory allocation, copyin, and filter lookup now while we're
212142058Srwatson	 * not holding any locks.  Avoids sleeping with a mutex, as well as
213142058Srwatson	 * introducing a lock order between accept filter locks and socket
214142058Srwatson	 * locks here.
215142058Srwatson	 */
216142058Srwatson	MALLOC(afap, struct accept_filter_arg *, sizeof(*afap), M_TEMP,
217142058Srwatson	    M_WAITOK);
218142058Srwatson	/* don't put large objects on the kernel stack */
219142058Srwatson	error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap);
220142058Srwatson	afap->af_name[sizeof(afap->af_name)-1] = '\0';
221142058Srwatson	afap->af_arg[sizeof(afap->af_arg)-1] = '\0';
222142058Srwatson	if (error) {
223142058Srwatson		FREE(afap, M_TEMP);
224142058Srwatson		return (error);
225142058Srwatson	}
226142058Srwatson	afp = accept_filt_get(afap->af_name);
227142058Srwatson	if (afp == NULL) {
228142058Srwatson		FREE(afap, M_TEMP);
229142058Srwatson		return (ENOENT);
230142058Srwatson	}
231142058Srwatson
232142058Srwatson	/*
233142058Srwatson	 * Allocate the new accept filter instance storage.  We may have to
234142058Srwatson	 * free it again later if we fail to attach it.  If attached
235142058Srwatson	 * properly, 'newaf' is NULLed to avoid a free() while in use.
236142058Srwatson	 */
237142058Srwatson	MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK |
238142058Srwatson	    M_ZERO);
239142058Srwatson	if (afp->accf_create != NULL && afap->af_name[0] != '\0') {
240142058Srwatson		int len = strlen(afap->af_name) + 1;
241142058Srwatson		MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF,
242142058Srwatson		    M_WAITOK);
243142058Srwatson		strcpy(newaf->so_accept_filter_str, afap->af_name);
244142058Srwatson	}
245142058Srwatson
246142058Srwatson	SOCK_LOCK(so);
247142058Srwatson	/* must remove previous filter first */
248142058Srwatson	if (so->so_accf != NULL) {
249142058Srwatson		error = EINVAL;
250142058Srwatson		goto out;
251142058Srwatson	}
252142058Srwatson	/*
253142058Srwatson	 * Invoke the accf_create() method of the filter if required.
254142058Srwatson	 * XXXRW: the socket mutex is held over this call, so the create
255142058Srwatson	 * method cannot block.  This may be something we have to change, but
256142058Srwatson	 * it would require addressing possible races.
257142058Srwatson	 */
258142058Srwatson	if (afp->accf_create != NULL) {
259142058Srwatson		newaf->so_accept_filter_arg =
260142058Srwatson		    afp->accf_create(so, afap->af_arg);
261142058Srwatson		if (newaf->so_accept_filter_arg == NULL) {
262142058Srwatson			error = EINVAL;
263142058Srwatson			goto out;
264142058Srwatson		}
265142058Srwatson	}
266142058Srwatson	newaf->so_accept_filter = afp;
267142058Srwatson	so->so_accf = newaf;
268142058Srwatson	so->so_options |= SO_ACCEPTFILTER;
269142058Srwatson	newaf = NULL;
270142058Srwatsonout:
271142058Srwatson	SOCK_UNLOCK(so);
272142058Srwatson	if (newaf != NULL) {
273142058Srwatson		if (newaf->so_accept_filter_str != NULL)
274142058Srwatson			FREE(newaf->so_accept_filter_str, M_ACCF);
275142058Srwatson		FREE(newaf, M_ACCF);
276142058Srwatson	}
277142058Srwatson	if (afap != NULL)
278142058Srwatson		FREE(afap, M_TEMP);
279142058Srwatson	return (error);
280142058Srwatson}
281