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)
50 #define PFIL_GETMPLOCK(pfh) \
52 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
56 #define PFIL_RELMPLOCK(pfh) \
58 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
63 * The packet filter hooks are designed for anything to call them to
64 * possibly intercept the packet.
66 struct packet_filter_hook
{
67 TAILQ_ENTRY(packet_filter_hook
) pfil_link
;
68 pfil_func_t pfil_func
;
74 struct netmsg_base base
;
75 pfil_func_t pfil_func
;
78 struct pfil_head
*pfil_ph
;
81 static LIST_HEAD(, pfil_head
) pfil_head_list
=
82 LIST_HEAD_INITIALIZER(&pfil_head_list
);
84 static pfil_list_t
*pfil_list_alloc(void);
85 static void pfil_list_free(pfil_list_t
*);
86 static void pfil_list_dup(const pfil_list_t
*, pfil_list_t
*,
87 const struct packet_filter_hook
*);
88 static void pfil_list_add(pfil_list_t
*, pfil_func_t
, void *, int);
89 static struct packet_filter_hook
*
90 pfil_list_find(const pfil_list_t
*, pfil_func_t
,
93 static void pfil_remove_hook_dispatch(netmsg_t
);
94 static void pfil_add_hook_dispatch(netmsg_t
);
96 int filters_default_to_accept
= 0;
97 SYSCTL_INT(_net
, OID_AUTO
, filters_default_to_accept
, CTLFLAG_RW
,
98 &filters_default_to_accept
, 0,
99 "cause ipfw* modules to not block by default");
100 TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept
);
103 * pfil_run_hooks() runs the specified packet filter hooks.
106 pfil_run_hooks(struct pfil_head
*ph
, struct mbuf
**mp
, struct ifnet
*ifp
,
109 struct packet_filter_hook
*pfh
;
110 struct mbuf
*m
= *mp
;
116 else if (dir
== PFIL_OUT
)
119 return 0; /* XXX panic? */
121 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
122 if (pfh
->pfil_func
!= NULL
) {
124 rv
= pfh
->pfil_func(pfh
->pfil_arg
, &m
, ifp
, dir
);
127 if (rv
!= 0 || m
== NULL
)
137 * pfil_head_register() registers a pfil_head with the packet filter
141 pfil_head_register(struct pfil_head
*ph
)
143 struct pfil_head
*lph
;
145 LIST_FOREACH(lph
, &pfil_head_list
, ph_list
) {
146 if (ph
->ph_type
== lph
->ph_type
&&
147 ph
->ph_un
.phu_val
== lph
->ph_un
.phu_val
)
151 ph
->ph_in
= pfil_list_alloc();
152 ph
->ph_out
= pfil_list_alloc();
155 LIST_INSERT_HEAD(&pfil_head_list
, ph
, ph_list
);
161 * pfil_head_unregister() removes a pfil_head from the packet filter
165 pfil_head_unregister(struct pfil_head
*pfh
)
167 LIST_REMOVE(pfh
, ph_list
);
172 * pfil_head_get() returns the pfil_head for a given key/dlt.
175 pfil_head_get(int type
, u_long val
)
177 struct pfil_head
*ph
;
179 LIST_FOREACH(ph
, &pfil_head_list
, ph_list
) {
180 if (ph
->ph_type
== type
&& ph
->ph_un
.phu_val
== val
)
187 pfil_add_hook_dispatch(netmsg_t nmsg
)
189 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
190 pfil_func_t func
= pfilmsg
->pfil_func
;
191 void *arg
= pfilmsg
->pfil_arg
;
192 int flags
= pfilmsg
->pfil_flags
;
193 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
194 const struct packet_filter_hook
*pfh
;
195 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
196 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
199 /* This probably should not happen ... */
200 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
201 goto reply
; /* XXX panic? */
204 * If pfil hooks exist on any of the requested lists,
207 if (flags
& PFIL_IN
) {
208 pfh
= pfil_list_find(ph
->ph_in
, func
, arg
);
214 if (flags
& PFIL_OUT
) {
215 pfh
= pfil_list_find(ph
->ph_out
, func
, arg
);
223 * Duplicate the requested lists, install new hooks
226 if (flags
& PFIL_IN
) {
227 list_in
= pfil_list_alloc();
228 pfil_list_dup(ph
->ph_in
, list_in
, NULL
);
229 pfil_list_add(list_in
, func
, arg
, flags
& ~PFIL_OUT
);
231 if (flags
& PFIL_OUT
) {
232 list_out
= pfil_list_alloc();
233 pfil_list_dup(ph
->ph_out
, list_out
, NULL
);
234 pfil_list_add(list_out
, func
, arg
, flags
& ~PFIL_IN
);
238 * Switch list pointers, but keep the old ones
240 if (list_in
!= NULL
) {
241 old_list_in
= ph
->ph_in
;
244 if (list_out
!= NULL
) {
245 old_list_out
= ph
->ph_out
;
246 ph
->ph_out
= list_out
;
250 * Wait until everyone has finished the old lists iteration
252 netmsg_service_sync();
256 * Now it is safe to free the old lists, since no one sees it
258 if (old_list_in
!= NULL
)
259 pfil_list_free(old_list_in
);
260 if (old_list_out
!= NULL
)
261 pfil_list_free(old_list_out
);
263 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
267 * pfil_add_hook() adds a function to the packet filter hook. the
269 * PFIL_IN call me on incoming packets
270 * PFIL_OUT call me on outgoing packets
271 * PFIL_ALL call me on all of the above
272 * PFIL_MPSAFE call me without BGL
275 pfil_add_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
277 struct netmsg_pfil pfilmsg
;
281 nmsg
= &pfilmsg
.base
;
282 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
283 0, pfil_add_hook_dispatch
);
284 pfilmsg
.pfil_func
= func
;
285 pfilmsg
.pfil_arg
= arg
;
286 pfilmsg
.pfil_flags
= flags
;
287 pfilmsg
.pfil_ph
= ph
;
289 error
= lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
294 pfil_remove_hook_dispatch(netmsg_t nmsg
)
296 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
297 pfil_func_t func
= pfilmsg
->pfil_func
;
298 void *arg
= pfilmsg
->pfil_arg
;
299 int flags
= pfilmsg
->pfil_flags
;
300 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
301 struct packet_filter_hook
*skip_in
= NULL
, *skip_out
= NULL
;
302 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
303 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
306 /* This probably should not happen ... */
307 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
308 goto reply
; /* XXX panic? */
311 * The pfil hook should exist on all requested lists,
312 * if not just bail out
314 if (flags
& PFIL_IN
) {
315 skip_in
= pfil_list_find(ph
->ph_in
, func
, arg
);
321 if (flags
& PFIL_OUT
) {
322 skip_out
= pfil_list_find(ph
->ph_out
, func
, arg
);
330 * Duplicate the requested lists, but the pfil hook to
331 * be deleted is not copied
333 if (flags
& PFIL_IN
) {
334 KKASSERT(skip_in
!= NULL
);
335 list_in
= pfil_list_alloc();
336 pfil_list_dup(ph
->ph_in
, list_in
, skip_in
);
338 if (flags
& PFIL_OUT
) {
339 KKASSERT(skip_out
!= NULL
);
340 list_out
= pfil_list_alloc();
341 pfil_list_dup(ph
->ph_out
, list_out
, skip_out
);
345 * Switch list pointers, but keep the old ones
347 if (list_in
!= NULL
) {
348 old_list_in
= ph
->ph_in
;
351 if (list_out
!= NULL
) {
352 old_list_out
= ph
->ph_out
;
353 ph
->ph_out
= list_out
;
357 * Wait until everyone has finished the old lists iteration
359 if (TAILQ_EMPTY(ph
->ph_in
) && TAILQ_EMPTY(ph
->ph_out
))
361 netmsg_service_sync();
364 * Now it is safe to free the old lists, since no one sees it
366 if (old_list_in
!= NULL
)
367 pfil_list_free(old_list_in
);
368 if (old_list_out
!= NULL
)
369 pfil_list_free(old_list_out
);
371 lwkt_replymsg(&nmsg
->base
.lmsg
, err
);
375 * pfil_remove_hook removes a specific function from the packet filter
379 pfil_remove_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
381 struct netmsg_pfil pfilmsg
;
384 nmsg
= &pfilmsg
.base
;
385 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
386 0, pfil_remove_hook_dispatch
);
387 pfilmsg
.pfil_func
= func
;
388 pfilmsg
.pfil_arg
= arg
;
389 pfilmsg
.pfil_flags
= flags
;
390 pfilmsg
.pfil_ph
= ph
;
392 return lwkt_domsg(PFIL_CFGPORT
, &nmsg
->lmsg
, 0);
396 pfil_list_add(pfil_list_t
*list
, pfil_func_t func
, void *arg
, int flags
)
398 struct packet_filter_hook
*pfh
;
400 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
402 pfh
= kmalloc(sizeof(*pfh
), M_IFADDR
, M_WAITOK
);
404 pfh
->pfil_func
= func
;
406 pfh
->pfil_flags
= flags
;
409 * Insert the input list in reverse order of the output list
410 * so that the same path is followed in or out of the kernel.
413 TAILQ_INSERT_HEAD(list
, pfh
, pfil_link
);
415 TAILQ_INSERT_TAIL(list
, pfh
, pfil_link
);
419 pfil_list_dup(const pfil_list_t
*from
, pfil_list_t
*to
,
420 const struct packet_filter_hook
*skip
)
422 struct packet_filter_hook
*pfh_to
, *pfh_from
;
424 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
425 KKASSERT(TAILQ_EMPTY(to
));
427 TAILQ_FOREACH(pfh_from
, from
, pfil_link
) {
428 if (pfh_from
== skip
)
431 pfh_to
= kmalloc(sizeof(*pfh_to
), M_IFADDR
, M_WAITOK
);
432 bcopy(pfh_from
, pfh_to
, sizeof(*pfh_to
));
434 TAILQ_INSERT_TAIL(to
, pfh_to
, pfil_link
);
439 pfil_list_alloc(void)
443 list
= kmalloc(sizeof(*list
), M_IFADDR
, M_WAITOK
);
449 pfil_list_free(pfil_list_t
*list
)
451 struct packet_filter_hook
*pfh
;
453 while ((pfh
= TAILQ_FIRST(list
)) != NULL
) {
454 TAILQ_REMOVE(list
, pfh
, pfil_link
);
455 kfree(pfh
, M_IFADDR
);
457 kfree(list
, M_IFADDR
);
460 static struct packet_filter_hook
*
461 pfil_list_find(const pfil_list_t
*list
, pfil_func_t func
, const void *arg
)
463 struct packet_filter_hook
*pfh
;
465 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
467 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
468 if (pfh
->pfil_func
== func
&& pfh
->pfil_arg
== arg
)