xref: /freebsd/sys/net/pfil.c (revision c93410229ceb75d52da41e681f70f352141b7d97)
1e9c7bebfSDarren Reed /*	$FreeBSD$ */
2134ea224SSam Leffler /*	$NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $	*/
3e9c7bebfSDarren Reed 
4c398230bSWarner Losh /*-
5fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
6fe267a55SPedro F. Giffuni  *
7b252313fSGleb Smirnoff  * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
8e9c7bebfSDarren Reed  * Copyright (c) 1996 Matthew R. Green
9e9c7bebfSDarren Reed  * All rights reserved.
10e9c7bebfSDarren Reed  *
11e9c7bebfSDarren Reed  * Redistribution and use in source and binary forms, with or without
12e9c7bebfSDarren Reed  * modification, are permitted provided that the following conditions
13e9c7bebfSDarren Reed  * are met:
14e9c7bebfSDarren Reed  * 1. Redistributions of source code must retain the above copyright
15e9c7bebfSDarren Reed  *    notice, this list of conditions and the following disclaimer.
16e9c7bebfSDarren Reed  * 2. Redistributions in binary form must reproduce the above copyright
17e9c7bebfSDarren Reed  *    notice, this list of conditions and the following disclaimer in the
18e9c7bebfSDarren Reed  *    documentation and/or other materials provided with the distribution.
19e9c7bebfSDarren Reed  * 3. The name of the author may not be used to endorse or promote products
20e9c7bebfSDarren Reed  *    derived from this software without specific prior written permission.
21e9c7bebfSDarren Reed  *
22e9c7bebfSDarren Reed  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23e9c7bebfSDarren Reed  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24e9c7bebfSDarren Reed  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25e9c7bebfSDarren Reed  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26e9c7bebfSDarren Reed  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27e9c7bebfSDarren Reed  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28e9c7bebfSDarren Reed  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29e9c7bebfSDarren Reed  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30e9c7bebfSDarren Reed  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31e9c7bebfSDarren Reed  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32e9c7bebfSDarren Reed  * SUCH DAMAGE.
33e9c7bebfSDarren Reed  */
34e9c7bebfSDarren Reed 
35e9c7bebfSDarren Reed #include <sys/param.h>
36b252313fSGleb Smirnoff #include <sys/conf.h>
37134ea224SSam Leffler #include <sys/kernel.h>
38b252313fSGleb Smirnoff #include <sys/epoch.h>
39e9c7bebfSDarren Reed #include <sys/errno.h>
40604afec4SChristian S.J. Peron #include <sys/lock.h>
41e9c7bebfSDarren Reed #include <sys/malloc.h>
42e9c7bebfSDarren Reed #include <sys/socket.h>
43e9c7bebfSDarren Reed #include <sys/socketvar.h>
44e9c7bebfSDarren Reed #include <sys/systm.h>
45134ea224SSam Leffler #include <sys/lock.h>
46134ea224SSam Leffler #include <sys/mutex.h>
47134ea224SSam Leffler #include <sys/proc.h>
48e9c7bebfSDarren Reed #include <sys/queue.h>
493ca1c423SGleb Smirnoff #include <sys/ucred.h>
503ca1c423SGleb Smirnoff #include <sys/jail.h>
51e9c7bebfSDarren Reed 
52e9c7bebfSDarren Reed #include <net/if.h>
5376039bc8SGleb Smirnoff #include <net/if_var.h>
54e9c7bebfSDarren Reed #include <net/pfil.h>
55e9c7bebfSDarren Reed 
56b252313fSGleb Smirnoff static MALLOC_DEFINE(M_PFIL, "pfil", "pfil(9) packet filter hooks");
57e9c7bebfSDarren Reed 
58b252313fSGleb Smirnoff static int pfil_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
59b252313fSGleb Smirnoff static struct cdevsw pfil_cdevsw = {
60b252313fSGleb Smirnoff 	.d_ioctl =	pfil_ioctl,
61b252313fSGleb Smirnoff 	.d_name =	PFILDEV,
62b252313fSGleb Smirnoff 	.d_version =	D_VERSION,
63b252313fSGleb Smirnoff };
64b252313fSGleb Smirnoff static struct cdev *pfil_dev;
65134ea224SSam Leffler 
66b252313fSGleb Smirnoff static struct mtx pfil_lock;
67b252313fSGleb Smirnoff MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF);
68b252313fSGleb Smirnoff #define	PFIL_LOCK()	mtx_lock(&pfil_lock)
69b252313fSGleb Smirnoff #define	PFIL_UNLOCK()	mtx_unlock(&pfil_lock)
70b252313fSGleb Smirnoff #define	PFIL_LOCK_ASSERT()	mtx_assert(&pfil_lock, MA_OWNED)
71b252313fSGleb Smirnoff 
72b252313fSGleb Smirnoff #define	PFIL_EPOCH		net_epoch_preempt
73b252313fSGleb Smirnoff #define	PFIL_EPOCH_ENTER(et)	epoch_enter_preempt(net_epoch_preempt, &(et))
74b252313fSGleb Smirnoff #define	PFIL_EPOCH_EXIT(et)	epoch_exit_preempt(net_epoch_preempt, &(et))
75b252313fSGleb Smirnoff 
76b252313fSGleb Smirnoff struct pfil_hook {
77b252313fSGleb Smirnoff 	pfil_func_t	 hook_func;
78b252313fSGleb Smirnoff 	void		*hook_ruleset;
79b252313fSGleb Smirnoff 	int		 hook_flags;
80b252313fSGleb Smirnoff 	int		 hook_links;
81b252313fSGleb Smirnoff 	enum pfil_types	 hook_type;
82b252313fSGleb Smirnoff 	const char	*hook_modname;
83b252313fSGleb Smirnoff 	const char	*hook_rulname;
84b252313fSGleb Smirnoff 	LIST_ENTRY(pfil_hook) hook_list;
85b252313fSGleb Smirnoff };
86b252313fSGleb Smirnoff 
87b252313fSGleb Smirnoff struct pfil_link {
88b252313fSGleb Smirnoff 	CK_STAILQ_ENTRY(pfil_link) link_chain;
89b252313fSGleb Smirnoff 	pfil_func_t		 link_func;
90b252313fSGleb Smirnoff 	void			*link_ruleset;
91b252313fSGleb Smirnoff 	int			 link_flags;
92b252313fSGleb Smirnoff 	struct pfil_hook	*link_hook;
93b252313fSGleb Smirnoff 	struct epoch_context	 link_epoch_ctx;
94b252313fSGleb Smirnoff };
95b252313fSGleb Smirnoff 
96b252313fSGleb Smirnoff typedef CK_STAILQ_HEAD(pfil_chain, pfil_link)	pfil_chain_t;
97b252313fSGleb Smirnoff struct pfil_head {
98b252313fSGleb Smirnoff 	int		 head_nhooksin;
99b252313fSGleb Smirnoff 	int		 head_nhooksout;
100b252313fSGleb Smirnoff 	pfil_chain_t	 head_in;
101b252313fSGleb Smirnoff 	pfil_chain_t	 head_out;
102b252313fSGleb Smirnoff 	int		 head_flags;
103b252313fSGleb Smirnoff 	enum pfil_types	 head_type;
104b252313fSGleb Smirnoff 	LIST_ENTRY(pfil_head) head_list;
105b252313fSGleb Smirnoff 	const char	*head_name;
106b252313fSGleb Smirnoff };
107134ea224SSam Leffler 
1080b4b0b0fSJulian Elischer LIST_HEAD(pfilheadhead, pfil_head);
109b252313fSGleb Smirnoff VNET_DEFINE_STATIC(struct pfilheadhead, pfil_head_list) =
110b252313fSGleb Smirnoff     LIST_HEAD_INITIALIZER(pfil_head_list);
1110b4b0b0fSJulian Elischer #define	V_pfil_head_list	VNET(pfil_head_list)
112134ea224SSam Leffler 
113b252313fSGleb Smirnoff LIST_HEAD(pfilhookhead, pfil_hook);
114b252313fSGleb Smirnoff VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) =
115b252313fSGleb Smirnoff     LIST_HEAD_INITIALIZER(pfil_hook_list);
116b252313fSGleb Smirnoff #define	V_pfil_hook_list	VNET(pfil_hook_list)
117af48c203SAndrey V. Elsukov 
118b252313fSGleb Smirnoff static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t );
119b252313fSGleb Smirnoff static void pfil_link_free(epoch_context_t);
120af48c203SAndrey V. Elsukov 
121*c9341022SGleb Smirnoff int
122*c9341022SGleb Smirnoff pfil_realloc(pfil_packet_t *p, int flags, struct ifnet *ifp)
123*c9341022SGleb Smirnoff {
124*c9341022SGleb Smirnoff 	struct mbuf *m;
125*c9341022SGleb Smirnoff 
126*c9341022SGleb Smirnoff 	MPASS(flags & PFIL_MEMPTR);
127*c9341022SGleb Smirnoff 
128*c9341022SGleb Smirnoff 	if ((m = m_devget(p->mem, PFIL_LENGTH(flags), 0, ifp, NULL)) == NULL)
129*c9341022SGleb Smirnoff 		return (ENOMEM);
130*c9341022SGleb Smirnoff 	*p = pfil_packet_align(*p);
131*c9341022SGleb Smirnoff 	*p->m = m;
132*c9341022SGleb Smirnoff 
133*c9341022SGleb Smirnoff 	return (0);
134*c9341022SGleb Smirnoff }
135*c9341022SGleb Smirnoff 
136b252313fSGleb Smirnoff static __noinline int
137*c9341022SGleb Smirnoff pfil_fake_mbuf(pfil_func_t func, pfil_packet_t *p, struct ifnet *ifp, int flags,
138b252313fSGleb Smirnoff     void *ruleset, struct inpcb *inp)
139b252313fSGleb Smirnoff {
140b252313fSGleb Smirnoff 	struct mbuf m, *mp;
141b252313fSGleb Smirnoff 	pfil_return_t rv;
142b252313fSGleb Smirnoff 
143b252313fSGleb Smirnoff 	(void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR);
144*c9341022SGleb Smirnoff 	m_extadd(&m, p->mem, PFIL_LENGTH(flags), NULL, NULL, NULL, 0,
145*c9341022SGleb Smirnoff 	    EXT_RXRING);
146b252313fSGleb Smirnoff 	m.m_len = m.m_pkthdr.len = PFIL_LENGTH(flags);
147b252313fSGleb Smirnoff 	mp = &m;
148b252313fSGleb Smirnoff 	flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
149b252313fSGleb Smirnoff 
150b252313fSGleb Smirnoff 	rv = func(&mp, ifp, flags, ruleset, inp);
151b252313fSGleb Smirnoff 	if (rv == PFIL_PASS && mp != &m) {
152b252313fSGleb Smirnoff 		/*
153b252313fSGleb Smirnoff 		 * Firewalls that need pfil_fake_mbuf() most likely don't
154*c9341022SGleb Smirnoff 		 * know they need return PFIL_REALLOCED.
155b252313fSGleb Smirnoff 		 */
156b252313fSGleb Smirnoff 		rv = PFIL_REALLOCED;
157*c9341022SGleb Smirnoff 		*p = pfil_packet_align(*p);
158*c9341022SGleb Smirnoff 		*p->m = mp;
159b252313fSGleb Smirnoff 	}
160b252313fSGleb Smirnoff 
161b252313fSGleb Smirnoff 	return (rv);
162b252313fSGleb Smirnoff }
163af48c203SAndrey V. Elsukov 
164134ea224SSam Leffler /*
1658da01399SAndre Oppermann  * pfil_run_hooks() runs the specified packet filter hook chain.
166134ea224SSam Leffler  */
167134ea224SSam Leffler int
168b252313fSGleb Smirnoff pfil_run_hooks(struct pfil_head *head, pfil_packet_t p, struct ifnet *ifp,
169b252313fSGleb Smirnoff     int flags, struct inpcb *inp)
170134ea224SSam Leffler {
171b252313fSGleb Smirnoff 	struct epoch_tracker et;
172b252313fSGleb Smirnoff 	pfil_chain_t *pch;
173b252313fSGleb Smirnoff 	struct pfil_link *link;
174b9fdb4b3SGleb Smirnoff 	pfil_return_t rv;
175b9fdb4b3SGleb Smirnoff 	bool realloc = false;
176134ea224SSam Leffler 
177b252313fSGleb Smirnoff 	if (PFIL_DIR(flags) == PFIL_IN)
178b252313fSGleb Smirnoff 		pch = &head->head_in;
179b252313fSGleb Smirnoff 	else if (__predict_true(PFIL_DIR(flags) == PFIL_OUT))
180b252313fSGleb Smirnoff 		pch = &head->head_out;
181887c60fcSAndre Oppermann 	else
182b252313fSGleb Smirnoff 		panic("%s: bogus flags %d", __func__, flags);
183b252313fSGleb Smirnoff 
184b252313fSGleb Smirnoff 	rv = PFIL_PASS;
185b252313fSGleb Smirnoff 	PFIL_EPOCH_ENTER(et);
186b252313fSGleb Smirnoff 	CK_STAILQ_FOREACH(link, pch, link_chain) {
187b252313fSGleb Smirnoff 		if ((flags & PFIL_MEMPTR) && !(link->link_flags & PFIL_MEMPTR))
188*c9341022SGleb Smirnoff 			rv = pfil_fake_mbuf(link->link_func, &p, ifp, flags,
189*c9341022SGleb Smirnoff 			    link->link_ruleset, inp);
190b252313fSGleb Smirnoff 		else
191b9fdb4b3SGleb Smirnoff 			rv = (*link->link_func)(p, ifp, flags,
192b252313fSGleb Smirnoff 			    link->link_ruleset, inp);
193b9fdb4b3SGleb Smirnoff 		if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
194b252313fSGleb Smirnoff 			break;
195b9fdb4b3SGleb Smirnoff 		else if (rv == PFIL_REALLOCED) {
196b252313fSGleb Smirnoff 			flags &= ~(PFIL_MEMPTR | PFIL_LENMASK);
197b9fdb4b3SGleb Smirnoff 			realloc = true;
198887c60fcSAndre Oppermann 		}
1994dab1a18SAlexander V. Chernikov 	}
200b252313fSGleb Smirnoff 	PFIL_EPOCH_EXIT(et);
201b9fdb4b3SGleb Smirnoff 	if (realloc && rv == PFIL_PASS)
202b9fdb4b3SGleb Smirnoff 		rv = PFIL_REALLOCED;
203b9fdb4b3SGleb Smirnoff 	return (rv);
2044dab1a18SAlexander V. Chernikov }
2058da01399SAndre Oppermann 
2064dab1a18SAlexander V. Chernikov /*
207cee81198SRobert Watson  * pfil_head_register() registers a pfil_head with the packet filter hook
208cee81198SRobert Watson  * mechanism.
209134ea224SSam Leffler  */
210b252313fSGleb Smirnoff pfil_head_t
211b252313fSGleb Smirnoff pfil_head_register(struct pfil_head_args *pa)
212134ea224SSam Leffler {
213b252313fSGleb Smirnoff 	struct pfil_head *head, *list;
214134ea224SSam Leffler 
215b252313fSGleb Smirnoff 	MPASS(pa->pa_version == PFIL_VERSION);
216b252313fSGleb Smirnoff 
217b252313fSGleb Smirnoff 	head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK);
218b252313fSGleb Smirnoff 
219b252313fSGleb Smirnoff 	head->head_nhooksin = head->head_nhooksout = 0;
220b252313fSGleb Smirnoff 	head->head_flags = pa->pa_flags;
221b252313fSGleb Smirnoff 	head->head_type = pa->pa_type;
222b252313fSGleb Smirnoff 	head->head_name = pa->pa_headname;
223b252313fSGleb Smirnoff 	CK_STAILQ_INIT(&head->head_in);
224b252313fSGleb Smirnoff 	CK_STAILQ_INIT(&head->head_out);
225b252313fSGleb Smirnoff 
226b252313fSGleb Smirnoff 	PFIL_LOCK();
227b252313fSGleb Smirnoff 	LIST_FOREACH(list, &V_pfil_head_list, head_list)
228b252313fSGleb Smirnoff 		if (strcmp(pa->pa_headname, list->head_name) == 0) {
229b252313fSGleb Smirnoff 			printf("pfil: duplicate head \"%s\"\n",
230b252313fSGleb Smirnoff 			    pa->pa_headname);
231134ea224SSam Leffler 		}
232b252313fSGleb Smirnoff 	LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list);
233b252313fSGleb Smirnoff 	PFIL_UNLOCK();
234b252313fSGleb Smirnoff 
235b252313fSGleb Smirnoff 	return (head);
236134ea224SSam Leffler }
237134ea224SSam Leffler 
238134ea224SSam Leffler /*
239d2c205d5SRobert Watson  * pfil_head_unregister() removes a pfil_head from the packet filter hook
240d2c205d5SRobert Watson  * mechanism.  The producer of the hook promises that all outstanding
241d2c205d5SRobert Watson  * invocations of the hook have completed before it unregisters the hook.
242134ea224SSam Leffler  */
243b252313fSGleb Smirnoff void
244b252313fSGleb Smirnoff pfil_head_unregister(pfil_head_t ph)
245134ea224SSam Leffler {
246b252313fSGleb Smirnoff 	struct pfil_link *link, *next;
247134ea224SSam Leffler 
248b252313fSGleb Smirnoff 	PFIL_LOCK();
249b252313fSGleb Smirnoff 	LIST_REMOVE(ph, head_list);
250b252313fSGleb Smirnoff 
251b252313fSGleb Smirnoff 	CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) {
252b252313fSGleb Smirnoff 		link->link_hook->hook_links--;
253b252313fSGleb Smirnoff 		free(link, M_PFIL);
254b252313fSGleb Smirnoff 	}
255b252313fSGleb Smirnoff 	CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) {
256b252313fSGleb Smirnoff 		link->link_hook->hook_links--;
257b252313fSGleb Smirnoff 		free(link, M_PFIL);
258b252313fSGleb Smirnoff 	}
259b252313fSGleb Smirnoff 	PFIL_UNLOCK();
260134ea224SSam Leffler }
261134ea224SSam Leffler 
262b252313fSGleb Smirnoff pfil_hook_t
263b252313fSGleb Smirnoff pfil_add_hook(struct pfil_hook_args *pa)
264134ea224SSam Leffler {
265b252313fSGleb Smirnoff 	struct pfil_hook *hook, *list;
266134ea224SSam Leffler 
267b252313fSGleb Smirnoff 	MPASS(pa->pa_version == PFIL_VERSION);
268b252313fSGleb Smirnoff 
269b252313fSGleb Smirnoff 	hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO);
270b252313fSGleb Smirnoff 	hook->hook_func = pa->pa_func;
271b252313fSGleb Smirnoff 	hook->hook_ruleset = pa->pa_ruleset;
272b252313fSGleb Smirnoff 	hook->hook_flags = pa->pa_flags;
273b252313fSGleb Smirnoff 	hook->hook_type = pa->pa_type;
274b252313fSGleb Smirnoff 	hook->hook_modname = pa->pa_modname;
275b252313fSGleb Smirnoff 	hook->hook_rulname = pa->pa_rulname;
276b252313fSGleb Smirnoff 
277b252313fSGleb Smirnoff 	PFIL_LOCK();
278b252313fSGleb Smirnoff 	LIST_FOREACH(list, &V_pfil_hook_list, hook_list)
279b252313fSGleb Smirnoff 		if (strcmp(pa->pa_modname, list->hook_modname) == 0 &&
280b252313fSGleb Smirnoff 		    strcmp(pa->pa_rulname, list->hook_rulname) == 0) {
281b252313fSGleb Smirnoff 			printf("pfil: duplicate hook \"%s:%s\"\n",
282b252313fSGleb Smirnoff 			    pa->pa_modname, pa->pa_rulname);
283e9c7bebfSDarren Reed 		}
284b252313fSGleb Smirnoff 	LIST_INSERT_HEAD(&V_pfil_hook_list, hook, hook_list);
285b252313fSGleb Smirnoff 	PFIL_UNLOCK();
286e9c7bebfSDarren Reed 
287b252313fSGleb Smirnoff 	return (hook);
288effaab88SKristof Provost }
289effaab88SKristof Provost 
290effaab88SKristof Provost static int
291b252313fSGleb Smirnoff pfil_unlink(struct pfil_link_args *pa, pfil_head_t head, pfil_hook_t hook)
292effaab88SKristof Provost {
293b252313fSGleb Smirnoff 	struct pfil_link *in, *out;
294e9c7bebfSDarren Reed 
295b252313fSGleb Smirnoff 	PFIL_LOCK_ASSERT();
296e9c7bebfSDarren Reed 
297b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_IN) {
298b252313fSGleb Smirnoff 		in = pfil_link_remove(&head->head_in, hook);
299b252313fSGleb Smirnoff 		if (in != NULL) {
300b252313fSGleb Smirnoff 			head->head_nhooksin--;
301b252313fSGleb Smirnoff 			hook->hook_links--;
302effaab88SKristof Provost 		}
303b252313fSGleb Smirnoff 	} else
304b252313fSGleb Smirnoff 		in = NULL;
305b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_OUT) {
306b252313fSGleb Smirnoff 		out = pfil_link_remove(&head->head_out, hook);
307b252313fSGleb Smirnoff 		if (out != NULL) {
308b252313fSGleb Smirnoff 			head->head_nhooksout--;
309b252313fSGleb Smirnoff 			hook->hook_links--;
310604afec4SChristian S.J. Peron 		}
311b252313fSGleb Smirnoff 	} else
312b252313fSGleb Smirnoff 		out = NULL;
313b252313fSGleb Smirnoff 	PFIL_UNLOCK();
314e9c7bebfSDarren Reed 
315b252313fSGleb Smirnoff 	if (in != NULL)
316b252313fSGleb Smirnoff 		epoch_call(PFIL_EPOCH, &in->link_epoch_ctx, pfil_link_free);
317b252313fSGleb Smirnoff 	if (out != NULL)
318b252313fSGleb Smirnoff 		epoch_call(PFIL_EPOCH, &out->link_epoch_ctx, pfil_link_free);
319134ea224SSam Leffler 
320b252313fSGleb Smirnoff 	if (in == NULL && out == NULL)
321b252313fSGleb Smirnoff 		return (ENOENT);
322134ea224SSam Leffler 	else
323cee81198SRobert Watson 		return (0);
324134ea224SSam Leffler }
325134ea224SSam Leffler 
326b252313fSGleb Smirnoff int
327b252313fSGleb Smirnoff pfil_link(struct pfil_link_args *pa)
328b252313fSGleb Smirnoff {
329b252313fSGleb Smirnoff 	struct pfil_link *in, *out, *link;
330b252313fSGleb Smirnoff 	struct pfil_head *head;
331b252313fSGleb Smirnoff 	struct pfil_hook *hook;
332b252313fSGleb Smirnoff 	int error;
333b252313fSGleb Smirnoff 
334b252313fSGleb Smirnoff 	MPASS(pa->pa_version == PFIL_VERSION);
335b252313fSGleb Smirnoff 
336b252313fSGleb Smirnoff 	if ((pa->pa_flags & (PFIL_IN | PFIL_UNLINK)) == PFIL_IN)
337b252313fSGleb Smirnoff 		in = malloc(sizeof(*in), M_PFIL, M_WAITOK | M_ZERO);
338b252313fSGleb Smirnoff 	else
339b252313fSGleb Smirnoff 		in = NULL;
340b252313fSGleb Smirnoff 	if ((pa->pa_flags & (PFIL_OUT | PFIL_UNLINK)) == PFIL_OUT)
341b252313fSGleb Smirnoff 		out = malloc(sizeof(*out), M_PFIL, M_WAITOK | M_ZERO);
342b252313fSGleb Smirnoff 	else
343b252313fSGleb Smirnoff 		out = NULL;
344b252313fSGleb Smirnoff 
345b252313fSGleb Smirnoff 	PFIL_LOCK();
346b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_HEADPTR)
347b252313fSGleb Smirnoff 		head = pa->pa_head;
348b252313fSGleb Smirnoff 	else
349b252313fSGleb Smirnoff 		LIST_FOREACH(head, &V_pfil_head_list, head_list)
350b252313fSGleb Smirnoff 			if (strcmp(pa->pa_headname, head->head_name) == 0)
351b252313fSGleb Smirnoff 				break;
352b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_HOOKPTR)
353b252313fSGleb Smirnoff 		hook = pa->pa_hook;
354b252313fSGleb Smirnoff 	else
355b252313fSGleb Smirnoff 		LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
356b252313fSGleb Smirnoff 			if (strcmp(pa->pa_modname, hook->hook_modname) == 0 &&
357b252313fSGleb Smirnoff 			    strcmp(pa->pa_rulname, hook->hook_rulname) == 0)
358b252313fSGleb Smirnoff 				break;
359b252313fSGleb Smirnoff 	if (head == NULL || hook == NULL) {
360b252313fSGleb Smirnoff 		error = ENOENT;
361b252313fSGleb Smirnoff 		goto fail;
362b252313fSGleb Smirnoff 	}
363b252313fSGleb Smirnoff 
364b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_UNLINK)
365b252313fSGleb Smirnoff 		return (pfil_unlink(pa, head, hook));
366b252313fSGleb Smirnoff 
367b252313fSGleb Smirnoff 	if (head->head_type != hook->hook_type ||
368b252313fSGleb Smirnoff 	    ((hook->hook_flags & pa->pa_flags) & ~head->head_flags)) {
369b252313fSGleb Smirnoff 		error = EINVAL;
370b252313fSGleb Smirnoff 		goto fail;
371b252313fSGleb Smirnoff 	}
372b252313fSGleb Smirnoff 
373b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_IN)
374b252313fSGleb Smirnoff 		CK_STAILQ_FOREACH(link, &head->head_in, link_chain)
375b252313fSGleb Smirnoff 			if (link->link_hook == hook) {
376b252313fSGleb Smirnoff 				error = EEXIST;
377b252313fSGleb Smirnoff 				goto fail;
378b252313fSGleb Smirnoff 			}
379b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_OUT)
380b252313fSGleb Smirnoff 		CK_STAILQ_FOREACH(link, &head->head_out, link_chain)
381b252313fSGleb Smirnoff 			if (link->link_hook == hook) {
382b252313fSGleb Smirnoff 				error = EEXIST;
383b252313fSGleb Smirnoff 				goto fail;
384b252313fSGleb Smirnoff 			}
385b252313fSGleb Smirnoff 
386b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_IN) {
387b252313fSGleb Smirnoff 		in->link_hook = hook;
388b252313fSGleb Smirnoff 		in->link_func = hook->hook_func;
389b252313fSGleb Smirnoff 		in->link_flags = hook->hook_flags;
390b252313fSGleb Smirnoff 		in->link_ruleset = hook->hook_ruleset;
391b252313fSGleb Smirnoff 		if (pa->pa_flags & PFIL_APPEND)
392b252313fSGleb Smirnoff 			CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain);
393b252313fSGleb Smirnoff 		else
394b252313fSGleb Smirnoff 			CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain);
395b252313fSGleb Smirnoff 		hook->hook_links++;
396b252313fSGleb Smirnoff 		head->head_nhooksin++;
397b252313fSGleb Smirnoff 	}
398b252313fSGleb Smirnoff 	if (pa->pa_flags & PFIL_OUT) {
399b252313fSGleb Smirnoff 		out->link_hook = hook;
400b252313fSGleb Smirnoff 		out->link_func = hook->hook_func;
401b252313fSGleb Smirnoff 		out->link_flags = hook->hook_flags;
402b252313fSGleb Smirnoff 		out->link_ruleset = hook->hook_ruleset;
403b252313fSGleb Smirnoff 		if (pa->pa_flags & PFIL_APPEND)
404b252313fSGleb Smirnoff 			CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain);
405b252313fSGleb Smirnoff 		else
406b252313fSGleb Smirnoff 			CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain);
407b252313fSGleb Smirnoff 		hook->hook_links++;
408b252313fSGleb Smirnoff 		head->head_nhooksout++;
409b252313fSGleb Smirnoff 	}
410b252313fSGleb Smirnoff 	PFIL_UNLOCK();
411b252313fSGleb Smirnoff 
412b252313fSGleb Smirnoff 	return (0);
413b252313fSGleb Smirnoff 
414b252313fSGleb Smirnoff fail:
415b252313fSGleb Smirnoff 	PFIL_UNLOCK();
416b252313fSGleb Smirnoff 	free(in, M_PFIL);
417b252313fSGleb Smirnoff 	free(out, M_PFIL);
418b252313fSGleb Smirnoff 	return (error);
419b252313fSGleb Smirnoff }
420b252313fSGleb Smirnoff 
421b252313fSGleb Smirnoff static void
422b252313fSGleb Smirnoff pfil_link_free(epoch_context_t ctx)
423b252313fSGleb Smirnoff {
424b252313fSGleb Smirnoff 	struct pfil_link *link;
425b252313fSGleb Smirnoff 
426b252313fSGleb Smirnoff 	link = __containerof(ctx, struct pfil_link, link_epoch_ctx);
427b252313fSGleb Smirnoff 	free(link, M_PFIL);
428b252313fSGleb Smirnoff }
429b252313fSGleb Smirnoff 
430b252313fSGleb Smirnoff /*
431b252313fSGleb Smirnoff  * pfil_remove_hook removes a filter from all filtering points.
432b252313fSGleb Smirnoff  */
433b252313fSGleb Smirnoff void
434b252313fSGleb Smirnoff pfil_remove_hook(pfil_hook_t hook)
435b252313fSGleb Smirnoff {
436b252313fSGleb Smirnoff 	struct pfil_head *head;
437b252313fSGleb Smirnoff 	struct pfil_link *in, *out;
438b252313fSGleb Smirnoff 
439b252313fSGleb Smirnoff 	PFIL_LOCK();
440b252313fSGleb Smirnoff 	LIST_FOREACH(head, &V_pfil_head_list, head_list) {
441b252313fSGleb Smirnoff retry:
442b252313fSGleb Smirnoff 		in = pfil_link_remove(&head->head_in, hook);
443b252313fSGleb Smirnoff 		if (in != NULL) {
444b252313fSGleb Smirnoff 			head->head_nhooksin--;
445b252313fSGleb Smirnoff 			hook->hook_links--;
446b252313fSGleb Smirnoff 			epoch_call(PFIL_EPOCH, &in->link_epoch_ctx,
447b252313fSGleb Smirnoff 			    pfil_link_free);
448b252313fSGleb Smirnoff 		}
449b252313fSGleb Smirnoff 		out = pfil_link_remove(&head->head_out, hook);
450b252313fSGleb Smirnoff 		if (out != NULL) {
451b252313fSGleb Smirnoff 			head->head_nhooksout--;
452b252313fSGleb Smirnoff 			hook->hook_links--;
453b252313fSGleb Smirnoff 			epoch_call(PFIL_EPOCH, &out->link_epoch_ctx,
454b252313fSGleb Smirnoff 			    pfil_link_free);
455b252313fSGleb Smirnoff 		}
456b252313fSGleb Smirnoff 		if (in != NULL || out != NULL)
457b252313fSGleb Smirnoff 			/* What if some stupid admin put same filter twice? */
458b252313fSGleb Smirnoff 			goto retry;
459b252313fSGleb Smirnoff 	}
460b252313fSGleb Smirnoff 	LIST_REMOVE(hook, hook_list);
461b252313fSGleb Smirnoff 	PFIL_UNLOCK();
462b252313fSGleb Smirnoff 	MPASS(hook->hook_links == 0);
463b252313fSGleb Smirnoff 	free(hook, M_PFIL);
464b252313fSGleb Smirnoff }
465b252313fSGleb Smirnoff 
466e9c7bebfSDarren Reed /*
4678da01399SAndre Oppermann  * Internal: Remove a pfil hook from a hook chain.
468e9c7bebfSDarren Reed  */
469b252313fSGleb Smirnoff static struct pfil_link *
470b252313fSGleb Smirnoff pfil_link_remove(pfil_chain_t *chain, pfil_hook_t hook)
471e9c7bebfSDarren Reed {
472b252313fSGleb Smirnoff 	struct pfil_link *link;
473e9c7bebfSDarren Reed 
474b252313fSGleb Smirnoff 	PFIL_LOCK_ASSERT();
475b252313fSGleb Smirnoff 
476b252313fSGleb Smirnoff 	CK_STAILQ_FOREACH(link, chain, link_chain)
477b252313fSGleb Smirnoff 		if (link->link_hook == hook) {
478b252313fSGleb Smirnoff 			CK_STAILQ_REMOVE(chain, link, pfil_link, link_chain);
479b252313fSGleb Smirnoff 			return (link);
480e9c7bebfSDarren Reed 		}
4810b4b0b0fSJulian Elischer 
482b252313fSGleb Smirnoff 	return (NULL);
483b252313fSGleb Smirnoff }
484b252313fSGleb Smirnoff 
485b9dbac48SBjoern A. Zeeb static void
486b252313fSGleb Smirnoff pfil_init(const void *unused __unused)
4870b4b0b0fSJulian Elischer {
488b252313fSGleb Smirnoff 	struct make_dev_args args;
489b252313fSGleb Smirnoff 	int error;
490cee81198SRobert Watson 
491b252313fSGleb Smirnoff 	make_dev_args_init(&args);
492b252313fSGleb Smirnoff 	args.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
493b252313fSGleb Smirnoff 	args.mda_devsw = &pfil_cdevsw;
494b252313fSGleb Smirnoff 	args.mda_uid = UID_ROOT;
495b252313fSGleb Smirnoff 	args.mda_gid = GID_WHEEL;
496b252313fSGleb Smirnoff 	args.mda_mode = 0600;
497b252313fSGleb Smirnoff 	error = make_dev_s(&args, &pfil_dev, PFILDEV);
498b252313fSGleb Smirnoff 	KASSERT(error == 0, ("%s: failed to create dev: %d", __func__, error));
4990b4b0b0fSJulian Elischer }
500cee81198SRobert Watson /*
50189856f7eSBjoern A. Zeeb  * Make sure the pfil bits are first before any possible subsystem which
50289856f7eSBjoern A. Zeeb  * might piggyback on the SI_SUB_PROTO_PFIL.
5030b4b0b0fSJulian Elischer  */
504b252313fSGleb Smirnoff SYSINIT(pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, pfil_init, NULL);
5050b4b0b0fSJulian Elischer 
5060b4b0b0fSJulian Elischer /*
507b252313fSGleb Smirnoff  * User control interface.
5080b4b0b0fSJulian Elischer  */
509b252313fSGleb Smirnoff static int pfilioc_listheads(struct pfilioc_list *);
510b252313fSGleb Smirnoff static int pfilioc_listhooks(struct pfilioc_list *);
511b252313fSGleb Smirnoff static int pfilioc_link(struct pfilioc_link *);
512b252313fSGleb Smirnoff 
513b252313fSGleb Smirnoff static int
514b252313fSGleb Smirnoff pfil_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
515b252313fSGleb Smirnoff     struct thread *td)
516b252313fSGleb Smirnoff {
517b252313fSGleb Smirnoff 	int error;
518b252313fSGleb Smirnoff 
5193ca1c423SGleb Smirnoff 	CURVNET_SET(TD_TO_VNET(td));
520b252313fSGleb Smirnoff 	error = 0;
521b252313fSGleb Smirnoff 	switch (cmd) {
522b252313fSGleb Smirnoff 	case PFILIOC_LISTHEADS:
523b252313fSGleb Smirnoff 		error = pfilioc_listheads((struct pfilioc_list *)addr);
524b252313fSGleb Smirnoff 		break;
525b252313fSGleb Smirnoff 	case PFILIOC_LISTHOOKS:
526b252313fSGleb Smirnoff 		error = pfilioc_listhooks((struct pfilioc_list *)addr);
527b252313fSGleb Smirnoff 		break;
528b252313fSGleb Smirnoff 	case PFILIOC_LINK:
529b252313fSGleb Smirnoff 		error = pfilioc_link((struct pfilioc_link *)addr);
530b252313fSGleb Smirnoff 		break;
531b252313fSGleb Smirnoff 	default:
5323ca1c423SGleb Smirnoff 		error = EINVAL;
5333ca1c423SGleb Smirnoff 		break;
534b252313fSGleb Smirnoff 	}
5353ca1c423SGleb Smirnoff 	CURVNET_RESTORE();
536b252313fSGleb Smirnoff 	return (error);
537b252313fSGleb Smirnoff }
538b252313fSGleb Smirnoff 
539b252313fSGleb Smirnoff static int
540b252313fSGleb Smirnoff pfilioc_listheads(struct pfilioc_list *req)
541b252313fSGleb Smirnoff {
542b252313fSGleb Smirnoff 	struct pfil_head *head;
543b252313fSGleb Smirnoff 	struct pfil_link *link;
544b252313fSGleb Smirnoff 	struct pfilioc_head *iohead;
545b252313fSGleb Smirnoff 	struct pfilioc_hook *iohook;
546b252313fSGleb Smirnoff 	u_int nheads, nhooks, hd, hk;
547b252313fSGleb Smirnoff 	int error;
548b252313fSGleb Smirnoff 
549b252313fSGleb Smirnoff 	PFIL_LOCK();
550b252313fSGleb Smirnoff restart:
551b252313fSGleb Smirnoff 	nheads = nhooks = 0;
552b252313fSGleb Smirnoff 	LIST_FOREACH(head, &V_pfil_head_list, head_list) {
553b252313fSGleb Smirnoff 		nheads++;
554b252313fSGleb Smirnoff 		nhooks += head->head_nhooksin + head->head_nhooksout;
555b252313fSGleb Smirnoff 	}
556b252313fSGleb Smirnoff 	PFIL_UNLOCK();
557b252313fSGleb Smirnoff 
558b252313fSGleb Smirnoff 	if (req->pio_nheads < nheads || req->pio_nhooks < nhooks) {
559b252313fSGleb Smirnoff 		req->pio_nheads = nheads;
560b252313fSGleb Smirnoff 		req->pio_nhooks = nhooks;
561b252313fSGleb Smirnoff 		return (0);
562b252313fSGleb Smirnoff 	}
563b252313fSGleb Smirnoff 
564b252313fSGleb Smirnoff 	iohead = malloc(sizeof(*iohead) * nheads, M_TEMP, M_WAITOK);
565b252313fSGleb Smirnoff 	iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
566b252313fSGleb Smirnoff 
567b252313fSGleb Smirnoff 	hd = hk = 0;
568b252313fSGleb Smirnoff 	PFIL_LOCK();
569b252313fSGleb Smirnoff 	LIST_FOREACH(head, &V_pfil_head_list, head_list) {
570b252313fSGleb Smirnoff 		if (hd + 1 > nheads ||
571b252313fSGleb Smirnoff 		    hk + head->head_nhooksin + head->head_nhooksout > nhooks) {
572b252313fSGleb Smirnoff 			/* Configuration changed during malloc(). */
573b252313fSGleb Smirnoff 			free(iohead, M_TEMP);
574b252313fSGleb Smirnoff 			free(iohook, M_TEMP);
575b252313fSGleb Smirnoff 			goto restart;
576b252313fSGleb Smirnoff 		}
577b252313fSGleb Smirnoff 		strlcpy(iohead[hd].pio_name, head->head_name,
578b252313fSGleb Smirnoff 			sizeof(iohead[0].pio_name));
579b252313fSGleb Smirnoff 		iohead[hd].pio_nhooksin = head->head_nhooksin;
580b252313fSGleb Smirnoff 		iohead[hd].pio_nhooksout = head->head_nhooksout;
581b252313fSGleb Smirnoff 		iohead[hd].pio_type = head->head_type;
582b252313fSGleb Smirnoff 		CK_STAILQ_FOREACH(link, &head->head_in, link_chain) {
583b252313fSGleb Smirnoff 			strlcpy(iohook[hk].pio_module,
584b252313fSGleb Smirnoff 			    link->link_hook->hook_modname,
585b252313fSGleb Smirnoff 			    sizeof(iohook[0].pio_module));
586b252313fSGleb Smirnoff 			strlcpy(iohook[hk].pio_ruleset,
587b252313fSGleb Smirnoff 			    link->link_hook->hook_rulname,
588b252313fSGleb Smirnoff 			    sizeof(iohook[0].pio_ruleset));
589b252313fSGleb Smirnoff 			hk++;
590b252313fSGleb Smirnoff 		}
591b252313fSGleb Smirnoff 		CK_STAILQ_FOREACH(link, &head->head_out, link_chain) {
592b252313fSGleb Smirnoff 			strlcpy(iohook[hk].pio_module,
593b252313fSGleb Smirnoff 			    link->link_hook->hook_modname,
594b252313fSGleb Smirnoff 			    sizeof(iohook[0].pio_module));
595b252313fSGleb Smirnoff 			strlcpy(iohook[hk].pio_ruleset,
596b252313fSGleb Smirnoff 			    link->link_hook->hook_rulname,
597b252313fSGleb Smirnoff 			    sizeof(iohook[0].pio_ruleset));
598b252313fSGleb Smirnoff 			hk++;
599b252313fSGleb Smirnoff 		}
600b252313fSGleb Smirnoff 		hd++;
601b252313fSGleb Smirnoff 	}
602b252313fSGleb Smirnoff 	PFIL_UNLOCK();
603b252313fSGleb Smirnoff 
604b252313fSGleb Smirnoff 	error = copyout(iohead, req->pio_heads,
605b252313fSGleb Smirnoff 	    sizeof(*iohead) * min(hd, req->pio_nheads));
606b252313fSGleb Smirnoff 	if (error == 0)
607b252313fSGleb Smirnoff 		error = copyout(iohook, req->pio_hooks,
608b252313fSGleb Smirnoff 		    sizeof(*iohook) * min(req->pio_nhooks, hk));
609b252313fSGleb Smirnoff 
610b252313fSGleb Smirnoff 	req->pio_nheads = hd;
611b252313fSGleb Smirnoff 	req->pio_nhooks = hk;
612b252313fSGleb Smirnoff 
613b252313fSGleb Smirnoff 	free(iohead, M_TEMP);
614b252313fSGleb Smirnoff 	free(iohook, M_TEMP);
615b252313fSGleb Smirnoff 
616b252313fSGleb Smirnoff 	return (error);
617b252313fSGleb Smirnoff }
618b252313fSGleb Smirnoff 
619b252313fSGleb Smirnoff static int
620b252313fSGleb Smirnoff pfilioc_listhooks(struct pfilioc_list *req)
621b252313fSGleb Smirnoff {
622b252313fSGleb Smirnoff 	struct pfil_hook *hook;
623b252313fSGleb Smirnoff 	struct pfilioc_hook *iohook;
624b252313fSGleb Smirnoff 	u_int nhooks, hk;
625b252313fSGleb Smirnoff 	int error;
626b252313fSGleb Smirnoff 
627b252313fSGleb Smirnoff 	PFIL_LOCK();
628b252313fSGleb Smirnoff restart:
629b252313fSGleb Smirnoff 	nhooks = 0;
630b252313fSGleb Smirnoff 	LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
631b252313fSGleb Smirnoff 		nhooks++;
632b252313fSGleb Smirnoff 	PFIL_UNLOCK();
633b252313fSGleb Smirnoff 
634b252313fSGleb Smirnoff 	if (req->pio_nhooks < nhooks) {
635b252313fSGleb Smirnoff 		req->pio_nhooks = nhooks;
636b252313fSGleb Smirnoff 		return (0);
637b252313fSGleb Smirnoff 	}
638b252313fSGleb Smirnoff 
639b252313fSGleb Smirnoff 	iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
640b252313fSGleb Smirnoff 
641b252313fSGleb Smirnoff 	hk = 0;
642b252313fSGleb Smirnoff 	PFIL_LOCK();
643b252313fSGleb Smirnoff 	LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) {
644b252313fSGleb Smirnoff 		if (hk + 1 > nhooks) {
645b252313fSGleb Smirnoff 			/* Configuration changed during malloc(). */
646b252313fSGleb Smirnoff 			free(iohook, M_TEMP);
647b252313fSGleb Smirnoff 			goto restart;
648b252313fSGleb Smirnoff 		}
649b252313fSGleb Smirnoff 		strlcpy(iohook[hk].pio_module, hook->hook_modname,
650b252313fSGleb Smirnoff 		    sizeof(iohook[0].pio_module));
651b252313fSGleb Smirnoff 		strlcpy(iohook[hk].pio_ruleset, hook->hook_rulname,
652b252313fSGleb Smirnoff 		    sizeof(iohook[0].pio_ruleset));
653b252313fSGleb Smirnoff 		iohook[hk].pio_type = hook->hook_type;
654b252313fSGleb Smirnoff 		iohook[hk].pio_flags = hook->hook_flags;
655b252313fSGleb Smirnoff 		hk++;
656b252313fSGleb Smirnoff 	}
657b252313fSGleb Smirnoff 	PFIL_UNLOCK();
658b252313fSGleb Smirnoff 
659b252313fSGleb Smirnoff 	error = copyout(iohook, req->pio_hooks,
660b252313fSGleb Smirnoff 	    sizeof(*iohook) * min(req->pio_nhooks, hk));
661b252313fSGleb Smirnoff 	req->pio_nhooks = hk;
662b252313fSGleb Smirnoff 	free(iohook, M_TEMP);
663b252313fSGleb Smirnoff 
664b252313fSGleb Smirnoff 	return (error);
665b252313fSGleb Smirnoff }
666b252313fSGleb Smirnoff 
667b252313fSGleb Smirnoff static int
668b252313fSGleb Smirnoff pfilioc_link(struct pfilioc_link *req)
669b252313fSGleb Smirnoff {
670b252313fSGleb Smirnoff 	struct pfil_link_args args;
671b252313fSGleb Smirnoff 
672b252313fSGleb Smirnoff 	if (req->pio_flags & ~(PFIL_IN | PFIL_OUT | PFIL_UNLINK | PFIL_APPEND))
673b252313fSGleb Smirnoff 		return (EINVAL);
674b252313fSGleb Smirnoff 
675b252313fSGleb Smirnoff 	args.pa_version = PFIL_VERSION;
676b252313fSGleb Smirnoff 	args.pa_flags = req->pio_flags;
677b252313fSGleb Smirnoff 	args.pa_headname = req->pio_name;
678b252313fSGleb Smirnoff 	args.pa_modname = req->pio_module;
679b252313fSGleb Smirnoff 	args.pa_rulname = req->pio_ruleset;
680b252313fSGleb Smirnoff 
681b252313fSGleb Smirnoff 	return (pfil_link(&args));
682b252313fSGleb Smirnoff }
683