sort(1): Fix some obvious issues
[dragonfly.git] / sys / net / pfil.c
CommitLineData
9e99d0a2 1/* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */
77e0b69a 2/* $DragonFly: src/sys/net/pfil.c,v 1.14 2008/09/20 06:08:13 sephe Exp $ */
9e99d0a2
JR
3
4/*
5 * Copyright (c) 1996 Matthew R. Green
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/param.h>
33#include <sys/errno.h>
34#include <sys/malloc.h>
35#include <sys/socket.h>
36#include <sys/socketvar.h>
37#include <sys/systm.h>
38#include <sys/proc.h>
39#include <sys/queue.h>
d0930e0d 40#include <sys/sysctl.h>
9e99d0a2
JR
41
42#include <net/if.h>
43#include <net/pfil.h>
f0fad8e1 44#include <net/netmsg2.h>
5337421c 45#include <net/netisr2.h>
684a93c4 46#include <sys/mplock2.h>
f0fad8e1 47
ec7f7fc8 48#define PFIL_CFGPORT netisr_cpuport(0)
77e0b69a 49
caa0af9f
SZ
50/*
51 * The packet filter hooks are designed for anything to call them to
52 * possibly intercept the packet.
53 */
54struct packet_filter_hook {
55 TAILQ_ENTRY(packet_filter_hook) pfil_link;
56 pfil_func_t pfil_func;
57 void *pfil_arg;
58 int pfil_flags;
59};
60
f0fad8e1 61struct netmsg_pfil {
002c1265 62 struct netmsg_base base;
f0fad8e1
SZ
63 pfil_func_t pfil_func;
64 void *pfil_arg;
65 int pfil_flags;
66 struct pfil_head *pfil_ph;
67};
9e99d0a2 68
d66da3ad
SZ
69static LIST_HEAD(, pfil_head) pfil_head_list =
70 LIST_HEAD_INITIALIZER(&pfil_head_list);
9e99d0a2 71
d66da3ad
SZ
72static pfil_list_t *pfil_list_alloc(void);
73static void pfil_list_free(pfil_list_t *);
74static void pfil_list_dup(const pfil_list_t *, pfil_list_t *,
75 const struct packet_filter_hook *);
76static void pfil_list_add(pfil_list_t *, pfil_func_t, void *, int);
77static struct packet_filter_hook *
78 pfil_list_find(const pfil_list_t *, pfil_func_t,
79 const void *);
80
002c1265
MD
81static void pfil_remove_hook_dispatch(netmsg_t);
82static void pfil_add_hook_dispatch(netmsg_t);
9e99d0a2 83
d0930e0d
MD
84int filters_default_to_accept = 0;
85SYSCTL_INT(_net, OID_AUTO, filters_default_to_accept, CTLFLAG_RW,
86 &filters_default_to_accept, 0,
87 "cause ipfw* modules to not block by default");
88TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept);
89
9e99d0a2
JR
90/*
91 * pfil_run_hooks() runs the specified packet filter hooks.
92 */
93int
94pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
95 int dir)
96{
97 struct packet_filter_hook *pfh;
98 struct mbuf *m = *mp;
7f8565be 99 pfil_list_t *list;
9e99d0a2
JR
100 int rv = 0;
101
7f8565be 102 if (dir == PFIL_IN)
d66da3ad 103 list = ph->ph_in;
7f8565be 104 else if (dir == PFIL_OUT)
d66da3ad 105 list = ph->ph_out;
7f8565be
SZ
106 else
107 return 0; /* XXX panic? */
108
109 TAILQ_FOREACH(pfh, list, pfil_link) {
9e99d0a2 110 if (pfh->pfil_func != NULL) {
77e0b69a 111 rv = pfh->pfil_func(pfh->pfil_arg, &m, ifp, dir);
9e99d0a2
JR
112 if (rv != 0 || m == NULL)
113 break;
114 }
115 }
116
117 *mp = m;
118 return (rv);
119}
120
121/*
122 * pfil_head_register() registers a pfil_head with the packet filter
123 * hook mechanism.
124 */
125int
126pfil_head_register(struct pfil_head *ph)
127{
128 struct pfil_head *lph;
129
94171836 130 LIST_FOREACH(lph, &pfil_head_list, ph_list) {
9e99d0a2
JR
131 if (ph->ph_type == lph->ph_type &&
132 ph->ph_un.phu_val == lph->ph_un.phu_val)
133 return EEXIST;
134 }
135
d66da3ad
SZ
136 ph->ph_in = pfil_list_alloc();
137 ph->ph_out = pfil_list_alloc();
afabe90c 138 ph->ph_hashooks = 0;
9e99d0a2
JR
139
140 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list);
141
142 return (0);
143}
144
145/*
146 * pfil_head_unregister() removes a pfil_head from the packet filter
147 * hook mechanism.
148 */
149int
150pfil_head_unregister(struct pfil_head *pfh)
151{
9e99d0a2
JR
152 LIST_REMOVE(pfh, ph_list);
153 return (0);
154}
155
156/*
157 * pfil_head_get() returns the pfil_head for a given key/dlt.
158 */
159struct pfil_head *
160pfil_head_get(int type, u_long val)
161{
162 struct pfil_head *ph;
163
94171836
SZ
164 LIST_FOREACH(ph, &pfil_head_list, ph_list) {
165 if (ph->ph_type == type && ph->ph_un.phu_val == val)
9e99d0a2
JR
166 break;
167 }
9e99d0a2
JR
168 return (ph);
169}
170
f0fad8e1 171static void
002c1265 172pfil_add_hook_dispatch(netmsg_t nmsg)
9e99d0a2 173{
f0fad8e1
SZ
174 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg;
175 pfil_func_t func = pfilmsg->pfil_func;
176 void *arg = pfilmsg->pfil_arg;
177 int flags = pfilmsg->pfil_flags;
178 struct pfil_head *ph = pfilmsg->pfil_ph;
d66da3ad
SZ
179 const struct packet_filter_hook *pfh;
180 pfil_list_t *list_in = NULL, *list_out = NULL;
181 pfil_list_t *old_list_in = NULL, *old_list_out = NULL;
9e99d0a2
JR
182 int err = 0;
183
d66da3ad
SZ
184 /* This probably should not happen ... */
185 if ((flags & (PFIL_IN | PFIL_OUT)) == 0)
186 goto reply; /* XXX panic? */
187
188 /*
189 * If pfil hooks exist on any of the requested lists,
190 * then bail out.
191 */
9e99d0a2 192 if (flags & PFIL_IN) {
d66da3ad
SZ
193 pfh = pfil_list_find(ph->ph_in, func, arg);
194 if (pfh != NULL) {
195 err = EEXIST;
f0fad8e1 196 goto reply;
d66da3ad 197 }
9e99d0a2
JR
198 }
199 if (flags & PFIL_OUT) {
d66da3ad
SZ
200 pfh = pfil_list_find(ph->ph_out, func, arg);
201 if (pfh != NULL) {
202 err = EEXIST;
203 goto reply;
9e99d0a2
JR
204 }
205 }
d66da3ad
SZ
206
207 /*
208 * Duplicate the requested lists, install new hooks
209 * on the duplication
210 */
211 if (flags & PFIL_IN) {
212 list_in = pfil_list_alloc();
213 pfil_list_dup(ph->ph_in, list_in, NULL);
214 pfil_list_add(list_in, func, arg, flags & ~PFIL_OUT);
215 }
216 if (flags & PFIL_OUT) {
217 list_out = pfil_list_alloc();
218 pfil_list_dup(ph->ph_out, list_out, NULL);
219 pfil_list_add(list_out, func, arg, flags & ~PFIL_IN);
220 }
221
222 /*
223 * Switch list pointers, but keep the old ones
224 */
225 if (list_in != NULL) {
226 old_list_in = ph->ph_in;
227 ph->ph_in = list_in;
228 }
229 if (list_out != NULL) {
230 old_list_out = ph->ph_out;
231 ph->ph_out = list_out;
232 }
233
234 /*
235 * Wait until everyone has finished the old lists iteration
236 */
237 netmsg_service_sync();
238 ph->ph_hashooks = 1;
239
240 /*
241 * Now it is safe to free the old lists, since no one sees it
242 */
243 if (old_list_in != NULL)
244 pfil_list_free(old_list_in);
245 if (old_list_out != NULL)
246 pfil_list_free(old_list_out);
f0fad8e1 247reply:
002c1265 248 lwkt_replymsg(&nmsg->base.lmsg, err);
f0fad8e1
SZ
249}
250
251/*
252 * pfil_add_hook() adds a function to the packet filter hook. the
253 * flags are:
254 * PFIL_IN call me on incoming packets
255 * PFIL_OUT call me on outgoing packets
256 * PFIL_ALL call me on all of the above
f0fad8e1
SZ
257 */
258int
259pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
260{
261 struct netmsg_pfil pfilmsg;
002c1265
MD
262 netmsg_base_t nmsg;
263 int error;
f0fad8e1 264
002c1265 265 nmsg = &pfilmsg.base;
48e7b118
MD
266 netmsg_init(nmsg, NULL, &curthread->td_msgport,
267 0, pfil_add_hook_dispatch);
f0fad8e1
SZ
268 pfilmsg.pfil_func = func;
269 pfilmsg.pfil_arg = arg;
270 pfilmsg.pfil_flags = flags;
271 pfilmsg.pfil_ph = ph;
272
002c1265
MD
273 error = lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0);
274 return error;
9e99d0a2
JR
275}
276
d66da3ad 277static void
002c1265 278pfil_remove_hook_dispatch(netmsg_t nmsg)
9e99d0a2 279{
d66da3ad
SZ
280 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg;
281 pfil_func_t func = pfilmsg->pfil_func;
282 void *arg = pfilmsg->pfil_arg;
283 int flags = pfilmsg->pfil_flags;
284 struct pfil_head *ph = pfilmsg->pfil_ph;
285 struct packet_filter_hook *skip_in = NULL, *skip_out = NULL;
286 pfil_list_t *list_in = NULL, *list_out = NULL;
287 pfil_list_t *old_list_in = NULL, *old_list_out = NULL;
288 int err = 0;
f0fad8e1 289
d66da3ad
SZ
290 /* This probably should not happen ... */
291 if ((flags & (PFIL_IN | PFIL_OUT)) == 0)
292 goto reply; /* XXX panic? */
9e99d0a2
JR
293
294 /*
d66da3ad
SZ
295 * The pfil hook should exist on all requested lists,
296 * if not just bail out
9e99d0a2 297 */
d66da3ad
SZ
298 if (flags & PFIL_IN) {
299 skip_in = pfil_list_find(ph->ph_in, func, arg);
300 if (!skip_in) {
301 err = ENOENT;
302 goto reply;
303 }
304 }
305 if (flags & PFIL_OUT) {
306 skip_out = pfil_list_find(ph->ph_out, func, arg);
307 if (!skip_out) {
308 err = ENOENT;
309 goto reply;
310 }
9e99d0a2
JR
311 }
312
d66da3ad
SZ
313 /*
314 * Duplicate the requested lists, but the pfil hook to
315 * be deleted is not copied
316 */
317 if (flags & PFIL_IN) {
318 KKASSERT(skip_in != NULL);
319 list_in = pfil_list_alloc();
320 pfil_list_dup(ph->ph_in, list_in, skip_in);
321 }
322 if (flags & PFIL_OUT) {
323 KKASSERT(skip_out != NULL);
324 list_out = pfil_list_alloc();
325 pfil_list_dup(ph->ph_out, list_out, skip_out);
326 }
9e99d0a2
JR
327
328 /*
d66da3ad 329 * Switch list pointers, but keep the old ones
9e99d0a2 330 */
d66da3ad
SZ
331 if (list_in != NULL) {
332 old_list_in = ph->ph_in;
333 ph->ph_in = list_in;
334 }
335 if (list_out != NULL) {
336 old_list_out = ph->ph_out;
337 ph->ph_out = list_out;
338 }
9e99d0a2 339
d66da3ad
SZ
340 /*
341 * Wait until everyone has finished the old lists iteration
342 */
343 if (TAILQ_EMPTY(ph->ph_in) && TAILQ_EMPTY(ph->ph_out))
344 ph->ph_hashooks = 0;
345 netmsg_service_sync();
f0fad8e1 346
d66da3ad
SZ
347 /*
348 * Now it is safe to free the old lists, since no one sees it
349 */
350 if (old_list_in != NULL)
351 pfil_list_free(old_list_in);
352 if (old_list_out != NULL)
353 pfil_list_free(old_list_out);
354reply:
002c1265 355 lwkt_replymsg(&nmsg->base.lmsg, err);
f0fad8e1
SZ
356}
357
9e99d0a2
JR
358/*
359 * pfil_remove_hook removes a specific function from the packet filter
360 * hook list.
361 */
362int
3bc01464 363pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
9e99d0a2 364{
f0fad8e1 365 struct netmsg_pfil pfilmsg;
002c1265 366 netmsg_base_t nmsg;
9e99d0a2 367
002c1265 368 nmsg = &pfilmsg.base;
48e7b118
MD
369 netmsg_init(nmsg, NULL, &curthread->td_msgport,
370 0, pfil_remove_hook_dispatch);
f0fad8e1
SZ
371 pfilmsg.pfil_func = func;
372 pfilmsg.pfil_arg = arg;
373 pfilmsg.pfil_flags = flags;
374 pfilmsg.pfil_ph = ph;
375
002c1265 376 return lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0);
9e99d0a2
JR
377}
378
d66da3ad
SZ
379static void
380pfil_list_add(pfil_list_t *list, pfil_func_t func, void *arg, int flags)
9e99d0a2
JR
381{
382 struct packet_filter_hook *pfh;
afabe90c 383
f0fad8e1
SZ
384 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
385
d66da3ad
SZ
386 pfh = kmalloc(sizeof(*pfh), M_IFADDR, M_WAITOK);
387
388 pfh->pfil_func = func;
389 pfh->pfil_arg = arg;
77d9cd11 390 pfh->pfil_flags = flags;
d66da3ad
SZ
391
392 /*
393 * Insert the input list in reverse order of the output list
394 * so that the same path is followed in or out of the kernel.
395 */
396 if (flags & PFIL_IN)
397 TAILQ_INSERT_HEAD(list, pfh, pfil_link);
398 else
399 TAILQ_INSERT_TAIL(list, pfh, pfil_link);
400}
401
402static void
403pfil_list_dup(const pfil_list_t *from, pfil_list_t *to,
404 const struct packet_filter_hook *skip)
405{
406 struct packet_filter_hook *pfh_to, *pfh_from;
407
408 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
409 KKASSERT(TAILQ_EMPTY(to));
410
411 TAILQ_FOREACH(pfh_from, from, pfil_link) {
412 if (pfh_from == skip)
413 continue;
414
415 pfh_to = kmalloc(sizeof(*pfh_to), M_IFADDR, M_WAITOK);
416 bcopy(pfh_from, pfh_to, sizeof(*pfh_to));
417
418 TAILQ_INSERT_TAIL(to, pfh_to, pfil_link);
419 }
420}
421
422static pfil_list_t *
423pfil_list_alloc(void)
424{
425 pfil_list_t *list;
426
427 list = kmalloc(sizeof(*list), M_IFADDR, M_WAITOK);
428 TAILQ_INIT(list);
429 return list;
430}
431
432static void
433pfil_list_free(pfil_list_t *list)
434{
435 struct packet_filter_hook *pfh;
436
437 while ((pfh = TAILQ_FIRST(list)) != NULL) {
438 TAILQ_REMOVE(list, pfh, pfil_link);
439 kfree(pfh, M_IFADDR);
440 }
441 kfree(list, M_IFADDR);
442}
443
444static struct packet_filter_hook *
445pfil_list_find(const pfil_list_t *list, pfil_func_t func, const void *arg)
446{
447 struct packet_filter_hook *pfh;
448
449 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
9e99d0a2 450
94171836 451 TAILQ_FOREACH(pfh, list, pfil_link) {
d66da3ad
SZ
452 if (pfh->pfil_func == func && pfh->pfil_arg == arg)
453 return pfh;
9e99d0a2 454 }
d66da3ad 455 return NULL;
9e99d0a2 456}