1 /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */
4 * Copyright (c) 1996 Matthew R. Green
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * 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
31 #include <sys/param.h>
32 #include <sys/errno.h>
33 #include <sys/malloc.h>
34 #include <sys/socket.h>
35 #include <sys/socketvar.h>
36 #include <sys/systm.h>
38 #include <sys/queue.h>
39 #include <sys/sysctl.h>
42 #include <net/if_var.h>
44 #include <net/netmsg2.h>
45 #include <net/netisr2.h>
47 #define PFIL_CFGPORT netisr_cpuport(0)
50 * The packet filter hooks are designed for anything to call them to
51 * possibly intercept the packet.
53 struct packet_filter_hook
{
54 TAILQ_ENTRY(packet_filter_hook
) pfil_link
;
55 pfil_func_t pfil_func
;
61 struct netmsg_base base
;
62 pfil_func_t pfil_func
;
65 struct pfil_head
*pfil_ph
;
68 static LIST_HEAD(, pfil_head
) pfil_head_list
=
69 LIST_HEAD_INITIALIZER(&pfil_head_list
);
71 static pfil_list_t
*pfil_list_alloc(void);
72 static void pfil_list_free(pfil_list_t
*);
73 static void pfil_list_dup(const pfil_list_t
*, pfil_list_t
*,
74 const struct packet_filter_hook
*);
75 static void pfil_list_add(pfil_list_t
*, pfil_func_t
, void *, int);
76 static struct packet_filter_hook
*
77 pfil_list_find(const pfil_list_t
*, pfil_func_t
,
80 static void pfil_remove_hook_dispatch(netmsg_t
);
81 static void pfil_add_hook_dispatch(netmsg_t
);
83 int filters_default_to_accept
= 0;
84 SYSCTL_INT(_net
, OID_AUTO
, filters_default_to_accept
, CTLFLAG_RW
,
85 &filters_default_to_accept
, 0,
86 "cause ipfw* modules to not block by default");
87 TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept
);
90 * pfil_run_hooks() runs the specified packet filter hooks.
93 pfil_run_hooks(struct pfil_head
*ph
, struct mbuf
**mp
, struct ifnet
*ifp
,
96 struct packet_filter_hook
*pfh
;
103 else if (dir
== PFIL_OUT
)
106 return 0; /* XXX panic? */
108 /* Make sure 'list' is really used. */
110 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
111 if (pfh
->pfil_func
!= NULL
) {
112 rv
= pfh
->pfil_func(pfh
->pfil_arg
, &m
, ifp
, dir
);
113 if (rv
!= 0 || m
== NULL
)
123 * pfil_head_register() registers a pfil_head with the packet filter
127 pfil_head_register(struct pfil_head
*ph
)
129 struct pfil_head
*lph
;
131 LIST_FOREACH(lph
, &pfil_head_list
, ph_list
) {
132 if (ph
->ph_type
== lph
->ph_type
&&
133 ph
->ph_un
.phu_val
== lph
->ph_un
.phu_val
)
137 ph
->ph_in
= pfil_list_alloc();
138 ph
->ph_out
= pfil_list_alloc();
141 LIST_INSERT_HEAD(&pfil_head_list
, ph
, ph_list
);
147 * pfil_head_unregister() removes a pfil_head from the packet filter
151 pfil_head_unregister(struct pfil_head
*pfh
)
153 LIST_REMOVE(pfh
, ph_list
);
158 * pfil_head_get() returns the pfil_head for a given key/dlt.
161 pfil_head_get(int type
, u_long val
)
163 struct pfil_head
*ph
;
165 LIST_FOREACH(ph
, &pfil_head_list
, ph_list
) {
166 if (ph
->ph_type
== type
&& ph
->ph_un
.phu_val
== val
)
173 pfil_add_hook_dispatch(netmsg_t nmsg
)
175 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
176 pfil_func_t func
= pfilmsg
->pfil_func
;
177 void *arg
= pfilmsg
->pfil_arg
;
178 int flags
= pfilmsg
->pfil_flags
;
179 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
180 const struct packet_filter_hook
*pfh
;
181 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
182 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
185 /* This probably should not happen ... */
186 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
187 goto reply
; /* XXX panic? */
190 * If pfil hooks exist on any of the requested lists,
193 if (flags
& PFIL_IN
) {
194 pfh
= pfil_list_find(ph
->ph_in
, func
, arg
);
200 if (flags
& PFIL_OUT
) {
201 pfh
= pfil_list_find(ph
->ph_out
, func
, arg
);
209 * Duplicate the requested lists, install new hooks
212 if (flags
& PFIL_IN
) {
213 list_in
= pfil_list_alloc();
214 pfil_list_dup(ph
->ph_in
, list_in
, NULL
);
215 pfil_list_add(list_in
, func
, arg
, flags
& ~PFIL_OUT
);
217 if (flags
& PFIL_OUT
) {
218 list_out
= pfil_list_alloc();
219 pfil_list_dup(ph
->ph_out
, list_out
, NULL
);
220 pfil_list_add(list_out
, func
, arg
, flags
& ~PFIL_IN
);
224 * Switch list pointers, but keep the old ones
226 if (list_in
!= NULL
) {
227 old_list_in
= ph
->ph_in
;
230 if (list_out
!= NULL
) {
231 old_list_out
= ph
->ph_out
;
232 ph
->ph_out
= list_out
;
236 * Wait until everyone has finished the old lists iteration
238 netmsg_service_sync();
242 * Now it is safe to free the old lists, since no one sees it
244 if (old_list_in
!= NULL
)
245 pfil_list_free(old_list_in
);
246 if (old_list_out
!= NULL
)
247 pfil_list_free(old_list_out
);
249 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
253 * pfil_add_hook() adds a function to the packet filter hook. the
255 * PFIL_IN call me on incoming packets
256 * PFIL_OUT call me on outgoing packets
257 * PFIL_ALL call me on all of the above
260 pfil_add_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
262 struct netmsg_pfil pfilmsg
;
266 nmsg
= &pfilmsg
.base
;
267 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
268 0, pfil_add_hook_dispatch
);
269 pfilmsg
.pfil_func
= func
;
270 pfilmsg
.pfil_arg
= arg
;
271 pfilmsg
.pfil_flags
= flags
;
272 pfilmsg
.pfil_ph
= ph
;
274 error
= lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
279 pfil_remove_hook_dispatch(netmsg_t nmsg
)
281 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
282 pfil_func_t func
= pfilmsg
->pfil_func
;
283 void *arg
= pfilmsg
->pfil_arg
;
284 int flags
= pfilmsg
->pfil_flags
;
285 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
286 struct packet_filter_hook
*skip_in
= NULL
, *skip_out
= NULL
;
287 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
288 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
291 /* This probably should not happen ... */
292 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
293 goto reply
; /* XXX panic? */
296 * The pfil hook should exist on all requested lists,
297 * if not just bail out
299 if (flags
& PFIL_IN
) {
300 skip_in
= pfil_list_find(ph
->ph_in
, func
, arg
);
306 if (flags
& PFIL_OUT
) {
307 skip_out
= pfil_list_find(ph
->ph_out
, func
, arg
);
315 * Duplicate the requested lists, but the pfil hook to
316 * be deleted is not copied
318 if (flags
& PFIL_IN
) {
319 KKASSERT(skip_in
!= NULL
);
320 list_in
= pfil_list_alloc();
321 pfil_list_dup(ph
->ph_in
, list_in
, skip_in
);
323 if (flags
& PFIL_OUT
) {
324 KKASSERT(skip_out
!= NULL
);
325 list_out
= pfil_list_alloc();
326 pfil_list_dup(ph
->ph_out
, list_out
, skip_out
);
330 * Switch list pointers, but keep the old ones
332 if (list_in
!= NULL
) {
333 old_list_in
= ph
->ph_in
;
336 if (list_out
!= NULL
) {
337 old_list_out
= ph
->ph_out
;
338 ph
->ph_out
= list_out
;
342 * Wait until everyone has finished the old lists iteration
344 if (TAILQ_EMPTY(ph
->ph_in
) && TAILQ_EMPTY(ph
->ph_out
))
346 netmsg_service_sync();
349 * Now it is safe to free the old lists, since no one sees it
351 if (old_list_in
!= NULL
)
352 pfil_list_free(old_list_in
);
353 if (old_list_out
!= NULL
)
354 pfil_list_free(old_list_out
);
356 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
360 * pfil_remove_hook removes a specific function from the packet filter
364 pfil_remove_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
366 struct netmsg_pfil pfilmsg
;
369 nmsg
= &pfilmsg
.base
;
370 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
371 0, pfil_remove_hook_dispatch
);
372 pfilmsg
.pfil_func
= func
;
373 pfilmsg
.pfil_arg
= arg
;
374 pfilmsg
.pfil_flags
= flags
;
375 pfilmsg
.pfil_ph
= ph
;
377 return lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
381 pfil_list_add(pfil_list_t
*list
, pfil_func_t func
, void *arg
, int flags
)
383 struct packet_filter_hook
*pfh
;
385 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
387 pfh
= kmalloc(sizeof(*pfh
), M_IFADDR
, M_WAITOK
);
389 pfh
->pfil_func
= func
;
391 pfh
->pfil_flags
= flags
;
394 * Insert the input list in reverse order of the output list
395 * so that the same path is followed in or out of the kernel.
398 TAILQ_INSERT_HEAD(list
, pfh
, pfil_link
);
400 TAILQ_INSERT_TAIL(list
, pfh
, pfil_link
);
404 pfil_list_dup(const pfil_list_t
*from
, pfil_list_t
*to
,
405 const struct packet_filter_hook
*skip
)
407 struct packet_filter_hook
*pfh_to
, *pfh_from
;
409 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
410 KKASSERT(TAILQ_EMPTY(to
));
412 TAILQ_FOREACH(pfh_from
, from
, pfil_link
) {
413 if (pfh_from
== skip
)
416 pfh_to
= kmalloc(sizeof(*pfh_to
), M_IFADDR
, M_WAITOK
);
417 bcopy(pfh_from
, pfh_to
, sizeof(*pfh_to
));
419 TAILQ_INSERT_TAIL(to
, pfh_to
, pfil_link
);
424 pfil_list_alloc(void)
428 list
= kmalloc(sizeof(*list
), M_IFADDR
, M_WAITOK
);
434 pfil_list_free(pfil_list_t
*list
)
436 struct packet_filter_hook
*pfh
;
438 while ((pfh
= TAILQ_FIRST(list
)) != NULL
) {
439 TAILQ_REMOVE(list
, pfh
, pfil_link
);
440 kfree(pfh
, M_IFADDR
);
442 kfree(list
, M_IFADDR
);
445 static struct packet_filter_hook
*
446 pfil_list_find(const pfil_list_t
*list
, pfil_func_t func
, const void *arg
)
448 struct packet_filter_hook
*pfh
;
450 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
452 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
453 if (pfh
->pfil_func
== func
&& pfh
->pfil_arg
== arg
)