The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netpfil/ipfw/ip_fw_iface.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 08304fc81e813fe39eadec3182bb4bfb


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.