pfil.c revision 254774
1120386Ssam/*	$FreeBSD: head/sys/net/pfil.c 254774 2013-08-24 11:24:15Z andre $ */
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
55254773Sandrestatic struct packet_filter_hook *pfil_chain_get(int, struct pfil_head *);
56254773Sandrestatic int pfil_chain_add(pfil_chain_t *, struct packet_filter_hook *, int);
57254773Sandrestatic int pfil_chain_remove(pfil_chain_t *, pfil_func_t, void *);
58120386Ssam
59197952SjulianLIST_HEAD(pfilheadhead, pfil_head);
60197952SjulianVNET_DEFINE(struct pfilheadhead, pfil_head_list);
61197952Sjulian#define	V_pfil_head_list	VNET(pfil_head_list)
62241888SmelifaroVNET_DEFINE(struct rmlock, pfil_lock);
63241888Smelifaro#define	V_pfil_lock	VNET(pfil_lock)
64120386Ssam
65120386Ssam/*
66254773Sandre * pfil_run_hooks() runs the specified packet filter hook chain.
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"));
79254773Sandre	for (pfh = pfil_chain_get(dir, ph); pfh != NULL;
80254773Sandre	     pfh = TAILQ_NEXT(pfh, pfil_chain)) {
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
93254771Sandrestatic struct packet_filter_hook *
94254773Sandrepfil_chain_get(int dir, struct pfil_head *ph)
95254771Sandre{
96254771Sandre
97254771Sandre	if (dir == PFIL_IN)
98254771Sandre		return (TAILQ_FIRST(&ph->ph_in));
99254771Sandre	else if (dir == PFIL_OUT)
100254771Sandre		return (TAILQ_FIRST(&ph->ph_out));
101254771Sandre	else
102254771Sandre		return (NULL);
103254771Sandre}
104254771Sandre
105120386Ssam/*
106241888Smelifaro * pfil_try_rlock() acquires rm reader lock for specified head
107248490Sae * if this is immediately possible.
108241888Smelifaro */
109241888Smelifaroint
110241888Smelifaropfil_try_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
111241888Smelifaro{
112248490Sae
113248490Sae	return (PFIL_TRY_RLOCK(ph, tracker));
114241888Smelifaro}
115241888Smelifaro
116241888Smelifaro/*
117241888Smelifaro * pfil_rlock() acquires rm reader lock for specified head.
118241888Smelifaro */
119241888Smelifarovoid
120241888Smelifaropfil_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
121241888Smelifaro{
122248490Sae
123241888Smelifaro	PFIL_RLOCK(ph, tracker);
124241888Smelifaro}
125241888Smelifaro
126241888Smelifaro/*
127241888Smelifaro * pfil_runlock() releases reader lock for specified head.
128241888Smelifaro */
129241888Smelifarovoid
130241888Smelifaropfil_runlock(struct pfil_head *ph, struct rm_priotracker *tracker)
131241888Smelifaro{
132248490Sae
133241888Smelifaro	PFIL_RUNLOCK(ph, tracker);
134241888Smelifaro}
135241888Smelifaro
136241888Smelifaro/*
137241888Smelifaro * pfil_wlock() acquires writer lock for specified head.
138241888Smelifaro */
139241888Smelifarovoid
140241888Smelifaropfil_wlock(struct pfil_head *ph)
141241888Smelifaro{
142248490Sae
143241888Smelifaro	PFIL_WLOCK(ph);
144241888Smelifaro}
145241888Smelifaro
146241888Smelifaro/*
147241888Smelifaro * pfil_wunlock() releases writer lock for specified head.
148241888Smelifaro */
149241888Smelifarovoid
150241888Smelifaropfil_wunlock(struct pfil_head *ph)
151241888Smelifaro{
152248490Sae
153241888Smelifaro	PFIL_WUNLOCK(ph);
154241888Smelifaro}
155241888Smelifaro
156241888Smelifaro/*
157248490Sae * pfil_wowned() returns a non-zero value if the current thread owns
158248490Sae * an exclusive lock.
159241888Smelifaro */
160241888Smelifaroint
161241888Smelifaropfil_wowned(struct pfil_head *ph)
162241888Smelifaro{
163248490Sae
164248490Sae	return (PFIL_WOWNED(ph));
165241888Smelifaro}
166254773Sandre
167241888Smelifaro/*
168198233Srwatson * pfil_head_register() registers a pfil_head with the packet filter hook
169198233Srwatson * mechanism.
170120386Ssam */
171120386Ssamint
172120386Ssampfil_head_register(struct pfil_head *ph)
173120386Ssam{
174120386Ssam	struct pfil_head *lph;
175120386Ssam
176254774Sandre	PFIL_HEADLIST_LOCK();
177197952Sjulian	LIST_FOREACH(lph, &V_pfil_head_list, ph_list) {
178120386Ssam		if (ph->ph_type == lph->ph_type &&
179120386Ssam		    ph->ph_un.phu_val == lph->ph_un.phu_val) {
180254774Sandre			PFIL_HEADLIST_UNLOCK();
181198233Srwatson			return (EEXIST);
182120386Ssam		}
183186187Srwatson	}
184173904Smlaier	PFIL_LOCK_INIT(ph);
185155201Scsjp	ph->ph_nhooks = 0;
18660317Sdarrenr	TAILQ_INIT(&ph->ph_in);
18760317Sdarrenr	TAILQ_INIT(&ph->ph_out);
188197952Sjulian	LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list);
189254774Sandre	PFIL_HEADLIST_UNLOCK();
190120386Ssam	return (0);
19160317Sdarrenr}
19260317Sdarrenr
19360317Sdarrenr/*
194186187Srwatson * pfil_head_unregister() removes a pfil_head from the packet filter hook
195186187Srwatson * mechanism.  The producer of the hook promises that all outstanding
196186187Srwatson * invocations of the hook have completed before it unregisters the hook.
197120386Ssam */
198120386Ssamint
199120386Ssampfil_head_unregister(struct pfil_head *ph)
200120386Ssam{
201120386Ssam	struct packet_filter_hook *pfh, *pfnext;
202120386Ssam
203254774Sandre	PFIL_HEADLIST_LOCK();
204120386Ssam	LIST_REMOVE(ph, ph_list);
205254774Sandre	PFIL_HEADLIST_UNLOCK();
206254773Sandre	TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_chain, pfnext)
207120386Ssam		free(pfh, M_IFADDR);
208254773Sandre	TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_chain, pfnext)
209120386Ssam		free(pfh, M_IFADDR);
210173904Smlaier	PFIL_LOCK_DESTROY(ph);
211120386Ssam	return (0);
212120386Ssam}
213120386Ssam
214120386Ssam/*
215120386Ssam * pfil_head_get() returns the pfil_head for a given key/dlt.
216120386Ssam */
217120386Ssamstruct pfil_head *
218120386Ssampfil_head_get(int type, u_long val)
219120386Ssam{
220120386Ssam	struct pfil_head *ph;
221120386Ssam
222254774Sandre	PFIL_HEADLIST_LOCK();
223197952Sjulian	LIST_FOREACH(ph, &V_pfil_head_list, ph_list)
224120386Ssam		if (ph->ph_type == type && ph->ph_un.phu_val == val)
225120386Ssam			break;
226254774Sandre	PFIL_HEADLIST_UNLOCK();
227120386Ssam	return (ph);
228120386Ssam}
229120386Ssam
230120386Ssam/*
23160317Sdarrenr * pfil_add_hook() adds a function to the packet filter hook.  the
23260317Sdarrenr * flags are:
23360317Sdarrenr *	PFIL_IN		call me on incoming packets
23460317Sdarrenr *	PFIL_OUT	call me on outgoing packets
23560317Sdarrenr *	PFIL_ALL	call me on all of the above
236111119Simp *	PFIL_WAITOK	OK to call malloc with M_WAITOK.
23760317Sdarrenr */
23860317Sdarrenrint
239254769Sandrepfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
24060317Sdarrenr{
241120386Ssam	struct packet_filter_hook *pfh1 = NULL;
242120386Ssam	struct packet_filter_hook *pfh2 = NULL;
243120386Ssam	int err;
24460317Sdarrenr
245120386Ssam	if (flags & PFIL_IN) {
246120386Ssam		pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
247120386Ssam		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
248120386Ssam		if (pfh1 == NULL) {
249120386Ssam			err = ENOMEM;
250120386Ssam			goto error;
251120386Ssam		}
252120386Ssam	}
253120386Ssam	if (flags & PFIL_OUT) {
254120386Ssam		pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
255120386Ssam		    M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
256120386Ssam		if (pfh2 == NULL) {
257120386Ssam			err = ENOMEM;
258120386Ssam			goto error;
259120386Ssam		}
260120386Ssam	}
261155201Scsjp	PFIL_WLOCK(ph);
262120386Ssam	if (flags & PFIL_IN) {
263120386Ssam		pfh1->pfil_func = func;
264120386Ssam		pfh1->pfil_arg = arg;
265254773Sandre		err = pfil_chain_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT);
266120386Ssam		if (err)
267186187Srwatson			goto locked_error;
268155201Scsjp		ph->ph_nhooks++;
269120386Ssam	}
270120386Ssam	if (flags & PFIL_OUT) {
271120386Ssam		pfh2->pfil_func = func;
272120386Ssam		pfh2->pfil_arg = arg;
273254773Sandre		err = pfil_chain_add(&ph->ph_out, pfh2, flags & ~PFIL_IN);
274120386Ssam		if (err) {
275120386Ssam			if (flags & PFIL_IN)
276254773Sandre				pfil_chain_remove(&ph->ph_in, func, arg);
277186187Srwatson			goto locked_error;
278120386Ssam		}
279155201Scsjp		ph->ph_nhooks++;
280120386Ssam	}
281120386Ssam	PFIL_WUNLOCK(ph);
282198233Srwatson	return (0);
283186187Srwatsonlocked_error:
284120386Ssam	PFIL_WUNLOCK(ph);
285120386Ssamerror:
286120386Ssam	if (pfh1 != NULL)
287120386Ssam		free(pfh1, M_IFADDR);
288120386Ssam	if (pfh2 != NULL)
289120386Ssam		free(pfh2, M_IFADDR);
290198233Srwatson	return (err);
29160317Sdarrenr}
29260317Sdarrenr
29360317Sdarrenr/*
294198233Srwatson * pfil_remove_hook removes a specific function from the packet filter hook
295254773Sandre * chain.
29660317Sdarrenr */
29760317Sdarrenrint
298254769Sandrepfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
29960317Sdarrenr{
30060317Sdarrenr	int err = 0;
30160317Sdarrenr
302155201Scsjp	PFIL_WLOCK(ph);
303155201Scsjp	if (flags & PFIL_IN) {
304254773Sandre		err = pfil_chain_remove(&ph->ph_in, func, arg);
305155201Scsjp		if (err == 0)
306155201Scsjp			ph->ph_nhooks--;
307155201Scsjp	}
308155201Scsjp	if ((err == 0) && (flags & PFIL_OUT)) {
309254773Sandre		err = pfil_chain_remove(&ph->ph_out, func, arg);
310155201Scsjp		if (err == 0)
311155201Scsjp			ph->ph_nhooks--;
312155201Scsjp	}
313120386Ssam	PFIL_WUNLOCK(ph);
314198233Srwatson	return (err);
31560317Sdarrenr}
31660317Sdarrenr
317254773Sandre/*
318254773Sandre * Internal: Add a new pfil hook into a hook chain.
319254773Sandre */
320120386Ssamstatic int
321254773Sandrepfil_chain_add(pfil_chain_t *chain, struct packet_filter_hook *pfh1, int flags)
322120386Ssam{
323120386Ssam	struct packet_filter_hook *pfh;
324120386Ssam
325120386Ssam	/*
326120386Ssam	 * First make sure the hook is not already there.
327120386Ssam	 */
328254773Sandre	TAILQ_FOREACH(pfh, chain, pfil_chain)
329120386Ssam		if (pfh->pfil_func == pfh1->pfil_func &&
330120386Ssam		    pfh->pfil_arg == pfh1->pfil_arg)
331198233Srwatson			return (EEXIST);
332198233Srwatson
333120386Ssam	/*
334198233Srwatson	 * Insert the input list in reverse order of the output list so that
335198233Srwatson	 * the same path is followed in or out of the kernel.
336120386Ssam	 */
337120386Ssam	if (flags & PFIL_IN)
338254773Sandre		TAILQ_INSERT_HEAD(chain, pfh1, pfil_chain);
339120386Ssam	else
340254773Sandre		TAILQ_INSERT_TAIL(chain, pfh1, pfil_chain);
341198233Srwatson	return (0);
342120386Ssam}
343120386Ssam
34460317Sdarrenr/*
345254773Sandre * Internal: Remove a pfil hook from a hook chain.
34660317Sdarrenr */
34760317Sdarrenrstatic int
348254773Sandrepfil_chain_remove(pfil_chain_t *chain, pfil_func_t func, void *arg)
34960317Sdarrenr{
35060317Sdarrenr	struct packet_filter_hook *pfh;
35160317Sdarrenr
352254773Sandre	TAILQ_FOREACH(pfh, chain, pfil_chain)
353120386Ssam		if (pfh->pfil_func == func && pfh->pfil_arg == arg) {
354254773Sandre			TAILQ_REMOVE(chain, pfh, pfil_chain);
35560317Sdarrenr			free(pfh, M_IFADDR);
356198233Srwatson			return (0);
35760317Sdarrenr		}
358198233Srwatson	return (ENOENT);
35960317Sdarrenr}
360197952Sjulian
361198233Srwatson/*
362198233Srwatson * Stuff that must be initialized for every instance (including the first of
363198233Srwatson * course).
364197952Sjulian */
365197952Sjulianstatic int
366197952Sjulianvnet_pfil_init(const void *unused)
367197952Sjulian{
368198233Srwatson
369197952Sjulian	LIST_INIT(&V_pfil_head_list);
370241888Smelifaro	PFIL_LOCK_INIT_REAL(&V_pfil_lock, "shared");
371197952Sjulian	return (0);
372197952Sjulian}
373197952Sjulian
374198233Srwatson/*
375197952Sjulian * Called for the removal of each instance.
376197952Sjulian */
377197952Sjulianstatic int
378197952Sjulianvnet_pfil_uninit(const void *unused)
379197952Sjulian{
380198233Srwatson
381254773Sandre	KASSERT(LIST_EMPTY(&V_pfil_head_list),
382254773Sandre	    ("%s: pfil_head_list %p not empty", __func__, &V_pfil_head_list));
383241888Smelifaro	PFIL_LOCK_DESTROY_REAL(&V_pfil_lock);
384198233Srwatson	return (0);
385197952Sjulian}
386197952Sjulian
387197952Sjulian/* Define startup order. */
388197952Sjulian#define	PFIL_SYSINIT_ORDER	SI_SUB_PROTO_BEGIN
389197952Sjulian#define	PFIL_MODEVENT_ORDER	(SI_ORDER_FIRST) /* On boot slot in here. */
390197952Sjulian#define	PFIL_VNET_ORDER		(PFIL_MODEVENT_ORDER + 2) /* Later still. */
391197952Sjulian
392197952Sjulian/*
393198233Srwatson * Starting up.
394198233Srwatson *
395197952Sjulian * VNET_SYSINIT is called for each existing vnet and each new vnet.
396197952Sjulian */
397197952SjulianVNET_SYSINIT(vnet_pfil_init, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
398198233Srwatson    vnet_pfil_init, NULL);
399197952Sjulian
400197952Sjulian/*
401198233Srwatson * Closing up shop.  These are done in REVERSE ORDER.  Not called on reboot.
402198233Srwatson *
403197952Sjulian * VNET_SYSUNINIT is called for each exiting vnet as it exits.
404197952Sjulian */
405197952SjulianVNET_SYSUNINIT(vnet_pfil_uninit, PFIL_SYSINIT_ORDER, PFIL_VNET_ORDER,
406198233Srwatson    vnet_pfil_uninit, NULL);
407