source: rtems-libbsd/freebsd/sys/netpfil/ipfw/ip_fw_iface.c @ 3c967ca

55-freebsd-126-freebsd-12
Last change on this file since 3c967ca was 3c967ca, checked in by Sebastian Huber <sebastian.huber@…>, on 06/08/17 at 11:15:12

Use <sys/lock.h> provided by Newlib

  • Property mode set to 100644
File size: 11.5 KB
Line 
1#include <machine/rtems-bsd-kernel-space.h>
2
3/*-
4 * Copyright (c) 2014 Yandex LLC.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31/*
32 * Kernel interface tracking API.
33 *
34 */
35
36#include <rtems/bsd/local/opt_ipfw.h>
37#include <rtems/bsd/local/opt_inet.h>
38#ifndef INET
39#error IPFIREWALL requires INET.
40#endif /* INET */
41#include <rtems/bsd/local/opt_inet6.h>
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/malloc.h>
46#include <sys/kernel.h>
47#include <sys/lock.h>
48#include <sys/rwlock.h>
49#include <sys/rmlock.h>
50#include <sys/socket.h>
51#include <sys/queue.h>
52#include <sys/eventhandler.h>
53#include <net/if.h>
54#include <net/if_var.h>
55#include <net/pfil.h>
56#include <net/vnet.h>
57
58#include <netinet/in.h>
59#include <netinet/ip_var.h>     /* struct ipfw_rule_ref */
60#include <netinet/ip_fw.h>
61
62#include <netpfil/ipfw/ip_fw_private.h>
63
64#define CHAIN_TO_II(ch)         ((struct namedobj_instance *)ch->ifcfg)
65
66#define DEFAULT_IFACES  128
67
68static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
69    uint16_t ifindex);
70static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
71    uint16_t ifindex);
72static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
73    struct sockopt_data *sd);
74
75static struct ipfw_sopt_handler scodes[] = {
76        { IP_FW_XIFLIST,        0,      HDIR_GET,       list_ifaces },
77};
78
79/*
80 * FreeBSD Kernel interface.
81 */
82static void ipfw_kifhandler(void *arg, struct ifnet *ifp);
83static int ipfw_kiflookup(char *name);
84static void iface_khandler_register(void);
85static void iface_khandler_deregister(void);
86
87static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
88static int num_vnets = 0;
89static struct mtx vnet_mtx;
90
91/*
92 * Checks if kernel interface is contained in our tracked
93 * interface list and calls attach/detach handler.
94 */
95static void
96ipfw_kifhandler(void *arg, struct ifnet *ifp)
97{
98        struct ip_fw_chain *ch;
99        struct ipfw_iface *iif;
100        struct namedobj_instance *ii;
101        uintptr_t htype;
102
103        if (V_ipfw_vnet_ready == 0)
104                return;
105
106        ch = &V_layer3_chain;
107        htype = (uintptr_t)arg;
108
109        IPFW_UH_WLOCK(ch);
110        ii = CHAIN_TO_II(ch);
111        if (ii == NULL) {
112                IPFW_UH_WUNLOCK(ch);
113                return;
114        }
115        iif = (struct ipfw_iface*)ipfw_objhash_lookup_name(ii, 0,
116            if_name(ifp));
117        if (iif != NULL) {
118                if (htype == 1)
119                        handle_ifattach(ch, iif, ifp->if_index);
120                else
121                        handle_ifdetach(ch, iif, ifp->if_index);
122        }
123        IPFW_UH_WUNLOCK(ch);
124}
125
126/*
127 * Reference current VNET as iface tracking API user.
128 * Registers interface tracking handlers for first VNET.
129 */
130static void
131iface_khandler_register()
132{
133        int create;
134
135        create = 0;
136
137        mtx_lock(&vnet_mtx);
138        if (num_vnets == 0)
139                create = 1;
140        num_vnets++;
141        mtx_unlock(&vnet_mtx);
142
143        if (create == 0)
144                return;
145
146        printf("IPFW: starting up interface tracker\n");
147
148        ipfw_ifdetach_event = EVENTHANDLER_REGISTER(
149            ifnet_departure_event, ipfw_kifhandler, NULL,
150            EVENTHANDLER_PRI_ANY);
151        ipfw_ifattach_event = EVENTHANDLER_REGISTER(
152            ifnet_arrival_event, ipfw_kifhandler, (void*)((uintptr_t)1),
153            EVENTHANDLER_PRI_ANY);
154}
155
156/*
157 *
158 * Detach interface event handlers on last VNET instance
159 * detach.
160 */
161static void
162iface_khandler_deregister()
163{
164        int destroy;
165
166        destroy = 0;
167        mtx_lock(&vnet_mtx);
168        if (num_vnets == 1)
169                destroy = 1;
170        num_vnets--;
171        mtx_unlock(&vnet_mtx);
172
173        if (destroy == 0)
174                return;
175
176        EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
177            ipfw_ifattach_event);
178        EVENTHANDLER_DEREGISTER(ifnet_departure_event,
179            ipfw_ifdetach_event);
180}
181
182/*
183 * Retrieves ifindex for given @name.
184 *
185 * Returns ifindex or 0.
186 */
187static int
188ipfw_kiflookup(char *name)
189{
190        struct ifnet *ifp;
191        int ifindex;
192
193        ifindex = 0;
194
195        if ((ifp = ifunit_ref(name)) != NULL) {
196                ifindex = ifp->if_index;
197                if_rele(ifp);
198        }
199
200        return (ifindex);
201}
202
203/*
204 * Global ipfw startup hook.
205 * Since we perform lazy initialization, do nothing except
206 * mutex init.
207 */
208int
209ipfw_iface_init()
210{
211
212        mtx_init(&vnet_mtx, "IPFW ifhandler mtx", NULL, MTX_DEF);
213        IPFW_ADD_SOPT_HANDLER(1, scodes);
214        return (0);
215}
216
217/*
218 * Global ipfw destroy hook.
219 * Unregister khandlers iff init has been done.
220 */
221void
222ipfw_iface_destroy()
223{
224
225        IPFW_DEL_SOPT_HANDLER(1, scodes);
226        mtx_destroy(&vnet_mtx);
227}
228
229/*
230 * Perform actual init on internal request.
231 * Inits both namehash and global khandler.
232 */
233static void
234vnet_ipfw_iface_init(struct ip_fw_chain *ch)
235{
236        struct namedobj_instance *ii;
237
238        ii = ipfw_objhash_create(DEFAULT_IFACES);
239        IPFW_UH_WLOCK(ch);
240        if (ch->ifcfg == NULL) {
241                ch->ifcfg = ii;
242                ii = NULL;
243        }
244        IPFW_UH_WUNLOCK(ch);
245
246        if (ii != NULL) {
247                /* Already initialized. Free namehash. */
248                ipfw_objhash_destroy(ii);
249        } else {
250                /* We're the first ones. Init kernel hooks. */
251                iface_khandler_register();
252        }
253}
254
255static int
256destroy_iface(struct namedobj_instance *ii, struct named_object *no,
257    void *arg)
258{
259
260        /* Assume all consumers have been already detached */
261        free(no, M_IPFW);
262        return (0);
263}
264
265/*
266 * Per-VNET ipfw detach hook.
267 *
268 */
269void
270vnet_ipfw_iface_destroy(struct ip_fw_chain *ch)
271{
272        struct namedobj_instance *ii;
273
274        IPFW_UH_WLOCK(ch);
275        ii = CHAIN_TO_II(ch);
276        ch->ifcfg = NULL;
277        IPFW_UH_WUNLOCK(ch);
278
279        if (ii != NULL) {
280                ipfw_objhash_foreach(ii, destroy_iface, ch);
281                ipfw_objhash_destroy(ii);
282                iface_khandler_deregister();
283        }
284}
285
286/*
287 * Notify the subsystem that we are interested in tracking
288 * interface @name. This function has to be called without
289 * holding any locks to permit allocating the necessary states
290 * for proper interface tracking.
291 *
292 * Returns 0 on success.
293 */
294int
295ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
296    struct ipfw_ifc *ic)
297{
298        struct namedobj_instance *ii;
299        struct ipfw_iface *iif, *tmp;
300
301        if (strlen(name) >= sizeof(iif->ifname))
302                return (EINVAL);
303
304        IPFW_UH_WLOCK(ch);
305
306        ii = CHAIN_TO_II(ch);
307        if (ii == NULL) {
308
309                /*
310                 * First request to subsystem.
311                 * Let's perform init.
312                 */
313                IPFW_UH_WUNLOCK(ch);
314                vnet_ipfw_iface_init(ch);
315                IPFW_UH_WLOCK(ch);
316                ii = CHAIN_TO_II(ch);
317        }
318
319        iif = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
320
321        if (iif != NULL) {
322                iif->no.refcnt++;
323                ic->iface = iif;
324                IPFW_UH_WUNLOCK(ch);
325                return (0);
326        }
327
328        IPFW_UH_WUNLOCK(ch);
329
330        /* Not found. Let's create one */
331        iif = malloc(sizeof(struct ipfw_iface), M_IPFW, M_WAITOK | M_ZERO);
332        TAILQ_INIT(&iif->consumers);
333        iif->no.name = iif->ifname;
334        strlcpy(iif->ifname, name, sizeof(iif->ifname));
335
336        /*
337         * Ref & link to the list.
338         *
339         * We assume  ifnet_arrival_event / ifnet_departure_event
340         * are not holding any locks.
341         */
342        iif->no.refcnt = 1;
343        IPFW_UH_WLOCK(ch);
344
345        tmp = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
346        if (tmp != NULL) {
347                /* Interface has been created since unlock. Ref and return */
348                tmp->no.refcnt++;
349                ic->iface = tmp;
350                IPFW_UH_WUNLOCK(ch);
351                free(iif, M_IPFW);
352                return (0);
353        }
354
355        iif->ifindex = ipfw_kiflookup(name);
356        if (iif->ifindex != 0)
357                iif->resolved = 1;
358
359        ipfw_objhash_add(ii, &iif->no);
360        ic->iface = iif;
361
362        IPFW_UH_WUNLOCK(ch);
363
364        return (0);
365}
366
367/*
368 * Adds @ic to the list of iif interface consumers.
369 * Must be called with holding both UH+WLOCK.
370 * Callback may be immediately called (if interface exists).
371 */
372void
373ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
374{
375        struct ipfw_iface *iif;
376
377        IPFW_UH_WLOCK_ASSERT(ch);
378        IPFW_WLOCK_ASSERT(ch);
379
380        iif = ic->iface;
381       
382        TAILQ_INSERT_TAIL(&iif->consumers, ic, next);
383        if (iif->resolved != 0)
384                ic->cb(ch, ic->cbdata, iif->ifindex);
385}
386
387/*
388 * Unlinks interface tracker object @ic from interface.
389 * Must be called while holding UH lock.
390 */
391void
392ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
393{
394        struct ipfw_iface *iif;
395
396        IPFW_UH_WLOCK_ASSERT(ch);
397
398        iif = ic->iface;
399        TAILQ_REMOVE(&iif->consumers, ic, next);
400}
401
402/*
403 * Unreference interface specified by @ic.
404 * Must be called while holding UH lock.
405 */
406void
407ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
408{
409        struct ipfw_iface *iif;
410
411        IPFW_UH_WLOCK_ASSERT(ch);
412
413        iif = ic->iface;
414        ic->iface = NULL;
415
416        iif->no.refcnt--;
417        /* TODO: check for references & delete */
418}
419
420/*
421 * Interface arrival handler.
422 */
423static void
424handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
425    uint16_t ifindex)
426{
427        struct ipfw_ifc *ic;
428
429        IPFW_UH_WLOCK_ASSERT(ch);
430
431        iif->gencnt++;
432        iif->resolved = 1;
433        iif->ifindex = ifindex;
434
435        IPFW_WLOCK(ch);
436        TAILQ_FOREACH(ic, &iif->consumers, next)
437                ic->cb(ch, ic->cbdata, iif->ifindex);
438        IPFW_WUNLOCK(ch);
439}
440
441/*
442 * Interface departure handler.
443 */
444static void
445handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
446    uint16_t ifindex)
447{
448        struct ipfw_ifc *ic;
449
450        IPFW_UH_WLOCK_ASSERT(ch);
451
452        IPFW_WLOCK(ch);
453        TAILQ_FOREACH(ic, &iif->consumers, next)
454                ic->cb(ch, ic->cbdata, 0);
455        IPFW_WUNLOCK(ch);
456
457        iif->gencnt++;
458        iif->resolved = 0;
459        iif->ifindex = 0;
460}
461
462struct dump_iface_args {
463        struct ip_fw_chain *ch;
464        struct sockopt_data *sd;
465};
466
467static int
468export_iface_internal(struct namedobj_instance *ii, struct named_object *no,
469    void *arg)
470{
471        ipfw_iface_info *i;
472        struct dump_iface_args *da;
473        struct ipfw_iface *iif;
474
475        da = (struct dump_iface_args *)arg;
476
477        i = (ipfw_iface_info *)ipfw_get_sopt_space(da->sd, sizeof(*i));
478        KASSERT(i != NULL, ("previously checked buffer is not enough"));
479
480        iif = (struct ipfw_iface *)no;
481
482        strlcpy(i->ifname, iif->ifname, sizeof(i->ifname));
483        if (iif->resolved)
484                i->flags |= IPFW_IFFLAG_RESOLVED;
485        i->ifindex = iif->ifindex;
486        i->refcnt = iif->no.refcnt;
487        i->gencnt = iif->gencnt;
488        return (0);
489}
490
491/*
492 * Lists all interface currently tracked by ipfw.
493 * Data layout (v0)(current):
494 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
495 * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ]
496 *
497 * Returns 0 on success
498 */
499static int
500list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
501    struct sockopt_data *sd)
502{
503        struct namedobj_instance *ii;
504        struct _ipfw_obj_lheader *olh;
505        struct dump_iface_args da;
506        uint32_t count, size;
507
508        olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
509        if (olh == NULL)
510                return (EINVAL);
511        if (sd->valsize < olh->size)
512                return (EINVAL);
513
514        IPFW_UH_RLOCK(ch);
515        ii = CHAIN_TO_II(ch);
516        if (ii != NULL)
517                count = ipfw_objhash_count(ii);
518        else
519                count = 0;
520        size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader);
521
522        /* Fill in header regadless of buffer size */
523        olh->count = count;
524        olh->objsize = sizeof(ipfw_iface_info);
525
526        if (size > olh->size) {
527                olh->size = size;
528                IPFW_UH_RUNLOCK(ch);
529                return (ENOMEM);
530        }
531        olh->size = size;
532
533        da.ch = ch;
534        da.sd = sd;
535
536        if (ii != NULL)
537                ipfw_objhash_foreach(ii, export_iface_internal, &da);
538        IPFW_UH_RUNLOCK(ch);
539
540        return (0);
541}
542
Note: See TracBrowser for help on using the repository browser.