pfil.c revision 1.16
1/*	$NetBSD: pfil.c,v 1.16 2000/11/11 00:52:36 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 1996 Matthew R. Green
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/param.h>
32#include <sys/errno.h>
33#include <sys/malloc.h>
34#include <sys/socket.h>
35#include <sys/socketvar.h>
36#include <sys/systm.h>
37#include <sys/proc.h>
38#include <sys/queue.h>
39
40#include <net/if.h>
41#include <net/pfil.h>
42
43static int pfil_list_add(pfil_list_t *,
44    int (*)(void *, struct mbuf **, struct ifnet *, int), void *, int);
45
46static int pfil_list_remove(pfil_list_t *,
47    int (*)(void *, struct mbuf **, struct ifnet *, int), void *);
48
49LIST_HEAD(, pfil_head) pfil_head_list =
50    LIST_HEAD_INITIALIZER(&pfil_head_list);
51
52/*
53 * pfil_run_hooks() runs the specified packet filter hooks.
54 */
55int
56pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
57    int dir)
58{
59	struct packet_filter_hook *pfh;
60	struct mbuf *m = *mp;
61	int rv = 0;
62
63	for (pfh = pfil_hook_get(dir, ph); pfh != NULL;
64	     pfh = TAILQ_NEXT(pfh, pfil_link)) {
65		if (pfh->pfil_func != NULL) {
66			rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir);
67			if (rv != 0 || m == NULL)
68				break;
69		}
70	}
71
72	*mp = m;
73	return (rv);
74}
75
76/*
77 * pfil_head_register() registers a pfil_head with the packet filter
78 * hook mechanism.
79 */
80int
81pfil_head_register(struct pfil_head *ph)
82{
83	struct pfil_head *lph;
84
85	for (lph = LIST_FIRST(&pfil_head_list); lph != NULL;
86	     lph = LIST_NEXT(lph, ph_list)) {
87		if (lph->ph_key == ph->ph_key &&
88		    lph->ph_dlt == ph->ph_dlt)
89			return EEXIST;
90	}
91
92	TAILQ_INIT(&ph->ph_in);
93	TAILQ_INIT(&ph->ph_out);
94
95	LIST_INSERT_HEAD(&pfil_head_list, ph, ph_list);
96
97	return (0);
98}
99
100/*
101 * pfil_head_unregister() removes a pfil_head from the packet filter
102 * hook mechanism.
103 */
104int
105pfil_head_unregister(struct pfil_head *pfh)
106{
107
108	LIST_REMOVE(pfh, ph_list);
109	return (0);
110}
111
112/*
113 * pfil_head_get() returns the pfil_head for a given key/dlt.
114 */
115struct pfil_head *
116pfil_head_get(void *key, int dlt)
117{
118	struct pfil_head *ph;
119
120	for (ph = LIST_FIRST(&pfil_head_list); ph != NULL;
121	     ph = LIST_NEXT(ph, ph_list)) {
122		if (ph->ph_key == key && ph->ph_dlt == dlt)
123			break;
124	}
125
126	return (ph);
127}
128
129/*
130 * pfil_add_hook() adds a function to the packet filter hook.  the
131 * flags are:
132 *	PFIL_IN		call me on incoming packets
133 *	PFIL_OUT	call me on outgoing packets
134 *	PFIL_ALL	call me on all of the above
135 *	PFIL_WAITOK	OK to call malloc with M_WAITOK.
136 */
137int
138pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int),
139    void *arg, int flags, struct pfil_head *ph)
140{
141	int err = 0;
142
143	if (flags & PFIL_IN) {
144		err = pfil_list_add(&ph->ph_in, func, arg, flags & ~PFIL_OUT);
145		if (err)
146			return err;
147	}
148	if (flags & PFIL_OUT) {
149		err = pfil_list_add(&ph->ph_out, func, arg, flags & ~PFIL_IN);
150		if (err) {
151			if (flags & PFIL_IN)
152				pfil_list_remove(&ph->ph_in, func, arg);
153			return err;
154		}
155	}
156	return 0;
157}
158
159static int
160pfil_list_add(pfil_list_t *list,
161    int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg,
162    int flags)
163{
164	struct packet_filter_hook *pfh;
165
166	/*
167	 * First make sure the hook is not already there.
168	 */
169	for (pfh = TAILQ_FIRST(list); pfh != NULL;
170	     pfh = TAILQ_NEXT(pfh, pfil_link)) {
171		if (pfh->pfil_func == func &&
172		    pfh->pfil_arg == arg)
173			return EEXIST;
174	}
175
176	pfh = (struct packet_filter_hook *)malloc(sizeof(*pfh), M_IFADDR,
177	    (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
178	if (pfh == NULL)
179		return ENOMEM;
180
181	pfh->pfil_func = func;
182	pfh->pfil_arg  = arg;
183
184	/*
185	 * insert the input list in reverse order of the output list
186	 * so that the same path is followed in or out of the kernel.
187	 */
188	if (flags & PFIL_IN)
189		TAILQ_INSERT_HEAD(list, pfh, pfil_link);
190	else
191		TAILQ_INSERT_TAIL(list, pfh, pfil_link);
192
193	return 0;
194}
195
196/*
197 * pfil_remove_hook removes a specific function from the packet filter
198 * hook list.
199 */
200int
201pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int),
202    void *arg, int flags, struct pfil_head *ph)
203{
204	int err = 0;
205
206	if (flags & PFIL_IN)
207		err = pfil_list_remove(&ph->ph_in, func, arg);
208	if ((err == 0) && (flags & PFIL_OUT))
209		err = pfil_list_remove(&ph->ph_out, func, arg);
210	return err;
211}
212
213/*
214 * pfil_list_remove is an internal function that takes a function off the
215 * specified list.
216 */
217static int
218pfil_list_remove(pfil_list_t *list,
219    int (*func)(void *, struct mbuf **, struct ifnet *, int), void *arg)
220{
221	struct packet_filter_hook *pfh;
222
223	for (pfh = TAILQ_FIRST(list); pfh != NULL;
224	     pfh = TAILQ_NEXT(pfh, pfil_link)) {
225		if (pfh->pfil_func == func && pfh->pfil_arg == arg) {
226			TAILQ_REMOVE(list, pfh, pfil_link);
227			free(pfh, M_IFADDR);
228			return 0;
229		}
230	}
231	return ENOENT;
232}
233