pfil.c revision 241888
1120386Ssam/*	$FreeBSD: head/sys/net/pfil.c 241888 2012-10-22 14:10:17Z melifaro $ */
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)
64241888SmelifaroVNET_DEFINE(struct rmlock, pfil_lock);
65241888Smelifaro#define	V_pfil_lock	VNET(pfil_lock)
66120386Ssam
67120386Ssam/*
68120386Ssam * pfil_run_hooks() runs the specified packet filter hooks.
69120386Ssam */
70120386Ssamint
71120386Ssampfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
72135920Smlaier    int dir, struct inpcb *inp)
73120386Ssam{
74173904Smlaier	struct rm_priotracker rmpt;
75120386Ssam	struct packet_filter_hook *pfh;
76120386Ssam	struct mbuf *m = *mp;
77120386Ssam	int rv = 0;
78120386Ssam
79173904Smlaier	PFIL_RLOCK(ph, &rmpt);
80155201Scsjp	KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0"));
81120386Ssam	for (pfh = pfil_hook_get(dir, ph); pfh != NULL;
82120386Ssam	     pfh = TAILQ_NEXT(pfh, pfil_link)) {
83120386Ssam		if (pfh->pfil_func != NULL) {
84198198Srwatson			rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir,
85198198Srwatson			    inp);
86120386Ssam			if (rv != 0 || m == NULL)
87120386Ssam				break;
88120386Ssam		}
89120386Ssam	}
90173904Smlaier	PFIL_RUNLOCK(ph, &rmpt);
91120386Ssam	*mp = m;
92120386Ssam	return (rv);
93120386Ssam}
94120386Ssam
95120386Ssam/*
96241888Smelifaro * pfil_try_rlock() acquires rm reader lock for specified head
97241888Smelifaro * if this is immediately possible,
98241888Smelifaro */
99241888Smelifaroint
100241888Smelifaropfil_try_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
101241888Smelifaro{
102241888Smelifaro	return PFIL_TRY_RLOCK(ph, tracker);
103241888Smelifaro}
104241888Smelifaro
105241888Smelifaro/*
106241888Smelifaro * pfil_rlock() acquires rm reader lock for specified head.
107241888Smelifaro */
108241888Smelifarovoid
109241888Smelifaropfil_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
110241888Smelifaro{
111241888Smelifaro	PFIL_RLOCK(ph, tracker);
112241888Smelifaro}
113241888Smelifaro
114241888Smelifaro/*
115241888Smelifaro * pfil_runlock() releases reader lock for specified head.
116241888Smelifaro */
117241888Smelifarovoid
118241888Smelifaropfil_runlock(struct pfil_head *ph, struct rm_priotracker *tracker)
119241888Smelifaro{
120241888Smelifaro	PFIL_RUNLOCK(ph, tracker);
121241888Smelifaro}
122241888Smelifaro
123241888Smelifaro/*
124241888Smelifaro * pfil_wlock() acquires writer lock for specified head.
125241888Smelifaro */
126241888Smelifarovoid
127241888Smelifaropfil_wlock(struct pfil_head *ph)
128241888Smelifaro{
129241888Smelifaro	PFIL_WLOCK(ph);
130241888Smelifaro}
131241888Smelifaro
132241888Smelifaro/*
133241888Smelifaro * pfil_wunlock() releases writer lock for specified head.
134241888Smelifaro */
135241888Smelifarovoid
136241888Smelifaropfil_wunlock(struct pfil_head *ph)
137241888Smelifaro{
138241888Smelifaro	PFIL_WUNLOCK(ph);
139241888Smelifaro}
140241888Smelifaro
141241888Smelifaro/*
142241888Smelifaro * pfil_wowned() releases writer lock for specified head.
143241888Smelifaro */
144241888Smelifaroint
145241888Smelifaropfil_wowned(struct pfil_head *ph)
146241888Smelifaro{
147241888Smelifaro	return PFIL_WOWNED(ph);
148241888Smelifaro}
149241888Smelifaro/*
150198233Srwatson * pfil_head_register() registers a pfil_head with the packet filter hook
151198233Srwatson * mechanism.
152120386Ssam */
153120386Ssamint
154120386Ssampfil_head_register(struct pfil_head *ph)
155120386Ssam{
156120386Ssam	struct pfil_head *lph;
157120386Ssam
158120386Ssam	PFIL_LIST_LOCK();
159197952Sjulian	LIST_FOREACH(lph, &V_pfil_head_list, ph_list) {
160120386Ssam		if (ph->ph_type == lph->ph_type &&
161120386Ssam		    ph->ph_un.phu_val == lph->ph_un.phu_val) {
162120386Ssam			PFIL_LIST_UNLOCK();
163198233Srwatson			return (EEXIST);
164120386Ssam		}
165186187Srwatson	}
166173904Smlaier	PFIL_LOCK_INIT(ph);
167155201Scsjp	ph->ph_nhooks = 0;
16860317Sdarrenr	TAILQ_INIT(&ph->ph_in);
16960317Sdarrenr	TAILQ_INIT(&ph->ph_out);
170197952Sjulian	LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list);
171120386Ssam	PFIL_LIST_UNLOCK();
172120386Ssam	return (0);
17360317Sdarrenr}
17460317Sdarrenr
17560317Sdarrenr/*
176186187Srwatson * pfil_head_unregister() removes a pfil_head from the packet filter hook
177186187Srwatson * mechanism.  The producer of the hook promises that all outstanding
178186187Srwatson * invocations of the hook have completed before it unregisters the hook.
179120386Ssam */
180120386Ssamint
181120386Ssampfil_head_unregister(struct pfil_head *ph)
182120386Ssam{
183120386Ssam	struct packet_filter_hook *pfh, *pfnext;
184120386Ssam
185120386Ssam	PFIL_LIST_LOCK();
186120386Ssam	LIST_REMOVE(ph, ph_list);
187120386Ssam	PFIL_LIST_UNLOCK();
188120386Ssam	TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_link, pfnext)
189120386Ssam		free(pfh, M_IFADDR);
190120386Ssam	TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_link, pfnext)
191120386Ssam		free(pfh, M_IFADDR);
192173904Smlaier	PFIL_LOCK_DESTROY(ph);
193120386Ssam	return (0);
194120386Ssam}
195120386Ssam
196120386Ssam/*
197120386Ssam * pfil_head_get() returns the pfil_head for a given key/dlt.
198120386Ssam */
199120386Ssamstruct pfil_head *
200120386Ssampfil_head_get(int type, u_long val)
201120386Ssam{
202120386Ssam	struct pfil_head *ph;
203120386Ssam
204120386Ssam	PFIL_LIST_LOCK();
205197952Sjulian	LIST_FOREACH(ph, &V_pfil_head_list, ph_list)
206120386Ssam		if (ph->ph_type == type && ph->ph_un.phu_val == val)
207120386Ssam			break;
208120386Ssam	PFIL_LIST_UNLOCK();
209120386Ssam	return (ph);
210120386Ssam}
211120386Ssam
212120386Ssam/*
21360317Sdarrenr * pfil_add_hook() adds a function to the packet filter hook.  the
21460317Sdarrenr * flags are:
21560317Sdarrenr *	PFIL_IN		call me on incoming packets
21660317Sdarrenr *	PFIL_OUT	call me on outgoing packets
21760317Sdarrenr *	PFIL_ALL	call me on all of the above
218111119Simp *	PFIL_WAITOK	OK to call malloc with M_WAITOK.
21960317Sdarrenr */
22060317Sdarrenrint
221186187Srwatsonpfil_add_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int,
222186187Srwatson  struct inpcb *), void *arg, int flags, struct pfil_head *ph)
22360317Sdarrenr{
224120386Ssam	struct packet_filter_hook *pfh1 = NULL;
225120386Ssam	struct packet_filter_hook *pfh2 = NULL;
226120386Ssam	int err;
22760317Sdarrenr
228120386Ssam	if (flags & PFIL_IN) {
229120386Ssam		pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
230120386Ssam		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
231120386Ssam		if (pfh1 == NULL) {
232120386Ssam			err = ENOMEM;
233120386Ssam			goto error;
234120386Ssam		}
235120386Ssam	}
236120386Ssam	if (flags & PFIL_OUT) {
237120386Ssam		pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
238120386Ssam		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
239120386Ssam		if (pfh2 == NULL) {
240120386Ssam			err = ENOMEM;
241120386Ssam			goto error;
242120386Ssam		}
243120386Ssam	}
244155201Scsjp	PFIL_WLOCK(ph);
245120386Ssam	if (flags & PFIL_IN) {
246120386Ssam		pfh1->pfil_func = func;
247120386Ssam		pfh1->pfil_arg = arg;
248120386Ssam		err = pfil_list_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT);
249120386Ssam		if (err)
250186187Srwatson			goto locked_error;
251155201Scsjp		ph->ph_nhooks++;
252120386Ssam	}
253120386Ssam	if (flags & PFIL_OUT) {
254120386Ssam		pfh2->pfil_func = func;
255120386Ssam		pfh2->pfil_arg = arg;
256120386Ssam		err = pfil_list_add(&ph->ph_out, pfh2, flags & ~PFIL_IN);
257120386Ssam		if (err) {
258120386Ssam			if (flags & PFIL_IN)
259120386Ssam				pfil_list_remove(&ph->ph_in, func, arg);
260186187Srwatson			goto locked_error;
261120386Ssam		}
262155201Scsjp		ph->ph_nhooks++;
263120386Ssam	}
264120386Ssam	PFIL_WUNLOCK(ph);
265198233Srwatson	return (0);
266186187Srwatsonlocked_error:
267120386Ssam	PFIL_WUNLOCK(ph);
268120386Ssamerror:
269120386Ssam	if (pfh1 != NULL)
270120386Ssam		free(pfh1, M_IFADDR);
271120386Ssam	if (pfh2 != NULL)
272120386Ssam		free(pfh2, M_IFADDR);
273198233Srwatson	return (err);
27460317Sdarrenr}
27560317Sdarrenr
27660317Sdarrenr/*
277198233Srwatson * pfil_remove_hook removes a specific function from the packet filter hook
278198233Srwatson * list.
27960317Sdarrenr */
28060317Sdarrenrint
281198198Srwatsonpfil_remove_hook(int (*func)(void *, struct mbuf **, struct ifnet *, int,
282198198Srwatson    struct inpcb *), void *arg, int flags, struct pfil_head *ph)
28360317Sdarrenr{
28460317Sdarrenr	int err = 0;
28560317Sdarrenr
286155201Scsjp	PFIL_WLOCK(ph);
287155201Scsjp	if (flags & PFIL_IN) {
288120386Ssam		err = pfil_list_remove(&ph->ph_in, func, arg);
289155201Scsjp		if (err == 0)
290155201Scsjp			ph->ph_nhooks--;
291155201Scsjp	}
292155201Scsjp	if ((err == 0) && (flags & PFIL_OUT)) {
293120386Ssam		err = pfil_list_remove(&ph->ph_out, func, arg);
294155201Scsjp		if (err == 0)
295155201Scsjp			ph->ph_nhooks--;
296155201Scsjp	}
297120386Ssam	PFIL_WUNLOCK(ph);
298198233Srwatson	return (err);
29960317Sdarrenr}
30060317Sdarrenr
301120386Ssamstatic int
302120386Ssampfil_list_add(pfil_list_t *list, struct packet_filter_hook *pfh1, int flags)
303120386Ssam{
304120386Ssam	struct packet_filter_hook *pfh;
305120386Ssam
306120386Ssam	/*
307120386Ssam	 * First make sure the hook is not already there.
308120386Ssam	 */
309120386Ssam	TAILQ_FOREACH(pfh, list, pfil_link)
310120386Ssam		if (pfh->pfil_func == pfh1->pfil_func &&
311120386Ssam		    pfh->pfil_arg == pfh1->pfil_arg)
312198233Srwatson			return (EEXIST);
313198233Srwatson
314120386Ssam	/*
315198233Srwatson	 * Insert the input list in reverse order of the output list so that
316198233Srwatson	 * the same path is followed in or out of the kernel.
317120386Ssam	 */
318120386Ssam	if (flags & PFIL_IN)
319120386Ssam		TAILQ_INSERT_HEAD(list, pfh1, pfil_link);
320120386Ssam	else
321120386Ssam		TAILQ_INSERT_TAIL(list, pfh1, pfil_link);
322198233Srwatson	return (0);
323120386Ssam}
324120386Ssam
32560317Sdarrenr/*
32660317Sdarrenr * pfil_list_remove is an internal function that takes a function off the
32760317Sdarrenr * specified list.
32860317Sdarrenr */
32960317Sdarrenrstatic int
330120386Ssampfil_list_remove(pfil_list_t *list,
331198198Srwatson    int (*func)(void *, struct mbuf **, struct ifnet *, int, struct inpcb *),
332198198Srwatson    void *arg)
33360317Sdarrenr{
33460317Sdarrenr	struct packet_filter_hook *pfh;
33560317Sdarrenr
33671999Sphk	TAILQ_FOREACH(pfh, list, pfil_link)
337120386Ssam		if (pfh->pfil_func == func && pfh->pfil_arg == arg) {
33860317Sdarrenr			TAILQ_REMOVE(list, pfh, pfil_link);
33960317Sdarrenr			free(pfh, M_IFADDR);
340198233Srwatson			return (0);
34160317Sdarrenr		}
342198233Srwatson	return (ENOENT);
34360317Sdarrenr}
344197952Sjulian
345198233Srwatson/*
346198233Srwatson * Stuff that must be initialized for every instance (including the first of
347198233Srwatson * course).
348197952Sjulian */
349197952Sjulianstatic int
350197952Sjulianvnet_pfil_init(const void *unused)
351197952Sjulian{
352198233Srwatson
353197952Sjulian	LIST_INIT(&V_pfil_head_list);
354241888Smelifaro	PFIL_LOCK_INIT_REAL(&V_pfil_lock, "shared");
355197952Sjulian	return (0);
356197952Sjulian}
357197952Sjulian
358198233Srwatson/*
359197952Sjulian * Called for the removal of each instance.
360197952Sjulian */
361197952Sjulianstatic int
362197952Sjulianvnet_pfil_uninit(const void *unused)
363197952Sjulian{
364198233Srwatson
365197952Sjulian	/*  XXX should panic if list is not empty */
366241888Smelifaro	PFIL_LOCK_DESTROY_REAL(&V_pfil_lock);
367198233Srwatson	return (0);
368197952Sjulian}
369197952Sjulian
370197952Sjulian/* Define startup order. */
371197952Sjulian#define	PFIL_SYSINIT_ORDER	SI_SUB_PROTO_BEGIN
372197952Sjulian#define	PFIL_MODEVENT_ORDER	(SI_ORDER_FIRST) /* On boot slot in here. */
373197952Sjulian#define	PFIL_VNET_ORDER		(PFIL_MODEVENT_ORDER + 2) /* Later still. */
374197952Sjulian
375197952Sjulian/*
376198233Srwatson * Starting up.
377198233Srwatson *
378197952Sjulian * VNET_SYSINIT is called for each existing vnet and each new vnet.
379197952Sjulian */
380197952SjulianVNET_SYSINIT(vnet_pfil_init, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
381198233Srwatson    vnet_pfil_init, NULL);
382197952Sjulian
383197952Sjulian/*
384198233Srwatson * Closing up shop.  These are done in REVERSE ORDER.  Not called on reboot.
385198233Srwatson *
386197952Sjulian * VNET_SYSUNINIT is called for each exiting vnet as it exits.
387197952Sjulian */
388197952SjulianVNET_SYSUNINIT(vnet_pfil_uninit, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
389198233Srwatson    vnet_pfil_uninit, NULL);
390