pc64 - If appropriate, determine TSC frequency via CPUID.
[dragonfly.git] / sys / net / pfil.c
CommitLineData
9e99d0a2 1/* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */
77e0b69a 2/* $DragonFly: src/sys/net/pfil.c,v 1.14 2008/09/20 06:08:13 sephe Exp $ */
9e99d0a2
JR
3
4/*
5 * Copyright (c) 1996 Matthew R. Green
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
18 *
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
29 * SUCH DAMAGE.
30 */
31
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>
38#include <sys/proc.h>
39#include <sys/queue.h>
d0930e0d 40#include <sys/sysctl.h>
9e99d0a2
JR
41
42#include <net/if.h>
43#include <net/pfil.h>
f0fad8e1 44#include <net/netmsg2.h>
5337421c 45#include <net/netisr2.h>
684a93c4 46#include <sys/mplock2.h>
f0fad8e1 47
ec7f7fc8 48#define PFIL_CFGPORT netisr_cpuport(0)
77e0b69a 49
caa0af9f
SZ
50/*
51 * The packet filter hooks are designed for anything to call them to
52 * possibly intercept the packet.
53 */
54struct packet_filter_hook {
55 TAILQ_ENTRY(packet_filter_hook) pfil_link;
56 pfil_func_t pfil_func;
57 void *pfil_arg;
58 int pfil_flags;
59};
60
f0fad8e1 61struct netmsg_pfil {
002c1265 62 struct netmsg_base base;
f0fad8e1
SZ
63 pfil_func_t pfil_func;
64 void *pfil_arg;
65 int pfil_flags;
66 struct pfil_head *pfil_ph;
67};
9e99d0a2 68
d66da3ad
SZ
69static LIST_HEAD(, pfil_head) pfil_head_list =
70 LIST_HEAD_INITIALIZER(&pfil_head_list);
9e99d0a2 71
d66da3ad
SZ
72static pfil_list_t *pfil_list_alloc(void);
73static void pfil_list_free(pfil_list_t *);
74static void pfil_list_dup(const pfil_list_t *, pfil_list_t *,
75 const struct packet_filter_hook *);
76static void pfil_list_add(pfil_list_t *, pfil_func_t, void *, int);
77static struct packet_filter_hook *
78 pfil_list_find(const pfil_list_t *, pfil_func_t,
79 const void *);
80
002c1265
MD
81static void pfil_remove_hook_dispatch(netmsg_t);
82static void pfil_add_hook_dispatch(netmsg_t);
9e99d0a2 83
d0930e0d
MD
84int filters_default_to_accept = 0;
85SYSCTL_INT(_net, OID_AUTO, filters_default_to_accept, CTLFLAG_RW,
86 &filters_default_to_accept, 0,
87 "cause ipfw* modules to not block by default");
88TUNABLE_INT("net.filters_default_to_accept", &filters_default_to_accept);
89
9e99d0a2
JR
90/*
91 * pfil_run_hooks() runs the specified packet filter hooks.
92 */
93int
94pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
95 int dir)
96{
97 struct packet_filter_hook *pfh;
98 struct mbuf *m = *mp;
7f8565be 99 pfil_list_t *list;
9e99d0a2
JR
100 int rv = 0;
101
7f8565be 102 if (dir == PFIL_IN)
d66da3ad 103 list = ph->ph_in;
7f8565be 104 else if (dir == PFIL_OUT)
d66da3ad 105 list = ph->ph_out;
7f8565be
SZ
106 else
107 return 0; /* XXX panic? */
108
a9821961
SZ
109 /* Make sure 'list' is really used. */
110 cpu_ccfence();
7f8565be 111 TAILQ_FOREACH(pfh, list, pfil_link) {
9e99d0a2 112 if (pfh->pfil_func != NULL) {
77e0b69a 113 rv = pfh->pfil_func(pfh->pfil_arg, &m, ifp, dir);
9e99d0a2
JR
114 if (rv != 0 || m == NULL)
115 break;
116 }
117 }
118
119 *mp = m;
120 return (rv);
121}
122
123/*
124 * pfil_head_register() registers a pfil_head with the packet filter
125 * hook mechanism.
126 */
127int
128pfil_head_register(struct pfil_head *ph)
129{
130 struct pfil_head *lph;
131
94171836 132 LIST_FOREACH(lph, &pfil_head_list, ph_list) {
9e99d0a2
JR
133 if (ph->ph_type == lph->ph_type &&
134 ph->ph_un.phu_val == lph->ph_un.phu_val)
135 return EEXIST;
136 }
137
d66da3ad
SZ
138 ph->ph_in = pfil_list_alloc();
139 ph->ph_out = pfil_list_alloc();
afabe90c 140 ph->ph_hashooks = 0;
9e99d0a2
JR
141
142 LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list);
143
144 return (0);
145}
146
147/*
148 * pfil_head_unregister() removes a pfil_head from the packet filter
149 * hook mechanism.
150 */
151int
152pfil_head_unregister(struct pfil_head *pfh)
153{
9e99d0a2
JR
154 LIST_REMOVE(pfh, ph_list);
155 return (0);
156}
157
158/*
159 * pfil_head_get() returns the pfil_head for a given key/dlt.
160 */
161struct pfil_head *
162pfil_head_get(int type, u_long val)
163{
164 struct pfil_head *ph;
165
94171836
SZ
166 LIST_FOREACH(ph, &pfil_head_list, ph_list) {
167 if (ph->ph_type == type && ph->ph_un.phu_val == val)
9e99d0a2
JR
168 break;
169 }
9e99d0a2
JR
170 return (ph);
171}
172
f0fad8e1 173static void
002c1265 174pfil_add_hook_dispatch(netmsg_t nmsg)
9e99d0a2 175{
f0fad8e1
SZ
176 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg;
177 pfil_func_t func = pfilmsg->pfil_func;
178 void *arg = pfilmsg->pfil_arg;
179 int flags = pfilmsg->pfil_flags;
180 struct pfil_head *ph = pfilmsg->pfil_ph;
d66da3ad
SZ
181 const struct packet_filter_hook *pfh;
182 pfil_list_t *list_in = NULL, *list_out = NULL;
183 pfil_list_t *old_list_in = NULL, *old_list_out = NULL;
9e99d0a2
JR
184 int err = 0;
185
d66da3ad
SZ
186 /* This probably should not happen ... */
187 if ((flags & (PFIL_IN | PFIL_OUT)) == 0)
188 goto reply; /* XXX panic? */
189
190 /*
191 * If pfil hooks exist on any of the requested lists,
192 * then bail out.
193 */
9e99d0a2 194 if (flags & PFIL_IN) {
d66da3ad
SZ
195 pfh = pfil_list_find(ph->ph_in, func, arg);
196 if (pfh != NULL) {
197 err = EEXIST;
f0fad8e1 198 goto reply;
d66da3ad 199 }
9e99d0a2
JR
200 }
201 if (flags & PFIL_OUT) {
d66da3ad
SZ
202 pfh = pfil_list_find(ph->ph_out, func, arg);
203 if (pfh != NULL) {
204 err = EEXIST;
205 goto reply;
9e99d0a2
JR
206 }
207 }
d66da3ad
SZ
208
209 /*
210 * Duplicate the requested lists, install new hooks
211 * on the duplication
212 */
213 if (flags & PFIL_IN) {
214 list_in = pfil_list_alloc();
215 pfil_list_dup(ph->ph_in, list_in, NULL);
216 pfil_list_add(list_in, func, arg, flags & ~PFIL_OUT);
217 }
218 if (flags & PFIL_OUT) {
219 list_out = pfil_list_alloc();
220 pfil_list_dup(ph->ph_out, list_out, NULL);
221 pfil_list_add(list_out, func, arg, flags & ~PFIL_IN);
222 }
223
224 /*
225 * Switch list pointers, but keep the old ones
226 */
227 if (list_in != NULL) {
228 old_list_in = ph->ph_in;
229 ph->ph_in = list_in;
230 }
231 if (list_out != NULL) {
232 old_list_out = ph->ph_out;
233 ph->ph_out = list_out;
234 }
235
236 /*
237 * Wait until everyone has finished the old lists iteration
238 */
239 netmsg_service_sync();
240 ph->ph_hashooks = 1;
241
242 /*
243 * Now it is safe to free the old lists, since no one sees it
244 */
245 if (old_list_in != NULL)
246 pfil_list_free(old_list_in);
247 if (old_list_out != NULL)
248 pfil_list_free(old_list_out);
f0fad8e1 249reply:
002c1265 250 lwkt_replymsg(&nmsg->base.lmsg, err);
f0fad8e1
SZ
251}
252
253/*
254 * pfil_add_hook() adds a function to the packet filter hook. the
255 * flags are:
256 * PFIL_IN call me on incoming packets
257 * PFIL_OUT call me on outgoing packets
258 * PFIL_ALL call me on all of the above
f0fad8e1
SZ
259 */
260int
261pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
262{
263 struct netmsg_pfil pfilmsg;
002c1265
MD
264 netmsg_base_t nmsg;
265 int error;
f0fad8e1 266
002c1265 267 nmsg = &pfilmsg.base;
48e7b118
MD
268 netmsg_init(nmsg, NULL, &curthread->td_msgport,
269 0, pfil_add_hook_dispatch);
f0fad8e1
SZ
270 pfilmsg.pfil_func = func;
271 pfilmsg.pfil_arg = arg;
272 pfilmsg.pfil_flags = flags;
273 pfilmsg.pfil_ph = ph;
274
002c1265
MD
275 error = lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0);
276 return error;
9e99d0a2
JR
277}
278
d66da3ad 279static void
002c1265 280pfil_remove_hook_dispatch(netmsg_t nmsg)
9e99d0a2 281{
d66da3ad
SZ
282 struct netmsg_pfil *pfilmsg = (struct netmsg_pfil *)nmsg;
283 pfil_func_t func = pfilmsg->pfil_func;
284 void *arg = pfilmsg->pfil_arg;
285 int flags = pfilmsg->pfil_flags;
286 struct pfil_head *ph = pfilmsg->pfil_ph;
287 struct packet_filter_hook *skip_in = NULL, *skip_out = NULL;
288 pfil_list_t *list_in = NULL, *list_out = NULL;
289 pfil_list_t *old_list_in = NULL, *old_list_out = NULL;
290 int err = 0;
f0fad8e1 291
d66da3ad
SZ
292 /* This probably should not happen ... */
293 if ((flags & (PFIL_IN | PFIL_OUT)) == 0)
294 goto reply; /* XXX panic? */
9e99d0a2
JR
295
296 /*
d66da3ad
SZ
297 * The pfil hook should exist on all requested lists,
298 * if not just bail out
9e99d0a2 299 */
d66da3ad
SZ
300 if (flags & PFIL_IN) {
301 skip_in = pfil_list_find(ph->ph_in, func, arg);
302 if (!skip_in) {
303 err = ENOENT;
304 goto reply;
305 }
306 }
307 if (flags & PFIL_OUT) {
308 skip_out = pfil_list_find(ph->ph_out, func, arg);
309 if (!skip_out) {
310 err = ENOENT;
311 goto reply;
312 }
9e99d0a2
JR
313 }
314
d66da3ad
SZ
315 /*
316 * Duplicate the requested lists, but the pfil hook to
317 * be deleted is not copied
318 */
319 if (flags & PFIL_IN) {
320 KKASSERT(skip_in != NULL);
321 list_in = pfil_list_alloc();
322 pfil_list_dup(ph->ph_in, list_in, skip_in);
323 }
324 if (flags & PFIL_OUT) {
325 KKASSERT(skip_out != NULL);
326 list_out = pfil_list_alloc();
327 pfil_list_dup(ph->ph_out, list_out, skip_out);
328 }
9e99d0a2
JR
329
330 /*
d66da3ad 331 * Switch list pointers, but keep the old ones
9e99d0a2 332 */
d66da3ad
SZ
333 if (list_in != NULL) {
334 old_list_in = ph->ph_in;
335 ph->ph_in = list_in;
336 }
337 if (list_out != NULL) {
338 old_list_out = ph->ph_out;
339 ph->ph_out = list_out;
340 }
9e99d0a2 341
d66da3ad
SZ
342 /*
343 * Wait until everyone has finished the old lists iteration
344 */
345 if (TAILQ_EMPTY(ph->ph_in) && TAILQ_EMPTY(ph->ph_out))
346 ph->ph_hashooks = 0;
347 netmsg_service_sync();
f0fad8e1 348
d66da3ad
SZ
349 /*
350 * Now it is safe to free the old lists, since no one sees it
351 */
352 if (old_list_in != NULL)
353 pfil_list_free(old_list_in);
354 if (old_list_out != NULL)
355 pfil_list_free(old_list_out);
356reply:
002c1265 357 lwkt_replymsg(&nmsg->base.lmsg, err);
f0fad8e1
SZ
358}
359
9e99d0a2
JR
360/*
361 * pfil_remove_hook removes a specific function from the packet filter
362 * hook list.
363 */
364int
3bc01464 365pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
9e99d0a2 366{
f0fad8e1 367 struct netmsg_pfil pfilmsg;
002c1265 368 netmsg_base_t nmsg;
9e99d0a2 369
002c1265 370 nmsg = &pfilmsg.base;
48e7b118
MD
371 netmsg_init(nmsg, NULL, &curthread->td_msgport,
372 0, pfil_remove_hook_dispatch);
f0fad8e1
SZ
373 pfilmsg.pfil_func = func;
374 pfilmsg.pfil_arg = arg;
375 pfilmsg.pfil_flags = flags;
376 pfilmsg.pfil_ph = ph;
377
002c1265 378 return lwkt_domsg(PFIL_CFGPORT, &nmsg->lmsg, 0);
9e99d0a2
JR
379}
380
d66da3ad
SZ
381static void
382pfil_list_add(pfil_list_t *list, pfil_func_t func, void *arg, int flags)
9e99d0a2
JR
383{
384 struct packet_filter_hook *pfh;
afabe90c 385
f0fad8e1
SZ
386 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
387
d66da3ad
SZ
388 pfh = kmalloc(sizeof(*pfh), M_IFADDR, M_WAITOK);
389
390 pfh->pfil_func = func;
391 pfh->pfil_arg = arg;
77d9cd11 392 pfh->pfil_flags = flags;
d66da3ad
SZ
393
394 /*
395 * Insert the input list in reverse order of the output list
396 * so that the same path is followed in or out of the kernel.
397 */
398 if (flags & PFIL_IN)
399 TAILQ_INSERT_HEAD(list, pfh, pfil_link);
400 else
401 TAILQ_INSERT_TAIL(list, pfh, pfil_link);
402}
403
404static void
405pfil_list_dup(const pfil_list_t *from, pfil_list_t *to,
406 const struct packet_filter_hook *skip)
407{
408 struct packet_filter_hook *pfh_to, *pfh_from;
409
410 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
411 KKASSERT(TAILQ_EMPTY(to));
412
413 TAILQ_FOREACH(pfh_from, from, pfil_link) {
414 if (pfh_from == skip)
415 continue;
416
417 pfh_to = kmalloc(sizeof(*pfh_to), M_IFADDR, M_WAITOK);
418 bcopy(pfh_from, pfh_to, sizeof(*pfh_to));
419
420 TAILQ_INSERT_TAIL(to, pfh_to, pfil_link);
421 }
422}
423
424static pfil_list_t *
425pfil_list_alloc(void)
426{
427 pfil_list_t *list;
428
429 list = kmalloc(sizeof(*list), M_IFADDR, M_WAITOK);
430 TAILQ_INIT(list);
431 return list;
432}
433
434static void
435pfil_list_free(pfil_list_t *list)
436{
437 struct packet_filter_hook *pfh;
438
439 while ((pfh = TAILQ_FIRST(list)) != NULL) {
440 TAILQ_REMOVE(list, pfh, pfil_link);
441 kfree(pfh, M_IFADDR);
442 }
443 kfree(list, M_IFADDR);
444}
445
446static struct packet_filter_hook *
447pfil_list_find(const pfil_list_t *list, pfil_func_t func, const void *arg)
448{
449 struct packet_filter_hook *pfh;
450
451 KKASSERT(&curthread->td_msgport == PFIL_CFGPORT);
9e99d0a2 452
94171836 453 TAILQ_FOREACH(pfh, list, pfil_link) {
d66da3ad
SZ
454 if (pfh->pfil_func == func && pfh->pfil_arg == arg)
455 return pfh;
9e99d0a2 456 }
d66da3ad 457 return NULL;
9e99d0a2 458}