1 /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */
2 /* $DragonFly: src/sys/net/pfil.c,v 1.14 2008/09/20 06:08:13 sephe Exp $ */
5 * Copyright (c) 1996 Matthew R. Green
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
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>
39 #include <sys/queue.h>
40 #include <sys/sysctl.h>
44 #include <net/netmsg2.h>
45 #include <net/netisr2.h>
46 #include <sys/mplock2.h>
48 #define PFIL_CFGPORT netisr_cpuport(0)
51 * The packet filter hooks are designed for anything to call them to
52 * possibly intercept the packet.
54 struct packet_filter_hook
{
55 TAILQ_ENTRY(packet_filter_hook
) pfil_link
;
56 pfil_func_t pfil_func
;
62 struct netmsg_base base
;
63 pfil_func_t pfil_func
;
66 struct pfil_head
*pfil_ph
;
69 static LIST_HEAD(, pfil_head
) pfil_head_list
=
70 LIST_HEAD_INITIALIZER(&pfil_head_list
);
72 static pfil_list_t
*pfil_list_alloc(void);
73 static void pfil_list_free(pfil_list_t
*);
74 static void pfil_list_dup(const pfil_list_t
*, pfil_list_t
*,
75 const struct packet_filter_hook
*);
76 static void pfil_list_add(pfil_list_t
*, pfil_func_t
, void *, int);
77 static struct packet_filter_hook
*
78 pfil_list_find(const pfil_list_t
*, pfil_func_t
,
81 static void pfil_remove_hook_dispatch(netmsg_t
);
82 static void pfil_add_hook_dispatch(netmsg_t
);
84 int filters_default_to_accept
= 0;
85 SYSCTL_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");
88 TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept
);
91 * pfil_run_hooks() runs the specified packet filter hooks.
94 pfil_run_hooks(struct pfil_head
*ph
, struct mbuf
**mp
, struct ifnet
*ifp
,
97 struct packet_filter_hook
*pfh
;
104 else if (dir
== PFIL_OUT
)
107 return 0; /* XXX panic? */
109 /* Make sure 'list' is really used. */
111 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
112 if (pfh
->pfil_func
!= NULL
) {
113 rv
= pfh
->pfil_func(pfh
->pfil_arg
, &m
, ifp
, dir
);
114 if (rv
!= 0 || m
== NULL
)
124 * pfil_head_register() registers a pfil_head with the packet filter
128 pfil_head_register(struct pfil_head
*ph
)
130 struct pfil_head
*lph
;
132 LIST_FOREACH(lph
, &pfil_head_list
, ph_list
) {
133 if (ph
->ph_type
== lph
->ph_type
&&
134 ph
->ph_un
.phu_val
== lph
->ph_un
.phu_val
)
138 ph
->ph_in
= pfil_list_alloc();
139 ph
->ph_out
= pfil_list_alloc();
142 LIST_INSERT_HEAD(&pfil_head_list
, ph
, ph_list
);
148 * pfil_head_unregister() removes a pfil_head from the packet filter
152 pfil_head_unregister(struct pfil_head
*pfh
)
154 LIST_REMOVE(pfh
, ph_list
);
159 * pfil_head_get() returns the pfil_head for a given key/dlt.
162 pfil_head_get(int type
, u_long val
)
164 struct pfil_head
*ph
;
166 LIST_FOREACH(ph
, &pfil_head_list
, ph_list
) {
167 if (ph
->ph_type
== type
&& ph
->ph_un
.phu_val
== val
)
174 pfil_add_hook_dispatch(netmsg_t nmsg
)
176 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
177 pfil_func_t func
= pfilmsg
->pfil_func
;
178 void *arg
= pfilmsg
->pfil_arg
;
179 int flags
= pfilmsg
->pfil_flags
;
180 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
181 const struct packet_filter_hook
*pfh
;
182 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
183 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
186 /* This probably should not happen ... */
187 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
188 goto reply
; /* XXX panic? */
191 * If pfil hooks exist on any of the requested lists,
194 if (flags
& PFIL_IN
) {
195 pfh
= pfil_list_find(ph
->ph_in
, func
, arg
);
201 if (flags
& PFIL_OUT
) {
202 pfh
= pfil_list_find(ph
->ph_out
, func
, arg
);
210 * Duplicate the requested lists, install new hooks
213 if (flags
& PFIL_IN
) {
214 list_in
= pfil_list_alloc();
215 pfil_list_dup(ph
->ph_in
, list_in
, NULL
);
216 pfil_list_add(list_in
, func
, arg
, flags
& ~PFIL_OUT
);
218 if (flags
& PFIL_OUT
) {
219 list_out
= pfil_list_alloc();
220 pfil_list_dup(ph
->ph_out
, list_out
, NULL
);
221 pfil_list_add(list_out
, func
, arg
, flags
& ~PFIL_IN
);
225 * Switch list pointers, but keep the old ones
227 if (list_in
!= NULL
) {
228 old_list_in
= ph
->ph_in
;
231 if (list_out
!= NULL
) {
232 old_list_out
= ph
->ph_out
;
233 ph
->ph_out
= list_out
;
237 * Wait until everyone has finished the old lists iteration
239 netmsg_service_sync();
243 * Now it is safe to free the old lists, since no one sees it
245 if (old_list_in
!= NULL
)
246 pfil_list_free(old_list_in
);
247 if (old_list_out
!= NULL
)
248 pfil_list_free(old_list_out
);
250 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
254 * pfil_add_hook() adds a function to the packet filter hook. the
256 * PFIL_IN call me on incoming packets
257 * PFIL_OUT call me on outgoing packets
258 * PFIL_ALL call me on all of the above
261 pfil_add_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
263 struct netmsg_pfil pfilmsg
;
267 nmsg
= &pfilmsg
.base
;
268 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
269 0, pfil_add_hook_dispatch
);
270 pfilmsg
.pfil_func
= func
;
271 pfilmsg
.pfil_arg
= arg
;
272 pfilmsg
.pfil_flags
= flags
;
273 pfilmsg
.pfil_ph
= ph
;
275 error
= lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
280 pfil_remove_hook_dispatch(netmsg_t nmsg
)
282 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
283 pfil_func_t func
= pfilmsg
->pfil_func
;
284 void *arg
= pfilmsg
->pfil_arg
;
285 int flags
= pfilmsg
->pfil_flags
;
286 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
287 struct packet_filter_hook
*skip_in
= NULL
, *skip_out
= NULL
;
288 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
289 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
292 /* This probably should not happen ... */
293 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
294 goto reply
; /* XXX panic? */
297 * The pfil hook should exist on all requested lists,
298 * if not just bail out
300 if (flags
& PFIL_IN
) {
301 skip_in
= pfil_list_find(ph
->ph_in
, func
, arg
);
307 if (flags
& PFIL_OUT
) {
308 skip_out
= pfil_list_find(ph
->ph_out
, func
, arg
);
316 * Duplicate the requested lists, but the pfil hook to
317 * be deleted is not copied
319 if (flags
& PFIL_IN
) {
320 KKASSERT(skip_in
!= NULL
);
321 list_in
= pfil_list_alloc();
322 pfil_list_dup(ph
->ph_in
, list_in
, skip_in
);
324 if (flags
& PFIL_OUT
) {
325 KKASSERT(skip_out
!= NULL
);
326 list_out
= pfil_list_alloc();
327 pfil_list_dup(ph
->ph_out
, list_out
, skip_out
);
331 * Switch list pointers, but keep the old ones
333 if (list_in
!= NULL
) {
334 old_list_in
= ph
->ph_in
;
337 if (list_out
!= NULL
) {
338 old_list_out
= ph
->ph_out
;
339 ph
->ph_out
= list_out
;
343 * Wait until everyone has finished the old lists iteration
345 if (TAILQ_EMPTY(ph
->ph_in
) && TAILQ_EMPTY(ph
->ph_out
))
347 netmsg_service_sync();
350 * Now it is safe to free the old lists, since no one sees it
352 if (old_list_in
!= NULL
)
353 pfil_list_free(old_list_in
);
354 if (old_list_out
!= NULL
)
355 pfil_list_free(old_list_out
);
357 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
361 * pfil_remove_hook removes a specific function from the packet filter
365 pfil_remove_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
367 struct netmsg_pfil pfilmsg
;
370 nmsg
= &pfilmsg
.base
;
371 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
372 0, pfil_remove_hook_dispatch
);
373 pfilmsg
.pfil_func
= func
;
374 pfilmsg
.pfil_arg
= arg
;
375 pfilmsg
.pfil_flags
= flags
;
376 pfilmsg
.pfil_ph
= ph
;
378 return lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
382 pfil_list_add(pfil_list_t
*list
, pfil_func_t func
, void *arg
, int flags
)
384 struct packet_filter_hook
*pfh
;
386 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
388 pfh
= kmalloc(sizeof(*pfh
), M_IFADDR
, M_WAITOK
);
390 pfh
->pfil_func
= func
;
392 pfh
->pfil_flags
= flags
;
395 * Insert the input list in reverse order of the output list
396 * so that the same path is followed in or out of the kernel.
399 TAILQ_INSERT_HEAD(list
, pfh
, pfil_link
);
401 TAILQ_INSERT_TAIL(list
, pfh
, pfil_link
);
405 pfil_list_dup(const pfil_list_t
*from
, pfil_list_t
*to
,
406 const struct packet_filter_hook
*skip
)
408 struct packet_filter_hook
*pfh_to
, *pfh_from
;
410 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
411 KKASSERT(TAILQ_EMPTY(to
));
413 TAILQ_FOREACH(pfh_from
, from
, pfil_link
) {
414 if (pfh_from
== skip
)
417 pfh_to
= kmalloc(sizeof(*pfh_to
), M_IFADDR
, M_WAITOK
);
418 bcopy(pfh_from
, pfh_to
, sizeof(*pfh_to
));
420 TAILQ_INSERT_TAIL(to
, pfh_to
, pfil_link
);
425 pfil_list_alloc(void)
429 list
= kmalloc(sizeof(*list
), M_IFADDR
, M_WAITOK
);
435 pfil_list_free(pfil_list_t
*list
)
437 struct packet_filter_hook
*pfh
;
439 while ((pfh
= TAILQ_FIRST(list
)) != NULL
) {
440 TAILQ_REMOVE(list
, pfh
, pfil_link
);
441 kfree(pfh
, M_IFADDR
);
443 kfree(list
, M_IFADDR
);
446 static struct packet_filter_hook
*
447 pfil_list_find(const pfil_list_t
*list
, pfil_func_t func
, const void *arg
)
449 struct packet_filter_hook
*pfh
;
451 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
453 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
454 if (pfh
->pfil_func
== func
&& pfh
->pfil_arg
== arg
)