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>
43 #include <net/netmsg2.h>
45 #define PFIL_CFGPORT cpu_portfn(0)
47 #define PFIL_GETMPLOCK(pfh) \
49 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
53 #define PFIL_RELMPLOCK(pfh) \
55 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
60 * The packet filter hooks are designed for anything to call them to
61 * possibly intercept the packet.
63 struct packet_filter_hook
{
64 TAILQ_ENTRY(packet_filter_hook
) pfil_link
;
65 pfil_func_t pfil_func
;
71 struct netmsg pfil_nmsg
;
72 pfil_func_t pfil_func
;
75 struct pfil_head
*pfil_ph
;
78 static LIST_HEAD(, pfil_head
) pfil_head_list
=
79 LIST_HEAD_INITIALIZER(&pfil_head_list
);
81 static pfil_list_t
*pfil_list_alloc(void);
82 static void pfil_list_free(pfil_list_t
*);
83 static void pfil_list_dup(const pfil_list_t
*, pfil_list_t
*,
84 const struct packet_filter_hook
*);
85 static void pfil_list_add(pfil_list_t
*, pfil_func_t
, void *, int);
86 static struct packet_filter_hook
*
87 pfil_list_find(const pfil_list_t
*, pfil_func_t
,
90 static void pfil_remove_hook_dispatch(struct netmsg
*);
91 static void pfil_add_hook_dispatch(struct netmsg
*);
94 * pfil_run_hooks() runs the specified packet filter hooks.
97 pfil_run_hooks(struct pfil_head
*ph
, struct mbuf
**mp
, struct ifnet
*ifp
,
100 struct packet_filter_hook
*pfh
;
101 struct mbuf
*m
= *mp
;
107 else if (dir
== PFIL_OUT
)
110 return 0; /* XXX panic? */
112 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
113 if (pfh
->pfil_func
!= NULL
) {
115 rv
= pfh
->pfil_func(pfh
->pfil_arg
, &m
, ifp
, dir
);
118 if (rv
!= 0 || m
== NULL
)
128 * pfil_head_register() registers a pfil_head with the packet filter
132 pfil_head_register(struct pfil_head
*ph
)
134 struct pfil_head
*lph
;
136 LIST_FOREACH(lph
, &pfil_head_list
, ph_list
) {
137 if (ph
->ph_type
== lph
->ph_type
&&
138 ph
->ph_un
.phu_val
== lph
->ph_un
.phu_val
)
142 ph
->ph_in
= pfil_list_alloc();
143 ph
->ph_out
= pfil_list_alloc();
146 LIST_INSERT_HEAD(&pfil_head_list
, ph
, ph_list
);
152 * pfil_head_unregister() removes a pfil_head from the packet filter
156 pfil_head_unregister(struct pfil_head
*pfh
)
158 LIST_REMOVE(pfh
, ph_list
);
163 * pfil_head_get() returns the pfil_head for a given key/dlt.
166 pfil_head_get(int type
, u_long val
)
168 struct pfil_head
*ph
;
170 LIST_FOREACH(ph
, &pfil_head_list
, ph_list
) {
171 if (ph
->ph_type
== type
&& ph
->ph_un
.phu_val
== val
)
178 pfil_add_hook_dispatch(struct netmsg
*nmsg
)
180 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
181 pfil_func_t func
= pfilmsg
->pfil_func
;
182 void *arg
= pfilmsg
->pfil_arg
;
183 int flags
= pfilmsg
->pfil_flags
;
184 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
185 const struct packet_filter_hook
*pfh
;
186 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
187 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
190 /* This probably should not happen ... */
191 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
192 goto reply
; /* XXX panic? */
195 * If pfil hooks exist on any of the requested lists,
198 if (flags
& PFIL_IN
) {
199 pfh
= pfil_list_find(ph
->ph_in
, func
, arg
);
205 if (flags
& PFIL_OUT
) {
206 pfh
= pfil_list_find(ph
->ph_out
, func
, arg
);
214 * Duplicate the requested lists, install new hooks
217 if (flags
& PFIL_IN
) {
218 list_in
= pfil_list_alloc();
219 pfil_list_dup(ph
->ph_in
, list_in
, NULL
);
220 pfil_list_add(list_in
, func
, arg
, flags
& ~PFIL_OUT
);
222 if (flags
& PFIL_OUT
) {
223 list_out
= pfil_list_alloc();
224 pfil_list_dup(ph
->ph_out
, list_out
, NULL
);
225 pfil_list_add(list_out
, func
, arg
, flags
& ~PFIL_IN
);
229 * Switch list pointers, but keep the old ones
231 if (list_in
!= NULL
) {
232 old_list_in
= ph
->ph_in
;
235 if (list_out
!= NULL
) {
236 old_list_out
= ph
->ph_out
;
237 ph
->ph_out
= list_out
;
241 * Wait until everyone has finished the old lists iteration
243 netmsg_service_sync();
247 * Now it is safe to free the old lists, since no one sees it
249 if (old_list_in
!= NULL
)
250 pfil_list_free(old_list_in
);
251 if (old_list_out
!= NULL
)
252 pfil_list_free(old_list_out
);
254 lwkt_replymsg(&nmsg
->nm_lmsg
, err
);
258 * pfil_add_hook() adds a function to the packet filter hook. the
260 * PFIL_IN call me on incoming packets
261 * PFIL_OUT call me on outgoing packets
262 * PFIL_ALL call me on all of the above
263 * PFIL_MPSAFE call me without BGL
266 pfil_add_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
268 struct netmsg_pfil pfilmsg
;
271 nmsg
= &pfilmsg
.pfil_nmsg
;
272 netmsg_init(nmsg
, &curthread
->td_msgport
, 0, pfil_add_hook_dispatch
);
273 pfilmsg
.pfil_func
= func
;
274 pfilmsg
.pfil_arg
= arg
;
275 pfilmsg
.pfil_flags
= flags
;
276 pfilmsg
.pfil_ph
= ph
;
278 return lwkt_domsg(PFIL_CFGPORT
, &nmsg
->nm_lmsg
, 0);
282 pfil_remove_hook_dispatch(struct netmsg
*nmsg
)
284 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
285 pfil_func_t func
= pfilmsg
->pfil_func
;
286 void *arg
= pfilmsg
->pfil_arg
;
287 int flags
= pfilmsg
->pfil_flags
;
288 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
289 struct packet_filter_hook
*skip_in
= NULL
, *skip_out
= NULL
;
290 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
291 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
294 /* This probably should not happen ... */
295 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
296 goto reply
; /* XXX panic? */
299 * The pfil hook should exist on all requested lists,
300 * if not just bail out
302 if (flags
& PFIL_IN
) {
303 skip_in
= pfil_list_find(ph
->ph_in
, func
, arg
);
309 if (flags
& PFIL_OUT
) {
310 skip_out
= pfil_list_find(ph
->ph_out
, func
, arg
);
318 * Duplicate the requested lists, but the pfil hook to
319 * be deleted is not copied
321 if (flags
& PFIL_IN
) {
322 KKASSERT(skip_in
!= NULL
);
323 list_in
= pfil_list_alloc();
324 pfil_list_dup(ph
->ph_in
, list_in
, skip_in
);
326 if (flags
& PFIL_OUT
) {
327 KKASSERT(skip_out
!= NULL
);
328 list_out
= pfil_list_alloc();
329 pfil_list_dup(ph
->ph_out
, list_out
, skip_out
);
333 * Switch list pointers, but keep the old ones
335 if (list_in
!= NULL
) {
336 old_list_in
= ph
->ph_in
;
339 if (list_out
!= NULL
) {
340 old_list_out
= ph
->ph_out
;
341 ph
->ph_out
= list_out
;
345 * Wait until everyone has finished the old lists iteration
347 if (TAILQ_EMPTY(ph
->ph_in
) && TAILQ_EMPTY(ph
->ph_out
))
349 netmsg_service_sync();
352 * Now it is safe to free the old lists, since no one sees it
354 if (old_list_in
!= NULL
)
355 pfil_list_free(old_list_in
);
356 if (old_list_out
!= NULL
)
357 pfil_list_free(old_list_out
);
359 lwkt_replymsg(&nmsg
->nm_lmsg
, err
);
363 * pfil_remove_hook removes a specific function from the packet filter
367 pfil_remove_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
369 struct netmsg_pfil pfilmsg
;
372 nmsg
= &pfilmsg
.pfil_nmsg
;
373 netmsg_init(nmsg
, &curthread
->td_msgport
, 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
->nm_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
)