xref: /freebsd/sys/kern/uipc_accf.c (revision b00ab754)
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, 0, "Accept filters");
66 SYSCTL_INT(_net_accf, OID_AUTO, unloadable, CTLFLAG_RW, &unloadable, 0,
67 	"Allow unload of accept filters (not recommended)");
68 
69 /*
70  * Must be passed a malloc'd structure so we don't explode if the kld is
71  * unloaded, we leak the struct on deallocation to deal with this, but if a
72  * filter is loaded with the same name as a leaked one we re-use the entry.
73  */
74 int
75 accept_filt_add(struct accept_filter *filt)
76 {
77 	struct accept_filter *p;
78 
79 	ACCEPT_FILTER_LOCK();
80 	SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
81 		if (strcmp(p->accf_name, filt->accf_name) == 0)  {
82 			if (p->accf_callback != NULL) {
83 				ACCEPT_FILTER_UNLOCK();
84 				return (EEXIST);
85 			} else {
86 				p->accf_callback = filt->accf_callback;
87 				ACCEPT_FILTER_UNLOCK();
88 				free(filt, M_ACCF);
89 				return (0);
90 			}
91 		}
92 
93 	if (p == NULL)
94 		SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next);
95 	ACCEPT_FILTER_UNLOCK();
96 	return (0);
97 }
98 
99 int
100 accept_filt_del(char *name)
101 {
102 	struct accept_filter *p;
103 
104 	p = accept_filt_get(name);
105 	if (p == NULL)
106 		return (ENOENT);
107 
108 	p->accf_callback = NULL;
109 	return (0);
110 }
111 
112 struct accept_filter *
113 accept_filt_get(char *name)
114 {
115 	struct accept_filter *p;
116 
117 	ACCEPT_FILTER_LOCK();
118 	SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
119 		if (strcmp(p->accf_name, name) == 0)
120 			break;
121 	ACCEPT_FILTER_UNLOCK();
122 
123 	return (p);
124 }
125 
126 int
127 accept_filt_generic_mod_event(module_t mod, int event, void *data)
128 {
129 	struct accept_filter *p;
130 	struct accept_filter *accfp = (struct accept_filter *) data;
131 	int error;
132 
133 	switch (event) {
134 	case MOD_LOAD:
135 		p = malloc(sizeof(*p), M_ACCF, M_WAITOK);
136 		bcopy(accfp, p, sizeof(*p));
137 		error = accept_filt_add(p);
138 		break;
139 
140 	case MOD_UNLOAD:
141 		/*
142 		 * Do not support unloading yet. we don't keep track of
143 		 * refcounts and unloading an accept filter callback and then
144 		 * having it called is a bad thing.  A simple fix would be to
145 		 * track the refcount in the struct accept_filter.
146 		 */
147 		if (unloadable != 0) {
148 			error = accept_filt_del(accfp->accf_name);
149 		} else
150 			error = EOPNOTSUPP;
151 		break;
152 
153 	case MOD_SHUTDOWN:
154 		error = 0;
155 		break;
156 
157 	default:
158 		error = EOPNOTSUPP;
159 		break;
160 	}
161 
162 	return (error);
163 }
164 
165 int
166 accept_filt_getopt(struct socket *so, struct sockopt *sopt)
167 {
168 	struct accept_filter_arg *afap;
169 	int error;
170 
171 	error = 0;
172 	afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK | M_ZERO);
173 	SOCK_LOCK(so);
174 	if ((so->so_options & SO_ACCEPTCONN) == 0) {
175 		error = EINVAL;
176 		goto out;
177 	}
178 	if (so->sol_accept_filter == NULL) {
179 		error = EINVAL;
180 		goto out;
181 	}
182 	strcpy(afap->af_name, so->sol_accept_filter->accf_name);
183 	if (so->sol_accept_filter_str != NULL)
184 		strcpy(afap->af_arg, so->sol_accept_filter_str);
185 out:
186 	SOCK_UNLOCK(so);
187 	if (error == 0)
188 		error = sooptcopyout(sopt, afap, sizeof(*afap));
189 	free(afap, M_TEMP);
190 	return (error);
191 }
192 
193 int
194 accept_filt_setopt(struct socket *so, struct sockopt *sopt)
195 {
196 	struct accept_filter_arg *afap;
197 	struct accept_filter *afp;
198 	char *accept_filter_str = NULL;
199 	void *accept_filter_arg = NULL;
200 	int error;
201 
202 	/*
203 	 * Handle the simple delete case first.
204 	 */
205 	if (sopt == NULL || sopt->sopt_val == NULL) {
206 		struct socket *sp, *sp1;
207 		int wakeup;
208 
209 		SOCK_LOCK(so);
210 		if ((so->so_options & SO_ACCEPTCONN) == 0) {
211 			SOCK_UNLOCK(so);
212 			return (EINVAL);
213 		}
214 		if (so->sol_accept_filter == NULL) {
215 			SOCK_UNLOCK(so);
216 			return (0);
217 		}
218 		if (so->sol_accept_filter->accf_destroy != NULL)
219 			so->sol_accept_filter->accf_destroy(so);
220 		if (so->sol_accept_filter_str != NULL)
221 			free(so->sol_accept_filter_str, M_ACCF);
222 		so->sol_accept_filter = NULL;
223 		so->sol_accept_filter_arg = NULL;
224 		so->sol_accept_filter_str = NULL;
225 		so->so_options &= ~SO_ACCEPTFILTER;
226 
227 		/*
228 		 * Move from incomplete queue to complete only those
229 		 * connections, that are blocked by us.
230 		 */
231 		wakeup = 0;
232 		TAILQ_FOREACH_SAFE(sp, &so->sol_incomp, so_list, sp1) {
233 			SOCK_LOCK(sp);
234 			if (sp->so_options & SO_ACCEPTFILTER) {
235 				TAILQ_REMOVE(&so->sol_incomp, sp, so_list);
236 				TAILQ_INSERT_TAIL(&so->sol_comp, sp, so_list);
237 				sp->so_qstate = SQ_COMP;
238 				sp->so_options &= ~SO_ACCEPTFILTER;
239 				so->sol_incqlen--;
240 				so->sol_qlen++;
241 				wakeup = 1;
242 			}
243 			SOCK_UNLOCK(sp);
244 		}
245 		if (wakeup)
246 			solisten_wakeup(so);  /* unlocks */
247 		else
248 			SOLISTEN_UNLOCK(so);
249 		return (0);
250 	}
251 
252 	/*
253 	 * Pre-allocate any memory we may need later to avoid blocking at
254 	 * untimely moments.  This does not optimize for invalid arguments.
255 	 */
256 	afap = malloc(sizeof(*afap), M_TEMP, M_WAITOK);
257 	error = sooptcopyin(sopt, afap, sizeof *afap, sizeof *afap);
258 	afap->af_name[sizeof(afap->af_name)-1] = '\0';
259 	afap->af_arg[sizeof(afap->af_arg)-1] = '\0';
260 	if (error) {
261 		free(afap, M_TEMP);
262 		return (error);
263 	}
264 	afp = accept_filt_get(afap->af_name);
265 	if (afp == NULL) {
266 		free(afap, M_TEMP);
267 		return (ENOENT);
268 	}
269 	if (afp->accf_create != NULL && afap->af_name[0] != '\0') {
270 		size_t len = strlen(afap->af_name) + 1;
271 		accept_filter_str = malloc(len, M_ACCF, M_WAITOK);
272 		strcpy(accept_filter_str, afap->af_name);
273 	}
274 
275 	/*
276 	 * Require a listen socket; don't try to replace an existing filter
277 	 * without first removing it.
278 	 */
279 	SOCK_LOCK(so);
280 	if ((so->so_options & SO_ACCEPTCONN) == 0 ||
281 	    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 	so->so_options |= SO_ACCEPTFILTER;
302 out:
303 	SOCK_UNLOCK(so);
304 	if (accept_filter_str != NULL)
305 		free(accept_filter_str, M_ACCF);
306 	free(afap, M_TEMP);
307 	return (error);
308 }
309