uipc_accf.c revision 1.2 1 /*-
2 * Copyright (c) 2000 Paycounter, Inc.
3 * Copyright (c) 2005 Robert N. M. Watson
4 * Author: Alfred Perlstein <alfred (at) paycounter.com>, <alfred (at) FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: uipc_accf.c,v 1.2 2008/08/06 15:01:23 plunky Exp $");
31
32 #define ACCEPT_FILTER_MOD
33
34 #include "opt_inet.h"
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/domain.h>
38 #include <sys/kernel.h>
39 #include <sys/lock.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/lkm.h>
43 #include <sys/mutex.h>
44 #include <sys/protosw.h>
45 #include <sys/sysctl.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
48 #include <sys/queue.h>
49 #include <sys/once.h>
50
51 static kmutex_t accept_filter_mtx;
52 #define ACCEPT_FILTER_LOCK() mutex_spin_enter(&accept_filter_mtx)
53 #define ACCEPT_FILTER_UNLOCK() mutex_spin_exit(&accept_filter_mtx);
54 #define SOCK_LOCK(so)
55 #define SOCK_UNLOCK(so)
56
57 static SLIST_HEAD(, accept_filter) accept_filtlsthd =
58 SLIST_HEAD_INITIALIZER(&accept_filtlsthd);
59
60 MALLOC_DEFINE(M_ACCF, "accf", "accept filter data");
61
62 static int unloadable = 0;
63
64 /*
65 * Names of Accept filter sysctl objects
66 */
67
68 #define ACCFCTL_UNLOADABLE 1 /* Allow module to be unloaded */
69
70
71 SYSCTL_SETUP(sysctl_net_inet_accf_setup, "sysctl net.inet.accf subtree setup")
72 {
73 sysctl_createv(clog, 0, NULL, NULL,
74 CTLFLAG_PERMANENT,
75 CTLTYPE_NODE, "net", NULL,
76 NULL, 0, NULL, 0,
77 CTL_NET, CTL_EOL);
78 sysctl_createv(clog, 0, NULL, NULL,
79 CTLFLAG_PERMANENT,
80 CTLTYPE_NODE, "inet", NULL,
81 NULL, 0, NULL, 0,
82 CTL_NET, PF_INET, CTL_EOL);
83 sysctl_createv(clog, 0, NULL, NULL,
84 CTLFLAG_PERMANENT,
85 CTLTYPE_NODE, "accf",
86 SYSCTL_DESCR("Accept filters"),
87 NULL, 0, NULL, 0,
88 CTL_NET, PF_INET, SO_ACCEPTFILTER, CTL_EOL);
89 sysctl_createv(clog, 0, NULL, NULL,
90 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
91 CTLTYPE_INT, "unloadable",
92 SYSCTL_DESCR("Allow unload of accept filters "
93 "(not recommended)"),
94 NULL, 0, &unloadable, 0,
95 CTL_NET, PF_INET, SO_ACCEPTFILTER,
96 ACCFCTL_UNLOADABLE, CTL_EOL);
97 }
98
99 /*
100 * Must be passed a malloc'd structure so we don't explode if the kld is
101 * unloaded, we leak the struct on deallocation to deal with this, but if a
102 * filter is loaded with the same name as a leaked one we re-use the entry.
103 */
104 int
105 accept_filt_add(struct accept_filter *filt)
106 {
107 struct accept_filter *p;
108
109 ACCEPT_FILTER_LOCK();
110 SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
111 if (strcmp(p->accf_name, filt->accf_name) == 0) {
112 if (p->accf_callback != NULL) {
113 ACCEPT_FILTER_UNLOCK();
114 return (EEXIST);
115 } else {
116 p->accf_callback = filt->accf_callback;
117 ACCEPT_FILTER_UNLOCK();
118 FREE(filt, M_ACCF);
119 return (0);
120 }
121 }
122
123 if (p == NULL)
124 SLIST_INSERT_HEAD(&accept_filtlsthd, filt, accf_next);
125 ACCEPT_FILTER_UNLOCK();
126 return (0);
127 }
128
129 int
130 accept_filt_del(char *name)
131 {
132 struct accept_filter *p;
133
134 p = accept_filt_get(name);
135 if (p == NULL)
136 return (ENOENT);
137
138 p->accf_callback = NULL;
139 return (0);
140 }
141
142 struct accept_filter *
143 accept_filt_get(char *name)
144 {
145 struct accept_filter *p;
146
147 ACCEPT_FILTER_LOCK();
148 SLIST_FOREACH(p, &accept_filtlsthd, accf_next)
149 if (strcmp(p->accf_name, name) == 0)
150 break;
151 ACCEPT_FILTER_UNLOCK();
152
153 return (p);
154 }
155
156 /*
157 * Accept filter initialization routine.
158 * This should be called only once.
159 */
160
161 static int
162 accept_filter_init0(void)
163 {
164 mutex_init(&accept_filter_mtx, MUTEX_DEFAULT, IPL_NET);
165
166 return 0;
167 }
168
169 /*
170 * Initialization routine: This can also be replaced with
171 * accept_filt_generic_mod_event for attaching new accept filter.
172 */
173
174 void
175 accept_filter_init(void)
176 {
177 static ONCE_DECL(accept_filter_init_once);
178
179 RUN_ONCE(&accept_filter_init_once, accept_filter_init0);
180 }
181
182 int
183 accept_filt_generic_mod_event(struct lkm_table *lkmtp, int event, void *data)
184 {
185 struct accept_filter *p;
186 struct accept_filter *accfp = (struct accept_filter *) data;
187 int error;
188
189 switch (event) {
190 case LKM_E_LOAD:
191 accept_filter_init();
192 MALLOC(p, struct accept_filter *, sizeof(*p), M_ACCF,
193 M_WAITOK);
194 bcopy(accfp, p, sizeof(*p));
195 error = accept_filt_add(p);
196 break;
197
198 case LKM_E_UNLOAD:
199 /*
200 * Do not support unloading yet. we don't keep track of
201 * refcounts and unloading an accept filter callback and then
202 * having it called is a bad thing. A simple fix would be to
203 * track the refcount in the struct accept_filter.
204 */
205 if (unloadable != 0) {
206 error = accept_filt_del(accfp->accf_name);
207 } else
208 error = EOPNOTSUPP;
209 break;
210
211 case LKM_E_STAT:
212 error = 0;
213 break;
214
215 default:
216 error = EOPNOTSUPP;
217 break;
218 }
219
220 return (error);
221 }
222
223 int
224 do_getopt_accept_filter(struct socket *so, struct sockopt *sopt)
225 {
226 struct accept_filter_arg afa;
227 int error;
228
229 SOCK_LOCK(so);
230 if ((so->so_options & SO_ACCEPTCONN) == 0) {
231 error = EINVAL;
232 goto out;
233 }
234 if ((so->so_options & SO_ACCEPTFILTER) == 0) {
235 error = EINVAL;
236 goto out;
237 }
238
239 memset(&afa, 0, sizeof(afa));
240 strcpy(afa.af_name, so->so_accf->so_accept_filter->accf_name);
241 if (so->so_accf->so_accept_filter_str != NULL)
242 strcpy(afa.af_arg, so->so_accf->so_accept_filter_str);
243 error = sockopt_set(sopt, &afa, sizeof(afa));
244 out:
245 SOCK_UNLOCK(so);
246 return (error);
247 }
248
249 int
250 do_setopt_accept_filter(struct socket *so, const struct sockopt *sopt)
251 {
252 struct accept_filter_arg afa;
253 struct accept_filter *afp;
254 struct so_accf *newaf;
255 int error;
256
257 /*
258 * Handle the simple delete case first.
259 */
260 if (sopt == NULL || sopt->sopt_size == 0) {
261 SOCK_LOCK(so);
262 if ((so->so_options & SO_ACCEPTCONN) == 0) {
263 SOCK_UNLOCK(so);
264 return (EINVAL);
265 }
266 if (so->so_accf != NULL) {
267 struct so_accf *af = so->so_accf;
268 if (af->so_accept_filter != NULL &&
269 af->so_accept_filter->accf_destroy != NULL) {
270 af->so_accept_filter->accf_destroy(so);
271 }
272 if (af->so_accept_filter_str != NULL)
273 FREE(af->so_accept_filter_str, M_ACCF);
274 FREE(af, M_ACCF);
275 so->so_accf = NULL;
276 }
277 so->so_options &= ~SO_ACCEPTFILTER;
278 SOCK_UNLOCK(so);
279 return (0);
280 }
281
282 /*
283 * Pre-allocate any memory we may need later to avoid blocking at
284 * untimely moments. This does not optimize for invalid arguments.
285 */
286 error = sockopt_get(sopt, &afa, sizeof(afa));
287 if (error) {
288 return (error);
289 }
290 afa.af_name[sizeof(afa.af_name)-1] = '\0';
291 afa.af_arg[sizeof(afa.af_arg)-1] = '\0';
292 afp = accept_filt_get(afa.af_name);
293 if (afp == NULL) {
294 return (ENOENT);
295 }
296 /*
297 * Allocate the new accept filter instance storage. We may
298 * have to free it again later if we fail to attach it. If
299 * attached properly, 'newaf' is NULLed to avoid a free()
300 * while in use.
301 */
302 MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK |
303 M_ZERO);
304 if (afp->accf_create != NULL && afa.af_name[0] != '\0') {
305 int len = strlen(afa.af_name) + 1;
306 MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF,
307 M_WAITOK);
308 strcpy(newaf->so_accept_filter_str, afa.af_name);
309 }
310
311 /*
312 * Require a listen socket; don't try to replace an existing filter
313 * without first removing it.
314 */
315 SOCK_LOCK(so);
316 if (((so->so_options & SO_ACCEPTCONN) == 0) ||
317 (so->so_accf != NULL)) {
318 error = EINVAL;
319 goto out;
320 }
321
322 /*
323 * Invoke the accf_create() method of the filter if required. The
324 * socket mutex is held over this call, so create methods for filters
325 * can't block.
326 */
327 if (afp->accf_create != NULL) {
328 newaf->so_accept_filter_arg =
329 afp->accf_create(so, afa.af_arg);
330 if (newaf->so_accept_filter_arg == NULL) {
331 error = EINVAL;
332 goto out;
333 }
334 }
335 newaf->so_accept_filter = afp;
336 so->so_accf = newaf;
337 so->so_options |= SO_ACCEPTFILTER;
338 newaf = NULL;
339 out:
340 SOCK_UNLOCK(so);
341 if (newaf != NULL) {
342 if (newaf->so_accept_filter_str != NULL)
343 FREE(newaf->so_accept_filter_str, M_ACCF);
344 FREE(newaf, M_ACCF);
345 }
346 return (error);
347 }
348