]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/pfil.c
libfdt: Update to 1.4.6, switch to using libfdt for overlay support
[FreeBSD/FreeBSD.git] / sys / net / pfil.c
1 /*      $FreeBSD$ */
2 /*      $NetBSD: pfil.c,v 1.20 2001/11/12 23:49:46 lukem Exp $  */
3
4 /*-
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Copyright (c) 1996 Matthew R. Green
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/errno.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/rmlock.h>
40 #include <sys/socket.h>
41 #include <sys/socketvar.h>
42 #include <sys/systm.h>
43 #include <sys/condvar.h>
44 #include <sys/lock.h>
45 #include <sys/mutex.h>
46 #include <sys/proc.h>
47 #include <sys/queue.h>
48
49 #include <net/if.h>
50 #include <net/if_var.h>
51 #include <net/pfil.h>
52
53 static struct mtx pfil_global_lock;
54
55 MTX_SYSINIT(pfil_heads_lock, &pfil_global_lock, "pfil_head_list lock",
56   MTX_DEF);
57
58 static struct packet_filter_hook *pfil_chain_get(int, struct pfil_head *);
59 static int pfil_chain_add(pfil_chain_t *, struct packet_filter_hook *, int);
60 static int pfil_chain_remove(pfil_chain_t *, pfil_func_t, void *);
61
62 LIST_HEAD(pfilheadhead, pfil_head);
63 VNET_DEFINE(struct pfilheadhead, pfil_head_list);
64 #define V_pfil_head_list        VNET(pfil_head_list)
65 VNET_DEFINE(struct rmlock, pfil_lock);
66
67 #define PFIL_LOCK_INIT_REAL(l, t)       \
68         rm_init_flags(l, "PFil " t " rmlock", RM_RECURSE)
69 #define PFIL_LOCK_DESTROY_REAL(l)       \
70         rm_destroy(l)
71 #define PFIL_LOCK_INIT(p)       do {                    \
72         if ((p)->flags & PFIL_FLAG_PRIVATE_LOCK) {      \
73                 PFIL_LOCK_INIT_REAL(&(p)->ph_lock, "private");  \
74                 (p)->ph_plock = &(p)->ph_lock;          \
75         } else                                          \
76                 (p)->ph_plock = &V_pfil_lock;           \
77 } while (0)
78 #define PFIL_LOCK_DESTROY(p)    do {                    \
79         if ((p)->flags & PFIL_FLAG_PRIVATE_LOCK)        \
80                 PFIL_LOCK_DESTROY_REAL((p)->ph_plock);  \
81 } while (0)
82
83 #define PFIL_TRY_RLOCK(p, t)    rm_try_rlock((p)->ph_plock, (t))
84 #define PFIL_RLOCK(p, t)        rm_rlock((p)->ph_plock, (t))
85 #define PFIL_WLOCK(p)           rm_wlock((p)->ph_plock)
86 #define PFIL_RUNLOCK(p, t)      rm_runlock((p)->ph_plock, (t))
87 #define PFIL_WUNLOCK(p)         rm_wunlock((p)->ph_plock)
88 #define PFIL_WOWNED(p)          rm_wowned((p)->ph_plock)
89
90 #define PFIL_HEADLIST_LOCK()    mtx_lock(&pfil_global_lock)
91 #define PFIL_HEADLIST_UNLOCK()  mtx_unlock(&pfil_global_lock)
92
93 /*
94  * pfil_run_hooks() runs the specified packet filter hook chain.
95  */
96 int
97 pfil_run_hooks(struct pfil_head *ph, struct mbuf **mp, struct ifnet *ifp,
98     int dir, struct inpcb *inp)
99 {
100         struct rm_priotracker rmpt;
101         struct packet_filter_hook *pfh;
102         struct mbuf *m = *mp;
103         int rv = 0;
104
105         PFIL_RLOCK(ph, &rmpt);
106         KASSERT(ph->ph_nhooks >= 0, ("Pfil hook count dropped < 0"));
107         for (pfh = pfil_chain_get(dir, ph); pfh != NULL;
108              pfh = TAILQ_NEXT(pfh, pfil_chain)) {
109                 if (pfh->pfil_func != NULL) {
110                         rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir,
111                             inp);
112                         if (rv != 0 || m == NULL)
113                                 break;
114                 }
115         }
116         PFIL_RUNLOCK(ph, &rmpt);
117         *mp = m;
118         return (rv);
119 }
120
121 static struct packet_filter_hook *
122 pfil_chain_get(int dir, struct pfil_head *ph)
123 {
124
125         if (dir == PFIL_IN)
126                 return (TAILQ_FIRST(&ph->ph_in));
127         else if (dir == PFIL_OUT)
128                 return (TAILQ_FIRST(&ph->ph_out));
129         else
130                 return (NULL);
131 }
132
133 /*
134  * pfil_try_rlock() acquires rm reader lock for specified head
135  * if this is immediately possible.
136  */
137 int
138 pfil_try_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
139 {
140
141         return (PFIL_TRY_RLOCK(ph, tracker));
142 }
143
144 /*
145  * pfil_rlock() acquires rm reader lock for specified head.
146  */
147 void
148 pfil_rlock(struct pfil_head *ph, struct rm_priotracker *tracker)
149 {
150
151         PFIL_RLOCK(ph, tracker);
152 }
153
154 /*
155  * pfil_runlock() releases reader lock for specified head.
156  */
157 void
158 pfil_runlock(struct pfil_head *ph, struct rm_priotracker *tracker)
159 {
160
161         PFIL_RUNLOCK(ph, tracker);
162 }
163
164 /*
165  * pfil_wlock() acquires writer lock for specified head.
166  */
167 void
168 pfil_wlock(struct pfil_head *ph)
169 {
170
171         PFIL_WLOCK(ph);
172 }
173
174 /*
175  * pfil_wunlock() releases writer lock for specified head.
176  */
177 void
178 pfil_wunlock(struct pfil_head *ph)
179 {
180
181         PFIL_WUNLOCK(ph);
182 }
183
184 /*
185  * pfil_wowned() returns a non-zero value if the current thread owns
186  * an exclusive lock.
187  */
188 int
189 pfil_wowned(struct pfil_head *ph)
190 {
191
192         return (PFIL_WOWNED(ph));
193 }
194
195 /*
196  * pfil_head_register() registers a pfil_head with the packet filter hook
197  * mechanism.
198  */
199 int
200 pfil_head_register(struct pfil_head *ph)
201 {
202         struct pfil_head *lph;
203
204         PFIL_HEADLIST_LOCK();
205         LIST_FOREACH(lph, &V_pfil_head_list, ph_list) {
206                 if (ph->ph_type == lph->ph_type &&
207                     ph->ph_un.phu_val == lph->ph_un.phu_val) {
208                         PFIL_HEADLIST_UNLOCK();
209                         return (EEXIST);
210                 }
211         }
212         PFIL_LOCK_INIT(ph);
213         ph->ph_nhooks = 0;
214         TAILQ_INIT(&ph->ph_in);
215         TAILQ_INIT(&ph->ph_out);
216         LIST_INSERT_HEAD(&V_pfil_head_list, ph, ph_list);
217         PFIL_HEADLIST_UNLOCK();
218         return (0);
219 }
220
221 /*
222  * pfil_head_unregister() removes a pfil_head from the packet filter hook
223  * mechanism.  The producer of the hook promises that all outstanding
224  * invocations of the hook have completed before it unregisters the hook.
225  */
226 int
227 pfil_head_unregister(struct pfil_head *ph)
228 {
229         struct packet_filter_hook *pfh, *pfnext;
230                 
231         PFIL_HEADLIST_LOCK();
232         LIST_REMOVE(ph, ph_list);
233         PFIL_HEADLIST_UNLOCK();
234         TAILQ_FOREACH_SAFE(pfh, &ph->ph_in, pfil_chain, pfnext)
235                 free(pfh, M_IFADDR);
236         TAILQ_FOREACH_SAFE(pfh, &ph->ph_out, pfil_chain, pfnext)
237                 free(pfh, M_IFADDR);
238         PFIL_LOCK_DESTROY(ph);
239         return (0);
240 }
241
242 /*
243  * pfil_head_get() returns the pfil_head for a given key/dlt.
244  */
245 struct pfil_head *
246 pfil_head_get(int type, u_long val)
247 {
248         struct pfil_head *ph;
249
250         PFIL_HEADLIST_LOCK();
251         LIST_FOREACH(ph, &V_pfil_head_list, ph_list)
252                 if (ph->ph_type == type && ph->ph_un.phu_val == val)
253                         break;
254         PFIL_HEADLIST_UNLOCK();
255         return (ph);
256 }
257
258 /*
259  * pfil_add_hook() adds a function to the packet filter hook.  the
260  * flags are:
261  *      PFIL_IN         call me on incoming packets
262  *      PFIL_OUT        call me on outgoing packets
263  *      PFIL_ALL        call me on all of the above
264  *      PFIL_WAITOK     OK to call malloc with M_WAITOK.
265  */
266 int
267 pfil_add_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
268 {
269         struct packet_filter_hook *pfh1 = NULL;
270         struct packet_filter_hook *pfh2 = NULL;
271         int err;
272
273         if (flags & PFIL_IN) {
274                 pfh1 = (struct packet_filter_hook *)malloc(sizeof(*pfh1), 
275                     M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
276                 if (pfh1 == NULL) {
277                         err = ENOMEM;
278                         goto error;
279                 }
280         }
281         if (flags & PFIL_OUT) {
282                 pfh2 = (struct packet_filter_hook *)malloc(sizeof(*pfh1),
283                     M_IFADDR, (flags & PFIL_WAITOK) ? M_WAITOK : M_NOWAIT);
284                 if (pfh2 == NULL) {
285                         err = ENOMEM;
286                         goto error;
287                 }
288         }
289         PFIL_WLOCK(ph);
290         if (flags & PFIL_IN) {
291                 pfh1->pfil_func = func;
292                 pfh1->pfil_arg = arg;
293                 err = pfil_chain_add(&ph->ph_in, pfh1, flags & ~PFIL_OUT);
294                 if (err)
295                         goto locked_error;
296                 ph->ph_nhooks++;
297         }
298         if (flags & PFIL_OUT) {
299                 pfh2->pfil_func = func;
300                 pfh2->pfil_arg = arg;
301                 err = pfil_chain_add(&ph->ph_out, pfh2, flags & ~PFIL_IN);
302                 if (err) {
303                         if (flags & PFIL_IN)
304                                 pfil_chain_remove(&ph->ph_in, func, arg);
305                         goto locked_error;
306                 }
307                 ph->ph_nhooks++;
308         }
309         PFIL_WUNLOCK(ph);
310         return (0);
311 locked_error:
312         PFIL_WUNLOCK(ph);
313 error:
314         if (pfh1 != NULL)
315                 free(pfh1, M_IFADDR);
316         if (pfh2 != NULL)
317                 free(pfh2, M_IFADDR);
318         return (err);
319 }
320
321 /*
322  * pfil_remove_hook removes a specific function from the packet filter hook
323  * chain.
324  */
325 int
326 pfil_remove_hook(pfil_func_t func, void *arg, int flags, struct pfil_head *ph)
327 {
328         int err = 0;
329
330         PFIL_WLOCK(ph);
331         if (flags & PFIL_IN) {
332                 err = pfil_chain_remove(&ph->ph_in, func, arg);
333                 if (err == 0)
334                         ph->ph_nhooks--;
335         }
336         if ((err == 0) && (flags & PFIL_OUT)) {
337                 err = pfil_chain_remove(&ph->ph_out, func, arg);
338                 if (err == 0)
339                         ph->ph_nhooks--;
340         }
341         PFIL_WUNLOCK(ph);
342         return (err);
343 }
344
345 /*
346  * Internal: Add a new pfil hook into a hook chain.
347  */
348 static int
349 pfil_chain_add(pfil_chain_t *chain, struct packet_filter_hook *pfh1, int flags)
350 {
351         struct packet_filter_hook *pfh;
352
353         /*
354          * First make sure the hook is not already there.
355          */
356         TAILQ_FOREACH(pfh, chain, pfil_chain)
357                 if (pfh->pfil_func == pfh1->pfil_func &&
358                     pfh->pfil_arg == pfh1->pfil_arg)
359                         return (EEXIST);
360
361         /*
362          * Insert the input list in reverse order of the output list so that
363          * the same path is followed in or out of the kernel.
364          */
365         if (flags & PFIL_IN)
366                 TAILQ_INSERT_HEAD(chain, pfh1, pfil_chain);
367         else
368                 TAILQ_INSERT_TAIL(chain, pfh1, pfil_chain);
369         return (0);
370 }
371
372 /*
373  * Internal: Remove a pfil hook from a hook chain.
374  */
375 static int
376 pfil_chain_remove(pfil_chain_t *chain, pfil_func_t func, void *arg)
377 {
378         struct packet_filter_hook *pfh;
379
380         TAILQ_FOREACH(pfh, chain, pfil_chain)
381                 if (pfh->pfil_func == func && pfh->pfil_arg == arg) {
382                         TAILQ_REMOVE(chain, pfh, pfil_chain);
383                         free(pfh, M_IFADDR);
384                         return (0);
385                 }
386         return (ENOENT);
387 }
388
389 /*
390  * Stuff that must be initialized for every instance (including the first of
391  * course).
392  */
393 static void
394 vnet_pfil_init(const void *unused __unused)
395 {
396
397         LIST_INIT(&V_pfil_head_list);
398         PFIL_LOCK_INIT_REAL(&V_pfil_lock, "shared");
399 }
400
401 /*
402  * Called for the removal of each instance.
403  */
404 static void
405 vnet_pfil_uninit(const void *unused __unused)
406 {
407
408         KASSERT(LIST_EMPTY(&V_pfil_head_list),
409             ("%s: pfil_head_list %p not empty", __func__, &V_pfil_head_list));
410         PFIL_LOCK_DESTROY_REAL(&V_pfil_lock);
411 }
412
413 /*
414  * Starting up.
415  *
416  * VNET_SYSINIT is called for each existing vnet and each new vnet.
417  * Make sure the pfil bits are first before any possible subsystem which
418  * might piggyback on the SI_SUB_PROTO_PFIL.
419  */
420 VNET_SYSINIT(vnet_pfil_init, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST,
421     vnet_pfil_init, NULL);
422  
423 /*
424  * Closing up shop.  These are done in REVERSE ORDER.  Not called on reboot.
425  *
426  * VNET_SYSUNINIT is called for each exiting vnet as it exits.
427  */
428 VNET_SYSUNINIT(vnet_pfil_uninit, SI_SUB_PROTO_PFIL, SI_ORDER_FIRST,
429     vnet_pfil_uninit, NULL);