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/net/if_gif.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 /*      $NetBSD: if_gif.c,v 1.157 2022/09/03 02:47:59 thorpej Exp $     */
    2 /*      $KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc Exp $    */
    3 
    4 /*
    5  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the project nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.157 2022/09/03 02:47:59 thorpej Exp $");
   35 
   36 #ifdef _KERNEL_OPT
   37 #include "opt_inet.h"
   38 #include "opt_net_mpsafe.h"
   39 #endif
   40 
   41 #include <sys/param.h>
   42 #include <sys/systm.h>
   43 #include <sys/atomic.h>
   44 #include <sys/kernel.h>
   45 #include <sys/mbuf.h>
   46 #include <sys/socket.h>
   47 #include <sys/sockio.h>
   48 #include <sys/errno.h>
   49 #include <sys/ioctl.h>
   50 #include <sys/time.h>
   51 #include <sys/socketvar.h>
   52 #include <sys/syslog.h>
   53 #include <sys/proc.h>
   54 #include <sys/cpu.h>
   55 #include <sys/intr.h>
   56 #include <sys/kmem.h>
   57 #include <sys/sysctl.h>
   58 #include <sys/xcall.h>
   59 #include <sys/device.h>
   60 #include <sys/module.h>
   61 #include <sys/mutex.h>
   62 #include <sys/pserialize.h>
   63 #include <sys/psref.h>
   64 
   65 #include <net/if.h>
   66 #include <net/if_types.h>
   67 #include <net/route.h>
   68 #include <net/bpf.h>
   69 
   70 #include <netinet/in.h>
   71 #include <netinet/in_systm.h>
   72 #include <netinet/ip.h>
   73 #ifdef  INET
   74 #include <netinet/in_var.h>
   75 #endif  /* INET */
   76 #include <netinet/in_gif.h>
   77 
   78 #ifdef INET6
   79 #ifndef INET
   80 #include <netinet/in.h>
   81 #endif
   82 #include <netinet6/in6_var.h>
   83 #include <netinet/ip6.h>
   84 #include <netinet6/ip6_var.h>
   85 #include <netinet6/in6_gif.h>
   86 #endif /* INET6 */
   87 
   88 #include <netinet/ip_encap.h>
   89 #include <net/if_gif.h>
   90 
   91 #include "ioconf.h"
   92 
   93 #ifdef NET_MPSAFE
   94 #define GIF_MPSAFE      1
   95 #endif
   96 
   97 /*
   98  * gif global variable definitions
   99  */
  100 static struct {
  101         LIST_HEAD(gif_sclist, gif_softc) list;
  102         kmutex_t lock;
  103 } gif_softcs __cacheline_aligned;
  104 
  105 struct psref_class *gv_psref_class __read_mostly;
  106 
  107 static pktq_rps_hash_func_t gif_pktq_rps_hash_p;
  108 
  109 static int      gifattach0(struct gif_softc *);
  110 static int      gif_output(struct ifnet *, struct mbuf *,
  111                            const struct sockaddr *, const struct rtentry *);
  112 static void     gif_start(struct ifnet *);
  113 static int      gif_transmit(struct ifnet *, struct mbuf *);
  114 static int      gif_transmit_direct(struct gif_variant *, struct mbuf *);
  115 static int      gif_ioctl(struct ifnet *, u_long, void *);
  116 static int      gif_set_tunnel(struct ifnet *, struct sockaddr *,
  117                                struct sockaddr *);
  118 static void     gif_delete_tunnel(struct ifnet *);
  119 
  120 static int      gif_clone_create(struct if_clone *, int);
  121 static int      gif_clone_destroy(struct ifnet *);
  122 static int      gif_check_nesting(struct ifnet *, struct mbuf *);
  123 
  124 static int      gif_encap_attach(struct gif_variant *);
  125 static int      gif_encap_detach(struct gif_variant *);
  126 
  127 static void     gif_update_variant(struct gif_softc *, struct gif_variant *);
  128 
  129 static struct if_clone gif_cloner =
  130     IF_CLONE_INITIALIZER("gif", gif_clone_create, gif_clone_destroy);
  131 
  132 #ifndef MAX_GIF_NEST
  133 /*
  134  * This macro controls the upper limitation on nesting of gif tunnels.
  135  * Since, setting a large value to this macro with a careless configuration
  136  * may introduce system crash, we don't allow any nestings by default.
  137  * If you need to configure nested gif tunnels, you can define this macro
  138  * in your kernel configuration file.  However, if you do so, please be
  139  * careful to configure the tunnels so that it won't make a loop.
  140  */
  141 #define MAX_GIF_NEST 1
  142 #endif
  143 static int max_gif_nesting = MAX_GIF_NEST;
  144 
  145 static struct sysctllog *gif_sysctl;
  146 
  147 #ifdef INET6
  148 static int
  149 sysctl_gif_pmtu_global(SYSCTLFN_ARGS)
  150 {
  151         int error, pmtu;
  152         struct sysctlnode node = *rnode;
  153 
  154         pmtu = ip6_gif_pmtu;
  155         node.sysctl_data = &pmtu;
  156         error = sysctl_lookup(SYSCTLFN_CALL(&node));
  157         if (error || newp == NULL)
  158                 return error;
  159 
  160         switch (pmtu) {
  161         case GIF_PMTU_MINMTU:
  162         case GIF_PMTU_OUTERMTU:
  163                 ip6_gif_pmtu = pmtu;
  164                 break;
  165         default:
  166                 return EINVAL;
  167         }
  168 
  169         return 0;
  170 }
  171 
  172 static int
  173 sysctl_gif_pmtu_perif(SYSCTLFN_ARGS)
  174 {
  175         int error, pmtu;
  176         struct sysctlnode node = *rnode;
  177         struct gif_softc *sc = (struct gif_softc *)node.sysctl_data;
  178 
  179         pmtu = sc->gif_pmtu;
  180         node.sysctl_data = &pmtu;
  181         error = sysctl_lookup(SYSCTLFN_CALL(&node));
  182         if (error || newp == NULL)
  183                 return error;
  184 
  185         switch (pmtu) {
  186         case GIF_PMTU_SYSDEFAULT:
  187         case GIF_PMTU_MINMTU:
  188         case GIF_PMTU_OUTERMTU:
  189                 sc->gif_pmtu = pmtu;
  190                 break;
  191         default:
  192                 return EINVAL;
  193         }
  194 
  195         return 0;
  196 }
  197 #endif
  198 
  199 static void
  200 gif_sysctl_setup(void)
  201 {
  202         const struct sysctlnode *node = NULL;
  203 
  204         gif_sysctl = NULL;
  205 
  206 #ifdef INET
  207         /*
  208          * Previously create "net.inet.ip" entry to avoid sysctl_createv error.
  209          */
  210         sysctl_createv(NULL, 0, NULL, NULL,
  211                        CTLFLAG_PERMANENT,
  212                        CTLTYPE_NODE, "inet",
  213                        SYSCTL_DESCR("PF_INET related settings"),
  214                        NULL, 0, NULL, 0,
  215                        CTL_NET, PF_INET, CTL_EOL);
  216         sysctl_createv(NULL, 0, NULL, NULL,
  217                        CTLFLAG_PERMANENT,
  218                        CTLTYPE_NODE, "ip",
  219                        SYSCTL_DESCR("IPv4 related settings"),
  220                        NULL, 0, NULL, 0,
  221                        CTL_NET, PF_INET, IPPROTO_IP, CTL_EOL);
  222 
  223         sysctl_createv(&gif_sysctl, 0, NULL, NULL,
  224                        CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  225                        CTLTYPE_INT, "gifttl",
  226                        SYSCTL_DESCR("Default TTL for a gif tunnel datagram"),
  227                        NULL, 0, &ip_gif_ttl, 0,
  228                        CTL_NET, PF_INET, IPPROTO_IP,
  229                        IPCTL_GIF_TTL, CTL_EOL);
  230 #endif
  231 #ifdef INET6
  232         /*
  233          * Previously create "net.inet6.ip6" entry to avoid sysctl_createv error.
  234          */
  235         sysctl_createv(NULL, 0, NULL, NULL,
  236                        CTLFLAG_PERMANENT,
  237                        CTLTYPE_NODE, "inet6",
  238                        SYSCTL_DESCR("PF_INET6 related settings"),
  239                        NULL, 0, NULL, 0,
  240                        CTL_NET, PF_INET6, CTL_EOL);
  241         sysctl_createv(NULL, 0, NULL, NULL,
  242                        CTLFLAG_PERMANENT,
  243                        CTLTYPE_NODE, "ip6",
  244                        SYSCTL_DESCR("IPv6 related settings"),
  245                        NULL, 0, NULL, 0,
  246                        CTL_NET, PF_INET6, IPPROTO_IPV6, CTL_EOL);
  247 
  248         sysctl_createv(&gif_sysctl, 0, NULL, NULL,
  249                        CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  250                        CTLTYPE_INT, "gifhlim",
  251                        SYSCTL_DESCR("Default hop limit for a gif tunnel datagram"),
  252                        NULL, 0, &ip6_gif_hlim, 0,
  253                        CTL_NET, PF_INET6, IPPROTO_IPV6,
  254                        IPV6CTL_GIF_HLIM, CTL_EOL);
  255 
  256         sysctl_createv(&gif_sysctl, 0, NULL, NULL,
  257                        CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  258                        CTLTYPE_INT, "gifpmtu",
  259                        SYSCTL_DESCR("Default Path MTU setting for gif tunnels"),
  260                        sysctl_gif_pmtu_global, 0, NULL, 0,
  261                        CTL_NET, PF_INET6, IPPROTO_IPV6,
  262                        IPV6CTL_GIF_PMTU, CTL_EOL);
  263 #endif
  264 
  265         sysctl_createv(&gif_sysctl, 0, NULL, &node,
  266                        CTLFLAG_PERMANENT,
  267                        CTLTYPE_NODE, "gif",
  268                        SYSCTL_DESCR("gif global control"),
  269                        NULL, 0, NULL, 0,
  270                        CTL_NET, CTL_CREATE, CTL_EOL);
  271 
  272         sysctl_createv(&gif_sysctl, 0, &node, NULL,
  273                        CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
  274                        CTLTYPE_STRING, "rps_hash",
  275                        SYSCTL_DESCR("Interface rps hash function control"),
  276                        sysctl_pktq_rps_hash_handler, 0, (void *)&gif_pktq_rps_hash_p,
  277                        PKTQ_RPS_HASH_NAME_LEN,
  278                        CTL_CREATE, CTL_EOL);
  279 }
  280 
  281 static void
  282 gif_perif_sysctl_setup(struct sysctllog **clog, struct gif_softc *sc)
  283 {
  284 #ifdef INET6
  285         const struct sysctlnode *cnode, *rnode;
  286         struct ifnet *ifp = &sc->gif_if;
  287         const char *ifname = ifp->if_xname;
  288         int rv;
  289 
  290         /*
  291          * Already created in sysctl_sndq_setup().
  292          */
  293         sysctl_createv(clog, 0, NULL, &rnode,
  294                        CTLFLAG_PERMANENT,
  295                        CTLTYPE_NODE, "interfaces",
  296                        SYSCTL_DESCR("Per-interface controls"),
  297                        NULL, 0, NULL, 0,
  298                        CTL_NET, CTL_CREATE, CTL_EOL);
  299         sysctl_createv(clog, 0, &rnode, &rnode,
  300                        CTLFLAG_PERMANENT,
  301                        CTLTYPE_NODE, ifname,
  302                        SYSCTL_DESCR("Interface controls"),
  303                        NULL, 0, NULL, 0,
  304                        CTL_CREATE, CTL_EOL);
  305 
  306         rv = sysctl_createv(clog, 0, &rnode, &cnode,
  307                             CTLFLAG_PERMANENT,
  308                             CTLTYPE_INT, "pmtu",
  309                             SYSCTL_DESCR("Path MTU setting for this gif tunnel"),
  310                             sysctl_gif_pmtu_perif, 0, (void *)sc, 0,
  311                             CTL_CREATE, CTL_EOL);
  312         if (rv != 0)
  313                 log(LOG_WARNING, "%s: could not attach sysctl node pmtu\n", ifname);
  314 
  315         sc->gif_pmtu = GIF_PMTU_SYSDEFAULT;
  316 #endif
  317 }
  318 
  319 /* ARGSUSED */
  320 void
  321 gifattach(int count)
  322 {
  323         /*
  324          * Nothing to do here, initialization is handled by the
  325          * module initialization code in gifinit() below).
  326          */
  327 }
  328 
  329 static void
  330 gifinit(void)
  331 {
  332 
  333         mutex_init(&gif_softcs.lock, MUTEX_DEFAULT, IPL_NONE);
  334         LIST_INIT(&gif_softcs.list);
  335         if_clone_attach(&gif_cloner);
  336 
  337         gv_psref_class = psref_class_create("gifvar", IPL_SOFTNET);
  338 
  339         gif_pktq_rps_hash_p = pktq_rps_hash_default;
  340         gif_sysctl_setup();
  341 }
  342 
  343 static int
  344 gifdetach(void)
  345 {
  346 
  347         mutex_enter(&gif_softcs.lock);
  348         if (!LIST_EMPTY(&gif_softcs.list)) {
  349                 mutex_exit(&gif_softcs.lock);
  350                 return EBUSY;
  351         }
  352 
  353         psref_class_destroy(gv_psref_class);
  354 
  355         if_clone_detach(&gif_cloner);
  356         sysctl_teardown(&gif_sysctl);
  357         mutex_exit(&gif_softcs.lock);
  358         mutex_destroy(&gif_softcs.lock);
  359         return 0;
  360 }
  361 
  362 static int
  363 gif_clone_create(struct if_clone *ifc, int unit)
  364 {
  365         struct gif_softc *sc;
  366         struct gif_variant *var;
  367         struct ifnet *ifp;
  368         int rv;
  369 
  370         sc = kmem_zalloc(sizeof(struct gif_softc), KM_SLEEP);
  371 
  372         if_initname(&sc->gif_if, ifc->ifc_name, unit);
  373 
  374         rv = gifattach0(sc);
  375         if (rv != 0) {
  376                 kmem_free(sc, sizeof(struct gif_softc));
  377                 return rv;
  378         }
  379 
  380         ifp = &sc->gif_if;
  381         gif_perif_sysctl_setup(&ifp->if_sysctl_log, sc);
  382 
  383         var = kmem_zalloc(sizeof(*var), KM_SLEEP);
  384         var->gv_softc = sc;
  385         psref_target_init(&var->gv_psref, gv_psref_class);
  386 
  387         sc->gif_var = var;
  388         mutex_init(&sc->gif_lock, MUTEX_DEFAULT, IPL_NONE);
  389         sc->gif_psz = pserialize_create();
  390 
  391         sc->gif_ro_percpu = if_tunnel_alloc_ro_percpu();
  392         mutex_enter(&gif_softcs.lock);
  393         LIST_INSERT_HEAD(&gif_softcs.list, sc, gif_list);
  394         mutex_exit(&gif_softcs.lock);
  395         return 0;
  396 }
  397 
  398 static int
  399 gifattach0(struct gif_softc *sc)
  400 {
  401 
  402         sc->gif_if.if_addrlen = 0;
  403         sc->gif_if.if_mtu    = GIF_MTU;
  404         sc->gif_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
  405 #ifdef GIF_MPSAFE
  406         sc->gif_if.if_extflags  |= IFEF_MPSAFE;
  407 #endif
  408         sc->gif_if.if_ioctl  = gif_ioctl;
  409         sc->gif_if.if_output = gif_output;
  410         sc->gif_if.if_start = gif_start;
  411         sc->gif_if.if_transmit = gif_transmit;
  412         sc->gif_if.if_type   = IFT_GIF;
  413         sc->gif_if.if_dlt    = DLT_NULL;
  414         sc->gif_if.if_softc  = sc;
  415         IFQ_SET_READY(&sc->gif_if.if_snd);
  416         if_initialize(&sc->gif_if);
  417 
  418         sc->gif_if.if_link_state = LINK_STATE_DOWN;
  419         if_alloc_sadl(&sc->gif_if);
  420         bpf_attach(&sc->gif_if, DLT_NULL, sizeof(u_int));
  421         if_register(&sc->gif_if);
  422         return 0;
  423 }
  424 
  425 static int
  426 gif_clone_destroy(struct ifnet *ifp)
  427 {
  428         struct gif_softc *sc = (void *) ifp;
  429         struct gif_variant *var;
  430 
  431         LIST_REMOVE(sc, gif_list);
  432 
  433         gif_delete_tunnel(&sc->gif_if);
  434         bpf_detach(ifp);
  435         if_detach(ifp);
  436 
  437         if_tunnel_free_ro_percpu(sc->gif_ro_percpu);
  438 
  439         pserialize_destroy(sc->gif_psz);
  440         mutex_destroy(&sc->gif_lock);
  441 
  442         var = sc->gif_var;
  443         kmem_free(var, sizeof(*var));
  444         kmem_free(sc, sizeof(struct gif_softc));
  445 
  446         return 0;
  447 }
  448 
  449 #ifdef GIF_ENCAPCHECK
  450 int
  451 gif_encapcheck(struct mbuf *m, int off, int proto, void *arg)
  452 {
  453         struct ip ip;
  454         struct gif_softc *sc;
  455         struct gif_variant *var;
  456         struct psref psref;
  457         int ret = 0;
  458 
  459         sc = arg;
  460         if (sc == NULL)
  461                 return 0;
  462 
  463         if ((sc->gif_if.if_flags & IFF_UP) == 0)
  464                 return 0;
  465 
  466         var = gif_getref_variant(sc, &psref);
  467         /* no physical address */
  468         if (var->gv_psrc == NULL || var->gv_pdst == NULL)
  469                 goto out;
  470 
  471         switch (proto) {
  472 #ifdef INET
  473         case IPPROTO_IPV4:
  474                 break;
  475 #endif
  476 #ifdef INET6
  477         case IPPROTO_IPV6:
  478                 break;
  479 #endif
  480         default:
  481                 goto out;
  482         }
  483 
  484         /* Bail on short packets */
  485         KASSERT(m->m_flags & M_PKTHDR);
  486         if (m->m_pkthdr.len < sizeof(ip))
  487                 goto  out;
  488 
  489         m_copydata(m, 0, sizeof(ip), &ip);
  490 
  491         switch (ip.ip_v) {
  492 #ifdef INET
  493         case 4:
  494                 if (var->gv_psrc->sa_family != AF_INET ||
  495                     var->gv_pdst->sa_family != AF_INET)
  496                         goto out;
  497                 ret = gif_encapcheck4(m, off, proto, var);
  498                 break;
  499 #endif
  500 #ifdef INET6
  501         case 6:
  502                 if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
  503                         goto out;
  504                 if (var->gv_psrc->sa_family != AF_INET6 ||
  505                     var->gv_pdst->sa_family != AF_INET6)
  506                         goto out;
  507                 ret = gif_encapcheck6(m, off, proto, var);
  508                 break;
  509 #endif
  510         default:
  511                 goto out;
  512         }
  513 
  514 out:
  515         gif_putref_variant(var, &psref);
  516         return ret;
  517 }
  518 #endif
  519 
  520 /*
  521  * gif may cause infinite recursion calls when misconfigured.
  522  * We'll prevent this by introducing upper limit.
  523  */
  524 static int
  525 gif_check_nesting(struct ifnet *ifp, struct mbuf *m)
  526 {
  527 
  528         return if_tunnel_check_nesting(ifp, m, max_gif_nesting);
  529 }
  530 
  531 static int
  532 gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
  533     const struct rtentry *rt)
  534 {
  535         struct gif_softc *sc = ifp->if_softc;
  536         struct gif_variant *var = NULL;
  537         struct psref psref;
  538         int error = 0;
  539 
  540         IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
  541 
  542         if ((error = gif_check_nesting(ifp, m)) != 0) {
  543                 m_freem(m);
  544                 goto end;
  545         }
  546 
  547         if ((ifp->if_flags & IFF_UP) == 0) {
  548                 m_freem(m);
  549                 error = ENETDOWN;
  550                 goto end;
  551         }
  552 
  553         var = gif_getref_variant(sc, &psref);
  554         if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
  555                 m_freem(m);
  556                 error = ENETDOWN;
  557                 goto end;
  558         }
  559         /* XXX should we check if our outer source is legal? */
  560 
  561         m->m_flags &= ~(M_BCAST | M_MCAST);
  562 
  563         /* use DLT_NULL encapsulation here to pass inner af type */
  564         M_PREPEND(m, sizeof(int), M_DONTWAIT);
  565         if (!m) {
  566                 error = ENOBUFS;
  567                 goto end;
  568         }
  569         *mtod(m, int *) = dst->sa_family;
  570 
  571         /* Clear checksum-offload flags. */
  572         m->m_pkthdr.csum_flags = 0;
  573         m->m_pkthdr.csum_data = 0;
  574 
  575         error = if_transmit_lock(ifp, m);
  576 
  577 end:
  578         if (var != NULL)
  579                 gif_putref_variant(var, &psref);
  580         if (error)
  581                 if_statinc(ifp, if_oerrors);
  582         return error;
  583 }
  584 
  585 static void
  586 gif_start(struct ifnet *ifp)
  587 {
  588         struct gif_softc *sc;
  589         struct gif_variant *var;
  590         struct mbuf *m;
  591         struct psref psref;
  592         int family;
  593         int len;
  594         int error;
  595 
  596         sc = ifp->if_softc;
  597         var = gif_getref_variant(sc, &psref);
  598 
  599         KASSERT(var->gv_output != NULL);
  600 
  601         /* output processing */
  602         while (1) {
  603                 IFQ_DEQUEUE(&sc->gif_if.if_snd, m);
  604                 if (m == NULL)
  605                         break;
  606 
  607                 /* grab and chop off inner af type */
  608                 if (sizeof(int) > m->m_len) {
  609                         m = m_pullup(m, sizeof(int));
  610                         if (!m) {
  611                                 if_statinc(ifp, if_oerrors);
  612                                 continue;
  613                         }
  614                 }
  615                 family = *mtod(m, int *);
  616                 bpf_mtap(ifp, m, BPF_D_OUT);
  617                 m_adj(m, sizeof(int));
  618 
  619                 len = m->m_pkthdr.len;
  620 
  621                 error = var->gv_output(var, family, m);
  622                 if (error)
  623                         if_statinc(ifp, if_oerrors);
  624                 else
  625                         if_statadd2(ifp, if_opackets, 1, if_obytes, len);
  626         }
  627 
  628         gif_putref_variant(var, &psref);
  629 }
  630 
  631 static int
  632 gif_transmit(struct ifnet *ifp, struct mbuf *m)
  633 {
  634         struct gif_softc *sc;
  635         struct gif_variant *var;
  636         struct psref psref;
  637         int error;
  638 
  639         sc = ifp->if_softc;
  640 
  641         /* output processing */
  642         if (m == NULL)
  643                 return EINVAL;
  644 
  645         var = gif_getref_variant(sc, &psref);
  646         error = gif_transmit_direct(var, m);
  647         gif_putref_variant(var, &psref);
  648 
  649         return error;
  650 }
  651 
  652 static int
  653 gif_transmit_direct(struct gif_variant *var, struct mbuf *m)
  654 {
  655         struct ifnet *ifp = &var->gv_softc->gif_if;
  656         int error;
  657         int family;
  658         int len;
  659 
  660         KASSERT(gif_heldref_variant(var));
  661         KASSERT(var->gv_output != NULL);
  662 
  663         /* grab and chop off inner af type */
  664         if (sizeof(int) > m->m_len) {
  665                 m = m_pullup(m, sizeof(int));
  666                 if (!m) {
  667                         if_statinc(ifp, if_oerrors);
  668                         return ENOBUFS;
  669                 }
  670         }
  671         family = *mtod(m, int *);
  672         bpf_mtap(ifp, m, BPF_D_OUT);
  673         m_adj(m, sizeof(int));
  674 
  675         len = m->m_pkthdr.len;
  676 
  677         error = var->gv_output(var, family, m);
  678         if (error)
  679                 if_statinc(ifp, if_oerrors);
  680         else
  681                 if_statadd2(ifp, if_opackets, 1, if_obytes, len);
  682 
  683         return error;
  684 }
  685 
  686 void
  687 gif_input(struct mbuf *m, int af, struct ifnet *ifp)
  688 {
  689         pktqueue_t *pktq;
  690         size_t pktlen;
  691 
  692         if (ifp == NULL) {
  693                 /* just in case */
  694                 m_freem(m);
  695                 return;
  696         }
  697 
  698         m_set_rcvif(m, ifp);
  699         pktlen = m->m_pkthdr.len;
  700 
  701         bpf_mtap_af(ifp, af, m, BPF_D_IN);
  702 
  703         /*
  704          * Put the packet to the network layer input queue according to the
  705          * specified address family.  Note: we avoid direct call to the
  706          * input function of the network layer in order to avoid recursion.
  707          * This may be revisited in the future.
  708          */
  709         switch (af) {
  710 #ifdef INET
  711         case AF_INET:
  712                 pktq = ip_pktq;
  713                 break;
  714 #endif
  715 #ifdef INET6
  716         case AF_INET6:
  717                 pktq = ip6_pktq;
  718                 break;
  719 #endif
  720         default:
  721                 m_freem(m);
  722                 return;
  723         }
  724 
  725         const uint32_t h = pktq_rps_hash(&gif_pktq_rps_hash_p, m);
  726         if (__predict_true(pktq_enqueue(pktq, m, h))) {
  727                 if_statadd2(ifp, if_ibytes, pktlen, if_ipackets, 1);
  728         } else {
  729                 m_freem(m);
  730         }
  731 }
  732 
  733 /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
  734 static int
  735 gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
  736 {
  737         struct gif_softc *sc  = ifp->if_softc;
  738         struct ifreq     *ifr = (struct ifreq*)data;
  739         struct ifaddr    *ifa = (struct ifaddr*)data;
  740         int error = 0, size, bound;
  741         struct sockaddr *dst, *src;
  742         struct gif_variant *var;
  743         struct psref psref;
  744 
  745         switch (cmd) {
  746         case SIOCINITIFADDR:
  747                 ifp->if_flags |= IFF_UP;
  748                 ifa->ifa_rtrequest = p2p_rtrequest;
  749                 break;
  750 
  751         case SIOCADDMULTI:
  752         case SIOCDELMULTI:
  753                 switch (ifr->ifr_addr.sa_family) {
  754 #ifdef INET
  755                 case AF_INET:   /* IP supports Multicast */
  756                         break;
  757 #endif /* INET */
  758 #ifdef INET6
  759                 case AF_INET6:  /* IP6 supports Multicast */
  760                         break;
  761 #endif /* INET6 */
  762                 default:  /* Other protocols doesn't support Multicast */
  763                         error = EAFNOSUPPORT;
  764                         break;
  765                 }
  766                 break;
  767 
  768         case SIOCSIFMTU:
  769                 if (ifr->ifr_mtu < GIF_MTU_MIN || ifr->ifr_mtu > GIF_MTU_MAX)
  770                         return EINVAL;
  771                 else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
  772                         error = 0;
  773                 break;
  774 
  775 #ifdef INET
  776         case SIOCSIFPHYADDR:
  777 #endif
  778 #ifdef INET6
  779         case SIOCSIFPHYADDR_IN6:
  780 #endif /* INET6 */
  781         case SIOCSLIFPHYADDR:
  782                 switch (cmd) {
  783 #ifdef INET
  784                 case SIOCSIFPHYADDR:
  785                         src = (struct sockaddr *)
  786                                 &(((struct in_aliasreq *)data)->ifra_addr);
  787                         dst = (struct sockaddr *)
  788                                 &(((struct in_aliasreq *)data)->ifra_dstaddr);
  789                         break;
  790 #endif
  791 #ifdef INET6
  792                 case SIOCSIFPHYADDR_IN6:
  793                         src = (struct sockaddr *)
  794                                 &(((struct in6_aliasreq *)data)->ifra_addr);
  795                         dst = (struct sockaddr *)
  796                                 &(((struct in6_aliasreq *)data)->ifra_dstaddr);
  797                         break;
  798 #endif
  799                 case SIOCSLIFPHYADDR:
  800                         src = (struct sockaddr *)
  801                                 &(((struct if_laddrreq *)data)->addr);
  802                         dst = (struct sockaddr *)
  803                                 &(((struct if_laddrreq *)data)->dstaddr);
  804                         break;
  805                 default:
  806                         return EINVAL;
  807                 }
  808 
  809                 /* sa_family must be equal */
  810                 if (src->sa_family != dst->sa_family)
  811                         return EINVAL;
  812 
  813                 /* validate sa_len */
  814                 switch (src->sa_family) {
  815 #ifdef INET
  816                 case AF_INET:
  817                         if (src->sa_len != sizeof(struct sockaddr_in))
  818                                 return EINVAL;
  819                         break;
  820 #endif
  821 #ifdef INET6
  822                 case AF_INET6:
  823                         if (src->sa_len != sizeof(struct sockaddr_in6))
  824                                 return EINVAL;
  825                         break;
  826 #endif
  827                 default:
  828                         return EAFNOSUPPORT;
  829                 }
  830                 switch (dst->sa_family) {
  831 #ifdef INET
  832                 case AF_INET:
  833                         if (dst->sa_len != sizeof(struct sockaddr_in))
  834                                 return EINVAL;
  835                         break;
  836 #endif
  837 #ifdef INET6
  838                 case AF_INET6:
  839                         if (dst->sa_len != sizeof(struct sockaddr_in6))
  840                                 return EINVAL;
  841                         break;
  842 #endif
  843                 default:
  844                         return EAFNOSUPPORT;
  845                 }
  846 
  847                 /* check sa_family looks sane for the cmd */
  848                 switch (cmd) {
  849                 case SIOCSIFPHYADDR:
  850                         if (src->sa_family == AF_INET)
  851                                 break;
  852                         return EAFNOSUPPORT;
  853 #ifdef INET6
  854                 case SIOCSIFPHYADDR_IN6:
  855                         if (src->sa_family == AF_INET6)
  856                                 break;
  857                         return EAFNOSUPPORT;
  858 #endif /* INET6 */
  859                 case SIOCSLIFPHYADDR:
  860                         /* checks done in the above */
  861                         break;
  862                 }
  863 
  864                 /*
  865                  * calls gif_getref_variant() for other softcs to check
  866                  * address pair duplicattion
  867                  */
  868                 bound = curlwp_bind();
  869                 error = gif_set_tunnel(&sc->gif_if, src, dst);
  870                 if (error == 0)
  871                         if_link_state_change(&sc->gif_if, LINK_STATE_UP);
  872                 curlwp_bindx(bound);
  873 
  874                 break;
  875 
  876 #ifdef SIOCDIFPHYADDR
  877         case SIOCDIFPHYADDR:
  878                 bound = curlwp_bind();
  879                 gif_delete_tunnel(&sc->gif_if);
  880                 if_link_state_change(&sc->gif_if, LINK_STATE_DOWN);
  881                 curlwp_bindx(bound);
  882                 break;
  883 #endif
  884 
  885         case SIOCGIFPSRCADDR:
  886 #ifdef INET6
  887         case SIOCGIFPSRCADDR_IN6:
  888 #endif /* INET6 */
  889                 bound = curlwp_bind();
  890                 var = gif_getref_variant(sc, &psref);
  891                 if (var->gv_psrc == NULL) {
  892                         gif_putref_variant(var, &psref);
  893                         curlwp_bindx(bound);
  894                         error = EADDRNOTAVAIL;
  895                         goto bad;
  896                 }
  897                 src = var->gv_psrc;
  898                 switch (cmd) {
  899 #ifdef INET
  900                 case SIOCGIFPSRCADDR:
  901                         dst = &ifr->ifr_addr;
  902                         size = sizeof(ifr->ifr_addr);
  903                         break;
  904 #endif /* INET */
  905 #ifdef INET6
  906                 case SIOCGIFPSRCADDR_IN6:
  907                         dst = (struct sockaddr *)
  908                                 &(((struct in6_ifreq *)data)->ifr_addr);
  909                         size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
  910                         break;
  911 #endif /* INET6 */
  912                 default:
  913                         gif_putref_variant(var, &psref);
  914                         curlwp_bindx(bound);
  915                         error = EADDRNOTAVAIL;
  916                         goto bad;
  917                 }
  918                 if (src->sa_len > size) {
  919                         gif_putref_variant(var, &psref);
  920                         curlwp_bindx(bound);
  921                         return EINVAL;
  922                 }
  923                 memcpy(dst, src, src->sa_len);
  924                 gif_putref_variant(var, &psref);
  925                 curlwp_bindx(bound);
  926                 break;
  927 
  928         case SIOCGIFPDSTADDR:
  929 #ifdef INET6
  930         case SIOCGIFPDSTADDR_IN6:
  931 #endif /* INET6 */
  932                 bound = curlwp_bind();
  933                 var = gif_getref_variant(sc, &psref);
  934                 if (var->gv_pdst == NULL) {
  935                         gif_putref_variant(var, &psref);
  936                         curlwp_bindx(bound);
  937                         error = EADDRNOTAVAIL;
  938                         goto bad;
  939                 }
  940                 src = var->gv_pdst;
  941                 switch (cmd) {
  942 #ifdef INET
  943                 case SIOCGIFPDSTADDR:
  944                         dst = &ifr->ifr_addr;
  945                         size = sizeof(ifr->ifr_addr);
  946                         break;
  947 #endif /* INET */
  948 #ifdef INET6
  949                 case SIOCGIFPDSTADDR_IN6:
  950                         dst = (struct sockaddr *)
  951                                 &(((struct in6_ifreq *)data)->ifr_addr);
  952                         size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
  953                         break;
  954 #endif /* INET6 */
  955                 default:
  956                         gif_putref_variant(var, &psref);
  957                         curlwp_bindx(bound);
  958                         error = EADDRNOTAVAIL;
  959                         goto bad;
  960                 }
  961                 if (src->sa_len > size) {
  962                         gif_putref_variant(var, &psref);
  963                         curlwp_bindx(bound);
  964                         return EINVAL;
  965                 }
  966                 memcpy(dst, src, src->sa_len);
  967                 gif_putref_variant(var, &psref);
  968                 curlwp_bindx(bound);
  969                 break;
  970 
  971         case SIOCGLIFPHYADDR:
  972                 bound = curlwp_bind();
  973                 var = gif_getref_variant(sc, &psref);
  974                 if (var->gv_psrc == NULL || var->gv_pdst == NULL) {
  975                         gif_putref_variant(var, &psref);
  976                         curlwp_bindx(bound);
  977                         error = EADDRNOTAVAIL;
  978                         goto bad;
  979                 }
  980 
  981                 /* copy src */
  982                 src = var->gv_psrc;
  983                 dst = (struct sockaddr *)
  984                         &(((struct if_laddrreq *)data)->addr);
  985                 size = sizeof(((struct if_laddrreq *)data)->addr);
  986                 if (src->sa_len > size) {
  987                         gif_putref_variant(var, &psref);
  988                         curlwp_bindx(bound);
  989                         return EINVAL;
  990                 }
  991                 memcpy(dst, src, src->sa_len);
  992 
  993                 /* copy dst */
  994                 src = var->gv_pdst;
  995                 dst = (struct sockaddr *)
  996                         &(((struct if_laddrreq *)data)->dstaddr);
  997                 size = sizeof(((struct if_laddrreq *)data)->dstaddr);
  998                 if (src->sa_len > size) {
  999                         gif_putref_variant(var, &psref);
 1000                         curlwp_bindx(bound);
 1001                         return EINVAL;
 1002                 }
 1003                 memcpy(dst, src, src->sa_len);
 1004                 gif_putref_variant(var, &psref);
 1005                 curlwp_bindx(bound);
 1006                 break;
 1007 
 1008         default:
 1009                 return ifioctl_common(ifp, cmd, data);
 1010         }
 1011  bad:
 1012         return error;
 1013 }
 1014 
 1015 static int
 1016 gif_encap_attach(struct gif_variant *var)
 1017 {
 1018         int error;
 1019 
 1020         if (var == NULL || var->gv_psrc == NULL)
 1021                 return EINVAL;
 1022 
 1023         switch (var->gv_psrc->sa_family) {
 1024 #ifdef INET
 1025         case AF_INET:
 1026                 error = in_gif_attach(var);
 1027                 break;
 1028 #endif
 1029 #ifdef INET6
 1030         case AF_INET6:
 1031                 error = in6_gif_attach(var);
 1032                 break;
 1033 #endif
 1034         default:
 1035                 error = EINVAL;
 1036                 break;
 1037         }
 1038 
 1039         return error;
 1040 }
 1041 
 1042 static int
 1043 gif_encap_detach(struct gif_variant *var)
 1044 {
 1045         int error;
 1046 
 1047         if (var == NULL || var->gv_psrc == NULL)
 1048                 return EINVAL;
 1049 
 1050         switch (var->gv_psrc->sa_family) {
 1051 #ifdef INET
 1052         case AF_INET:
 1053                 error = in_gif_detach(var);
 1054                 break;
 1055 #endif
 1056 #ifdef INET6
 1057         case AF_INET6:
 1058                 error = in6_gif_detach(var);
 1059                 break;
 1060 #endif
 1061         default:
 1062                 error = EINVAL;
 1063                 break;
 1064         }
 1065 
 1066         return error;
 1067 }
 1068 
 1069 static int
 1070 gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
 1071 {
 1072         struct gif_softc *sc = ifp->if_softc;
 1073         struct gif_softc *sc2;
 1074         struct gif_variant *ovar, *nvar;
 1075         struct sockaddr *osrc, *odst;
 1076         struct sockaddr *nsrc, *ndst;
 1077         int error;
 1078 #ifndef GIF_MPSAFE
 1079         int s;
 1080 
 1081         s = splsoftnet();
 1082 #endif
 1083         error = encap_lock_enter();
 1084         if (error) {
 1085 #ifndef GIF_MPSAFE
 1086                 splx(s);
 1087 #endif
 1088                 return error;
 1089         }
 1090 
 1091         nsrc = sockaddr_dup(src, M_WAITOK);
 1092         ndst = sockaddr_dup(dst, M_WAITOK);
 1093         nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
 1094 
 1095         mutex_enter(&sc->gif_lock);
 1096 
 1097         ovar = sc->gif_var;
 1098 
 1099         if ((ovar->gv_pdst && sockaddr_cmp(ovar->gv_pdst, dst) == 0) &&
 1100             (ovar->gv_psrc && sockaddr_cmp(ovar->gv_psrc, src) == 0)) {
 1101                 /* address and port pair not changed. */
 1102                 error = 0;
 1103                 goto out;
 1104         }
 1105 
 1106         mutex_enter(&gif_softcs.lock);
 1107         LIST_FOREACH(sc2, &gif_softcs.list, gif_list) {
 1108                 struct gif_variant *var2;
 1109                 struct psref psref;
 1110 
 1111                 if (sc2 == sc)
 1112                         continue;
 1113                 var2 = gif_getref_variant(sc2, &psref);
 1114                 if (!var2->gv_pdst || !var2->gv_psrc) {
 1115                         gif_putref_variant(var2, &psref);
 1116                         continue;
 1117                 }
 1118                 /* can't configure same pair of address onto two gifs */
 1119                 if (sockaddr_cmp(var2->gv_pdst, dst) == 0 &&
 1120                     sockaddr_cmp(var2->gv_psrc, src) == 0) {
 1121                         /* continue to use the old configuration. */
 1122                         gif_putref_variant(var2, &psref);
 1123                         mutex_exit(&gif_softcs.lock);
 1124                         error =  EADDRNOTAVAIL;
 1125                         goto out;
 1126                 }
 1127                 gif_putref_variant(var2, &psref);
 1128                 /* XXX both end must be valid? (I mean, not 0.0.0.0) */
 1129         }
 1130         mutex_exit(&gif_softcs.lock);
 1131 
 1132         osrc = ovar->gv_psrc;
 1133         odst = ovar->gv_pdst;
 1134 
 1135         *nvar = *ovar;
 1136         nvar->gv_psrc = nsrc;
 1137         nvar->gv_pdst = ndst;
 1138         nvar->gv_encap_cookie4 = NULL;
 1139         nvar->gv_encap_cookie6 = NULL;
 1140         error = gif_encap_attach(nvar);
 1141         if (error)
 1142                 goto out;
 1143         psref_target_init(&nvar->gv_psref, gv_psref_class);
 1144         gif_update_variant(sc, nvar);
 1145 
 1146         mutex_exit(&sc->gif_lock);
 1147 
 1148         (void)gif_encap_detach(ovar);
 1149         encap_lock_exit();
 1150 
 1151         if (osrc)
 1152                 sockaddr_free(osrc);
 1153         if (odst)
 1154                 sockaddr_free(odst);
 1155         kmem_free(ovar, sizeof(*ovar));
 1156 
 1157 #ifndef GIF_MPSAFE
 1158         splx(s);
 1159 #endif
 1160         return 0;
 1161 
 1162  out:
 1163         sockaddr_free(nsrc);
 1164         sockaddr_free(ndst);
 1165         kmem_free(nvar, sizeof(*nvar));
 1166 
 1167         mutex_exit(&sc->gif_lock);
 1168         encap_lock_exit();
 1169 #ifndef GIF_MPSAFE
 1170         splx(s);
 1171 #endif
 1172         return error;
 1173 }
 1174 
 1175 static void
 1176 gif_delete_tunnel(struct ifnet *ifp)
 1177 {
 1178         struct gif_softc *sc = ifp->if_softc;
 1179         struct gif_variant *ovar, *nvar;
 1180         struct sockaddr *osrc, *odst;
 1181         int error;
 1182 #ifndef GIF_MPSAFE
 1183         int s;
 1184 
 1185         s = splsoftnet();
 1186 #endif
 1187         error = encap_lock_enter();
 1188         if (error) {
 1189 #ifndef GIF_MPSAFE
 1190                 splx(s);
 1191 #endif
 1192                 return;
 1193         }
 1194 
 1195         nvar = kmem_alloc(sizeof(*nvar), KM_SLEEP);
 1196 
 1197         mutex_enter(&sc->gif_lock);
 1198 
 1199         ovar = sc->gif_var;
 1200         osrc = ovar->gv_psrc;
 1201         odst = ovar->gv_pdst;
 1202         if (osrc == NULL || odst == NULL) {
 1203                 /* address pair not changed. */
 1204                 mutex_exit(&sc->gif_lock);
 1205                 encap_lock_exit();
 1206                 kmem_free(nvar, sizeof(*nvar));
 1207 #ifndef GIF_MPSAFE
 1208                 splx(s);
 1209 #endif
 1210                 return;
 1211         }
 1212 
 1213         *nvar = *ovar;
 1214         nvar->gv_psrc = NULL;
 1215         nvar->gv_pdst = NULL;
 1216         nvar->gv_encap_cookie4 = NULL;
 1217         nvar->gv_encap_cookie6 = NULL;
 1218         nvar->gv_output = NULL;
 1219         psref_target_init(&nvar->gv_psref, gv_psref_class);
 1220         gif_update_variant(sc, nvar);
 1221 
 1222         mutex_exit(&sc->gif_lock);
 1223 
 1224         gif_encap_detach(ovar);
 1225         encap_lock_exit();
 1226 
 1227         sockaddr_free(osrc);
 1228         sockaddr_free(odst);
 1229         kmem_free(ovar, sizeof(*ovar));
 1230 
 1231 #ifndef GIF_MPSAFE
 1232         splx(s);
 1233 #endif
 1234 }
 1235 
 1236 /*
 1237  * gif_variant update API.
 1238  *
 1239  * Assumption:
 1240  * reader side dereferences sc->gif_var in reader critical section only,
 1241  * that is, all of reader sides do not reader the sc->gif_var after
 1242  * pserialize_perform().
 1243  */
 1244 static void
 1245 gif_update_variant(struct gif_softc *sc, struct gif_variant *nvar)
 1246 {
 1247         struct ifnet *ifp = &sc->gif_if;
 1248         struct gif_variant *ovar = sc->gif_var;
 1249 
 1250         KASSERT(mutex_owned(&sc->gif_lock));
 1251 
 1252         atomic_store_release(&sc->gif_var, nvar);
 1253         pserialize_perform(sc->gif_psz);
 1254         psref_target_destroy(&ovar->gv_psref, gv_psref_class);
 1255 
 1256         if (nvar->gv_psrc != NULL && nvar->gv_pdst != NULL)
 1257                 ifp->if_flags |= IFF_RUNNING;
 1258         else
 1259                 ifp->if_flags &= ~IFF_RUNNING;
 1260 }
 1261 
 1262 /*
 1263  * Module infrastructure
 1264  */
 1265 #include "if_module.h"
 1266 
 1267 IF_MODULE(MODULE_CLASS_DRIVER, gif, "ip_ecn")

Cache object: ddd886b1f78e21887e26e8c5f8f01192


[ 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.