1139804Simp/*-
265534Salfred * Copyright (c) 2000 Paycounter, Inc.
3143427Srwatson * Copyright (c) 2005 Robert N. M. Watson
465534Salfred * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org>
561837Salfred * All rights reserved.
661837Salfred *
761837Salfred * Redistribution and use in source and binary forms, with or without
861837Salfred * modification, are permitted provided that the following conditions
961837Salfred * are met:
1061837Salfred * 1. Redistributions of source code must retain the above copyright
1161837Salfred *    notice, this list of conditions and the following disclaimer.
1261837Salfred * 2. Redistributions in binary form must reproduce the above copyright
1361837Salfred *    notice, this list of conditions and the following disclaimer in the
1461837Salfred *    documentation and/or other materials provided with the distribution.
1561837Salfred *
1661837Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1761837Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1861837Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1961837Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2061837Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2161837Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2261837Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2361837Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2461837Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2561837Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2661837Salfred * SUCH DAMAGE.
2761837Salfred */
2861837Salfred
29116182Sobrien#include <sys/cdefs.h>
30116182Sobrien__FBSDID("$FreeBSD$");
31116182Sobrien
3261837Salfred#define ACCEPT_FILTER_MOD
3361837Salfred
3477598Sjesper#include "opt_param.h"
3561837Salfred#include <sys/param.h>
3661837Salfred#include <sys/systm.h>
3761837Salfred#include <sys/domain.h>
3861837Salfred#include <sys/kernel.h>
39129920Srwatson#include <sys/lock.h>
4061837Salfred#include <sys/malloc.h>
4161837Salfred#include <sys/mbuf.h>
42129876Sphk#include <sys/module.h>
43129920Srwatson#include <sys/mutex.h>
4461837Salfred#include <sys/protosw.h>
4565534Salfred#include <sys/sysctl.h>
4661837Salfred#include <sys/socket.h>
4761837Salfred#include <sys/socketvar.h>
4861837Salfred#include <sys/queue.h>
4961837Salfred
50129920Srwatsonstatic struct mtx accept_filter_mtx;
51129920SrwatsonMTX_SYSINIT(accept_filter, &accept_filter_mtx, "accept_filter_mtx",
52129920Srwatson	MTX_DEF);
53129920Srwatson#define	ACCEPT_FILTER_LOCK()	mtx_lock(&accept_filter_mtx)
54129920Srwatson#define	ACCEPT_FILTER_UNLOCK()	mtx_unlock(&accept_filter_mtx)
55129920Srwatson
5661837Salfredstatic SLIST_HEAD(, accept_filter) accept_filtlsthd =
57201145Santoine	SLIST_HEAD_INITIALIZER(accept_filtlsthd);
5861837Salfred
5961837SalfredMALLOC_DEFINE(M_ACCF, "accf", "accept filter data");
6061837Salfred
6165534Salfredstatic int unloadable = 0;
6265534Salfred
6365534SalfredSYSCTL_DECL(_net_inet);	/* XXX: some header should do this for me */
6465534SalfredSYSCTL_NODE(_net_inet, OID_AUTO, accf, CTLFLAG_RW, 0, "Accept filters");
6565534SalfredSYSCTL_INT(_net_inet_accf, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0,
6665534Salfred	"Allow unload of accept filters (not recommended)");
6765534Salfred
6861837Salfred/*
69142056Srwatson * Must be passed a malloc'd structure so we don't explode if the kld is
70142056Srwatson * unloaded, we leak the struct on deallocation to deal with this, but if a
71142056Srwatson * filter is loaded with the same name as a leaked one we re-use the entry.
7261837Salfred */
7361837Salfredint
7461837Salfredaccept_filt_add(struct accept_filter *filt)
7561837Salfred{
7661837Salfred	struct accept_filter *p;
7761837Salfred
78129920Srwatson	ACCEPT_FILTER_LOCK();
7961837Salfred	SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
8061837Salfred		if (strcmp(p->accf_name, filt->accf_name) == 0)  {
8161837Salfred			if (p->accf_callback != NULL) {
82129920Srwatson				ACCEPT_FILTER_UNLOCK();
8361837Salfred				return (EEXIST);
8461837Salfred			} else {
8561837Salfred				p->accf_callback = filt->accf_callback;
86129920Srwatson				ACCEPT_FILTER_UNLOCK();
87184205Sdes				free(filt, M_ACCF);
8861837Salfred				return (0);
8961837Salfred			}
9061837Salfred		}
9161837Salfred
9261837Salfred	if (p == NULL)
9361837Salfred		SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next);
94129920Srwatson	ACCEPT_FILTER_UNLOCK();
9561837Salfred	return (0);
9661837Salfred}
9761837Salfred
9861837Salfredint
9961837Salfredaccept_filt_del(char *name)
10061837Salfred{
10161837Salfred	struct accept_filter *p;
10261837Salfred
10361837Salfred	p = accept_filt_get(name);
10461837Salfred	if (p == NULL)
10561837Salfred		return (ENOENT);
10661837Salfred
10761837Salfred	p->accf_callback = NULL;
10861837Salfred	return (0);
10961837Salfred}
11061837Salfred
11161837Salfredstruct accept_filter *
11261837Salfredaccept_filt_get(char *name)
11361837Salfred{
11461837Salfred	struct accept_filter *p;
11561837Salfred
116129920Srwatson	ACCEPT_FILTER_LOCK();
11761837Salfred	SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
11861837Salfred		if (strcmp(p->accf_name, name) == 0)
119129920Srwatson			break;
120129920Srwatson	ACCEPT_FILTER_UNLOCK();
12161837Salfred
122129920Srwatson	return (p);
12361837Salfred}
12461837Salfred
12561837Salfredint
12661837Salfredaccept_filt_generic_mod_event(module_t mod, int event, void *data)
12761837Salfred{
12861837Salfred	struct accept_filter *p;
12961837Salfred	struct accept_filter *accfp = (struct accept_filter *) data;
130142060Srwatson	int error;
13161837Salfred
13261837Salfred	switch (event) {
13361837Salfred	case MOD_LOAD:
134184205Sdes		p = malloc(sizeof(*p), M_ACCF,
135142056Srwatson		    M_WAITOK);
13661837Salfred		bcopy(accfp, p, sizeof(*p));
13761837Salfred		error = accept_filt_add(p);
13861837Salfred		break;
13961837Salfred
14061837Salfred	case MOD_UNLOAD:
14163645Salfred		/*
142142056Srwatson		 * Do not support unloading yet. we don't keep track of
143142056Srwatson		 * refcounts and unloading an accept filter callback and then
144142056Srwatson		 * having it called is a bad thing.  A simple fix would be to
145142056Srwatson		 * track the refcount in the struct accept_filter.
14663645Salfred		 */
14765534Salfred		if (unloadable != 0) {
14865534Salfred			error = accept_filt_del(accfp->accf_name);
14965534Salfred		} else
15065534Salfred			error = EOPNOTSUPP;
15161837Salfred		break;
15261837Salfred
15361837Salfred	case MOD_SHUTDOWN:
15461837Salfred		error = 0;
15561837Salfred		break;
15661837Salfred
15761837Salfred	default:
15861837Salfred		error = EOPNOTSUPP;
15961837Salfred		break;
16061837Salfred	}
16161837Salfred
16261837Salfred	return (error);
16361837Salfred}
164142058Srwatson
165142058Srwatsonint
166143463Srwatsondo_getopt_accept_filter(struct socket *so, struct sockopt *sopt)
167143463Srwatson{
168143463Srwatson	struct accept_filter_arg *afap;
169143463Srwatson	int error;
170143463Srwatson
171143463Srwatson	error = 0;
172184205Sdes	afap = malloc(sizeof(*afap), M_TEMP,
173143463Srwatson	    M_WAITOK | M_ZERO);
174143463Srwatson	SOCK_LOCK(so);
175143463Srwatson	if ((so->so_options & SO_ACCEPTCONN) == 0) {
176143463Srwatson		error = EINVAL;
177143463Srwatson		goto out;
178143463Srwatson	}
179147300Smaxim	if ((so->so_options & SO_ACCEPTFILTER) == 0) {
180147300Smaxim		error = EINVAL;
181143463Srwatson		goto out;
182147300Smaxim	}
183143463Srwatson	strcpy(afap->af_name, so->so_accf->so_accept_filter->accf_name);
184143463Srwatson	if (so->so_accf->so_accept_filter_str != NULL)
185143463Srwatson		strcpy(afap->af_arg, so->so_accf->so_accept_filter_str);
186143463Srwatsonout:
187143463Srwatson	SOCK_UNLOCK(so);
188143463Srwatson	if (error == 0)
189143463Srwatson		error = sooptcopyout(sopt, afap, sizeof(*afap));
190184205Sdes	free(afap, M_TEMP);
191143463Srwatson	return (error);
192143463Srwatson}
193143463Srwatson
194143463Srwatsonint
195142060Srwatsondo_setopt_accept_filter(struct socket *so, struct sockopt *sopt)
196142058Srwatson{
197142060Srwatson	struct accept_filter_arg *afap;
198142060Srwatson	struct accept_filter *afp;
199142060Srwatson	struct so_accf *newaf;
200142060Srwatson	int error = 0;
201142058Srwatson
202142058Srwatson	/*
203143427Srwatson	 * Handle the simple delete case first.
204142058Srwatson	 */
205147300Smaxim	if (sopt == NULL || sopt->sopt_val == NULL) {
206143427Srwatson		SOCK_LOCK(so);
207143427Srwatson		if ((so->so_options & SO_ACCEPTCONN) == 0) {
208143427Srwatson			SOCK_UNLOCK(so);
209143427Srwatson			return (EINVAL);
210143427Srwatson		}
211142058Srwatson		if (so->so_accf != NULL) {
212142058Srwatson			struct so_accf *af = so->so_accf;
213142058Srwatson			if (af->so_accept_filter != NULL &&
214142058Srwatson				af->so_accept_filter->accf_destroy != NULL) {
215142058Srwatson				af->so_accept_filter->accf_destroy(so);
216142058Srwatson			}
217143427Srwatson			if (af->so_accept_filter_str != NULL)
218184205Sdes				free(af->so_accept_filter_str, M_ACCF);
219184205Sdes			free(af, M_ACCF);
220142058Srwatson			so->so_accf = NULL;
221142058Srwatson		}
222142058Srwatson		so->so_options &= ~SO_ACCEPTFILTER;
223142058Srwatson		SOCK_UNLOCK(so);
224142058Srwatson		return (0);
225142058Srwatson	}
226143427Srwatson
227143427Srwatson	/*
228143461Srwatson	 * Pre-allocate any memory we may need later to avoid blocking at
229143461Srwatson	 * untimely moments.  This does not optimize for invalid arguments.
230143427Srwatson	 */
231184205Sdes	afap = malloc(sizeof(*afap), M_TEMP,
232142058Srwatson	    M_WAITOK);
233142058Srwatson	error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap);
234142058Srwatson	afap->af_name[sizeof(afap->af_name)-1] = '\0';
235142058Srwatson	afap->af_arg[sizeof(afap->af_arg)-1] = '\0';
236142058Srwatson	if (error) {
237184205Sdes		free(afap, M_TEMP);
238142058Srwatson		return (error);
239142058Srwatson	}
240142058Srwatson	afp = accept_filt_get(afap->af_name);
241142058Srwatson	if (afp == NULL) {
242184205Sdes		free(afap, M_TEMP);
243142058Srwatson		return (ENOENT);
244142058Srwatson	}
245142058Srwatson	/*
246143461Srwatson	 * Allocate the new accept filter instance storage.  We may
247143461Srwatson	 * have to free it again later if we fail to attach it.  If
248143461Srwatson	 * attached properly, 'newaf' is NULLed to avoid a free()
249143461Srwatson	 * while in use.
250142058Srwatson	 */
251184205Sdes	newaf = malloc(sizeof(*newaf), M_ACCF, M_WAITOK |
252142058Srwatson	    M_ZERO);
253142058Srwatson	if (afp->accf_create != NULL && afap->af_name[0] != '\0') {
254142058Srwatson		int len = strlen(afap->af_name) + 1;
255184205Sdes		newaf->so_accept_filter_str = malloc(len, M_ACCF,
256142058Srwatson		    M_WAITOK);
257142058Srwatson		strcpy(newaf->so_accept_filter_str, afap->af_name);
258142058Srwatson	}
259142058Srwatson
260143461Srwatson	/*
261143461Srwatson	 * Require a listen socket; don't try to replace an existing filter
262143461Srwatson	 * without first removing it.
263143461Srwatson	 */
264142058Srwatson	SOCK_LOCK(so);
265143461Srwatson	if (((so->so_options & SO_ACCEPTCONN) == 0) ||
266143461Srwatson	    (so->so_accf != NULL)) {
267142058Srwatson		error = EINVAL;
268142058Srwatson		goto out;
269142058Srwatson	}
270143461Srwatson
271142058Srwatson	/*
272143461Srwatson	 * Invoke the accf_create() method of the filter if required.  The
273143461Srwatson	 * socket mutex is held over this call, so create methods for filters
274143461Srwatson	 * can't block.
275142058Srwatson	 */
276142058Srwatson	if (afp->accf_create != NULL) {
277142058Srwatson		newaf->so_accept_filter_arg =
278142058Srwatson		    afp->accf_create(so, afap->af_arg);
279142058Srwatson		if (newaf->so_accept_filter_arg == NULL) {
280142058Srwatson			error = EINVAL;
281142058Srwatson			goto out;
282142058Srwatson		}
283142058Srwatson	}
284142058Srwatson	newaf->so_accept_filter = afp;
285142058Srwatson	so->so_accf = newaf;
286142058Srwatson	so->so_options |= SO_ACCEPTFILTER;
287142058Srwatson	newaf = NULL;
288142058Srwatsonout:
289142058Srwatson	SOCK_UNLOCK(so);
290142058Srwatson	if (newaf != NULL) {
291142058Srwatson		if (newaf->so_accept_filter_str != NULL)
292184205Sdes			free(newaf->so_accept_filter_str, M_ACCF);
293184205Sdes		free(newaf, M_ACCF);
294142058Srwatson	}
295142058Srwatson	if (afap != NULL)
296184205Sdes		free(afap, M_TEMP);
297142058Srwatson	return (error);
298142058Srwatson}
299