pfil.c revision 198198
1113656Sdeischen/*	$FreeBSD: head/sys/net/pfil.c 198198 2009-10-18 11:27:34Z rwatson $ */
2113656Sdeischen/*	$NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $	*/
3113656Sdeischen
4113656Sdeischen/*-
5113656Sdeischen * Copyright (c) 1996 Matthew R. Green
6113656Sdeischen * All rights reserved.
7113656Sdeischen *
8113656Sdeischen * Redistribution and use in source and binary forms, with or without
9113656Sdeischen * modification, are permitted provided that the following conditions
10113656Sdeischen * are met:
11113656Sdeischen * 1. Redistributions of source code must retain the above copyright
12113656Sdeischen *    notice, this list of conditions and the following disclaimer.
13113656Sdeischen * 2. Redistributions in binary form must reproduce the above copyright
14113656Sdeischen *    notice, this list of conditions and the following disclaimer in the
15113656Sdeischen *    documentation and/or other materials provided with the distribution.
16113656Sdeischen * 3. The name of the author may not be used to endorse or promote products
17113656Sdeischen *    derived from this software without specific prior written permission.
18113656Sdeischen *
19113656Sdeischen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20113656Sdeischen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21113656Sdeischen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22113656Sdeischen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23113656Sdeischen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24113656Sdeischen * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25113656Sdeischen * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26113656Sdeischen * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27113656Sdeischen * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28113656Sdeischen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29113656Sdeischen * SUCH DAMAGE.
30113656Sdeischen */
31113656Sdeischen
32113656Sdeischen#include <sys/param.h>
33113656Sdeischen#include <sys/kernel.h>
34113656Sdeischen#include <sys/errno.h>
35113656Sdeischen#include <sys/lock.h>
36113656Sdeischen#include <sys/malloc.h>
37113656Sdeischen#include <sys/rmlock.h>
38113656Sdeischen#include <sys/socket.h>
39113656Sdeischen#include <sys/socketvar.h>
40113656Sdeischen#include <sys/systm.h>
41113656Sdeischen#include <sys/condvar.h>
42113656Sdeischen#include <sys/lock.h>
43113656Sdeischen#include <sys/mutex.h>
44113656Sdeischen#include <sys/proc.h>
45113656Sdeischen#include <sys/queue.h>
46113656Sdeischen
47113656Sdeischen#include <net/if.h>
48113656Sdeischen#include <net/pfil.h>
49113656Sdeischen
50113656Sdeischenstatic struct mtx pfil_global_lock;
51114295Sdeischen
52114295SdeischenMTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock",
53114295Sdeischen  MTX_DEF);
54116771Smarcel
55116771Smarcelstatic int pfil_list_add(pfil_list_t *, struct packet_filter_hook *, int);
56116771Smarcel
57116771Smarcelstatic int pfil_list_remove(pfil_list_t *,
58116771Smarcel    int (*)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *),
59116771Smarcel    void *);
60116771Smarcel
61116771SmarcelLIST_HEAD(pfilheadhead, pfil_head);
62116771SmarcelVNET_DEFINE(struct pfilheadhead, pfil_head_list);
63116771Smarcel#define	V_pfil_head_list	VNET(pfil_head_list)
64116771Smarcel
65116771Smarcel/*
66113656Sdeischen * pfil_run_hooks() runs the specified packet filter hooks.
67 */
68int
69pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
70    int dir, struct inpcb *inp)
71{
72	struct rm_priotracker rmpt;
73	struct packet_filter_hook *pfh;
74	struct mbuf *m = *mp;
75	int rv = 0;
76
77	PFIL_RLOCK(ph, &rmpt);
78	KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0"));
79	for (pfh = pfil_hook_get(dir, ph); pfh != NULL;
80	     pfh = TAILQ_NEXT(pfh, pfil_link)) {
81		if (pfh->pfil_func != NULL) {
82			rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir,
83			    inp);
84			if (rv != 0 || m == NULL)
85				break;
86		}
87	}
88	PFIL_RUNLOCK(ph, &rmpt);
89
90	*mp = m;
91	return (rv);
92}
93
94/*
95 * pfil_head_register() registers a pfil_head with the packet filter
96 * hook mechanism.
97 */
98int
99pfil_head_register(struct pfil_head *ph)
100{
101	struct pfil_head *lph;
102
103	PFIL_LIST_LOCK();
104	LIST_FOREACH(lph, &V_pfil_head_list, ph_list) {
105		if (ph->ph_type == lph->ph_type &&
106		    ph->ph_un.phu_val == lph->ph_un.phu_val) {
107			PFIL_LIST_UNLOCK();
108			return EEXIST;
109		}
110	}
111	PFIL_LOCK_INIT(ph);
112	ph->ph_nhooks = 0;
113	TAILQ_INIT(&ph->ph_in);
114	TAILQ_INIT(&ph->ph_out);
115	LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list);
116	PFIL_LIST_UNLOCK();
117	return (0);
118}
119
120/*
121 * pfil_head_unregister() removes a pfil_head from the packet filter hook
122 * mechanism.  The producer of the hook promises that all outstanding
123 * invocations of the hook have completed before it unregisters the hook.
124 */
125int
126pfil_head_unregister(struct pfil_head *ph)
127{
128	struct packet_filter_hook *pfh, *pfnext;
129
130	PFIL_LIST_LOCK();
131	LIST_REMOVE(ph, ph_list);
132	PFIL_LIST_UNLOCK();
133	TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_link, pfnext)
134		free(pfh, M_IFADDR);
135	TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_link, pfnext)
136		free(pfh, M_IFADDR);
137	PFIL_LOCK_DESTROY(ph);
138	return (0);
139}
140
141/*
142 * pfil_head_get() returns the pfil_head for a given key/dlt.
143 */
144struct pfil_head *
145pfil_head_get(int type, u_long val)
146{
147	struct pfil_head *ph;
148
149	PFIL_LIST_LOCK();
150	LIST_FOREACH(ph, &V_pfil_head_list, ph_list)
151		if (ph->ph_type == type && ph->ph_un.phu_val == val)
152			break;
153	PFIL_LIST_UNLOCK();
154
155	return (ph);
156}
157
158/*
159 * pfil_add_hook() adds a function to the packet filter hook.  the
160 * flags are:
161 *	PFIL_IN		call me on incoming packets
162 *	PFIL_OUT	call me on outgoing packets
163 *	PFIL_ALL	call me on all of the above
164 *	PFIL_WAITOK	OK to call malloc with M_WAITOK.
165 */
166int
167pfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int,
168  struct inpcb *), void *arg, int flags, struct pfil_head *ph)
169{
170	struct packet_filter_hook *pfh1 = NULL;
171	struct packet_filter_hook *pfh2 = NULL;
172	int err;
173
174	if (flags & PFIL_IN) {
175		pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
176		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
177		if (pfh1 == NULL) {
178			err = ENOMEM;
179			goto error;
180		}
181	}
182	if (flags & PFIL_OUT) {
183		pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
184		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
185		if (pfh2 == NULL) {
186			err = ENOMEM;
187			goto error;
188		}
189	}
190	PFIL_WLOCK(ph);
191	if (flags & PFIL_IN) {
192		pfh1->pfil_func = func;
193		pfh1->pfil_arg = arg;
194		err = pfil_list_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT);
195		if (err)
196			goto locked_error;
197		ph->ph_nhooks++;
198	}
199	if (flags & PFIL_OUT) {
200		pfh2->pfil_func = func;
201		pfh2->pfil_arg = arg;
202		err = pfil_list_add(&ph->ph_out, pfh2, flags & ~PFIL_IN);
203		if (err) {
204			if (flags & PFIL_IN)
205				pfil_list_remove(&ph->ph_in, func, arg);
206			goto locked_error;
207		}
208		ph->ph_nhooks++;
209	}
210	PFIL_WUNLOCK(ph);
211	return 0;
212locked_error:
213	PFIL_WUNLOCK(ph);
214error:
215	if (pfh1 != NULL)
216		free(pfh1, M_IFADDR);
217	if (pfh2 != NULL)
218		free(pfh2, M_IFADDR);
219	return err;
220}
221
222/*
223 * pfil_remove_hook removes a specific function from the packet filter
224 * hook list.
225 */
226int
227pfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int,
228    struct inpcb *), void *arg, int flags, struct pfil_head *ph)
229{
230	int err = 0;
231
232	PFIL_WLOCK(ph);
233
234	if (flags & PFIL_IN) {
235		err = pfil_list_remove(&ph->ph_in, func, arg);
236		if (err == 0)
237			ph->ph_nhooks--;
238	}
239	if ((err == 0) && (flags & PFIL_OUT)) {
240		err = pfil_list_remove(&ph->ph_out, func, arg);
241		if (err == 0)
242			ph->ph_nhooks--;
243	}
244	PFIL_WUNLOCK(ph);
245
246	return err;
247}
248
249static int
250pfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags)
251{
252	struct packet_filter_hook *pfh;
253
254	/*
255	 * First make sure the hook is not already there.
256	 */
257	TAILQ_FOREACH(pfh, list, pfil_link)
258		if (pfh->pfil_func == pfh1->pfil_func &&
259		    pfh->pfil_arg == pfh1->pfil_arg)
260			return EEXIST;
261	/*
262	 * insert the input list in reverse order of the output list
263	 * so that the same path is followed in or out of the kernel.
264	 */
265	if (flags & PFIL_IN)
266		TAILQ_INSERT_HEAD(list, pfh1, pfil_link);
267	else
268		TAILQ_INSERT_TAIL(list, pfh1, pfil_link);
269
270	return 0;
271}
272
273/*
274 * pfil_list_remove is an internal function that takes a function off the
275 * specified list.
276 */
277static int
278pfil_list_remove(pfil_list_t *list,
279    int (*func)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *),
280    void *arg)
281{
282	struct packet_filter_hook *pfh;
283
284	TAILQ_FOREACH(pfh, list, pfil_link)
285		if (pfh->pfil_func == func && pfh->pfil_arg == arg) {
286			TAILQ_REMOVE(list, pfh, pfil_link);
287			free(pfh, M_IFADDR);
288			return 0;
289		}
290	return ENOENT;
291}
292
293/****************
294 * Stuff that must be initialized for every instance
295 * (including the first of course).
296 */
297static int
298vnet_pfil_init(const void *unused)
299{
300	LIST_INIT(&V_pfil_head_list);
301	return (0);
302}
303
304/***********************
305 * Called for the removal of each instance.
306 */
307static int
308vnet_pfil_uninit(const void *unused)
309{
310	/*  XXX should panic if list is not empty */
311	return 0;
312}
313
314/* Define startup order. */
315#define	PFIL_SYSINIT_ORDER	SI_SUB_PROTO_BEGIN
316#define	PFIL_MODEVENT_ORDER	(SI_ORDER_FIRST) /* On boot slot in here. */
317#define	PFIL_VNET_ORDER		(PFIL_MODEVENT_ORDER + 2) /* Later still. */
318
319/*
320 * Starting up.
321 * VNET_SYSINIT is called for each existing vnet and each new vnet.
322 */
323VNET_SYSINIT(vnet_pfil_init, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
324	    vnet_pfil_init, NULL);
325
326/*
327 * Closing up shop. These are done in REVERSE ORDER,
328 * Not called on reboot.
329 * VNET_SYSUNINIT is called for each exiting vnet as it exits.
330 */
331VNET_SYSUNINIT(vnet_pfil_uninit, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
332	    vnet_pfil_uninit, NULL);
333
334