1134ea224SSam Leffler /* $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $ */
2e9c7bebfSDarren Reed
3c398230bSWarner Losh /*-
4fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
5fe267a55SPedro F. Giffuni *
6b252313fSGleb Smirnoff * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
7e9c7bebfSDarren Reed * Copyright (c) 1996 Matthew R. Green
8e9c7bebfSDarren Reed * All rights reserved.
9e9c7bebfSDarren Reed *
10e9c7bebfSDarren Reed * Redistribution and use in source and binary forms, with or without
11e9c7bebfSDarren Reed * modification, are permitted provided that the following conditions
12e9c7bebfSDarren Reed * are met:
13e9c7bebfSDarren Reed * 1. Redistributions of source code must retain the above copyright
14e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer.
15e9c7bebfSDarren Reed * 2. Redistributions in binary form must reproduce the above copyright
16e9c7bebfSDarren Reed * notice, this list of conditions and the following disclaimer in the
17e9c7bebfSDarren Reed * documentation and/or other materials provided with the distribution.
18e9c7bebfSDarren Reed * 3. The name of the author may not be used to endorse or promote products
19e9c7bebfSDarren Reed * derived from this software without specific prior written permission.
20e9c7bebfSDarren Reed *
21e9c7bebfSDarren Reed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22e9c7bebfSDarren Reed * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23e9c7bebfSDarren Reed * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24e9c7bebfSDarren Reed * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25e9c7bebfSDarren Reed * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26e9c7bebfSDarren Reed * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27e9c7bebfSDarren Reed * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28e9c7bebfSDarren Reed * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29e9c7bebfSDarren Reed * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30e9c7bebfSDarren Reed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31e9c7bebfSDarren Reed * SUCH DAMAGE.
32e9c7bebfSDarren Reed */
33e9c7bebfSDarren Reed
34e9c7bebfSDarren Reed #include <sys/param.h>
35b252313fSGleb Smirnoff #include <sys/conf.h>
36134ea224SSam Leffler #include <sys/kernel.h>
37b252313fSGleb Smirnoff #include <sys/epoch.h>
38e9c7bebfSDarren Reed #include <sys/errno.h>
39604afec4SChristian S.J. Peron #include <sys/lock.h>
40e9c7bebfSDarren Reed #include <sys/malloc.h>
41e9c7bebfSDarren Reed #include <sys/socket.h>
42e9c7bebfSDarren Reed #include <sys/socketvar.h>
43e9c7bebfSDarren Reed #include <sys/systm.h>
44134ea224SSam Leffler #include <sys/lock.h>
45134ea224SSam Leffler #include <sys/mutex.h>
46134ea224SSam Leffler #include <sys/proc.h>
47e9c7bebfSDarren Reed #include <sys/queue.h>
483ca1c423SGleb Smirnoff #include <sys/ucred.h>
493ca1c423SGleb Smirnoff #include <sys/jail.h>
50e9c7bebfSDarren Reed
51e9c7bebfSDarren Reed #include <net/if.h>
5276039bc8SGleb Smirnoff #include <net/if_var.h>
53e9c7bebfSDarren Reed #include <net/pfil.h>
54e9c7bebfSDarren Reed
55b252313fSGleb Smirnoff static MALLOC_DEFINE(M_PFIL, "pfil", "pfil(9) packet filter hooks");
56e9c7bebfSDarren Reed
57b252313fSGleb Smirnoff static int pfil_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
58b252313fSGleb Smirnoff static struct cdevsw pfil_cdevsw = {
59b252313fSGleb Smirnoff .d_ioctl = pfil_ioctl,
60b252313fSGleb Smirnoff .d_name = PFILDEV,
61b252313fSGleb Smirnoff .d_version = D_VERSION,
62b252313fSGleb Smirnoff };
63b252313fSGleb Smirnoff static struct cdev *pfil_dev;
64134ea224SSam Leffler
65b252313fSGleb Smirnoff static struct mtx pfil_lock;
66b252313fSGleb Smirnoff MTX_SYSINIT(pfil_mtxinit, &pfil_lock, "pfil(9) lock", MTX_DEF);
67b252313fSGleb Smirnoff #define PFIL_LOCK() mtx_lock(&pfil_lock)
68b252313fSGleb Smirnoff #define PFIL_UNLOCK() mtx_unlock(&pfil_lock)
69b252313fSGleb Smirnoff #define PFIL_LOCK_ASSERT() mtx_assert(&pfil_lock, MA_OWNED)
70b252313fSGleb Smirnoff
71b252313fSGleb Smirnoff struct pfil_hook {
72caf32b26SGleb Smirnoff pfil_mbuf_chk_t hook_mbuf_chk;
73caf32b26SGleb Smirnoff pfil_mem_chk_t hook_mem_chk;
74b252313fSGleb Smirnoff void *hook_ruleset;
75b252313fSGleb Smirnoff int hook_flags;
76b252313fSGleb Smirnoff int hook_links;
77b252313fSGleb Smirnoff enum pfil_types hook_type;
78b252313fSGleb Smirnoff const char *hook_modname;
79b252313fSGleb Smirnoff const char *hook_rulname;
80b252313fSGleb Smirnoff LIST_ENTRY(pfil_hook) hook_list;
81b252313fSGleb Smirnoff };
82b252313fSGleb Smirnoff
83b252313fSGleb Smirnoff struct pfil_link {
84b252313fSGleb Smirnoff CK_STAILQ_ENTRY(pfil_link) link_chain;
85caf32b26SGleb Smirnoff pfil_mbuf_chk_t link_mbuf_chk;
86caf32b26SGleb Smirnoff pfil_mem_chk_t link_mem_chk;
87b252313fSGleb Smirnoff void *link_ruleset;
88b252313fSGleb Smirnoff int link_flags;
89b252313fSGleb Smirnoff struct pfil_hook *link_hook;
90b252313fSGleb Smirnoff struct epoch_context link_epoch_ctx;
91b252313fSGleb Smirnoff };
92b252313fSGleb Smirnoff
93b252313fSGleb Smirnoff typedef CK_STAILQ_HEAD(pfil_chain, pfil_link) pfil_chain_t;
94b252313fSGleb Smirnoff struct pfil_head {
95b252313fSGleb Smirnoff int head_nhooksin;
96b252313fSGleb Smirnoff int head_nhooksout;
97b252313fSGleb Smirnoff pfil_chain_t head_in;
98b252313fSGleb Smirnoff pfil_chain_t head_out;
99b252313fSGleb Smirnoff int head_flags;
100b252313fSGleb Smirnoff enum pfil_types head_type;
101b252313fSGleb Smirnoff LIST_ENTRY(pfil_head) head_list;
102b252313fSGleb Smirnoff const char *head_name;
103b252313fSGleb Smirnoff };
104134ea224SSam Leffler
1050b4b0b0fSJulian Elischer LIST_HEAD(pfilheadhead, pfil_head);
106b252313fSGleb Smirnoff VNET_DEFINE_STATIC(struct pfilheadhead, pfil_head_list) =
107b252313fSGleb Smirnoff LIST_HEAD_INITIALIZER(pfil_head_list);
1080b4b0b0fSJulian Elischer #define V_pfil_head_list VNET(pfil_head_list)
109134ea224SSam Leffler
110b252313fSGleb Smirnoff LIST_HEAD(pfilhookhead, pfil_hook);
111b252313fSGleb Smirnoff VNET_DEFINE_STATIC(struct pfilhookhead, pfil_hook_list) =
112b252313fSGleb Smirnoff LIST_HEAD_INITIALIZER(pfil_hook_list);
113b252313fSGleb Smirnoff #define V_pfil_hook_list VNET(pfil_hook_list)
114af48c203SAndrey V. Elsukov
115b252313fSGleb Smirnoff static struct pfil_link *pfil_link_remove(pfil_chain_t *, pfil_hook_t );
116b252313fSGleb Smirnoff static void pfil_link_free(epoch_context_t);
117af48c203SAndrey V. Elsukov
118caf32b26SGleb Smirnoff /*
119caf32b26SGleb Smirnoff * To couple a filtering point that provides memory pointer with a filter that
120caf32b26SGleb Smirnoff * works on mbufs only.
121caf32b26SGleb Smirnoff */
122b252313fSGleb Smirnoff static __noinline int
pfil_fake_mbuf(pfil_mbuf_chk_t func,void * mem,u_int len,struct ifnet * ifp,int flags,void * ruleset,struct mbuf ** mp)123caf32b26SGleb Smirnoff pfil_fake_mbuf(pfil_mbuf_chk_t func, void *mem, u_int len, struct ifnet *ifp,
124caf32b26SGleb Smirnoff int flags, void *ruleset, struct mbuf **mp)
125b252313fSGleb Smirnoff {
126caf32b26SGleb Smirnoff struct mbuf m;
127b252313fSGleb Smirnoff pfil_return_t rv;
128b252313fSGleb Smirnoff
129b252313fSGleb Smirnoff (void)m_init(&m, M_NOWAIT, MT_DATA, M_NOFREE | M_PKTHDR);
130caf32b26SGleb Smirnoff m_extadd(&m, mem, len, NULL, NULL, NULL, 0, EXT_RXRING);
131caf32b26SGleb Smirnoff m.m_len = m.m_pkthdr.len = len;
132caf32b26SGleb Smirnoff *mp = &m;
133b252313fSGleb Smirnoff
134caf32b26SGleb Smirnoff rv = func(mp, ifp, flags, ruleset, NULL);
135caf32b26SGleb Smirnoff if (rv == PFIL_PASS && *mp != &m) {
136b252313fSGleb Smirnoff /*
137b252313fSGleb Smirnoff * Firewalls that need pfil_fake_mbuf() most likely don't
138c9341022SGleb Smirnoff * know they need return PFIL_REALLOCED.
139b252313fSGleb Smirnoff */
140b252313fSGleb Smirnoff rv = PFIL_REALLOCED;
141b252313fSGleb Smirnoff }
142b252313fSGleb Smirnoff
143b252313fSGleb Smirnoff return (rv);
144b252313fSGleb Smirnoff }
145af48c203SAndrey V. Elsukov
146caf32b26SGleb Smirnoff static __always_inline int
pfil_mem_common(pfil_chain_t * pch,void * mem,u_int len,int flags,struct ifnet * ifp,struct mbuf ** m)147caf32b26SGleb Smirnoff pfil_mem_common(pfil_chain_t *pch, void *mem, u_int len, int flags,
148caf32b26SGleb Smirnoff struct ifnet *ifp, struct mbuf **m)
149134ea224SSam Leffler {
150b252313fSGleb Smirnoff struct pfil_link *link;
151b9fdb4b3SGleb Smirnoff pfil_return_t rv;
152b9fdb4b3SGleb Smirnoff bool realloc = false;
153134ea224SSam Leffler
154e87ad0abSGleb Smirnoff NET_EPOCH_ASSERT();
155caf32b26SGleb Smirnoff KASSERT(flags == PFIL_IN || flags == PFIL_OUT,
156caf32b26SGleb Smirnoff ("%s: unsupported flags %d", __func__, flags));
157b252313fSGleb Smirnoff
158b252313fSGleb Smirnoff rv = PFIL_PASS;
159b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, pch, link_chain) {
160caf32b26SGleb Smirnoff if (__predict_true(link->link_mem_chk != NULL && !realloc))
161caf32b26SGleb Smirnoff rv = link->link_mem_chk(mem, len, flags, ifp,
162caf32b26SGleb Smirnoff link->link_ruleset, m);
163caf32b26SGleb Smirnoff else if (!realloc)
164caf32b26SGleb Smirnoff rv = pfil_fake_mbuf(link->link_mbuf_chk, mem, len, ifp,
165caf32b26SGleb Smirnoff flags, link->link_ruleset, m);
166b252313fSGleb Smirnoff else
167caf32b26SGleb Smirnoff rv = link->link_mbuf_chk(m, ifp, flags,
168caf32b26SGleb Smirnoff link->link_ruleset, NULL);
169caf32b26SGleb Smirnoff
170b9fdb4b3SGleb Smirnoff if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED)
171b252313fSGleb Smirnoff break;
172caf32b26SGleb Smirnoff else if (rv == PFIL_REALLOCED)
173b9fdb4b3SGleb Smirnoff realloc = true;
174887c60fcSAndre Oppermann }
175b9fdb4b3SGleb Smirnoff if (realloc && rv == PFIL_PASS)
176b9fdb4b3SGleb Smirnoff rv = PFIL_REALLOCED;
177b9fdb4b3SGleb Smirnoff return (rv);
1784dab1a18SAlexander V. Chernikov }
1798da01399SAndre Oppermann
180caf32b26SGleb Smirnoff int
pfil_mem_in(struct pfil_head * head,void * mem,u_int len,struct ifnet * ifp,struct mbuf ** m)181caf32b26SGleb Smirnoff pfil_mem_in(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
182caf32b26SGleb Smirnoff struct mbuf **m)
183caf32b26SGleb Smirnoff {
184caf32b26SGleb Smirnoff
185caf32b26SGleb Smirnoff return (pfil_mem_common(&head->head_in, mem, len, PFIL_IN, ifp, m));
186caf32b26SGleb Smirnoff }
187caf32b26SGleb Smirnoff
188caf32b26SGleb Smirnoff int
pfil_mem_out(struct pfil_head * head,void * mem,u_int len,struct ifnet * ifp,struct mbuf ** m)189caf32b26SGleb Smirnoff pfil_mem_out(struct pfil_head *head, void *mem, u_int len, struct ifnet *ifp,
190caf32b26SGleb Smirnoff struct mbuf **m)
191caf32b26SGleb Smirnoff {
192caf32b26SGleb Smirnoff
193caf32b26SGleb Smirnoff return (pfil_mem_common(&head->head_out, mem, len, PFIL_OUT, ifp, m));
194caf32b26SGleb Smirnoff }
195caf32b26SGleb Smirnoff
1960b70e3e7SMateusz Guzik static __always_inline int
pfil_mbuf_common(pfil_chain_t * pch,struct mbuf ** m,struct ifnet * ifp,int flags,struct inpcb * inp)197caf32b26SGleb Smirnoff pfil_mbuf_common(pfil_chain_t *pch, struct mbuf **m, struct ifnet *ifp,
1980b70e3e7SMateusz Guzik int flags, struct inpcb *inp)
1990b70e3e7SMateusz Guzik {
2000b70e3e7SMateusz Guzik struct pfil_link *link;
2010b70e3e7SMateusz Guzik pfil_return_t rv;
2020b70e3e7SMateusz Guzik
2030b70e3e7SMateusz Guzik NET_EPOCH_ASSERT();
204b52b61c0SKristof Provost KASSERT((flags & ~(PFIL_IN|PFIL_OUT|PFIL_FWD)) == 0,
205b52b61c0SKristof Provost ("%s: unsupported flags %#x", __func__, flags));
206b52b61c0SKristof Provost KASSERT((flags & ~PFIL_FWD) == PFIL_IN ||
207b52b61c0SKristof Provost (flags & ~PFIL_FWD) == PFIL_OUT,
208b52b61c0SKristof Provost ("%s: conflicting directions %#x", __func__, flags));
2090b70e3e7SMateusz Guzik
2100b70e3e7SMateusz Guzik rv = PFIL_PASS;
2110b70e3e7SMateusz Guzik CK_STAILQ_FOREACH(link, pch, link_chain) {
212caf32b26SGleb Smirnoff rv = link->link_mbuf_chk(m, ifp, flags, link->link_ruleset,
213caf32b26SGleb Smirnoff inp);
214*ffeab76bSKristof Provost if (rv == PFIL_DROPPED || rv == PFIL_CONSUMED) {
215*ffeab76bSKristof Provost MPASS(*m == NULL);
2160b70e3e7SMateusz Guzik break;
217*ffeab76bSKristof Provost } else {
218*ffeab76bSKristof Provost MPASS(*m != NULL);
2190b70e3e7SMateusz Guzik }
220*ffeab76bSKristof Provost }
221*ffeab76bSKristof Provost
2220b70e3e7SMateusz Guzik return (rv);
2230b70e3e7SMateusz Guzik }
2240b70e3e7SMateusz Guzik
2250b70e3e7SMateusz Guzik int
pfil_mbuf_in(struct pfil_head * head,struct mbuf ** m,struct ifnet * ifp,struct inpcb * inp)226caf32b26SGleb Smirnoff pfil_mbuf_in(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
2270b70e3e7SMateusz Guzik struct inpcb *inp)
2280b70e3e7SMateusz Guzik {
2290b70e3e7SMateusz Guzik
230caf32b26SGleb Smirnoff return (pfil_mbuf_common(&head->head_in, m, ifp, PFIL_IN, inp));
2310b70e3e7SMateusz Guzik }
2320b70e3e7SMateusz Guzik
2330b70e3e7SMateusz Guzik int
pfil_mbuf_out(struct pfil_head * head,struct mbuf ** m,struct ifnet * ifp,struct inpcb * inp)234caf32b26SGleb Smirnoff pfil_mbuf_out(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
2350b70e3e7SMateusz Guzik struct inpcb *inp)
2360b70e3e7SMateusz Guzik {
2370b70e3e7SMateusz Guzik
238caf32b26SGleb Smirnoff return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT, inp));
2390b70e3e7SMateusz Guzik }
2400b70e3e7SMateusz Guzik
241b52b61c0SKristof Provost int
pfil_mbuf_fwd(struct pfil_head * head,struct mbuf ** m,struct ifnet * ifp,struct inpcb * inp)242b52b61c0SKristof Provost pfil_mbuf_fwd(struct pfil_head *head, struct mbuf **m, struct ifnet *ifp,
243b52b61c0SKristof Provost struct inpcb *inp)
244b52b61c0SKristof Provost {
245b52b61c0SKristof Provost
246b52b61c0SKristof Provost return (pfil_mbuf_common(&head->head_out, m, ifp, PFIL_OUT | PFIL_FWD, inp));
247b52b61c0SKristof Provost }
248b52b61c0SKristof Provost
2494dab1a18SAlexander V. Chernikov /*
250cee81198SRobert Watson * pfil_head_register() registers a pfil_head with the packet filter hook
251cee81198SRobert Watson * mechanism.
252134ea224SSam Leffler */
253b252313fSGleb Smirnoff pfil_head_t
pfil_head_register(struct pfil_head_args * pa)254b252313fSGleb Smirnoff pfil_head_register(struct pfil_head_args *pa)
255134ea224SSam Leffler {
256b252313fSGleb Smirnoff struct pfil_head *head, *list;
257134ea224SSam Leffler
258b252313fSGleb Smirnoff MPASS(pa->pa_version == PFIL_VERSION);
259b252313fSGleb Smirnoff
260b252313fSGleb Smirnoff head = malloc(sizeof(struct pfil_head), M_PFIL, M_WAITOK);
261b252313fSGleb Smirnoff
262b252313fSGleb Smirnoff head->head_nhooksin = head->head_nhooksout = 0;
263b252313fSGleb Smirnoff head->head_flags = pa->pa_flags;
264b252313fSGleb Smirnoff head->head_type = pa->pa_type;
265b252313fSGleb Smirnoff head->head_name = pa->pa_headname;
266b252313fSGleb Smirnoff CK_STAILQ_INIT(&head->head_in);
267b252313fSGleb Smirnoff CK_STAILQ_INIT(&head->head_out);
268b252313fSGleb Smirnoff
269b252313fSGleb Smirnoff PFIL_LOCK();
270b252313fSGleb Smirnoff LIST_FOREACH(list, &V_pfil_head_list, head_list)
271b252313fSGleb Smirnoff if (strcmp(pa->pa_headname, list->head_name) == 0) {
272b252313fSGleb Smirnoff printf("pfil: duplicate head \"%s\"\n",
273b252313fSGleb Smirnoff pa->pa_headname);
274134ea224SSam Leffler }
275b252313fSGleb Smirnoff LIST_INSERT_HEAD(&V_pfil_head_list, head, head_list);
276b252313fSGleb Smirnoff PFIL_UNLOCK();
277b252313fSGleb Smirnoff
278b252313fSGleb Smirnoff return (head);
279134ea224SSam Leffler }
280134ea224SSam Leffler
281134ea224SSam Leffler /*
282d2c205d5SRobert Watson * pfil_head_unregister() removes a pfil_head from the packet filter hook
283d2c205d5SRobert Watson * mechanism. The producer of the hook promises that all outstanding
284d2c205d5SRobert Watson * invocations of the hook have completed before it unregisters the hook.
285134ea224SSam Leffler */
286b252313fSGleb Smirnoff void
pfil_head_unregister(pfil_head_t ph)287b252313fSGleb Smirnoff pfil_head_unregister(pfil_head_t ph)
288134ea224SSam Leffler {
289b252313fSGleb Smirnoff struct pfil_link *link, *next;
290134ea224SSam Leffler
291b252313fSGleb Smirnoff PFIL_LOCK();
292b252313fSGleb Smirnoff LIST_REMOVE(ph, head_list);
293b252313fSGleb Smirnoff
294b252313fSGleb Smirnoff CK_STAILQ_FOREACH_SAFE(link, &ph->head_in, link_chain, next) {
295b252313fSGleb Smirnoff link->link_hook->hook_links--;
296b252313fSGleb Smirnoff free(link, M_PFIL);
297b252313fSGleb Smirnoff }
298b252313fSGleb Smirnoff CK_STAILQ_FOREACH_SAFE(link, &ph->head_out, link_chain, next) {
299b252313fSGleb Smirnoff link->link_hook->hook_links--;
300b252313fSGleb Smirnoff free(link, M_PFIL);
301b252313fSGleb Smirnoff }
302b252313fSGleb Smirnoff PFIL_UNLOCK();
303c1c55da4SGleb Smirnoff free(ph, M_PFIL);
304134ea224SSam Leffler }
305134ea224SSam Leffler
306b252313fSGleb Smirnoff pfil_hook_t
pfil_add_hook(struct pfil_hook_args * pa)307b252313fSGleb Smirnoff pfil_add_hook(struct pfil_hook_args *pa)
308134ea224SSam Leffler {
309b252313fSGleb Smirnoff struct pfil_hook *hook, *list;
310134ea224SSam Leffler
311b252313fSGleb Smirnoff MPASS(pa->pa_version == PFIL_VERSION);
312b252313fSGleb Smirnoff
313b252313fSGleb Smirnoff hook = malloc(sizeof(struct pfil_hook), M_PFIL, M_WAITOK | M_ZERO);
314caf32b26SGleb Smirnoff hook->hook_mbuf_chk = pa->pa_mbuf_chk;
315caf32b26SGleb Smirnoff hook->hook_mem_chk = pa->pa_mem_chk;
316b252313fSGleb Smirnoff hook->hook_ruleset = pa->pa_ruleset;
317b252313fSGleb Smirnoff hook->hook_flags = pa->pa_flags;
318b252313fSGleb Smirnoff hook->hook_type = pa->pa_type;
319b252313fSGleb Smirnoff hook->hook_modname = pa->pa_modname;
320b252313fSGleb Smirnoff hook->hook_rulname = pa->pa_rulname;
321b252313fSGleb Smirnoff
322b252313fSGleb Smirnoff PFIL_LOCK();
323b252313fSGleb Smirnoff LIST_FOREACH(list, &V_pfil_hook_list, hook_list)
324b252313fSGleb Smirnoff if (strcmp(pa->pa_modname, list->hook_modname) == 0 &&
325b252313fSGleb Smirnoff strcmp(pa->pa_rulname, list->hook_rulname) == 0) {
326b252313fSGleb Smirnoff printf("pfil: duplicate hook \"%s:%s\"\n",
327b252313fSGleb Smirnoff pa->pa_modname, pa->pa_rulname);
328e9c7bebfSDarren Reed }
329b252313fSGleb Smirnoff LIST_INSERT_HEAD(&V_pfil_hook_list, hook, hook_list);
330b252313fSGleb Smirnoff PFIL_UNLOCK();
331e9c7bebfSDarren Reed
332b252313fSGleb Smirnoff return (hook);
333effaab88SKristof Provost }
334effaab88SKristof Provost
335effaab88SKristof Provost static int
pfil_unlink(struct pfil_link_args * pa,pfil_head_t head,pfil_hook_t hook)336b252313fSGleb Smirnoff pfil_unlink(struct pfil_link_args *pa, pfil_head_t head, pfil_hook_t hook)
337effaab88SKristof Provost {
338b252313fSGleb Smirnoff struct pfil_link *in, *out;
339e9c7bebfSDarren Reed
340b252313fSGleb Smirnoff PFIL_LOCK_ASSERT();
341e9c7bebfSDarren Reed
342b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_IN) {
343b252313fSGleb Smirnoff in = pfil_link_remove(&head->head_in, hook);
344b252313fSGleb Smirnoff if (in != NULL) {
345b252313fSGleb Smirnoff head->head_nhooksin--;
346b252313fSGleb Smirnoff hook->hook_links--;
347effaab88SKristof Provost }
348b252313fSGleb Smirnoff } else
349b252313fSGleb Smirnoff in = NULL;
350b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_OUT) {
351b252313fSGleb Smirnoff out = pfil_link_remove(&head->head_out, hook);
352b252313fSGleb Smirnoff if (out != NULL) {
353b252313fSGleb Smirnoff head->head_nhooksout--;
354b252313fSGleb Smirnoff hook->hook_links--;
355604afec4SChristian S.J. Peron }
356b252313fSGleb Smirnoff } else
357b252313fSGleb Smirnoff out = NULL;
358b252313fSGleb Smirnoff PFIL_UNLOCK();
359e9c7bebfSDarren Reed
360b252313fSGleb Smirnoff if (in != NULL)
361e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
362b252313fSGleb Smirnoff if (out != NULL)
363e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
364134ea224SSam Leffler
365b252313fSGleb Smirnoff if (in == NULL && out == NULL)
366b252313fSGleb Smirnoff return (ENOENT);
367134ea224SSam Leffler else
368cee81198SRobert Watson return (0);
369134ea224SSam Leffler }
370134ea224SSam Leffler
371b252313fSGleb Smirnoff int
pfil_link(struct pfil_link_args * pa)372b252313fSGleb Smirnoff pfil_link(struct pfil_link_args *pa)
373b252313fSGleb Smirnoff {
374b252313fSGleb Smirnoff struct pfil_link *in, *out, *link;
375b252313fSGleb Smirnoff struct pfil_head *head;
376b252313fSGleb Smirnoff struct pfil_hook *hook;
377b252313fSGleb Smirnoff int error;
378b252313fSGleb Smirnoff
379b252313fSGleb Smirnoff MPASS(pa->pa_version == PFIL_VERSION);
380b252313fSGleb Smirnoff
381b252313fSGleb Smirnoff if ((pa->pa_flags & (PFIL_IN | PFIL_UNLINK)) == PFIL_IN)
382b252313fSGleb Smirnoff in = malloc(sizeof(*in), M_PFIL, M_WAITOK | M_ZERO);
383b252313fSGleb Smirnoff else
384b252313fSGleb Smirnoff in = NULL;
385b252313fSGleb Smirnoff if ((pa->pa_flags & (PFIL_OUT | PFIL_UNLINK)) == PFIL_OUT)
386b252313fSGleb Smirnoff out = malloc(sizeof(*out), M_PFIL, M_WAITOK | M_ZERO);
387b252313fSGleb Smirnoff else
388b252313fSGleb Smirnoff out = NULL;
389b252313fSGleb Smirnoff
390b252313fSGleb Smirnoff PFIL_LOCK();
391b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_HEADPTR)
392b252313fSGleb Smirnoff head = pa->pa_head;
393b252313fSGleb Smirnoff else
394b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list)
395b252313fSGleb Smirnoff if (strcmp(pa->pa_headname, head->head_name) == 0)
396b252313fSGleb Smirnoff break;
397b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_HOOKPTR)
398b252313fSGleb Smirnoff hook = pa->pa_hook;
399b252313fSGleb Smirnoff else
400b252313fSGleb Smirnoff LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
401b252313fSGleb Smirnoff if (strcmp(pa->pa_modname, hook->hook_modname) == 0 &&
402b252313fSGleb Smirnoff strcmp(pa->pa_rulname, hook->hook_rulname) == 0)
403b252313fSGleb Smirnoff break;
404b252313fSGleb Smirnoff if (head == NULL || hook == NULL) {
405b252313fSGleb Smirnoff error = ENOENT;
406b252313fSGleb Smirnoff goto fail;
407b252313fSGleb Smirnoff }
408b252313fSGleb Smirnoff
409b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_UNLINK)
410b252313fSGleb Smirnoff return (pfil_unlink(pa, head, hook));
411b252313fSGleb Smirnoff
412b252313fSGleb Smirnoff if (head->head_type != hook->hook_type ||
413b252313fSGleb Smirnoff ((hook->hook_flags & pa->pa_flags) & ~head->head_flags)) {
414b252313fSGleb Smirnoff error = EINVAL;
415b252313fSGleb Smirnoff goto fail;
416b252313fSGleb Smirnoff }
417b252313fSGleb Smirnoff
418b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_IN)
419b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_in, link_chain)
420b252313fSGleb Smirnoff if (link->link_hook == hook) {
421b252313fSGleb Smirnoff error = EEXIST;
422b252313fSGleb Smirnoff goto fail;
423b252313fSGleb Smirnoff }
424b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_OUT)
425b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_out, link_chain)
426b252313fSGleb Smirnoff if (link->link_hook == hook) {
427b252313fSGleb Smirnoff error = EEXIST;
428b252313fSGleb Smirnoff goto fail;
429b252313fSGleb Smirnoff }
430b252313fSGleb Smirnoff
431b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_IN) {
432b252313fSGleb Smirnoff in->link_hook = hook;
433caf32b26SGleb Smirnoff in->link_mbuf_chk = hook->hook_mbuf_chk;
434caf32b26SGleb Smirnoff in->link_mem_chk = hook->hook_mem_chk;
435b252313fSGleb Smirnoff in->link_flags = hook->hook_flags;
436b252313fSGleb Smirnoff in->link_ruleset = hook->hook_ruleset;
437b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_APPEND)
438b252313fSGleb Smirnoff CK_STAILQ_INSERT_TAIL(&head->head_in, in, link_chain);
439b252313fSGleb Smirnoff else
440b252313fSGleb Smirnoff CK_STAILQ_INSERT_HEAD(&head->head_in, in, link_chain);
441b252313fSGleb Smirnoff hook->hook_links++;
442b252313fSGleb Smirnoff head->head_nhooksin++;
443b252313fSGleb Smirnoff }
444b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_OUT) {
445b252313fSGleb Smirnoff out->link_hook = hook;
446caf32b26SGleb Smirnoff out->link_mbuf_chk = hook->hook_mbuf_chk;
447caf32b26SGleb Smirnoff out->link_mem_chk = hook->hook_mem_chk;
448b252313fSGleb Smirnoff out->link_flags = hook->hook_flags;
449b252313fSGleb Smirnoff out->link_ruleset = hook->hook_ruleset;
450b252313fSGleb Smirnoff if (pa->pa_flags & PFIL_APPEND)
451b252313fSGleb Smirnoff CK_STAILQ_INSERT_HEAD(&head->head_out, out, link_chain);
452b252313fSGleb Smirnoff else
453b252313fSGleb Smirnoff CK_STAILQ_INSERT_TAIL(&head->head_out, out, link_chain);
454b252313fSGleb Smirnoff hook->hook_links++;
455b252313fSGleb Smirnoff head->head_nhooksout++;
456b252313fSGleb Smirnoff }
457b252313fSGleb Smirnoff PFIL_UNLOCK();
458b252313fSGleb Smirnoff
459b252313fSGleb Smirnoff return (0);
460b252313fSGleb Smirnoff
461b252313fSGleb Smirnoff fail:
462b252313fSGleb Smirnoff PFIL_UNLOCK();
463b252313fSGleb Smirnoff free(in, M_PFIL);
464b252313fSGleb Smirnoff free(out, M_PFIL);
465b252313fSGleb Smirnoff return (error);
466b252313fSGleb Smirnoff }
467b252313fSGleb Smirnoff
468b252313fSGleb Smirnoff static void
pfil_link_free(epoch_context_t ctx)469b252313fSGleb Smirnoff pfil_link_free(epoch_context_t ctx)
470b252313fSGleb Smirnoff {
471b252313fSGleb Smirnoff struct pfil_link *link;
472b252313fSGleb Smirnoff
473b252313fSGleb Smirnoff link = __containerof(ctx, struct pfil_link, link_epoch_ctx);
474b252313fSGleb Smirnoff free(link, M_PFIL);
475b252313fSGleb Smirnoff }
476b252313fSGleb Smirnoff
477b252313fSGleb Smirnoff /*
478b252313fSGleb Smirnoff * pfil_remove_hook removes a filter from all filtering points.
479b252313fSGleb Smirnoff */
480b252313fSGleb Smirnoff void
pfil_remove_hook(pfil_hook_t hook)481b252313fSGleb Smirnoff pfil_remove_hook(pfil_hook_t hook)
482b252313fSGleb Smirnoff {
483b252313fSGleb Smirnoff struct pfil_head *head;
484b252313fSGleb Smirnoff struct pfil_link *in, *out;
485b252313fSGleb Smirnoff
486b252313fSGleb Smirnoff PFIL_LOCK();
487b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list) {
488b252313fSGleb Smirnoff retry:
489b252313fSGleb Smirnoff in = pfil_link_remove(&head->head_in, hook);
490b252313fSGleb Smirnoff if (in != NULL) {
491b252313fSGleb Smirnoff head->head_nhooksin--;
492b252313fSGleb Smirnoff hook->hook_links--;
493e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &in->link_epoch_ctx);
494b252313fSGleb Smirnoff }
495b252313fSGleb Smirnoff out = pfil_link_remove(&head->head_out, hook);
496b252313fSGleb Smirnoff if (out != NULL) {
497b252313fSGleb Smirnoff head->head_nhooksout--;
498b252313fSGleb Smirnoff hook->hook_links--;
499e87ad0abSGleb Smirnoff NET_EPOCH_CALL(pfil_link_free, &out->link_epoch_ctx);
500b252313fSGleb Smirnoff }
501b252313fSGleb Smirnoff if (in != NULL || out != NULL)
502b252313fSGleb Smirnoff /* What if some stupid admin put same filter twice? */
503b252313fSGleb Smirnoff goto retry;
504b252313fSGleb Smirnoff }
505b252313fSGleb Smirnoff LIST_REMOVE(hook, hook_list);
506b252313fSGleb Smirnoff PFIL_UNLOCK();
507b252313fSGleb Smirnoff MPASS(hook->hook_links == 0);
508b252313fSGleb Smirnoff free(hook, M_PFIL);
509b252313fSGleb Smirnoff }
510b252313fSGleb Smirnoff
511e9c7bebfSDarren Reed /*
5128da01399SAndre Oppermann * Internal: Remove a pfil hook from a hook chain.
513e9c7bebfSDarren Reed */
514b252313fSGleb Smirnoff static struct pfil_link *
pfil_link_remove(pfil_chain_t * chain,pfil_hook_t hook)515b252313fSGleb Smirnoff pfil_link_remove(pfil_chain_t *chain, pfil_hook_t hook)
516e9c7bebfSDarren Reed {
517b252313fSGleb Smirnoff struct pfil_link *link;
518e9c7bebfSDarren Reed
519b252313fSGleb Smirnoff PFIL_LOCK_ASSERT();
520b252313fSGleb Smirnoff
521b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, chain, link_chain)
522b252313fSGleb Smirnoff if (link->link_hook == hook) {
523b252313fSGleb Smirnoff CK_STAILQ_REMOVE(chain, link, pfil_link, link_chain);
524b252313fSGleb Smirnoff return (link);
525e9c7bebfSDarren Reed }
5260b4b0b0fSJulian Elischer
527b252313fSGleb Smirnoff return (NULL);
528b252313fSGleb Smirnoff }
529b252313fSGleb Smirnoff
530b9dbac48SBjoern A. Zeeb static void
pfil_init(const void * unused __unused)531b252313fSGleb Smirnoff pfil_init(const void *unused __unused)
5320b4b0b0fSJulian Elischer {
533b252313fSGleb Smirnoff struct make_dev_args args;
534c681cce9SMateusz Guzik int error __diagused;
535cee81198SRobert Watson
536b252313fSGleb Smirnoff make_dev_args_init(&args);
537b252313fSGleb Smirnoff args.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
538b252313fSGleb Smirnoff args.mda_devsw = &pfil_cdevsw;
539b252313fSGleb Smirnoff args.mda_uid = UID_ROOT;
540b252313fSGleb Smirnoff args.mda_gid = GID_WHEEL;
541b252313fSGleb Smirnoff args.mda_mode = 0600;
542b252313fSGleb Smirnoff error = make_dev_s(&args, &pfil_dev, PFILDEV);
543b252313fSGleb Smirnoff KASSERT(error == 0, ("%s: failed to create dev: %d", __func__, error));
5440b4b0b0fSJulian Elischer }
545cee81198SRobert Watson /*
54689856f7eSBjoern A. Zeeb * Make sure the pfil bits are first before any possible subsystem which
54789856f7eSBjoern A. Zeeb * might piggyback on the SI_SUB_PROTO_PFIL.
5480b4b0b0fSJulian Elischer */
549b252313fSGleb Smirnoff SYSINIT(pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST, pfil_init, NULL);
5500b4b0b0fSJulian Elischer
5510b4b0b0fSJulian Elischer /*
552b252313fSGleb Smirnoff * User control interface.
5530b4b0b0fSJulian Elischer */
554b252313fSGleb Smirnoff static int pfilioc_listheads(struct pfilioc_list *);
555b252313fSGleb Smirnoff static int pfilioc_listhooks(struct pfilioc_list *);
556b252313fSGleb Smirnoff static int pfilioc_link(struct pfilioc_link *);
557b252313fSGleb Smirnoff
558b252313fSGleb Smirnoff static int
pfil_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flags,struct thread * td)559b252313fSGleb Smirnoff pfil_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags,
560b252313fSGleb Smirnoff struct thread *td)
561b252313fSGleb Smirnoff {
562b252313fSGleb Smirnoff int error;
563b252313fSGleb Smirnoff
5643ca1c423SGleb Smirnoff CURVNET_SET(TD_TO_VNET(td));
565b252313fSGleb Smirnoff error = 0;
566b252313fSGleb Smirnoff switch (cmd) {
567b252313fSGleb Smirnoff case PFILIOC_LISTHEADS:
568b252313fSGleb Smirnoff error = pfilioc_listheads((struct pfilioc_list *)addr);
569b252313fSGleb Smirnoff break;
570b252313fSGleb Smirnoff case PFILIOC_LISTHOOKS:
571b252313fSGleb Smirnoff error = pfilioc_listhooks((struct pfilioc_list *)addr);
572b252313fSGleb Smirnoff break;
573b252313fSGleb Smirnoff case PFILIOC_LINK:
574b252313fSGleb Smirnoff error = pfilioc_link((struct pfilioc_link *)addr);
575b252313fSGleb Smirnoff break;
576b252313fSGleb Smirnoff default:
5773ca1c423SGleb Smirnoff error = EINVAL;
5783ca1c423SGleb Smirnoff break;
579b252313fSGleb Smirnoff }
5803ca1c423SGleb Smirnoff CURVNET_RESTORE();
581b252313fSGleb Smirnoff return (error);
582b252313fSGleb Smirnoff }
583b252313fSGleb Smirnoff
584b252313fSGleb Smirnoff static int
pfilioc_listheads(struct pfilioc_list * req)585b252313fSGleb Smirnoff pfilioc_listheads(struct pfilioc_list *req)
586b252313fSGleb Smirnoff {
587b252313fSGleb Smirnoff struct pfil_head *head;
588b252313fSGleb Smirnoff struct pfil_link *link;
589b252313fSGleb Smirnoff struct pfilioc_head *iohead;
590b252313fSGleb Smirnoff struct pfilioc_hook *iohook;
591b252313fSGleb Smirnoff u_int nheads, nhooks, hd, hk;
592b252313fSGleb Smirnoff int error;
593b252313fSGleb Smirnoff
594b252313fSGleb Smirnoff PFIL_LOCK();
595b252313fSGleb Smirnoff restart:
596b252313fSGleb Smirnoff nheads = nhooks = 0;
597b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list) {
598b252313fSGleb Smirnoff nheads++;
599b252313fSGleb Smirnoff nhooks += head->head_nhooksin + head->head_nhooksout;
600b252313fSGleb Smirnoff }
601b252313fSGleb Smirnoff PFIL_UNLOCK();
602b252313fSGleb Smirnoff
603b252313fSGleb Smirnoff if (req->pio_nheads < nheads || req->pio_nhooks < nhooks) {
604b252313fSGleb Smirnoff req->pio_nheads = nheads;
605b252313fSGleb Smirnoff req->pio_nhooks = nhooks;
606b252313fSGleb Smirnoff return (0);
607b252313fSGleb Smirnoff }
608b252313fSGleb Smirnoff
609b252313fSGleb Smirnoff iohead = malloc(sizeof(*iohead) * nheads, M_TEMP, M_WAITOK);
610b252313fSGleb Smirnoff iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
611b252313fSGleb Smirnoff
612b252313fSGleb Smirnoff hd = hk = 0;
613b252313fSGleb Smirnoff PFIL_LOCK();
614b252313fSGleb Smirnoff LIST_FOREACH(head, &V_pfil_head_list, head_list) {
615b252313fSGleb Smirnoff if (hd + 1 > nheads ||
616b252313fSGleb Smirnoff hk + head->head_nhooksin + head->head_nhooksout > nhooks) {
617b252313fSGleb Smirnoff /* Configuration changed during malloc(). */
618b252313fSGleb Smirnoff free(iohead, M_TEMP);
619b252313fSGleb Smirnoff free(iohook, M_TEMP);
620b252313fSGleb Smirnoff goto restart;
621b252313fSGleb Smirnoff }
622b252313fSGleb Smirnoff strlcpy(iohead[hd].pio_name, head->head_name,
623b252313fSGleb Smirnoff sizeof(iohead[0].pio_name));
624b252313fSGleb Smirnoff iohead[hd].pio_nhooksin = head->head_nhooksin;
625b252313fSGleb Smirnoff iohead[hd].pio_nhooksout = head->head_nhooksout;
626b252313fSGleb Smirnoff iohead[hd].pio_type = head->head_type;
627b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_in, link_chain) {
628b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_module,
629b252313fSGleb Smirnoff link->link_hook->hook_modname,
630b252313fSGleb Smirnoff sizeof(iohook[0].pio_module));
631b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_ruleset,
632b252313fSGleb Smirnoff link->link_hook->hook_rulname,
633b252313fSGleb Smirnoff sizeof(iohook[0].pio_ruleset));
634b252313fSGleb Smirnoff hk++;
635b252313fSGleb Smirnoff }
636b252313fSGleb Smirnoff CK_STAILQ_FOREACH(link, &head->head_out, link_chain) {
637b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_module,
638b252313fSGleb Smirnoff link->link_hook->hook_modname,
639b252313fSGleb Smirnoff sizeof(iohook[0].pio_module));
640b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_ruleset,
641b252313fSGleb Smirnoff link->link_hook->hook_rulname,
642b252313fSGleb Smirnoff sizeof(iohook[0].pio_ruleset));
643b252313fSGleb Smirnoff hk++;
644b252313fSGleb Smirnoff }
645b252313fSGleb Smirnoff hd++;
646b252313fSGleb Smirnoff }
647b252313fSGleb Smirnoff PFIL_UNLOCK();
648b252313fSGleb Smirnoff
649b252313fSGleb Smirnoff error = copyout(iohead, req->pio_heads,
650b252313fSGleb Smirnoff sizeof(*iohead) * min(hd, req->pio_nheads));
651b252313fSGleb Smirnoff if (error == 0)
652b252313fSGleb Smirnoff error = copyout(iohook, req->pio_hooks,
653b252313fSGleb Smirnoff sizeof(*iohook) * min(req->pio_nhooks, hk));
654b252313fSGleb Smirnoff
655b252313fSGleb Smirnoff req->pio_nheads = hd;
656b252313fSGleb Smirnoff req->pio_nhooks = hk;
657b252313fSGleb Smirnoff
658b252313fSGleb Smirnoff free(iohead, M_TEMP);
659b252313fSGleb Smirnoff free(iohook, M_TEMP);
660b252313fSGleb Smirnoff
661b252313fSGleb Smirnoff return (error);
662b252313fSGleb Smirnoff }
663b252313fSGleb Smirnoff
664b252313fSGleb Smirnoff static int
pfilioc_listhooks(struct pfilioc_list * req)665b252313fSGleb Smirnoff pfilioc_listhooks(struct pfilioc_list *req)
666b252313fSGleb Smirnoff {
667b252313fSGleb Smirnoff struct pfil_hook *hook;
668b252313fSGleb Smirnoff struct pfilioc_hook *iohook;
669b252313fSGleb Smirnoff u_int nhooks, hk;
670b252313fSGleb Smirnoff int error;
671b252313fSGleb Smirnoff
672b252313fSGleb Smirnoff PFIL_LOCK();
673b252313fSGleb Smirnoff restart:
674b252313fSGleb Smirnoff nhooks = 0;
675b252313fSGleb Smirnoff LIST_FOREACH(hook, &V_pfil_hook_list, hook_list)
676b252313fSGleb Smirnoff nhooks++;
677b252313fSGleb Smirnoff PFIL_UNLOCK();
678b252313fSGleb Smirnoff
679b252313fSGleb Smirnoff if (req->pio_nhooks < nhooks) {
680b252313fSGleb Smirnoff req->pio_nhooks = nhooks;
681b252313fSGleb Smirnoff return (0);
682b252313fSGleb Smirnoff }
683b252313fSGleb Smirnoff
684b252313fSGleb Smirnoff iohook = malloc(sizeof(*iohook) * nhooks, M_TEMP, M_WAITOK);
685b252313fSGleb Smirnoff
686b252313fSGleb Smirnoff hk = 0;
687b252313fSGleb Smirnoff PFIL_LOCK();
688b252313fSGleb Smirnoff LIST_FOREACH(hook, &V_pfil_hook_list, hook_list) {
689b252313fSGleb Smirnoff if (hk + 1 > nhooks) {
690b252313fSGleb Smirnoff /* Configuration changed during malloc(). */
691b252313fSGleb Smirnoff free(iohook, M_TEMP);
692b252313fSGleb Smirnoff goto restart;
693b252313fSGleb Smirnoff }
694b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_module, hook->hook_modname,
695b252313fSGleb Smirnoff sizeof(iohook[0].pio_module));
696b252313fSGleb Smirnoff strlcpy(iohook[hk].pio_ruleset, hook->hook_rulname,
697b252313fSGleb Smirnoff sizeof(iohook[0].pio_ruleset));
698b252313fSGleb Smirnoff iohook[hk].pio_type = hook->hook_type;
699b252313fSGleb Smirnoff iohook[hk].pio_flags = hook->hook_flags;
700b252313fSGleb Smirnoff hk++;
701b252313fSGleb Smirnoff }
702b252313fSGleb Smirnoff PFIL_UNLOCK();
703b252313fSGleb Smirnoff
704b252313fSGleb Smirnoff error = copyout(iohook, req->pio_hooks,
705b252313fSGleb Smirnoff sizeof(*iohook) * min(req->pio_nhooks, hk));
706b252313fSGleb Smirnoff req->pio_nhooks = hk;
707b252313fSGleb Smirnoff free(iohook, M_TEMP);
708b252313fSGleb Smirnoff
709b252313fSGleb Smirnoff return (error);
710b252313fSGleb Smirnoff }
711b252313fSGleb Smirnoff
712b252313fSGleb Smirnoff static int
pfilioc_link(struct pfilioc_link * req)713b252313fSGleb Smirnoff pfilioc_link(struct pfilioc_link *req)
714b252313fSGleb Smirnoff {
715b252313fSGleb Smirnoff struct pfil_link_args args;
716b252313fSGleb Smirnoff
717b252313fSGleb Smirnoff if (req->pio_flags & ~(PFIL_IN | PFIL_OUT | PFIL_UNLINK | PFIL_APPEND))
718b252313fSGleb Smirnoff return (EINVAL);
719b252313fSGleb Smirnoff
720b252313fSGleb Smirnoff args.pa_version = PFIL_VERSION;
721b252313fSGleb Smirnoff args.pa_flags = req->pio_flags;
722b252313fSGleb Smirnoff args.pa_headname = req->pio_name;
723b252313fSGleb Smirnoff args.pa_modname = req->pio_module;
724b252313fSGleb Smirnoff args.pa_rulname = req->pio_ruleset;
725b252313fSGleb Smirnoff
726b252313fSGleb Smirnoff return (pfil_link(&args));
727b252313fSGleb Smirnoff }
728