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/netiso/esis.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: esis.c,v 1.52 2008/05/11 20:20:27 dyoung Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 1991, 1993
    5  *      The Regents of the University of California.  All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. Neither the name of the University nor the names of its contributors
   16  *    may be used to endorse or promote products derived from this software
   17  *    without specific prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  *
   31  *      @(#)esis.c      8.3 (Berkeley) 3/20/95
   32  */
   33 
   34 /***********************************************************
   35                 Copyright IBM Corporation 1987
   36 
   37                       All Rights Reserved
   38 
   39 Permission to use, copy, modify, and distribute this software and its
   40 documentation for any purpose and without fee is hereby granted,
   41 provided that the above copyright notice appear in all copies and that
   42 both that copyright notice and this permission notice appear in
   43 supporting documentation, and that the name of IBM not be
   44 used in advertising or publicity pertaining to distribution of the
   45 software without specific, written prior permission.
   46 
   47 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
   48 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
   49 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
   50 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
   51 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
   52 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
   53 SOFTWARE.
   54 
   55 ******************************************************************/
   56 
   57 /*
   58  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
   59  */
   60 
   61 #include <sys/cdefs.h>
   62 __KERNEL_RCSID(0, "$NetBSD: esis.c,v 1.52 2008/05/11 20:20:27 dyoung Exp $");
   63 
   64 #include "opt_iso.h"
   65 #ifdef ISO
   66 
   67 #include <sys/param.h>
   68 #include <sys/systm.h>
   69 #include <sys/callout.h>
   70 #include <sys/mbuf.h>
   71 #include <sys/domain.h>
   72 #include <sys/protosw.h>
   73 #include <sys/socket.h>
   74 #include <sys/socketvar.h>
   75 #include <sys/errno.h>
   76 #include <sys/kernel.h>
   77 #include <sys/proc.h>
   78 #include <sys/kauth.h>
   79 
   80 #include <net/if.h>
   81 #include <net/if_dl.h>
   82 #include <net/route.h>
   83 #include <net/raw_cb.h>
   84 
   85 #include <netiso/iso.h>
   86 #include <netiso/iso_pcb.h>
   87 #include <netiso/iso_var.h>
   88 #include <netiso/iso_snpac.h>
   89 #include <netiso/clnl.h>
   90 #include <netiso/clnp.h>
   91 #include <netiso/clnp_stat.h>
   92 #include <netiso/esis.h>
   93 #include <netiso/argo_debug.h>
   94 
   95 #include <machine/stdarg.h>
   96 
   97 /*
   98  *      Global variables to esis implementation
   99  *
  100  *      esis_holding_time - the holding time (sec) parameter for outgoing pdus
  101  *      esis_config_time  - the frequency (sec) that hellos are generated
  102  *      esis_esconfig_time - suggested es configuration time placed in the ish.
  103  *
  104  */
  105 LIST_HEAD(, rawcb) esis_pcb;
  106 struct esis_stat esis_stat;
  107 int             esis_sendspace = 2048;
  108 int             esis_recvspace = 2048;
  109 short           esis_holding_time = ESIS_HT;
  110 short           esis_config_time = ESIS_CONFIG;
  111 short           esis_esconfig_time = ESIS_CONFIG;
  112 struct sockaddr_dl esis_dl = {
  113         .sdl_len = sizeof(esis_dl), 
  114         .sdl_family = AF_LINK,
  115 };
  116 
  117 struct callout  esis_config_ch;
  118 
  119 #define EXTEND_PACKET(m, mhdr, cp)\
  120         if (((m)->m_next = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {\
  121                 esis_stat.es_nomem++;\
  122                 m_freem(mhdr);\
  123                 return;\
  124         } else {\
  125                 (m) = (m)->m_next;\
  126                 (cp) = mtod((m), void *);\
  127                 (m)->m_len = 0;\
  128         }
  129 
  130 /*
  131  * FUNCTION:            esis_init
  132  *
  133  * PURPOSE:             Initialize the kernel portion of esis protocol
  134  *
  135  * RETURNS:             nothing
  136  *
  137  * SIDE EFFECTS:
  138  *
  139  * NOTES:
  140  */
  141 void
  142 esis_init(void)
  143 {
  144         extern struct clnl_protosw clnl_protox[256];
  145 
  146         LIST_INIT(&esis_pcb);
  147 
  148         callout_init(&snpac_age_ch, 0);
  149         callout_init(&esis_config_ch, 0);
  150 
  151         callout_reset(&snpac_age_ch, hz, snpac_age, NULL);
  152         callout_reset(&esis_config_ch, hz, esis_config, NULL);
  153 
  154         clnl_protox[ISO9542_ESIS].clnl_input = esis_input;
  155         clnl_protox[ISO10589_ISIS].clnl_input = isis_input;
  156 #ifdef  ISO_X25ESIS
  157         clnl_protox[ISO9542X25_ESIS].clnl_input = x25esis_input;
  158 #endif                          /* ISO_X25ESIS */
  159 }
  160 
  161 /*
  162  * FUNCTION:            esis_usrreq
  163  *
  164  * PURPOSE:             Handle user level esis requests
  165  *
  166  * RETURNS:             0 or appropriate errno
  167  *
  168  * SIDE EFFECTS:
  169  *
  170  */
  171 /* ARGSUSED */
  172 int
  173 esis_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
  174         struct mbuf *control, struct lwp *l)
  175 {
  176         struct rawcb *rp;
  177         int error = 0;
  178 
  179         if (req == PRU_CONTROL)
  180                 return (EOPNOTSUPP);
  181 
  182         rp = sotorawcb(so);
  183 #ifdef DIAGNOSTIC
  184         if (req != PRU_SEND && req != PRU_SENDOOB && control)
  185                 panic("esis_usrreq: unexpected control mbuf");
  186 #endif
  187         if (rp == 0 && req != PRU_ATTACH) {
  188                 error = EINVAL;
  189                 goto release;
  190         }
  191 
  192         switch (req) {
  193 
  194         case PRU_ATTACH:
  195                 sosetlock(so);
  196                 if (rp != 0) {
  197                         error = EISCONN;
  198                         break;
  199                 }
  200 
  201                 if (l == NULL) {
  202                         error = EACCES;
  203                         break;
  204                 }
  205 
  206                 /* XXX: raw socket permission is checked in socreate() */
  207 
  208                 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
  209                         error = soreserve(so, esis_sendspace, esis_recvspace);
  210                         if (error)
  211                                 break;
  212                 }
  213                 MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK|M_ZERO);
  214                 if (rp == 0) {
  215                         error = ENOBUFS;
  216                         break;
  217                 }
  218                 rp->rcb_socket = so;
  219                 LIST_INSERT_HEAD(&esis_pcb, rp, rcb_list);
  220                 so->so_pcb = rp;
  221                 break;
  222 
  223         case PRU_SEND:
  224                 if (control && control->m_len) {
  225                         m_freem(control);
  226                         m_freem(m);
  227                         error = EINVAL;
  228                         break;
  229                 }
  230                 if (nam == NULL) {
  231                         m_freem(m);
  232                         error = EINVAL;
  233                         break;
  234                 }
  235                 /* error checking here */
  236                 error = isis_output(m, mtod(nam, struct sockaddr_dl *));
  237                 break;
  238 
  239         case PRU_SENDOOB:
  240                 m_freem(control);
  241                 m_freem(m);
  242                 error = EOPNOTSUPP;
  243                 break;
  244 
  245         case PRU_DETACH:
  246                 raw_detach(rp);
  247                 break;
  248 
  249         case PRU_SHUTDOWN:
  250                 socantsendmore(so);
  251                 break;
  252 
  253         case PRU_SENSE:
  254                 /*
  255                  * stat: don't bother with a blocksize.
  256                  */
  257                 return (0);
  258 
  259         default:
  260                 error = EOPNOTSUPP;
  261                 break;
  262         }
  263 
  264 release:
  265         return (error);
  266 }
  267 
  268 /*
  269  * FUNCTION:            esis_input
  270  *
  271  * PURPOSE:             Process an incoming esis packet
  272  *
  273  * RETURNS:             nothing
  274  *
  275  * SIDE EFFECTS:
  276  *
  277  * NOTES:
  278  */
  279 void
  280 esis_input(struct mbuf *m0, ...)
  281 {
  282         struct snpa_hdr *shp;   /* subnetwork header */
  283         struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
  284         int    type;
  285         struct ifaddr *ifa;
  286         va_list ap;
  287 
  288         va_start(ap, m0);
  289         shp = va_arg(ap, struct snpa_hdr *);
  290         va_end(ap);
  291 
  292         IFADDR_FOREACH(ifa, shp->snh_ifp)
  293                 if (ifa->ifa_addr->sa_family == AF_ISO)
  294                         break;
  295         /* if we have no iso address just send it to the sockets */
  296         if (ifa == 0)
  297                 goto bad;
  298 
  299         /*
  300          *      check checksum if necessary
  301          */
  302         if (ESIS_CKSUM_REQUIRED(pdu) &&
  303             iso_check_csum(m0, (int) pdu->esis_hdr_len)) {
  304                 esis_stat.es_badcsum++;
  305                 goto bad;
  306         }
  307         /* check version */
  308         if (pdu->esis_vers != ESIS_VERSION) {
  309                 esis_stat.es_badvers++;
  310                 goto bad;
  311         }
  312         type = pdu->esis_type & 0x1f;
  313         switch (type) {
  314         case ESIS_ESH:
  315                 esis_eshinput(m0, shp);
  316                 break;
  317 
  318         case ESIS_ISH:
  319                 esis_ishinput(m0, shp);
  320                 break;
  321 
  322         case ESIS_RD:
  323                 esis_rdinput(m0, shp);
  324                 break;
  325 
  326         default:
  327                 esis_stat.es_badtype++;
  328         }
  329 
  330 bad:
  331         if (esis_pcb.lh_first != 0)
  332                 isis_input(m0, shp);
  333         else
  334                 m_freem(m0);
  335 }
  336 
  337 /*
  338  * FUNCTION:            esis_rdoutput
  339  *
  340  * PURPOSE:             Transmit a redirect pdu
  341  *
  342  * RETURNS:             nothing
  343  *
  344  * SIDE EFFECTS:
  345  *
  346  * NOTES:               Assumes there is enough space for fixed part of header,
  347  *                      DA, BSNPA and NET in first mbuf.
  348  */
  349 void
  350 esis_rdoutput(
  351         struct snpa_hdr *inbound_shp,   /* snpa hdr from incoming packet */
  352         struct mbuf    *inbound_m,      /* incoming pkt itself */
  353         struct clnp_optidx *inbound_oidx,       /* clnp options assoc with
  354                                                  * incoming pkt */
  355         struct iso_addr *rd_dstnsap,    /* ultimate destination of pkt */
  356         struct rtentry *rt)     /* snpa cache info regarding next hop of pkt */
  357 {
  358         struct mbuf    *m, *m0;
  359         char *cp;
  360         struct esis_fixed *pdu;
  361         int             len;
  362         struct sockaddr_iso siso;
  363         struct ifnet   *ifp = inbound_shp->snh_ifp;
  364         struct sockaddr_dl *sdl;
  365         const struct iso_addr *rd_gwnsap;
  366 
  367         if (rt->rt_flags & RTF_GATEWAY) {
  368                 rd_gwnsap = &satosiso(rt->rt_gateway)->siso_addr;
  369                 rt = rtalloc1(rt->rt_gateway, 0);
  370         } else
  371                 rd_gwnsap = &satocsiso(rt_getkey(rt))->siso_addr;
  372         if (rt == 0 || (sdl = (struct sockaddr_dl *) rt->rt_gateway) == 0 ||
  373             sdl->sdl_family != AF_LINK) {
  374                 /*
  375                  * maybe we should have a function that you could put in the
  376                  * iso_ifaddr structure which could translate iso_addrs into
  377                  * snpa's where there is a known mapping for that address
  378                  * type
  379                  */
  380                 esis_stat.es_badtype++;
  381                 return;
  382         }
  383         esis_stat.es_rdsent++;
  384 #ifdef ARGO_DEBUG
  385         if (argo_debug[D_ESISOUTPUT]) {
  386                 printf(
  387                     "esis_rdoutput: ifp %p (%s), ht %d, m %p, oidx %p\n",
  388                     ifp, ifp->if_xname, esis_holding_time,
  389                     inbound_m, inbound_oidx);
  390                 printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap));
  391                 printf("\tredirected toward:%s\n", clnp_iso_addrp(rd_gwnsap));
  392         }
  393 #endif
  394 
  395         if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
  396                 esis_stat.es_nomem++;
  397                 return;
  398         }
  399         bzero(mtod(m, void *), MHLEN);
  400 
  401         pdu = mtod(m, struct esis_fixed *);
  402         cp = (void *) (pdu + 1);        /* pointer arith.; 1st byte after
  403                                          * header */
  404         len = sizeof(struct esis_fixed);
  405 
  406         /*
  407          *      Build fixed part of header
  408          */
  409         pdu->esis_proto_id = ISO9542_ESIS;
  410         pdu->esis_vers = ESIS_VERSION;
  411         pdu->esis_type = ESIS_RD;
  412         HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, esis_holding_time);
  413 
  414         /* Insert destination address */
  415         (void) esis_insert_addr((void **)&cp, &len, rd_dstnsap, m, 0);
  416 
  417         /* Insert the snpa of better next hop */
  418         *cp++ = sdl->sdl_alen;
  419         bcopy(CLLADDR(sdl), cp, sdl->sdl_alen);
  420         cp += sdl->sdl_alen;
  421         len += (sdl->sdl_alen + 1);
  422 
  423         /*
  424          * If the next hop is not the destination, then it ought to be an IS
  425          * and it should be inserted next. Else, set the NETL to 0
  426          */
  427         /* PHASE2 use mask from ifp of outgoing interface */
  428         if (!iso_addrmatch1(rd_dstnsap, rd_gwnsap)) {
  429 #if 0
  430                 /* this should not happen: */
  431                 if ((nhop_sc->sc_flags & SNPA_IS) == 0) {
  432                         printf(
  433                     "esis_rdoutput: next hop is not dst and not an IS\n");
  434                         m_freem(m0);
  435                         return;
  436                 }
  437 #endif
  438                 (void) esis_insert_addr((void **)&cp, &len, rd_gwnsap, m, 0);
  439         } else {
  440                 *cp++ = 0;      /* NETL */
  441                 len++;
  442         }
  443         m->m_len = len;
  444 
  445         /*
  446          * PHASE2
  447          * If redirect is to an IS, add an address mask. The mask to be
  448          * used should be the mask present in the routing entry used to
  449          * forward the original data packet.
  450          */
  451 
  452         /*
  453          * Copy Qos, priority, or security options present in original npdu
  454          */
  455         if (inbound_oidx) {
  456                 /* THIS CODE IS CURRENTLY (mostly) UNTESTED */
  457                 int             optlen = 0;
  458                 if (inbound_oidx->cni_qos_formatp)
  459                         optlen += (inbound_oidx->cni_qos_len + 2);
  460                 if (inbound_oidx->cni_priorp)   /* priority option is 1 byte
  461                                                  * long */
  462                         optlen += 3;
  463                 if (inbound_oidx->cni_securep)
  464                         optlen += (inbound_oidx->cni_secure_len + 2);
  465                 if (M_TRAILINGSPACE(m) < optlen) {
  466                         EXTEND_PACKET(m, m0, cp);
  467                         m->m_len = 0;
  468                         /* assumes MLEN > optlen */
  469                 }
  470                 /* assume MLEN-len > optlen */
  471                 /*
  472                  * When copying options, copy from ptr - 2 in order to grab
  473                  * the option code and length
  474                  */
  475                 if (inbound_oidx->cni_qos_formatp) {
  476                         memcpy(cp, mtod(inbound_m, char *) +
  477                                 inbound_oidx->cni_qos_formatp - 2,
  478                               (unsigned) (inbound_oidx->cni_qos_len + 2));
  479                         cp += inbound_oidx->cni_qos_len + 2;
  480                 }
  481                 if (inbound_oidx->cni_priorp) {
  482                         memcpy(cp, mtod(inbound_m, char *) +
  483                                 inbound_oidx->cni_priorp - 2, 3);
  484                         cp += 3;
  485                 }
  486                 if (inbound_oidx->cni_securep) {
  487                         memcpy(cp, mtod(inbound_m, char *) +
  488                                 inbound_oidx->cni_securep - 2,
  489                               (unsigned) (inbound_oidx->cni_secure_len + 2));
  490                         cp += inbound_oidx->cni_secure_len + 2;
  491                 }
  492                 m->m_len += optlen;
  493                 len += optlen;
  494         }
  495         pdu->esis_hdr_len = m0->m_pkthdr.len = len;
  496         iso_gen_csum(m0, ESIS_CKSUM_OFF, (int) pdu->esis_hdr_len);
  497 
  498         bzero((void *) & siso, sizeof(siso));
  499         siso.siso_family = AF_ISO;
  500         siso.siso_data[0] = AFI_SNA;
  501         siso.siso_nlen = 6 + 1; /* should be taken from snpa_hdr */
  502         /* +1 is for AFI */
  503         bcopy(inbound_shp->snh_shost, siso.siso_data + 1, 6);
  504         (ifp->if_output) (ifp, m0, sisotosa(&siso), 0);
  505 }
  506 
  507 /*
  508  * FUNCTION:            esis_insert_addr
  509  *
  510  * PURPOSE:             Insert an iso_addr into a buffer
  511  *
  512  * RETURNS:             true if buffer was big enough, else false
  513  *
  514  * SIDE EFFECTS:        Increment buf & len according to size of iso_addr
  515  *
  516  * NOTES:               Plus 1 here is for length byte
  517  */
  518 int
  519 esis_insert_addr(
  520         void **bufv,            /* ptr to buffer to put address into */
  521         int     *len,           /* ptr to length of buffer so far */
  522         const struct iso_addr *isoa,    /* ptr to address */
  523         struct mbuf *m,         /* determine if there remains space */
  524         int     nsellen)
  525 {
  526         char *buf = *bufv;
  527         int    newlen, result = 0;
  528 
  529         newlen = isoa->isoa_len - nsellen + 1;
  530         if (newlen <= M_TRAILINGSPACE(m)) {
  531                 memcpy(buf, isoa, newlen);
  532                 *len += newlen;
  533                 buf += newlen;
  534                 m->m_len += newlen;
  535                 result = 1;
  536         }
  537         *bufv = buf;
  538         return (result);
  539 }
  540 
  541 #define ESIS_EXTRACT_ADDR(d, b) { d = (struct iso_addr *)(b); b += (1 + *b); \
  542             if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
  543 #define ESIS_NEXT_OPTION(b)     { b += (2 + b[1]); \
  544             if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
  545 int             ESHonly = 0;
  546 
  547 /*
  548  * FUNCTION:            esis_eshinput
  549  *
  550  * PURPOSE:             Process an incoming ESH pdu
  551  *
  552  * RETURNS:             nothing
  553  *
  554  * SIDE EFFECTS:
  555  *
  556  * NOTES:
  557  */
  558 void
  559 esis_eshinput(
  560         struct mbuf    *m,      /* esh pdu */
  561         struct snpa_hdr *shp)   /* subnetwork header */
  562 {
  563         struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
  564         u_short         ht;     /* holding time */
  565         struct iso_addr *nsap = NULL;
  566         int             naddr;
  567         u_char         *buf = (u_char *) (pdu + 1);
  568         u_char         *buflim = pdu->esis_hdr_len + (u_char *) pdu;
  569         int             new_entry = 0;
  570 
  571         esis_stat.es_eshrcvd++;
  572 
  573         CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
  574 
  575         naddr = *buf++;
  576         if (buf >= buflim)
  577                 goto bad;
  578         if (naddr == 1) {
  579                 ESIS_EXTRACT_ADDR(nsap, buf);
  580                 new_entry = snpac_add(shp->snh_ifp,
  581                                       nsap, shp->snh_shost, SNPA_ES, ht, 0);
  582         } else {
  583                 int             nsellength = 0, nlen = 0;
  584                 struct ifaddr *ifa;
  585                 /*
  586                  * See if we want to compress out multiple nsaps
  587                  * differing only by nsel
  588                  */
  589                 IFADDR_FOREACH(ifa, shp->snh_ifp)
  590                         if (ifa->ifa_addr->sa_family == AF_ISO) {
  591                                 nsellength =
  592                                 ((struct iso_ifaddr *) ifa)->ia_addr.siso_tlen;
  593                                 break;
  594                         }
  595 #ifdef ARGO_DEBUG
  596                 if (argo_debug[D_ESISINPUT]) {
  597                         printf(
  598                         "esis_eshinput: esh: ht %d, naddr %d nsellength %d\n",
  599                                ht, naddr, nsellength);
  600                 }
  601 #endif
  602                 while (naddr-- > 0) {
  603                         struct iso_addr *nsap2;
  604                         u_char         *buf2;
  605                         ESIS_EXTRACT_ADDR(nsap, buf);
  606                         /*
  607                          * see if there is at least one more nsap in ESH
  608                          * differing only by nsel
  609                          */
  610                         if (nsellength != 0)
  611                                 for (buf2 = buf; buf2 < buflim;) {
  612                                         ESIS_EXTRACT_ADDR(nsap2, buf2);
  613 #ifdef ARGO_DEBUG
  614                                         if (argo_debug[D_ESISINPUT]) {
  615                                                 printf(
  616                                                 "esis_eshinput: comparing %s ",
  617                                                        clnp_iso_addrp(nsap));
  618                                                 printf("and %s\n",
  619                                                        clnp_iso_addrp(nsap2));
  620                                         }
  621 #endif
  622                                         if (memcmp(nsap->isoa_genaddr,
  623                                                    nsap2->isoa_genaddr,
  624                                                    nsap->isoa_len - nsellength)
  625                                              == 0) {
  626                                                 nlen = nsellength;
  627                                                 break;
  628                                         }
  629                                 }
  630                         new_entry |= snpac_add(shp->snh_ifp,
  631                                    nsap, shp->snh_shost, SNPA_ES, ht, nlen);
  632                         nlen = 0;
  633                 }
  634         }
  635 #ifdef ARGO_DEBUG
  636         if (argo_debug[D_ESISINPUT]) {
  637                 printf("esis_eshinput: nsap %s is %s\n",
  638                        clnp_iso_addrp(nsap), new_entry ? "new" : "old");
  639         }
  640 #endif
  641         if (new_entry && (iso_systype & SNPA_IS))
  642                 esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time,
  643                               shp->snh_shost, 6, (struct iso_addr *) 0);
  644 bad:
  645         return;
  646 }
  647 
  648 /*
  649  * FUNCTION:            esis_ishinput
  650  *
  651  * PURPOSE:             process an incoming ISH pdu
  652  *
  653  * RETURNS:
  654  *
  655  * SIDE EFFECTS:
  656  *
  657  * NOTES:
  658  */
  659 void
  660 esis_ishinput(
  661         struct mbuf    *m,      /* esh pdu */
  662         struct snpa_hdr *shp)   /* subnetwork header */
  663 {
  664         struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
  665         u_short         ht, newct;      /* holding time */
  666         struct iso_addr *nsap;  /* Network Entity Title */
  667         u_char *buf = (u_char *) (pdu + 1);
  668         u_char *buflim = pdu->esis_hdr_len + (u_char *) pdu;
  669         int             new_entry;
  670 
  671         esis_stat.es_ishrcvd++;
  672         CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
  673 
  674 #ifdef ARGO_DEBUG
  675         if (argo_debug[D_ESISINPUT]) {
  676                 printf("esis_ishinput: ish: ht %d\n", ht);
  677         }
  678 #endif
  679         if (ESHonly)
  680                 goto bad;
  681 
  682         ESIS_EXTRACT_ADDR(nsap, buf);
  683 
  684         while (buf < buflim) {
  685                 switch (*buf) {
  686                 case ESISOVAL_ESCT:
  687                         if (iso_systype & SNPA_IS)
  688                                 break;
  689                         if (buf[1] != 2)
  690                                 goto bad;
  691                         CTOH(buf[2], buf[3], newct);
  692                         if ((u_short) esis_config_time != newct) {
  693                                 callout_stop(&esis_config_ch);
  694                                 esis_config_time = newct;
  695                                 esis_config(NULL);
  696                         }
  697                         break;
  698 
  699                 default:
  700                         printf("Unknown ISH option: %x\n", *buf);
  701                 }
  702                 ESIS_NEXT_OPTION(buf);
  703         }
  704         new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS,
  705                               ht, 0);
  706 #ifdef ARGO_DEBUG
  707         if (argo_debug[D_ESISINPUT]) {
  708                 printf("esis_ishinput: nsap %s is %s\n",
  709                    clnp_iso_addrp(nsap), new_entry ? "new" : "old");
  710         }
  711 #endif
  712 
  713         if (new_entry)
  714                 esis_shoutput(shp->snh_ifp,
  715                               iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
  716                 esis_holding_time, shp->snh_shost, 6, (struct iso_addr *) 0);
  717 bad:
  718         return;
  719 }
  720 
  721 /*
  722  * FUNCTION:            esis_rdinput
  723  *
  724  * PURPOSE:             Process an incoming RD pdu
  725  *
  726  * RETURNS:
  727  *
  728  * SIDE EFFECTS:
  729  *
  730  * NOTES:
  731  */
  732 void
  733 esis_rdinput(
  734         struct mbuf    *m0,     /* esh pdu */
  735         struct snpa_hdr *shp)   /* subnetwork header */
  736 {
  737         struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
  738         u_short         ht;     /* holding time */
  739         struct iso_addr *da, *net = 0, *netmask = 0, *snpamask = 0;
  740         struct iso_addr *bsnpa;
  741         u_char *buf = (u_char *) (pdu + 1);
  742         u_char *buflim = pdu->esis_hdr_len + (u_char *) pdu;
  743 
  744         esis_stat.es_rdrcvd++;
  745 
  746         /* intermediate systems ignore redirects */
  747         if (iso_systype & SNPA_IS)
  748                 return;
  749         if (ESHonly)
  750                 return;
  751 
  752         CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
  753         if (buf >= buflim)
  754                 return;
  755 
  756         /* Extract DA */
  757         ESIS_EXTRACT_ADDR(da, buf);
  758 
  759         /* Extract better snpa */
  760         ESIS_EXTRACT_ADDR(bsnpa, buf);
  761 
  762         /* Extract NET if present */
  763         if (buf < buflim) {
  764                 if (*buf == 0)
  765                         buf++;  /* no NET present, skip NETL anyway */
  766                 else
  767                         ESIS_EXTRACT_ADDR(net, buf);
  768         }
  769         /* process options */
  770         while (buf < buflim) {
  771                 switch (*buf) {
  772                 case ESISOVAL_SNPAMASK:
  773                         if (snpamask)   /* duplicate */
  774                                 return;
  775                         snpamask = (struct iso_addr *) (buf + 1);
  776                         break;
  777 
  778                 case ESISOVAL_NETMASK:
  779                         if (netmask)    /* duplicate */
  780                                 return;
  781                         netmask = (struct iso_addr *) (buf + 1);
  782                         break;
  783 
  784                 default:
  785                         printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
  786                 }
  787                 ESIS_NEXT_OPTION(buf);
  788         }
  789 
  790 #ifdef ARGO_DEBUG
  791         if (argo_debug[D_ESISINPUT]) {
  792                 printf("esis_rdinput: rd: ht %d, da %s\n", ht,
  793                     clnp_iso_addrp(da));
  794                 if (net)
  795                         printf("\t: net %s\n", clnp_iso_addrp(net));
  796         }
  797 #endif
  798         /*
  799          * If netl is zero, then redirect is to an ES. We need to add an entry
  800          * to the snpa cache for (destination, better snpa).
  801          * If netl is not zero, then the redirect is to an IS. In this
  802          * case, add an snpa cache entry for (net, better snpa).
  803          *
  804          * If the redirect is to an IS, add a route entry towards that
  805          * IS.
  806          */
  807         if (net == 0 || net->isoa_len == 0 || snpamask) {
  808                 /* redirect to an ES */
  809                 snpac_add(shp->snh_ifp, da,
  810                           bsnpa->isoa_genaddr, SNPA_ES, ht, 0);
  811         } else {
  812                 snpac_add(shp->snh_ifp, net,
  813                           bsnpa->isoa_genaddr, SNPA_IS, ht, 0);
  814                 snpac_addrt(shp->snh_ifp, da, net, netmask);
  815         }
  816 bad:    ;       /* Needed by ESIS_NEXT_OPTION */
  817 }
  818 
  819 /*
  820  * FUNCTION:            esis_config
  821  *
  822  * PURPOSE:             Report configuration
  823  *
  824  * RETURNS:
  825  *
  826  * SIDE EFFECTS:
  827  *
  828  * NOTES:               Called every esis_config_time seconds
  829  */
  830 /*ARGSUSED*/
  831 void
  832 esis_config(void *v)
  833 {
  834         struct ifnet *ifp;
  835 
  836         callout_reset(&esis_config_ch, hz * esis_config_time,
  837             esis_config, NULL);
  838 
  839         /*
  840          * Report configuration for each interface that - is UP - has
  841          * BROADCAST capability - has an ISO address
  842          */
  843         /*
  844          * Todo: a better way would be to construct the esh or ish once and
  845          * copy it out for all devices, possibly calling a method in the
  846          * iso_ifaddr structure to encapsulate and transmit it.  This could
  847          * work to advantage for non-broadcast media
  848          */
  849 
  850         TAILQ_FOREACH(ifp, &ifnet, if_list) {
  851                 if ((ifp->if_flags & IFF_UP) &&
  852                     (ifp->if_flags & IFF_BROADCAST)) {
  853                         /* search for an ISO address family */
  854                         struct ifaddr  *ifa;
  855 
  856                         IFADDR_FOREACH(ifa, ifp) {
  857                                 if (ifa->ifa_addr->sa_family == AF_ISO) {
  858                                         esis_shoutput(ifp,
  859                               iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
  860                               esis_holding_time,
  861                               (iso_systype & SNPA_ES ? all_is_snpa :
  862                                      all_es_snpa), 6, (struct iso_addr *) 0);
  863                                         break;
  864                                 }
  865                         }
  866                 }
  867         }
  868 }
  869 
  870 /*
  871  * FUNCTION:            esis_shoutput
  872  *
  873  * PURPOSE:             Transmit an esh or ish pdu
  874  *
  875  * RETURNS:             nothing
  876  *
  877  * SIDE EFFECTS:
  878  *
  879  * NOTES:
  880  */
  881 void
  882 esis_shoutput(
  883         struct ifnet   *ifp,
  884         int             type,
  885         int             ht,
  886         const void      *sn_addr,
  887         int             sn_len,
  888         struct iso_addr *isoa)
  889 {
  890         struct mbuf    *m, *m0;
  891         char *cp, *naddrp;
  892         int             naddr = 0;
  893         struct esis_fixed *pdu;
  894         struct iso_ifaddr *ia;
  895         int             len;
  896         struct sockaddr_iso siso;
  897 
  898         if (type == ESIS_ESH)
  899                 esis_stat.es_eshsent++;
  900         else if (type == ESIS_ISH)
  901                 esis_stat.es_ishsent++;
  902         else {
  903                 printf("esis_shoutput: bad pdu type\n");
  904                 return;
  905         }
  906 
  907 #ifdef ARGO_DEBUG
  908         if (argo_debug[D_ESISOUTPUT]) {
  909                 int             i;
  910                 printf("esis_shoutput: ifp %p (%s), %s, ht %d, to: [%d] ",
  911                     ifp, ifp->if_xname,
  912                     type == ESIS_ESH ? "esh" : "ish",
  913                     ht, sn_len);
  914                 for (i = 0; i < sn_len; i++)
  915                         printf("%x%c", *((const char *)sn_addr + i),
  916                             i < (sn_len - 1) ? ':' : ' ');
  917                 printf("\n");
  918         }
  919 #endif
  920 
  921         if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
  922                 esis_stat.es_nomem++;
  923                 return;
  924         }
  925         bzero(mtod(m, void *), MHLEN);
  926 
  927         pdu = mtod(m, struct esis_fixed *);
  928         naddrp = cp = (char *) (pdu + 1);
  929         len = sizeof(struct esis_fixed);
  930 
  931         /*
  932          *      Build fixed part of header
  933          */
  934         pdu->esis_proto_id = ISO9542_ESIS;
  935         pdu->esis_vers = ESIS_VERSION;
  936         pdu->esis_type = type;
  937         HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
  938 
  939         if (type == ESIS_ESH) {
  940                 cp++;
  941                 len++;
  942         }
  943         m->m_len = len;
  944         if (isoa) {
  945                 /*
  946                  * Here we are responding to a clnp packet sent to an NSAP
  947                  * that is ours which was sent to the MAC addr all_es's.
  948                  * It is possible that we did not specifically advertise this
  949                  * NSAP, even though it is ours, so we will respond
  950                  * directly to the sender that we are here.  If we do have
  951                  * multiple NSEL's we'll tack them on so he can compress
  952                  * them out.
  953                  */
  954                 (void) esis_insert_addr((void **)&cp, &len, isoa, m, 0);
  955                 naddr = 1;
  956         }
  957         TAILQ_FOREACH(ia, &iso_ifaddr, ia_list) {
  958                 int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
  959                 int n = ia->ia_addr.siso_nlen;
  960                 struct iso_ifaddr *ia2;
  961 
  962                 if (type == ESIS_ISH && naddr > 0)
  963                         break;
  964                 TAILQ_FOREACH(ia2, &iso_ifaddr, ia_list)
  965                         if (memcmp(ia->ia_addr.siso_data,
  966                                    ia2->ia_addr.siso_data, n) == 0)
  967                                 break;
  968                 if (ia2 != ia)
  969                         continue;       /* Means we have previously copied
  970                                          * this nsap */
  971                 if (isoa && memcmp(ia->ia_addr.siso_data,
  972                                    isoa->isoa_genaddr, n) == 0) {
  973                         isoa = 0;
  974                         continue;       /* Ditto */
  975                 }
  976 #ifdef ARGO_DEBUG
  977                 if (argo_debug[D_ESISOUTPUT]) {
  978                         printf("esis_shoutput: adding NSAP %s\n",
  979                             clnp_iso_addrp(&ia->ia_addr.siso_addr));
  980                 }
  981 #endif
  982                 if (!esis_insert_addr((void **)&cp, &len,
  983                                       &ia->ia_addr.siso_addr, m, nsellen)) {
  984                         EXTEND_PACKET(m, m0, cp);
  985                         (void) esis_insert_addr((void **)&cp, &len,
  986                                                 &ia->ia_addr.siso_addr, m,
  987                                                 nsellen);
  988                 }
  989                 naddr++;
  990         }
  991 
  992         if (type == ESIS_ESH)
  993                 *naddrp = naddr;
  994         else {
  995                 /* add suggested es config timer option to ISH */
  996                 if (M_TRAILINGSPACE(m) < 4) {
  997                         printf("esis_shoutput: extending packet\n");
  998                         EXTEND_PACKET(m, m0, cp);
  999                 }
 1000                 *cp++ = ESISOVAL_ESCT;
 1001                 *cp++ = 2;
 1002                 HTOC(*cp, *(cp + 1), esis_esconfig_time);
 1003                 len += 4;
 1004                 m->m_len += 4;
 1005 #ifdef ARGO_DEBUG
 1006                 if (argo_debug[D_ESISOUTPUT]) {
 1007                         printf("m0 %p, m %p, data %p, len %d, cp %p\n",
 1008                             m0, m, m->m_data, m->m_len, cp);
 1009                 }
 1010 #endif
 1011         }
 1012 
 1013         m0->m_pkthdr.len = len;
 1014         pdu->esis_hdr_len = len;
 1015         iso_gen_csum(m0, ESIS_CKSUM_OFF, (int) pdu->esis_hdr_len);
 1016 
 1017         bzero((void *) & siso, sizeof(siso));
 1018         siso.siso_family = AF_ISO;
 1019         siso.siso_data[0] = AFI_SNA;
 1020         siso.siso_nlen = sn_len + 1;
 1021         bcopy(sn_addr, siso.siso_data + 1, (unsigned) sn_len);
 1022         (ifp->if_output) (ifp, m0, sisotosa(&siso), 0);
 1023 }
 1024 
 1025 /*
 1026  * FUNCTION:            isis_input
 1027  *
 1028  * PURPOSE:             Process an incoming isis packet
 1029  *
 1030  * RETURNS:             nothing
 1031  *
 1032  * SIDE EFFECTS:
 1033  *
 1034  * NOTES:
 1035  */
 1036 void
 1037 isis_input(struct mbuf *m0, ...)
 1038 {
 1039         struct snpa_hdr *shp;   /* subnetwork header */
 1040         struct rawcb *rp, *first_rp = 0;
 1041         struct ifnet   *ifp;
 1042         struct mbuf    *mm;
 1043         va_list ap;
 1044 
 1045         va_start(ap, m0);
 1046         shp = va_arg(ap, struct snpa_hdr *);
 1047         va_end(ap);
 1048         ifp = shp->snh_ifp;
 1049 
 1050 #ifdef ARGO_DEBUG
 1051         if (argo_debug[D_ISISINPUT]) {
 1052                 int             i;
 1053 
 1054                 printf("isis_input: pkt on ifp %p (%s): from:",
 1055                     ifp, ifp->if_xname);
 1056                 for (i = 0; i < 6; i++)
 1057                         printf("%x%c", shp->snh_shost[i] & 0xff,
 1058                             (i < 5) ? ':' : ' ');
 1059                 printf(" to:");
 1060                 for (i = 0; i < 6; i++)
 1061                         printf("%x%c", shp->snh_dhost[i] & 0xff,
 1062                             (i < 5) ? ':' : ' ');
 1063                 printf("\n");
 1064         }
 1065 #endif
 1066         esis_dl.sdl_alen = ifp->if_addrlen;
 1067         esis_dl.sdl_index = ifp->if_index;
 1068         bcopy(shp->snh_shost, (void *) esis_dl.sdl_data, esis_dl.sdl_alen);
 1069         for (rp = esis_pcb.lh_first; rp != 0; rp = rp->rcb_list.le_next) {
 1070                 if (first_rp == 0) {
 1071                         first_rp = rp;
 1072                         continue;
 1073                 }
 1074                 /* can't block at interrupt level */
 1075                 if ((mm = m_copy(m0, 0, M_COPYALL)) != NULL) {
 1076                         if (sbappendaddr(&rp->rcb_socket->so_rcv,
 1077                                          (struct sockaddr *) &esis_dl, mm,
 1078                                          (struct mbuf *) 0) != 0) {
 1079                                 sorwakeup(rp->rcb_socket);
 1080                         } else {
 1081 #ifdef ARGO_DEBUG
 1082                                 if (argo_debug[D_ISISINPUT]) {
 1083                                         printf(
 1084                                     "Error in sbappenaddr, mm = %p\n", mm);
 1085                                 }
 1086 #endif
 1087                                 m_freem(mm);
 1088                         }
 1089                 }
 1090         }
 1091         if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv,
 1092                (struct sockaddr *) &esis_dl, m0, (struct mbuf *) 0) != 0) {
 1093                 sorwakeup(first_rp->rcb_socket);
 1094                 return;
 1095         }
 1096         m_freem(m0);
 1097 }
 1098 
 1099 int
 1100 isis_output(struct mbuf *m, ...)
 1101 {
 1102         struct sockaddr_dl *sdl;
 1103         struct ifnet *ifp;
 1104         struct ifaddr  *ifa;
 1105         struct sockaddr_iso siso;
 1106         int             error = 0;
 1107         unsigned        sn_len;
 1108         va_list ap;
 1109 
 1110         va_start(ap, m);
 1111         sdl = va_arg(ap, struct sockaddr_dl *);
 1112         va_end(ap);
 1113 
 1114         /* we assume here we have a sockaddr_dl ... check it */
 1115         if (sdl->sdl_family != AF_LINK) {
 1116                 error = EINVAL;
 1117                 goto release;
 1118         }
 1119         if (sdl->sdl_len < 8 + sdl->sdl_nlen + sdl->sdl_alen + sdl->sdl_slen) {
 1120                 error = EINVAL;
 1121                 goto release;
 1122         }
 1123 
 1124         ifa = ifa_ifwithnet((struct sockaddr *) sdl);   /* get ifp from sdl */
 1125         if (ifa == 0) {
 1126 #ifdef ARGO_DEBUG
 1127                 if (argo_debug[D_ISISOUTPUT]) {
 1128                         printf("isis_output: interface not found\n");
 1129                 }
 1130 #endif
 1131                 error = EINVAL;
 1132                 goto release;
 1133         }
 1134         ifp = ifa->ifa_ifp;
 1135         sn_len = sdl->sdl_alen;
 1136 #ifdef ARGO_DEBUG
 1137         if (argo_debug[D_ISISOUTPUT]) {
 1138                 const u_char *cp = (const u_char *)CLLADDR(sdl),
 1139                              *cplim = cp + sn_len;
 1140                 printf("isis_output: ifp %p (%s), to: ",
 1141                     ifp, ifp->if_xname);
 1142                 while (cp < cplim) {
 1143                         printf("%x", *cp++);
 1144                         printf("%c", (cp < cplim) ? ':' : ' ');
 1145                 }
 1146                 printf("\n");
 1147         }
 1148 #endif
 1149         bzero((void *) & siso, sizeof(siso));
 1150         siso.siso_family = AF_ISO;      /* This convention may be useful for
 1151                                          * X.25 */
 1152         if (sn_len == 0)
 1153                 siso.siso_nlen = 0;
 1154         else {
 1155                 siso.siso_data[0] = AFI_SNA;
 1156                 siso.siso_nlen = sn_len + 1;
 1157                 bcopy(CLLADDR(sdl), siso.siso_data + 1, sn_len);
 1158         }
 1159         error = (ifp->if_output) (ifp, m, sisotosa(&siso), 0);
 1160         if (error) {
 1161 #ifdef ARGO_DEBUG
 1162                 if (argo_debug[D_ISISOUTPUT]) {
 1163                         printf("isis_output: error from if_output is %d\n",
 1164                             error);
 1165                 }
 1166 #endif
 1167         }
 1168         return (error);
 1169 
 1170 release:
 1171         if (m != NULL)
 1172                 m_freem(m);
 1173         return (error);
 1174 }
 1175 
 1176 
 1177 /*
 1178  * FUNCTION:            esis_ctlinput
 1179  *
 1180  * PURPOSE:             Handle the PRC_IFDOWN transition
 1181  *
 1182  * RETURNS:             nothing
 1183  *
 1184  * SIDE EFFECTS:
 1185  *
 1186  * NOTES:               Calls snpac_flush for interface specified.
 1187  *                      The loop through iso_ifaddr is stupid because
 1188  *                      back in if_down, we knew the ifp...
 1189  */
 1190 void *
 1191 esis_ctlinput(
 1192     int    req,                 /* request: we handle only PRC_IFDOWN */
 1193     const struct sockaddr *siso,        /* address of ifp */
 1194     void *dummy)
 1195 {
 1196         struct iso_ifaddr *ia;  /* scan through interface addresses */
 1197 
 1198         /*XXX correct? */
 1199         if (siso->sa_family != AF_ISO)
 1200                 return NULL;
 1201         if (req == PRC_IFDOWN)
 1202                 TAILQ_FOREACH(ia, &iso_ifaddr, ia_list) {
 1203                         if (iso_addrmatch(IA_SIS(ia),
 1204                                           (const struct sockaddr_iso *)siso))
 1205                                 snpac_flushifp(ia->ia_ifp);
 1206                 }
 1207         return NULL;
 1208 }
 1209 
 1210 #endif /* ISO */

Cache object: e02a20941e610b2b32f4cc2253676818


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