pfil.c revision 198233
1120386Ssam/*	$FreeBSD: head/sys/net/pfil.c 198233 2009-10-19 15:19:14Z rwatson $ */
2120386Ssam/*	$NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $	*/
360317Sdarrenr
4139823Simp/*-
560317Sdarrenr * Copyright (c) 1996 Matthew R. Green
660317Sdarrenr * All rights reserved.
760317Sdarrenr *
860317Sdarrenr * Redistribution and use in source and binary forms, with or without
960317Sdarrenr * modification, are permitted provided that the following conditions
1060317Sdarrenr * are met:
1160317Sdarrenr * 1. Redistributions of source code must retain the above copyright
1260317Sdarrenr *    notice, this list of conditions and the following disclaimer.
1360317Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright
1460317Sdarrenr *    notice, this list of conditions and the following disclaimer in the
1560317Sdarrenr *    documentation and/or other materials provided with the distribution.
1660317Sdarrenr * 3. The name of the author may not be used to endorse or promote products
1760317Sdarrenr *    derived from this software without specific prior written permission.
1860317Sdarrenr *
1960317Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2060317Sdarrenr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2160317Sdarrenr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2260317Sdarrenr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2360317Sdarrenr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2460317Sdarrenr * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2560317Sdarrenr * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2660317Sdarrenr * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2760317Sdarrenr * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2860317Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2960317Sdarrenr * SUCH DAMAGE.
3060317Sdarrenr */
3160317Sdarrenr
3260317Sdarrenr#include <sys/param.h>
33120386Ssam#include <sys/kernel.h>
3460317Sdarrenr#include <sys/errno.h>
35155201Scsjp#include <sys/lock.h>
3660317Sdarrenr#include <sys/malloc.h>
37173904Smlaier#include <sys/rmlock.h>
3860317Sdarrenr#include <sys/socket.h>
3960317Sdarrenr#include <sys/socketvar.h>
4060317Sdarrenr#include <sys/systm.h>
41120386Ssam#include <sys/condvar.h>
42120386Ssam#include <sys/lock.h>
43120386Ssam#include <sys/mutex.h>
44120386Ssam#include <sys/proc.h>
4560317Sdarrenr#include <sys/queue.h>
4660317Sdarrenr
4760317Sdarrenr#include <net/if.h>
4860317Sdarrenr#include <net/pfil.h>
4960317Sdarrenr
50120386Ssamstatic struct mtx pfil_global_lock;
51120386Ssam
52198198SrwatsonMTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock",
53198198Srwatson  MTX_DEF);
54120386Ssam
55120386Ssamstatic int pfil_list_add(pfil_list_t *, struct packet_filter_hook *, int);
56120386Ssam
5760317Sdarrenrstatic int pfil_list_remove(pfil_list_t *,
58198198Srwatson    int (*)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *),
59198198Srwatson    void *);
6060317Sdarrenr
61197952SjulianLIST_HEAD(pfilheadhead, pfil_head);
62197952SjulianVNET_DEFINE(struct pfilheadhead, pfil_head_list);
63197952Sjulian#define	V_pfil_head_list	VNET(pfil_head_list)
64120386Ssam
65120386Ssam/*
66120386Ssam * pfil_run_hooks() runs the specified packet filter hooks.
67120386Ssam */
68120386Ssamint
69120386Ssampfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
70135920Smlaier    int dir, struct inpcb *inp)
71120386Ssam{
72173904Smlaier	struct rm_priotracker rmpt;
73120386Ssam	struct packet_filter_hook *pfh;
74120386Ssam	struct mbuf *m = *mp;
75120386Ssam	int rv = 0;
76120386Ssam
77173904Smlaier	PFIL_RLOCK(ph, &rmpt);
78155201Scsjp	KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0"));
79120386Ssam	for (pfh = pfil_hook_get(dir, ph); pfh != NULL;
80120386Ssam	     pfh = TAILQ_NEXT(pfh, pfil_link)) {
81120386Ssam		if (pfh->pfil_func != NULL) {
82198198Srwatson			rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir,
83198198Srwatson			    inp);
84120386Ssam			if (rv != 0 || m == NULL)
85120386Ssam				break;
86120386Ssam		}
87120386Ssam	}
88173904Smlaier	PFIL_RUNLOCK(ph, &rmpt);
89120386Ssam	*mp = m;
90120386Ssam	return (rv);
91120386Ssam}
92120386Ssam
93120386Ssam/*
94198233Srwatson * pfil_head_register() registers a pfil_head with the packet filter hook
95198233Srwatson * mechanism.
96120386Ssam */
97120386Ssamint
98120386Ssampfil_head_register(struct pfil_head *ph)
99120386Ssam{
100120386Ssam	struct pfil_head *lph;
101120386Ssam
102120386Ssam	PFIL_LIST_LOCK();
103197952Sjulian	LIST_FOREACH(lph, &V_pfil_head_list, ph_list) {
104120386Ssam		if (ph->ph_type == lph->ph_type &&
105120386Ssam		    ph->ph_un.phu_val == lph->ph_un.phu_val) {
106120386Ssam			PFIL_LIST_UNLOCK();
107198233Srwatson			return (EEXIST);
108120386Ssam		}
109186187Srwatson	}
110173904Smlaier	PFIL_LOCK_INIT(ph);
111155201Scsjp	ph->ph_nhooks = 0;
11260317Sdarrenr	TAILQ_INIT(&ph->ph_in);
11360317Sdarrenr	TAILQ_INIT(&ph->ph_out);
114197952Sjulian	LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list);
115120386Ssam	PFIL_LIST_UNLOCK();
116120386Ssam	return (0);
11760317Sdarrenr}
11860317Sdarrenr
11960317Sdarrenr/*
120186187Srwatson * pfil_head_unregister() removes a pfil_head from the packet filter hook
121186187Srwatson * mechanism.  The producer of the hook promises that all outstanding
122186187Srwatson * invocations of the hook have completed before it unregisters the hook.
123120386Ssam */
124120386Ssamint
125120386Ssampfil_head_unregister(struct pfil_head *ph)
126120386Ssam{
127120386Ssam	struct packet_filter_hook *pfh, *pfnext;
128120386Ssam
129120386Ssam	PFIL_LIST_LOCK();
130120386Ssam	LIST_REMOVE(ph, ph_list);
131120386Ssam	PFIL_LIST_UNLOCK();
132120386Ssam	TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_link, pfnext)
133120386Ssam		free(pfh, M_IFADDR);
134120386Ssam	TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_link, pfnext)
135120386Ssam		free(pfh, M_IFADDR);
136173904Smlaier	PFIL_LOCK_DESTROY(ph);
137120386Ssam	return (0);
138120386Ssam}
139120386Ssam
140120386Ssam/*
141120386Ssam * pfil_head_get() returns the pfil_head for a given key/dlt.
142120386Ssam */
143120386Ssamstruct pfil_head *
144120386Ssampfil_head_get(int type, u_long val)
145120386Ssam{
146120386Ssam	struct pfil_head *ph;
147120386Ssam
148120386Ssam	PFIL_LIST_LOCK();
149197952Sjulian	LIST_FOREACH(ph, &V_pfil_head_list, ph_list)
150120386Ssam		if (ph->ph_type == type && ph->ph_un.phu_val == val)
151120386Ssam			break;
152120386Ssam	PFIL_LIST_UNLOCK();
153120386Ssam	return (ph);
154120386Ssam}
155120386Ssam
156120386Ssam/*
15760317Sdarrenr * pfil_add_hook() adds a function to the packet filter hook.  the
15860317Sdarrenr * flags are:
15960317Sdarrenr *	PFIL_IN		call me on incoming packets
16060317Sdarrenr *	PFIL_OUT	call me on outgoing packets
16160317Sdarrenr *	PFIL_ALL	call me on all of the above
162111119Simp *	PFIL_WAITOK	OK to call malloc with M_WAITOK.
16360317Sdarrenr */
16460317Sdarrenrint
165186187Srwatsonpfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int,
166186187Srwatson  struct inpcb *), void *arg, int flags, struct pfil_head *ph)
16760317Sdarrenr{
168120386Ssam	struct packet_filter_hook *pfh1 = NULL;
169120386Ssam	struct packet_filter_hook *pfh2 = NULL;
170120386Ssam	int err;
17160317Sdarrenr
172120386Ssam	if (flags & PFIL_IN) {
173120386Ssam		pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
174120386Ssam		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
175120386Ssam		if (pfh1 == NULL) {
176120386Ssam			err = ENOMEM;
177120386Ssam			goto error;
178120386Ssam		}
179120386Ssam	}
180120386Ssam	if (flags & PFIL_OUT) {
181120386Ssam		pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
182120386Ssam		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
183120386Ssam		if (pfh2 == NULL) {
184120386Ssam			err = ENOMEM;
185120386Ssam			goto error;
186120386Ssam		}
187120386Ssam	}
188155201Scsjp	PFIL_WLOCK(ph);
189120386Ssam	if (flags & PFIL_IN) {
190120386Ssam		pfh1->pfil_func = func;
191120386Ssam		pfh1->pfil_arg = arg;
192120386Ssam		err = pfil_list_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT);
193120386Ssam		if (err)
194186187Srwatson			goto locked_error;
195155201Scsjp		ph->ph_nhooks++;
196120386Ssam	}
197120386Ssam	if (flags & PFIL_OUT) {
198120386Ssam		pfh2->pfil_func = func;
199120386Ssam		pfh2->pfil_arg = arg;
200120386Ssam		err = pfil_list_add(&ph->ph_out, pfh2, flags & ~PFIL_IN);
201120386Ssam		if (err) {
202120386Ssam			if (flags & PFIL_IN)
203120386Ssam				pfil_list_remove(&ph->ph_in, func, arg);
204186187Srwatson			goto locked_error;
205120386Ssam		}
206155201Scsjp		ph->ph_nhooks++;
207120386Ssam	}
208120386Ssam	PFIL_WUNLOCK(ph);
209198233Srwatson	return (0);
210186187Srwatsonlocked_error:
211120386Ssam	PFIL_WUNLOCK(ph);
212120386Ssamerror:
213120386Ssam	if (pfh1 != NULL)
214120386Ssam		free(pfh1, M_IFADDR);
215120386Ssam	if (pfh2 != NULL)
216120386Ssam		free(pfh2, M_IFADDR);
217198233Srwatson	return (err);
21860317Sdarrenr}
21960317Sdarrenr
22060317Sdarrenr/*
221198233Srwatson * pfil_remove_hook removes a specific function from the packet filter hook
222198233Srwatson * list.
22360317Sdarrenr */
22460317Sdarrenrint
225198198Srwatsonpfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int,
226198198Srwatson    struct inpcb *), void *arg, int flags, struct pfil_head *ph)
22760317Sdarrenr{
22860317Sdarrenr	int err = 0;
22960317Sdarrenr
230155201Scsjp	PFIL_WLOCK(ph);
231155201Scsjp	if (flags & PFIL_IN) {
232120386Ssam		err = pfil_list_remove(&ph->ph_in, func, arg);
233155201Scsjp		if (err == 0)
234155201Scsjp			ph->ph_nhooks--;
235155201Scsjp	}
236155201Scsjp	if ((err == 0) && (flags & PFIL_OUT)) {
237120386Ssam		err = pfil_list_remove(&ph->ph_out, func, arg);
238155201Scsjp		if (err == 0)
239155201Scsjp			ph->ph_nhooks--;
240155201Scsjp	}
241120386Ssam	PFIL_WUNLOCK(ph);
242198233Srwatson	return (err);
24360317Sdarrenr}
24460317Sdarrenr
245120386Ssamstatic int
246120386Ssampfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags)
247120386Ssam{
248120386Ssam	struct packet_filter_hook *pfh;
249120386Ssam
250120386Ssam	/*
251120386Ssam	 * First make sure the hook is not already there.
252120386Ssam	 */
253120386Ssam	TAILQ_FOREACH(pfh, list, pfil_link)
254120386Ssam		if (pfh->pfil_func == pfh1->pfil_func &&
255120386Ssam		    pfh->pfil_arg == pfh1->pfil_arg)
256198233Srwatson			return (EEXIST);
257198233Srwatson
258120386Ssam	/*
259198233Srwatson	 * Insert the input list in reverse order of the output list so that
260198233Srwatson	 * the same path is followed in or out of the kernel.
261120386Ssam	 */
262120386Ssam	if (flags & PFIL_IN)
263120386Ssam		TAILQ_INSERT_HEAD(list, pfh1, pfil_link);
264120386Ssam	else
265120386Ssam		TAILQ_INSERT_TAIL(list, pfh1, pfil_link);
266198233Srwatson	return (0);
267120386Ssam}
268120386Ssam
26960317Sdarrenr/*
27060317Sdarrenr * pfil_list_remove is an internal function that takes a function off the
27160317Sdarrenr * specified list.
27260317Sdarrenr */
27360317Sdarrenrstatic int
274120386Ssampfil_list_remove(pfil_list_t *list,
275198198Srwatson    int (*func)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *),
276198198Srwatson    void *arg)
27760317Sdarrenr{
27860317Sdarrenr	struct packet_filter_hook *pfh;
27960317Sdarrenr
28071999Sphk	TAILQ_FOREACH(pfh, list, pfil_link)
281120386Ssam		if (pfh->pfil_func == func && pfh->pfil_arg == arg) {
28260317Sdarrenr			TAILQ_REMOVE(list, pfh, pfil_link);
28360317Sdarrenr			free(pfh, M_IFADDR);
284198233Srwatson			return (0);
28560317Sdarrenr		}
286198233Srwatson	return (ENOENT);
28760317Sdarrenr}
288197952Sjulian
289198233Srwatson/*
290198233Srwatson * Stuff that must be initialized for every instance (including the first of
291198233Srwatson * course).
292197952Sjulian */
293197952Sjulianstatic int
294197952Sjulianvnet_pfil_init(const void *unused)
295197952Sjulian{
296198233Srwatson
297197952Sjulian	LIST_INIT(&V_pfil_head_list);
298197952Sjulian	return (0);
299197952Sjulian}
300197952Sjulian
301198233Srwatson/*
302197952Sjulian * Called for the removal of each instance.
303197952Sjulian */
304197952Sjulianstatic int
305197952Sjulianvnet_pfil_uninit(const void *unused)
306197952Sjulian{
307198233Srwatson
308197952Sjulian	/*  XXX should panic if list is not empty */
309198233Srwatson	return (0);
310197952Sjulian}
311197952Sjulian
312197952Sjulian/* Define startup order. */
313197952Sjulian#define	PFIL_SYSINIT_ORDER	SI_SUB_PROTO_BEGIN
314197952Sjulian#define	PFIL_MODEVENT_ORDER	(SI_ORDER_FIRST) /* On boot slot in here. */
315197952Sjulian#define	PFIL_VNET_ORDER		(PFIL_MODEVENT_ORDER + 2) /* Later still. */
316197952Sjulian
317197952Sjulian/*
318198233Srwatson * Starting up.
319198233Srwatson *
320197952Sjulian * VNET_SYSINIT is called for each existing vnet and each new vnet.
321197952Sjulian */
322197952SjulianVNET_SYSINIT(vnet_pfil_init, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
323198233Srwatson    vnet_pfil_init, NULL);
324197952Sjulian
325197952Sjulian/*
326198233Srwatson * Closing up shop.  These are done in REVERSE ORDER.  Not called on reboot.
327198233Srwatson *
328197952Sjulian * VNET_SYSUNINIT is called for each exiting vnet as it exits.
329197952Sjulian */
330197952SjulianVNET_SYSUNINIT(vnet_pfil_uninit, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
331198233Srwatson    vnet_pfil_uninit, NULL);
332