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>
43 #include <net/if_var.h>
45 #include <net/netmsg2.h>
46 #include <net/netisr2.h>
47 #include <sys/mplock2.h>
49 #define PFIL_CFGPORT netisr_cpuport(0)
52 * The packet filter hooks are designed for anything to call them to
53 * possibly intercept the packet.
55 struct packet_filter_hook
{
56 TAILQ_ENTRY(packet_filter_hook
) pfil_link
;
57 pfil_func_t pfil_func
;
63 struct netmsg_base base
;
64 pfil_func_t pfil_func
;
67 struct pfil_head
*pfil_ph
;
70 static LIST_HEAD(, pfil_head
) pfil_head_list
=
71 LIST_HEAD_INITIALIZER(&pfil_head_list
);
73 static pfil_list_t
*pfil_list_alloc(void);
74 static void pfil_list_free(pfil_list_t
*);
75 static void pfil_list_dup(const pfil_list_t
*, pfil_list_t
*,
76 const struct packet_filter_hook
*);
77 static void pfil_list_add(pfil_list_t
*, pfil_func_t
, void *, int);
78 static struct packet_filter_hook
*
79 pfil_list_find(const pfil_list_t
*, pfil_func_t
,
82 static void pfil_remove_hook_dispatch(netmsg_t
);
83 static void pfil_add_hook_dispatch(netmsg_t
);
85 int filters_default_to_accept
= 0;
86 SYSCTL_INT(_net
, OID_AUTO
, filters_default_to_accept
, CTLFLAG_RW
,
87 &filters_default_to_accept
, 0,
88 "cause ipfw* modules to not block by default");
89 TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept
);
92 * pfil_run_hooks() runs the specified packet filter hooks.
95 pfil_run_hooks(struct pfil_head
*ph
, struct mbuf
**mp
, struct ifnet
*ifp
,
98 struct packet_filter_hook
*pfh
;
105 else if (dir
== PFIL_OUT
)
108 return 0; /* XXX panic? */
110 /* Make sure 'list' is really used. */
112 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
113 if (pfh
->pfil_func
!= NULL
) {
114 rv
= pfh
->pfil_func(pfh
->pfil_arg
, &m
, ifp
, dir
);
115 if (rv
!= 0 || m
== NULL
)
125 * pfil_head_register() registers a pfil_head with the packet filter
129 pfil_head_register(struct pfil_head
*ph
)
131 struct pfil_head
*lph
;
133 LIST_FOREACH(lph
, &pfil_head_list
, ph_list
) {
134 if (ph
->ph_type
== lph
->ph_type
&&
135 ph
->ph_un
.phu_val
== lph
->ph_un
.phu_val
)
139 ph
->ph_in
= pfil_list_alloc();
140 ph
->ph_out
= pfil_list_alloc();
143 LIST_INSERT_HEAD(&pfil_head_list
, ph
, ph_list
);
149 * pfil_head_unregister() removes a pfil_head from the packet filter
153 pfil_head_unregister(struct pfil_head
*pfh
)
155 LIST_REMOVE(pfh
, ph_list
);
160 * pfil_head_get() returns the pfil_head for a given key/dlt.
163 pfil_head_get(int type
, u_long val
)
165 struct pfil_head
*ph
;
167 LIST_FOREACH(ph
, &pfil_head_list
, ph_list
) {
168 if (ph
->ph_type
== type
&& ph
->ph_un
.phu_val
== val
)
175 pfil_add_hook_dispatch(netmsg_t nmsg
)
177 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
178 pfil_func_t func
= pfilmsg
->pfil_func
;
179 void *arg
= pfilmsg
->pfil_arg
;
180 int flags
= pfilmsg
->pfil_flags
;
181 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
182 const struct packet_filter_hook
*pfh
;
183 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
184 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
187 /* This probably should not happen ... */
188 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
189 goto reply
; /* XXX panic? */
192 * If pfil hooks exist on any of the requested lists,
195 if (flags
& PFIL_IN
) {
196 pfh
= pfil_list_find(ph
->ph_in
, func
, arg
);
202 if (flags
& PFIL_OUT
) {
203 pfh
= pfil_list_find(ph
->ph_out
, func
, arg
);
211 * Duplicate the requested lists, install new hooks
214 if (flags
& PFIL_IN
) {
215 list_in
= pfil_list_alloc();
216 pfil_list_dup(ph
->ph_in
, list_in
, NULL
);
217 pfil_list_add(list_in
, func
, arg
, flags
& ~PFIL_OUT
);
219 if (flags
& PFIL_OUT
) {
220 list_out
= pfil_list_alloc();
221 pfil_list_dup(ph
->ph_out
, list_out
, NULL
);
222 pfil_list_add(list_out
, func
, arg
, flags
& ~PFIL_IN
);
226 * Switch list pointers, but keep the old ones
228 if (list_in
!= NULL
) {
229 old_list_in
= ph
->ph_in
;
232 if (list_out
!= NULL
) {
233 old_list_out
= ph
->ph_out
;
234 ph
->ph_out
= list_out
;
238 * Wait until everyone has finished the old lists iteration
240 netmsg_service_sync();
244 * Now it is safe to free the old lists, since no one sees it
246 if (old_list_in
!= NULL
)
247 pfil_list_free(old_list_in
);
248 if (old_list_out
!= NULL
)
249 pfil_list_free(old_list_out
);
251 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
255 * pfil_add_hook() adds a function to the packet filter hook. the
257 * PFIL_IN call me on incoming packets
258 * PFIL_OUT call me on outgoing packets
259 * PFIL_ALL call me on all of the above
262 pfil_add_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
264 struct netmsg_pfil pfilmsg
;
268 nmsg
= &pfilmsg
.base
;
269 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
270 0, pfil_add_hook_dispatch
);
271 pfilmsg
.pfil_func
= func
;
272 pfilmsg
.pfil_arg
= arg
;
273 pfilmsg
.pfil_flags
= flags
;
274 pfilmsg
.pfil_ph
= ph
;
276 error
= lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
281 pfil_remove_hook_dispatch(netmsg_t nmsg
)
283 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
284 pfil_func_t func
= pfilmsg
->pfil_func
;
285 void *arg
= pfilmsg
->pfil_arg
;
286 int flags
= pfilmsg
->pfil_flags
;
287 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
288 struct packet_filter_hook
*skip_in
= NULL
, *skip_out
= NULL
;
289 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
290 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
293 /* This probably should not happen ... */
294 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
295 goto reply
; /* XXX panic? */
298 * The pfil hook should exist on all requested lists,
299 * if not just bail out
301 if (flags
& PFIL_IN
) {
302 skip_in
= pfil_list_find(ph
->ph_in
, func
, arg
);
308 if (flags
& PFIL_OUT
) {
309 skip_out
= pfil_list_find(ph
->ph_out
, func
, arg
);
317 * Duplicate the requested lists, but the pfil hook to
318 * be deleted is not copied
320 if (flags
& PFIL_IN
) {
321 KKASSERT(skip_in
!= NULL
);
322 list_in
= pfil_list_alloc();
323 pfil_list_dup(ph
->ph_in
, list_in
, skip_in
);
325 if (flags
& PFIL_OUT
) {
326 KKASSERT(skip_out
!= NULL
);
327 list_out
= pfil_list_alloc();
328 pfil_list_dup(ph
->ph_out
, list_out
, skip_out
);
332 * Switch list pointers, but keep the old ones
334 if (list_in
!= NULL
) {
335 old_list_in
= ph
->ph_in
;
338 if (list_out
!= NULL
) {
339 old_list_out
= ph
->ph_out
;
340 ph
->ph_out
= list_out
;
344 * Wait until everyone has finished the old lists iteration
346 if (TAILQ_EMPTY(ph
->ph_in
) && TAILQ_EMPTY(ph
->ph_out
))
348 netmsg_service_sync();
351 * Now it is safe to free the old lists, since no one sees it
353 if (old_list_in
!= NULL
)
354 pfil_list_free(old_list_in
);
355 if (old_list_out
!= NULL
)
356 pfil_list_free(old_list_out
);
358 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
362 * pfil_remove_hook removes a specific function from the packet filter
366 pfil_remove_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
368 struct netmsg_pfil pfilmsg
;
371 nmsg
= &pfilmsg
.base
;
372 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
373 0, pfil_remove_hook_dispatch
);
374 pfilmsg
.pfil_func
= func
;
375 pfilmsg
.pfil_arg
= arg
;
376 pfilmsg
.pfil_flags
= flags
;
377 pfilmsg
.pfil_ph
= ph
;
379 return lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
383 pfil_list_add(pfil_list_t
*list
, pfil_func_t func
, void *arg
, int flags
)
385 struct packet_filter_hook
*pfh
;
387 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
389 pfh
= kmalloc(sizeof(*pfh
), M_IFADDR
, M_WAITOK
);
391 pfh
->pfil_func
= func
;
393 pfh
->pfil_flags
= flags
;
396 * Insert the input list in reverse order of the output list
397 * so that the same path is followed in or out of the kernel.
400 TAILQ_INSERT_HEAD(list
, pfh
, pfil_link
);
402 TAILQ_INSERT_TAIL(list
, pfh
, pfil_link
);
406 pfil_list_dup(const pfil_list_t
*from
, pfil_list_t
*to
,
407 const struct packet_filter_hook
*skip
)
409 struct packet_filter_hook
*pfh_to
, *pfh_from
;
411 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
412 KKASSERT(TAILQ_EMPTY(to
));
414 TAILQ_FOREACH(pfh_from
, from
, pfil_link
) {
415 if (pfh_from
== skip
)
418 pfh_to
= kmalloc(sizeof(*pfh_to
), M_IFADDR
, M_WAITOK
);
419 bcopy(pfh_from
, pfh_to
, sizeof(*pfh_to
));
421 TAILQ_INSERT_TAIL(to
, pfh_to
, pfil_link
);
426 pfil_list_alloc(void)
430 list
= kmalloc(sizeof(*list
), M_IFADDR
, M_WAITOK
);
436 pfil_list_free(pfil_list_t
*list
)
438 struct packet_filter_hook
*pfh
;
440 while ((pfh
= TAILQ_FIRST(list
)) != NULL
) {
441 TAILQ_REMOVE(list
, pfh
, pfil_link
);
442 kfree(pfh
, M_IFADDR
);
444 kfree(list
, M_IFADDR
);
447 static struct packet_filter_hook
*
448 pfil_list_find(const pfil_list_t
*list
, pfil_func_t func
, const void *arg
)
450 struct packet_filter_hook
*pfh
;
452 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
454 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
455 if (pfh
->pfil_func
== func
&& pfh
->pfil_arg
== arg
)