uipc_accf.c revision 1.1 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.1 2008/08/04 03:55:47 tls 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 mbuf *m)
225 {
226 int error;
227
228 error = 0;
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 m->m_len = sizeof(struct accept_filter_arg);
239 strcpy(mtod(m, struct accept_filter_arg *)->af_name, so->so_accf->so_accept_filter->accf_name);
240 if (so->so_accf->so_accept_filter_str != NULL)
241 strcpy(mtod(m, struct accept_filter_arg *)->af_arg, so->so_accf->so_accept_filter_str);
242 out:
243 SOCK_UNLOCK(so);
244 return (error);
245 }
246
247 int
248 do_setopt_accept_filter(struct socket *so, struct mbuf *m)
249 {
250 struct accept_filter_arg *afap;
251 struct accept_filter *afp;
252 struct so_accf *newaf;
253 int error = 0;
254
255 /*
256 * Handle the simple delete case first.
257 */
258 if (m == NULL || mtod(m, struct accept_filter_arg *) == NULL) {
259 SOCK_LOCK(so);
260 if ((so->so_options & SO_ACCEPTCONN) == 0) {
261 SOCK_UNLOCK(so);
262 return (EINVAL);
263 }
264 if (so->so_accf != NULL) {
265 struct so_accf *af = so->so_accf;
266 if (af->so_accept_filter != NULL &&
267 af->so_accept_filter->accf_destroy != NULL) {
268 af->so_accept_filter->accf_destroy(so);
269 }
270 if (af->so_accept_filter_str != NULL)
271 FREE(af->so_accept_filter_str, M_ACCF);
272 FREE(af, M_ACCF);
273 so->so_accf = NULL;
274 }
275 so->so_options &= ~SO_ACCEPTFILTER;
276 SOCK_UNLOCK(so);
277 return (0);
278 }
279
280 /*
281 * Pre-allocate any memory we may need later to avoid blocking at
282 * untimely moments. This does not optimize for invalid arguments.
283 */
284 if (m->m_len != sizeof(struct accept_filter_arg)) {
285 return (EINVAL);
286 }
287 afap = mtod(m, struct accept_filter_arg *);
288 afap->af_name[sizeof(afap->af_name)-1] = '\0';
289 afap->af_arg[sizeof(afap->af_arg)-1] = '\0';
290 afp = accept_filt_get(afap->af_name);
291 if (afp == NULL) {
292 return (ENOENT);
293 }
294 /*
295 * Allocate the new accept filter instance storage. We may
296 * have to free it again later if we fail to attach it. If
297 * attached properly, 'newaf' is NULLed to avoid a free()
298 * while in use.
299 */
300 MALLOC(newaf, struct so_accf *, sizeof(*newaf), M_ACCF, M_WAITOK |
301 M_ZERO);
302 if (afp->accf_create != NULL && afap->af_name[0] != '\0') {
303 int len = strlen(afap->af_name) + 1;
304 MALLOC(newaf->so_accept_filter_str, char *, len, M_ACCF,
305 M_WAITOK);
306 strcpy(newaf->so_accept_filter_str, afap->af_name);
307 }
308
309 /*
310 * Require a listen socket; don't try to replace an existing filter
311 * without first removing it.
312 */
313 SOCK_LOCK(so);
314 if (((so->so_options & SO_ACCEPTCONN) == 0) ||
315 (so->so_accf != NULL)) {
316 error = EINVAL;
317 goto out;
318 }
319
320 /*
321 * Invoke the accf_create() method of the filter if required. The
322 * socket mutex is held over this call, so create methods for filters
323 * can't block.
324 */
325 if (afp->accf_create != NULL) {
326 newaf->so_accept_filter_arg =
327 afp->accf_create(so, afap->af_arg);
328 if (newaf->so_accept_filter_arg == NULL) {
329 error = EINVAL;
330 goto out;
331 }
332 }
333 newaf->so_accept_filter = afp;
334 so->so_accf = newaf;
335 so->so_options |= SO_ACCEPTFILTER;
336 newaf = NULL;
337 out:
338 SOCK_UNLOCK(so);
339 if (newaf != NULL) {
340 if (newaf->so_accept_filter_str != NULL)
341 FREE(newaf->so_accept_filter_str, M_ACCF);
342 FREE(newaf, M_ACCF);
343 }
344 return (error);
345 }
346