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>
44 #include <sys/mplock2.h>
46 #define PFIL_CFGPORT cpu_portfn(0)
48 #define PFIL_GETMPLOCK(pfh) \
50 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
54 #define PFIL_RELMPLOCK(pfh) \
56 if (((pfh)->pfil_flags & PFIL_MPSAFE) == 0) \
61 * The packet filter hooks are designed for anything to call them to
62 * possibly intercept the packet.
64 struct packet_filter_hook
{
65 TAILQ_ENTRY(packet_filter_hook
) pfil_link
;
66 pfil_func_t pfil_func
;
72 struct netmsg pfil_nmsg
;
73 pfil_func_t pfil_func
;
76 struct pfil_head
*pfil_ph
;
79 static LIST_HEAD(, pfil_head
) pfil_head_list
=
80 LIST_HEAD_INITIALIZER(&pfil_head_list
);
82 static pfil_list_t
*pfil_list_alloc(void);
83 static void pfil_list_free(pfil_list_t
*);
84 static void pfil_list_dup(const pfil_list_t
*, pfil_list_t
*,
85 const struct packet_filter_hook
*);
86 static void pfil_list_add(pfil_list_t
*, pfil_func_t
, void *, int);
87 static struct packet_filter_hook
*
88 pfil_list_find(const pfil_list_t
*, pfil_func_t
,
91 static void pfil_remove_hook_dispatch(struct netmsg
*);
92 static void pfil_add_hook_dispatch(struct netmsg
*);
95 * pfil_run_hooks() runs the specified packet filter hooks.
98 pfil_run_hooks(struct pfil_head
*ph
, struct mbuf
**mp
, struct ifnet
*ifp
,
101 struct packet_filter_hook
*pfh
;
102 struct mbuf
*m
= *mp
;
108 else if (dir
== PFIL_OUT
)
111 return 0; /* XXX panic? */
113 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
114 if (pfh
->pfil_func
!= NULL
) {
116 rv
= pfh
->pfil_func(pfh
->pfil_arg
, &m
, ifp
, dir
);
119 if (rv
!= 0 || m
== NULL
)
129 * pfil_head_register() registers a pfil_head with the packet filter
133 pfil_head_register(struct pfil_head
*ph
)
135 struct pfil_head
*lph
;
137 LIST_FOREACH(lph
, &pfil_head_list
, ph_list
) {
138 if (ph
->ph_type
== lph
->ph_type
&&
139 ph
->ph_un
.phu_val
== lph
->ph_un
.phu_val
)
143 ph
->ph_in
= pfil_list_alloc();
144 ph
->ph_out
= pfil_list_alloc();
147 LIST_INSERT_HEAD(&pfil_head_list
, ph
, ph_list
);
153 * pfil_head_unregister() removes a pfil_head from the packet filter
157 pfil_head_unregister(struct pfil_head
*pfh
)
159 LIST_REMOVE(pfh
, ph_list
);
164 * pfil_head_get() returns the pfil_head for a given key/dlt.
167 pfil_head_get(int type
, u_long val
)
169 struct pfil_head
*ph
;
171 LIST_FOREACH(ph
, &pfil_head_list
, ph_list
) {
172 if (ph
->ph_type
== type
&& ph
->ph_un
.phu_val
== val
)
179 pfil_add_hook_dispatch(struct netmsg
*nmsg
)
181 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
182 pfil_func_t func
= pfilmsg
->pfil_func
;
183 void *arg
= pfilmsg
->pfil_arg
;
184 int flags
= pfilmsg
->pfil_flags
;
185 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
186 const struct packet_filter_hook
*pfh
;
187 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
188 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
191 /* This probably should not happen ... */
192 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
193 goto reply
; /* XXX panic? */
196 * If pfil hooks exist on any of the requested lists,
199 if (flags
& PFIL_IN
) {
200 pfh
= pfil_list_find(ph
->ph_in
, func
, arg
);
206 if (flags
& PFIL_OUT
) {
207 pfh
= pfil_list_find(ph
->ph_out
, func
, arg
);
215 * Duplicate the requested lists, install new hooks
218 if (flags
& PFIL_IN
) {
219 list_in
= pfil_list_alloc();
220 pfil_list_dup(ph
->ph_in
, list_in
, NULL
);
221 pfil_list_add(list_in
, func
, arg
, flags
& ~PFIL_OUT
);
223 if (flags
& PFIL_OUT
) {
224 list_out
= pfil_list_alloc();
225 pfil_list_dup(ph
->ph_out
, list_out
, NULL
);
226 pfil_list_add(list_out
, func
, arg
, flags
& ~PFIL_IN
);
230 * Switch list pointers, but keep the old ones
232 if (list_in
!= NULL
) {
233 old_list_in
= ph
->ph_in
;
236 if (list_out
!= NULL
) {
237 old_list_out
= ph
->ph_out
;
238 ph
->ph_out
= list_out
;
242 * Wait until everyone has finished the old lists iteration
244 netmsg_service_sync();
248 * Now it is safe to free the old lists, since no one sees it
250 if (old_list_in
!= NULL
)
251 pfil_list_free(old_list_in
);
252 if (old_list_out
!= NULL
)
253 pfil_list_free(old_list_out
);
255 lwkt_replymsg(&nmsg
->nm_lmsg
, err
);
259 * pfil_add_hook() adds a function to the packet filter hook. the
261 * PFIL_IN call me on incoming packets
262 * PFIL_OUT call me on outgoing packets
263 * PFIL_ALL call me on all of the above
264 * PFIL_MPSAFE call me without BGL
267 pfil_add_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
269 struct netmsg_pfil pfilmsg
;
272 nmsg
= &pfilmsg
.pfil_nmsg
;
273 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
274 0, pfil_add_hook_dispatch
);
275 pfilmsg
.pfil_func
= func
;
276 pfilmsg
.pfil_arg
= arg
;
277 pfilmsg
.pfil_flags
= flags
;
278 pfilmsg
.pfil_ph
= ph
;
280 return lwkt_domsg(PFIL_CFGPORT
, &nmsg
->nm_lmsg
, 0);
284 pfil_remove_hook_dispatch(struct netmsg
*nmsg
)
286 struct netmsg_pfil
*pfilmsg
= (struct netmsg_pfil
*)nmsg
;
287 pfil_func_t func
= pfilmsg
->pfil_func
;
288 void *arg
= pfilmsg
->pfil_arg
;
289 int flags
= pfilmsg
->pfil_flags
;
290 struct pfil_head
*ph
= pfilmsg
->pfil_ph
;
291 struct packet_filter_hook
*skip_in
= NULL
, *skip_out
= NULL
;
292 pfil_list_t
*list_in
= NULL
, *list_out
= NULL
;
293 pfil_list_t
*old_list_in
= NULL
, *old_list_out
= NULL
;
296 /* This probably should not happen ... */
297 if ((flags
& (PFIL_IN
| PFIL_OUT
)) == 0)
298 goto reply
; /* XXX panic? */
301 * The pfil hook should exist on all requested lists,
302 * if not just bail out
304 if (flags
& PFIL_IN
) {
305 skip_in
= pfil_list_find(ph
->ph_in
, func
, arg
);
311 if (flags
& PFIL_OUT
) {
312 skip_out
= pfil_list_find(ph
->ph_out
, func
, arg
);
320 * Duplicate the requested lists, but the pfil hook to
321 * be deleted is not copied
323 if (flags
& PFIL_IN
) {
324 KKASSERT(skip_in
!= NULL
);
325 list_in
= pfil_list_alloc();
326 pfil_list_dup(ph
->ph_in
, list_in
, skip_in
);
328 if (flags
& PFIL_OUT
) {
329 KKASSERT(skip_out
!= NULL
);
330 list_out
= pfil_list_alloc();
331 pfil_list_dup(ph
->ph_out
, list_out
, skip_out
);
335 * Switch list pointers, but keep the old ones
337 if (list_in
!= NULL
) {
338 old_list_in
= ph
->ph_in
;
341 if (list_out
!= NULL
) {
342 old_list_out
= ph
->ph_out
;
343 ph
->ph_out
= list_out
;
347 * Wait until everyone has finished the old lists iteration
349 if (TAILQ_EMPTY(ph
->ph_in
) && TAILQ_EMPTY(ph
->ph_out
))
351 netmsg_service_sync();
354 * Now it is safe to free the old lists, since no one sees it
356 if (old_list_in
!= NULL
)
357 pfil_list_free(old_list_in
);
358 if (old_list_out
!= NULL
)
359 pfil_list_free(old_list_out
);
361 lwkt_replymsg(&nmsg
->nm_lmsg
, err
);
365 * pfil_remove_hook removes a specific function from the packet filter
369 pfil_remove_hook(pfil_func_t func
, void *arg
, int flags
, struct pfil_head
*ph
)
371 struct netmsg_pfil pfilmsg
;
374 nmsg
= &pfilmsg
.pfil_nmsg
;
375 netmsg_init(nmsg
, NULL
, &curthread
->td_msgport
,
376 0, pfil_remove_hook_dispatch
);
377 pfilmsg
.pfil_func
= func
;
378 pfilmsg
.pfil_arg
= arg
;
379 pfilmsg
.pfil_flags
= flags
;
380 pfilmsg
.pfil_ph
= ph
;
382 return lwkt_domsg(PFIL_CFGPORT
, &nmsg
->nm_lmsg
, 0);
386 pfil_list_add(pfil_list_t
*list
, pfil_func_t func
, void *arg
, int flags
)
388 struct packet_filter_hook
*pfh
;
390 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
392 pfh
= kmalloc(sizeof(*pfh
), M_IFADDR
, M_WAITOK
);
394 pfh
->pfil_func
= func
;
396 pfh
->pfil_flags
= flags
;
399 * Insert the input list in reverse order of the output list
400 * so that the same path is followed in or out of the kernel.
403 TAILQ_INSERT_HEAD(list
, pfh
, pfil_link
);
405 TAILQ_INSERT_TAIL(list
, pfh
, pfil_link
);
409 pfil_list_dup(const pfil_list_t
*from
, pfil_list_t
*to
,
410 const struct packet_filter_hook
*skip
)
412 struct packet_filter_hook
*pfh_to
, *pfh_from
;
414 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
415 KKASSERT(TAILQ_EMPTY(to
));
417 TAILQ_FOREACH(pfh_from
, from
, pfil_link
) {
418 if (pfh_from
== skip
)
421 pfh_to
= kmalloc(sizeof(*pfh_to
), M_IFADDR
, M_WAITOK
);
422 bcopy(pfh_from
, pfh_to
, sizeof(*pfh_to
));
424 TAILQ_INSERT_TAIL(to
, pfh_to
, pfil_link
);
429 pfil_list_alloc(void)
433 list
= kmalloc(sizeof(*list
), M_IFADDR
, M_WAITOK
);
439 pfil_list_free(pfil_list_t
*list
)
441 struct packet_filter_hook
*pfh
;
443 while ((pfh
= TAILQ_FIRST(list
)) != NULL
) {
444 TAILQ_REMOVE(list
, pfh
, pfil_link
);
445 kfree(pfh
, M_IFADDR
);
447 kfree(list
, M_IFADDR
);
450 static struct packet_filter_hook
*
451 pfil_list_find(const pfil_list_t
*list
, pfil_func_t func
, const void *arg
)
453 struct packet_filter_hook
*pfh
;
455 KKASSERT(&curthread
->td_msgport
== PFIL_CFGPORT
);
457 TAILQ_FOREACH(pfh
, list
, pfil_link
) {
458 if (pfh
->pfil_func
== func
&& pfh
->pfil_arg
== arg
)