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/netgraph/ng_base.c

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

    1 
    2 /*
    3  * ng_base.c
    4  *
    5  * Copyright (c) 1996-1999 Whistle Communications, Inc.
    6  * All rights reserved.
    7  * 
    8  * Subject to the following obligations and disclaimer of warranty, use and
    9  * redistribution of this software, in source or object code forms, with or
   10  * without modifications are expressly permitted by Whistle Communications;
   11  * provided, however, that:
   12  * 1. Any and all reproductions of the source or object code must include the
   13  *    copyright notice above and the following disclaimer of warranties; and
   14  * 2. No rights are granted, in any manner or form, to use Whistle
   15  *    Communications, Inc. trademarks, including the mark "WHISTLE
   16  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
   17  *    such appears in the above copyright notice or in the software.
   18  * 
   19  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
   20  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
   21  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
   22  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
   23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
   24  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
   25  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
   26  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
   27  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
   28  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
   29  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   30  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
   31  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
   32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   34  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
   35  * OF SUCH DAMAGE.
   36  *
   37  * Authors: Julian Elischer <julian@freebsd.org>
   38  *          Archie Cobbs <archie@freebsd.org>
   39  *
   40  * $FreeBSD$
   41  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
   42  */
   43 
   44 /*
   45  * This file implements the base netgraph code.
   46  */
   47 
   48 #include <sys/param.h>
   49 #include <sys/systm.h>
   50 #include <sys/errno.h>
   51 #include <sys/kernel.h>
   52 #include <sys/malloc.h>
   53 #include <sys/syslog.h>
   54 #include <sys/linker.h>
   55 #include <sys/queue.h>
   56 #include <sys/mbuf.h>
   57 #include <sys/ctype.h>
   58 #include <machine/limits.h>
   59 
   60 #include <net/netisr.h>
   61 
   62 #include <netgraph/ng_message.h>
   63 #include <netgraph/netgraph.h>
   64 #include <netgraph/ng_parse.h>
   65 
   66 /* List of all nodes */
   67 static LIST_HEAD(, ng_node) nodelist;
   68 
   69 /* List of installed types */
   70 static LIST_HEAD(, ng_type) typelist;
   71 
   72 /* Hash releted definitions */
   73 #define ID_HASH_SIZE 32 /* most systems wont need even this many */
   74 static LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE];
   75 /* Don't nead to initialise them because it's a LIST */
   76 
   77 /* Internal functions */
   78 static int      ng_add_hook(node_p node, const char *name, hook_p * hookp);
   79 static int      ng_connect(hook_p hook1, hook_p hook2);
   80 static void     ng_disconnect_hook(hook_p hook);
   81 static int      ng_generic_msg(node_p here, struct ng_mesg *msg,
   82                         const char *retaddr, struct ng_mesg ** resp);
   83 static ng_ID_t  ng_decodeidname(const char *name);
   84 static int      ngb_mod_event(module_t mod, int event, void *data);
   85 static void     ngintr(void);
   86 
   87 /* Our own netgraph malloc type */
   88 MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
   89 
   90 /* Set this to Debugger("X") to catch all errors as they occur */
   91 #ifndef TRAP_ERROR
   92 #define TRAP_ERROR
   93 #endif
   94 
   95 static  ng_ID_t nextID = 1;
   96 
   97 #ifdef INVARIANTS
   98 #define CHECK_DATA_MBUF(m)      do {                                    \
   99                 struct mbuf *n;                                         \
  100                 int total;                                              \
  101                                                                         \
  102                 if (((m)->m_flags & M_PKTHDR) == 0)                     \
  103                         panic("%s: !PKTHDR", __FUNCTION__);             \
  104                 for (total = 0, n = (m); n != NULL; n = n->m_next)      \
  105                         total += n->m_len;                              \
  106                 if ((m)->m_pkthdr.len != total) {                       \
  107                         panic("%s: %d != %d",                           \
  108                             __FUNCTION__, (m)->m_pkthdr.len, total);    \
  109                 }                                                       \
  110         } while (0)
  111 #else
  112 #define CHECK_DATA_MBUF(m)
  113 #endif
  114 
  115 
  116 /************************************************************************
  117         Parse type definitions for generic messages
  118 ************************************************************************/
  119 
  120 /* Handy structure parse type defining macro */
  121 #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)                          \
  122 static const struct ng_parse_struct_info                                \
  123         ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args;      \
  124 static const struct ng_parse_type ng_generic_ ## lo ## _type = {        \
  125         &ng_parse_struct_type,                                          \
  126         &ng_ ## lo ## _type_info                                        \
  127 }
  128 
  129 DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
  130 DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
  131 DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
  132 DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
  133 DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
  134 DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
  135 DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
  136 
  137 /* Get length of an array when the length is stored as a 32 bit
  138    value immediately preceeding the array -- as with struct namelist
  139    and struct typelist. */
  140 static int
  141 ng_generic_list_getLength(const struct ng_parse_type *type,
  142         const u_char *start, const u_char *buf)
  143 {
  144         return *((const u_int32_t *)(buf - 4));
  145 }
  146 
  147 /* Get length of the array of struct linkinfo inside a struct hooklist */
  148 static int
  149 ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
  150         const u_char *start, const u_char *buf)
  151 {
  152         const struct hooklist *hl = (const struct hooklist *)start;
  153 
  154         return hl->nodeinfo.hooks;
  155 }
  156 
  157 /* Array type for a variable length array of struct namelist */
  158 static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
  159         &ng_generic_nodeinfo_type,
  160         &ng_generic_list_getLength
  161 };
  162 static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
  163         &ng_parse_array_type,
  164         &ng_nodeinfoarray_type_info
  165 };
  166 
  167 /* Array type for a variable length array of struct typelist */
  168 static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
  169         &ng_generic_typeinfo_type,
  170         &ng_generic_list_getLength
  171 };
  172 static const struct ng_parse_type ng_generic_typeinfoarray_type = {
  173         &ng_parse_array_type,
  174         &ng_typeinfoarray_type_info
  175 };
  176 
  177 /* Array type for array of struct linkinfo in struct hooklist */
  178 static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
  179         &ng_generic_linkinfo_type,
  180         &ng_generic_linkinfo_getLength
  181 };
  182 static const struct ng_parse_type ng_generic_linkinfo_array_type = {
  183         &ng_parse_array_type,
  184         &ng_generic_linkinfo_array_type_info
  185 };
  186 
  187 DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
  188 DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
  189         (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
  190 DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
  191         (&ng_generic_nodeinfoarray_type));
  192 
  193 /* List of commands and how to convert arguments to/from ASCII */
  194 static const struct ng_cmdlist ng_generic_cmds[] = {
  195         {
  196           NGM_GENERIC_COOKIE,
  197           NGM_SHUTDOWN,
  198           "shutdown",
  199           NULL,
  200           NULL
  201         },
  202         {
  203           NGM_GENERIC_COOKIE,
  204           NGM_MKPEER,
  205           "mkpeer",
  206           &ng_generic_mkpeer_type,
  207           NULL
  208         },
  209         {
  210           NGM_GENERIC_COOKIE,
  211           NGM_CONNECT,
  212           "connect",
  213           &ng_generic_connect_type,
  214           NULL
  215         },
  216         {
  217           NGM_GENERIC_COOKIE,
  218           NGM_NAME,
  219           "name",
  220           &ng_generic_name_type,
  221           NULL
  222         },
  223         {
  224           NGM_GENERIC_COOKIE,
  225           NGM_RMHOOK,
  226           "rmhook",
  227           &ng_generic_rmhook_type,
  228           NULL
  229         },
  230         {
  231           NGM_GENERIC_COOKIE,
  232           NGM_NODEINFO,
  233           "nodeinfo",
  234           NULL,
  235           &ng_generic_nodeinfo_type
  236         },
  237         {
  238           NGM_GENERIC_COOKIE,
  239           NGM_LISTHOOKS,
  240           "listhooks",
  241           NULL,
  242           &ng_generic_hooklist_type
  243         },
  244         {
  245           NGM_GENERIC_COOKIE,
  246           NGM_LISTNAMES,
  247           "listnames",
  248           NULL,
  249           &ng_generic_listnodes_type    /* same as NGM_LISTNODES */
  250         },
  251         {
  252           NGM_GENERIC_COOKIE,
  253           NGM_LISTNODES,
  254           "listnodes",
  255           NULL,
  256           &ng_generic_listnodes_type
  257         },
  258         {
  259           NGM_GENERIC_COOKIE,
  260           NGM_LISTTYPES,
  261           "listtypes",
  262           NULL,
  263           &ng_generic_typeinfo_type
  264         },
  265         {
  266           NGM_GENERIC_COOKIE,
  267           NGM_TEXT_STATUS,
  268           "textstatus",
  269           NULL,
  270           &ng_parse_string_type
  271         },
  272         {
  273           NGM_GENERIC_COOKIE,
  274           NGM_ASCII2BINARY,
  275           "ascii2binary",
  276           &ng_parse_ng_mesg_type,
  277           &ng_parse_ng_mesg_type
  278         },
  279         {
  280           NGM_GENERIC_COOKIE,
  281           NGM_BINARY2ASCII,
  282           "binary2ascii",
  283           &ng_parse_ng_mesg_type,
  284           &ng_parse_ng_mesg_type
  285         },
  286         { 0 }
  287 };
  288 
  289 /************************************************************************
  290                         Node routines
  291 ************************************************************************/
  292 
  293 /*
  294  * Instantiate a node of the requested type
  295  */
  296 int
  297 ng_make_node(const char *typename, node_p *nodepp)
  298 {
  299         struct ng_type *type;
  300 
  301         /* Check that the type makes sense */
  302         if (typename == NULL) {
  303                 TRAP_ERROR;
  304                 return (EINVAL);
  305         }
  306 
  307         /* Locate the node type */
  308         if ((type = ng_findtype(typename)) == NULL) {
  309                 char *path, filename[NG_TYPELEN + 4];
  310                 linker_file_t lf;
  311                 int error;
  312 
  313                 /* Not found, try to load it as a loadable module */
  314                 snprintf(filename, sizeof(filename), "ng_%s.ko", typename);
  315                 if ((path = linker_search_path(filename)) == NULL)
  316                         return (ENXIO);
  317                 error = linker_load_file(path, &lf);
  318                 FREE(path, M_LINKER);
  319                 if (error != 0)
  320                         return (error);
  321                 lf->userrefs++;         /* pretend loaded by the syscall */
  322 
  323                 /* Try again, as now the type should have linked itself in */
  324                 if ((type = ng_findtype(typename)) == NULL)
  325                         return (ENXIO);
  326         }
  327 
  328         /* Call the constructor */
  329         if (type->constructor != NULL)
  330                 return ((*type->constructor)(nodepp));
  331         else
  332                 return (ng_make_node_common(type, nodepp));
  333 }
  334 
  335 /*
  336  * Generic node creation. Called by node constructors.
  337  * The returned node has a reference count of 1.
  338  */
  339 int
  340 ng_make_node_common(struct ng_type *type, node_p *nodepp)
  341 {
  342         node_p node;
  343 
  344         /* Require the node type to have been already installed */
  345         if (ng_findtype(type->name) == NULL) {
  346                 TRAP_ERROR;
  347                 return (EINVAL);
  348         }
  349 
  350         /* Make a node and try attach it to the type */
  351         MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_WAITOK);
  352         if (node == NULL) {
  353                 TRAP_ERROR;
  354                 return (ENOMEM);
  355         }
  356         bzero(node, sizeof(*node));
  357         node->type = type;
  358         node->refs++;                           /* note reference */
  359         type->refs++;
  360 
  361         /* Link us into the node linked list */
  362         LIST_INSERT_HEAD(&nodelist, node, nodes);
  363 
  364         /* Initialize hook list for new node */
  365         LIST_INIT(&node->hooks);
  366 
  367         /* get an ID and put us in the hash chain */
  368         node->ID = nextID++; /* 137 per second for 1 year before wrap */
  369         LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes);
  370 
  371         /* Done */
  372         *nodepp = node;
  373         return (0);
  374 }
  375 
  376 /*
  377  * Forceably start the shutdown process on a node. Either call
  378  * it's shutdown method, or do the default shutdown if there is
  379  * no type-specific method.
  380  *
  381  * Persistent nodes must have a type-specific method which
  382  * resets the NG_INVALID flag.
  383  */
  384 void
  385 ng_rmnode(node_p node)
  386 {
  387         /* Check if it's already shutting down */
  388         if ((node->flags & NG_INVALID) != 0)
  389                 return;
  390 
  391         /* Add an extra reference so it doesn't go away during this */
  392         node->refs++;
  393 
  394         /* Mark it invalid so any newcomers know not to try use it */
  395         node->flags |= NG_INVALID;
  396 
  397         /* Ask the type if it has anything to do in this case */
  398         if (node->type && node->type->shutdown)
  399                 (*node->type->shutdown)(node);
  400         else {                          /* do the default thing */
  401                 ng_unname(node);
  402                 ng_cutlinks(node);
  403                 ng_unref(node);
  404         }
  405 
  406         /* Remove extra reference, possibly the last */
  407         ng_unref(node);
  408 }
  409 
  410 /*
  411  * Called by the destructor to remove any STANDARD external references
  412  */
  413 void
  414 ng_cutlinks(node_p node)
  415 {
  416         hook_p  hook;
  417 
  418         /* Make sure that this is set to stop infinite loops */
  419         node->flags |= NG_INVALID;
  420 
  421         /* If we have sleepers, wake them up; they'll see NG_INVALID */
  422         if (node->sleepers)
  423                 wakeup(node);
  424 
  425         /* Notify all remaining connected nodes to disconnect */
  426         while ((hook = LIST_FIRST(&node->hooks)) != NULL)
  427                 ng_destroy_hook(hook);
  428 }
  429 
  430 /*
  431  * Remove a reference to the node, possibly the last
  432  */
  433 void
  434 ng_unref(node_p node)
  435 {
  436         int     s;
  437 
  438         s = splhigh();
  439         if (--node->refs <= 0) {
  440                 node->type->refs--;
  441                 LIST_REMOVE(node, nodes);
  442                 LIST_REMOVE(node, idnodes);
  443                 FREE(node, M_NETGRAPH);
  444         }
  445         splx(s);
  446 }
  447 
  448 /*
  449  * Wait for a node to come ready. Returns a node with a reference count;
  450  * don't forget to drop it when we are done with it using ng_release_node().
  451  */
  452 int
  453 ng_wait_node(node_p node, char *msg)
  454 {
  455         int s, error = 0;
  456 
  457         if (msg == NULL)
  458                 msg = "netgraph";
  459         s = splnet();
  460         node->sleepers++;
  461         node->refs++;           /* the sleeping process counts as a reference */
  462         while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY)
  463                 error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0);
  464         node->sleepers--;
  465         if (node->flags & NG_INVALID) {
  466                 TRAP_ERROR;
  467                 error = ENXIO;
  468         } else {
  469                 KASSERT(node->refs > 1,
  470                     ("%s: refs=%d", __FUNCTION__, node->refs));
  471                 node->flags |= NG_BUSY;
  472         }
  473         splx(s);
  474 
  475         /* Release the reference we had on it */
  476         if (error != 0)
  477                 ng_unref(node);
  478         return error;
  479 }
  480 
  481 /*
  482  * Release a node acquired via ng_wait_node()
  483  */
  484 void
  485 ng_release_node(node_p node)
  486 {
  487         /* Declare that we don't want it */
  488         node->flags &= ~NG_BUSY;
  489 
  490         /* If we have sleepers, then wake them up */
  491         if (node->sleepers)
  492                 wakeup(node);
  493 
  494         /* We also have a reference.. drop it too */
  495         ng_unref(node);
  496 }
  497 
  498 /************************************************************************
  499                         Node ID handling
  500 ************************************************************************/
  501 static node_p
  502 ng_ID2node(ng_ID_t ID)
  503 {
  504         node_p np;
  505         LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) {
  506                 if (np->ID == ID)
  507                         break;
  508         }
  509         return(np);
  510 }
  511 
  512 ng_ID_t
  513 ng_node2ID(node_p node)
  514 {
  515         return (node->ID);
  516 }
  517 
  518 /************************************************************************
  519                         Node name handling
  520 ************************************************************************/
  521 
  522 /*
  523  * Assign a node a name. Once assigned, the name cannot be changed.
  524  */
  525 int
  526 ng_name_node(node_p node, const char *name)
  527 {
  528         int i;
  529 
  530         /* Check the name is valid */
  531         for (i = 0; i < NG_NODELEN + 1; i++) {
  532                 if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
  533                         break;
  534         }
  535         if (i == 0 || name[i] != '\0') {
  536                 TRAP_ERROR;
  537                 return (EINVAL);
  538         }
  539         if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
  540                 TRAP_ERROR;
  541                 return (EINVAL);
  542         }
  543 
  544         /* Check the node isn't already named */
  545         if (node->name != NULL) {
  546                 TRAP_ERROR;
  547                 return (EISCONN);
  548         }
  549 
  550         /* Check the name isn't already being used */
  551         if (ng_findname(node, name) != NULL) {
  552                 TRAP_ERROR;
  553                 return (EADDRINUSE);
  554         }
  555 
  556         /* Allocate space and copy it */
  557         MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK);
  558         if (node->name == NULL) {
  559                 TRAP_ERROR;
  560                 return (ENOMEM);
  561         }
  562         strcpy(node->name, name);
  563 
  564         /* The name counts as a reference */
  565         node->refs++;
  566         return (0);
  567 }
  568 
  569 /*
  570  * Find a node by absolute name. The name should NOT end with ':'
  571  * The name "." means "this node" and "[xxx]" means "the node
  572  * with ID (ie, at address) xxx".
  573  *
  574  * Returns the node if found, else NULL.
  575  */
  576 node_p
  577 ng_findname(node_p this, const char *name)
  578 {
  579         node_p node;
  580         ng_ID_t temp;
  581 
  582         /* "." means "this node" */
  583         if (strcmp(name, ".") == 0)
  584                 return(this);
  585 
  586         /* Check for name-by-ID */
  587         if ((temp = ng_decodeidname(name)) != 0) {
  588                 return (ng_ID2node(temp));
  589         }
  590 
  591         /* Find node by name */
  592         LIST_FOREACH(node, &nodelist, nodes) {
  593                 if (node->name != NULL && strcmp(node->name, name) == 0)
  594                         break;
  595         }
  596         return (node);
  597 }
  598 
  599 /*
  600  * Decode a ID name, eg. "[f03034de]". Returns 0 if the
  601  * string is not valid, otherwise returns the value.
  602  */
  603 static ng_ID_t
  604 ng_decodeidname(const char *name)
  605 {
  606         const int len = strlen(name);
  607         char *eptr;
  608         u_long val;
  609 
  610         /* Check for proper length, brackets, no leading junk */
  611         if (len < 3 || name[0] != '[' || name[len - 1] != ']'
  612             || !isxdigit(name[1]))
  613                 return (0);
  614 
  615         /* Decode number */
  616         val = strtoul(name + 1, &eptr, 16);
  617         if (eptr - name != len - 1 || val == ULONG_MAX || val == 0)
  618                 return ((ng_ID_t)0);
  619         return (ng_ID_t)val;
  620 }
  621 
  622 /*
  623  * Remove a name from a node. This should only be called
  624  * when shutting down and removing the node.
  625  */
  626 void
  627 ng_unname(node_p node)
  628 {
  629         if (node->name) {
  630                 FREE(node->name, M_NETGRAPH);
  631                 node->name = NULL;
  632                 ng_unref(node);
  633         }
  634 }
  635 
  636 /************************************************************************
  637                         Hook routines
  638 
  639  Names are not optional. Hooks are always connected, except for a
  640  brief moment within these routines.
  641 
  642 ************************************************************************/
  643 
  644 /*
  645  * Remove a hook reference
  646  */
  647 static void
  648 ng_unref_hook(hook_p hook)
  649 {
  650         int     s;
  651 
  652         s = splhigh();
  653         if (--hook->refs == 0)
  654                 FREE(hook, M_NETGRAPH);
  655         splx(s);
  656 }
  657 
  658 /*
  659  * Add an unconnected hook to a node. Only used internally.
  660  */
  661 static int
  662 ng_add_hook(node_p node, const char *name, hook_p *hookp)
  663 {
  664         hook_p hook;
  665         int error = 0;
  666 
  667         /* Check that the given name is good */
  668         if (name == NULL) {
  669                 TRAP_ERROR;
  670                 return (EINVAL);
  671         }
  672         if (ng_findhook(node, name) != NULL) {
  673                 TRAP_ERROR;
  674                 return (EEXIST);
  675         }
  676 
  677         /* Allocate the hook and link it up */
  678         MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_WAITOK);
  679         if (hook == NULL) {
  680                 TRAP_ERROR;
  681                 return (ENOMEM);
  682         }
  683         bzero(hook, sizeof(*hook));
  684         hook->refs = 1;
  685         hook->flags = HK_INVALID;
  686         hook->node = node;
  687         node->refs++;           /* each hook counts as a reference */
  688 
  689         /* Check if the node type code has something to say about it */
  690         if (node->type->newhook != NULL)
  691                 if ((error = (*node->type->newhook)(node, hook, name)) != 0)
  692                         goto fail;
  693 
  694         /*
  695          * The 'type' agrees so far, so go ahead and link it in.
  696          * We'll ask again later when we actually connect the hooks.
  697          */
  698         LIST_INSERT_HEAD(&node->hooks, hook, hooks);
  699         node->numhooks++;
  700 
  701         /* Set hook name */
  702         MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK);
  703         if (hook->name == NULL) {
  704                 error = ENOMEM;
  705                 LIST_REMOVE(hook, hooks);
  706                 node->numhooks--;
  707 fail:
  708                 hook->node = NULL;
  709                 ng_unref(node);
  710                 ng_unref_hook(hook);    /* this frees the hook */
  711                 return (error);
  712         }
  713         strcpy(hook->name, name);
  714         if (hookp)
  715                 *hookp = hook;
  716         return (error);
  717 }
  718 
  719 /*
  720  * Connect a pair of hooks. Only used internally.
  721  */
  722 static int
  723 ng_connect(hook_p hook1, hook_p hook2)
  724 {
  725         int     error;
  726 
  727         hook1->peer = hook2;
  728         hook2->peer = hook1;
  729 
  730         /* Give each node the opportunity to veto the impending connection */
  731         if (hook1->node->type->connect) {
  732                 if ((error = (*hook1->node->type->connect) (hook1))) {
  733                         ng_destroy_hook(hook1); /* also zaps hook2 */
  734                         return (error);
  735                 }
  736         }
  737         if (hook2->node->type->connect) {
  738                 if ((error = (*hook2->node->type->connect) (hook2))) {
  739                         ng_destroy_hook(hook2); /* also zaps hook1 */
  740                         return (error);
  741                 }
  742         }
  743         hook1->flags &= ~HK_INVALID;
  744         hook2->flags &= ~HK_INVALID;
  745         return (0);
  746 }
  747 
  748 /*
  749  * Find a hook
  750  *
  751  * Node types may supply their own optimized routines for finding
  752  * hooks.  If none is supplied, we just do a linear search.
  753  */
  754 hook_p
  755 ng_findhook(node_p node, const char *name)
  756 {
  757         hook_p hook;
  758 
  759         if (node->type->findhook != NULL)
  760                 return (*node->type->findhook)(node, name);
  761         LIST_FOREACH(hook, &node->hooks, hooks) {
  762                 if (hook->name != NULL && strcmp(hook->name, name) == 0)
  763                         return (hook);
  764         }
  765         return (NULL);
  766 }
  767 
  768 /*
  769  * Destroy a hook
  770  *
  771  * As hooks are always attached, this really destroys two hooks.
  772  * The one given, and the one attached to it. Disconnect the hooks
  773  * from each other first.
  774  */
  775 void
  776 ng_destroy_hook(hook_p hook)
  777 {
  778         hook_p peer = hook->peer;
  779 
  780         hook->flags |= HK_INVALID;              /* as soon as possible */
  781         if (peer) {
  782                 peer->flags |= HK_INVALID;      /* as soon as possible */
  783                 hook->peer = NULL;
  784                 peer->peer = NULL;
  785                 ng_disconnect_hook(peer);
  786         }
  787         ng_disconnect_hook(hook);
  788 }
  789 
  790 /*
  791  * Notify the node of the hook's demise. This may result in more actions
  792  * (e.g. shutdown) but we don't do that ourselves and don't know what
  793  * happens there. If there is no appropriate handler, then just remove it
  794  * (and decrement the reference count of it's node which in turn might
  795  * make something happen).
  796  */
  797 static void
  798 ng_disconnect_hook(hook_p hook)
  799 {
  800         node_p node = hook->node;
  801 
  802         /*
  803          * Remove the hook from the node's list to avoid possible recursion
  804          * in case the disconnection results in node shutdown.
  805          */
  806         LIST_REMOVE(hook, hooks);
  807         node->numhooks--;
  808         if (node->type->disconnect) {
  809                 /*
  810                  * The type handler may elect to destroy the peer so don't
  811                  * trust its existance after this point.
  812                  */
  813                 (*node->type->disconnect) (hook);
  814         }
  815         ng_unref(node);         /* might be the last reference */
  816         if (hook->name)
  817                 FREE(hook->name, M_NETGRAPH);
  818         hook->node = NULL;      /* may still be referenced elsewhere */
  819         ng_unref_hook(hook);
  820 }
  821 
  822 /*
  823  * Take two hooks on a node and merge the connection so that the given node
  824  * is effectively bypassed.
  825  */
  826 int
  827 ng_bypass(hook_p hook1, hook_p hook2)
  828 {
  829         if (hook1->node != hook2->node)
  830                 return (EINVAL);
  831         hook1->peer->peer = hook2->peer;
  832         hook2->peer->peer = hook1->peer;
  833 
  834         /* XXX If we ever cache methods on hooks update them as well */
  835         hook1->peer = NULL;
  836         hook2->peer = NULL;
  837         ng_destroy_hook(hook1);
  838         ng_destroy_hook(hook2);
  839         return (0);
  840 }
  841 
  842 /*
  843  * Install a new netgraph type
  844  */
  845 int
  846 ng_newtype(struct ng_type *tp)
  847 {
  848         const size_t namelen = strlen(tp->name);
  849 
  850         /* Check version and type name fields */
  851         if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) {
  852                 TRAP_ERROR;
  853                 return (EINVAL);
  854         }
  855 
  856         /* Check for name collision */
  857         if (ng_findtype(tp->name) != NULL) {
  858                 TRAP_ERROR;
  859                 return (EEXIST);
  860         }
  861 
  862         /* Link in new type */
  863         LIST_INSERT_HEAD(&typelist, tp, types);
  864         tp->refs = 0;
  865         return (0);
  866 }
  867 
  868 /*
  869  * Look for a type of the name given
  870  */
  871 struct ng_type *
  872 ng_findtype(const char *typename)
  873 {
  874         struct ng_type *type;
  875 
  876         LIST_FOREACH(type, &typelist, types) {
  877                 if (strcmp(type->name, typename) == 0)
  878                         break;
  879         }
  880         return (type);
  881 }
  882 
  883 
  884 /************************************************************************
  885                         Composite routines
  886 ************************************************************************/
  887 
  888 /*
  889  * Make a peer and connect. The order is arranged to minimise
  890  * the work needed to back out in case of error.
  891  */
  892 int
  893 ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
  894 {
  895         node_p  node2;
  896         hook_p  hook;
  897         hook_p  hook2;
  898         int     error;
  899 
  900         if ((error = ng_add_hook(node, name, &hook)))
  901                 return (error);
  902         if ((error = ng_make_node(type, &node2))) {
  903                 ng_destroy_hook(hook);
  904                 return (error);
  905         }
  906         if ((error = ng_add_hook(node2, name2, &hook2))) {
  907                 ng_rmnode(node2);
  908                 ng_destroy_hook(hook);
  909                 return (error);
  910         }
  911 
  912         /*
  913          * Actually link the two hooks together.. on failure they are
  914          * destroyed so we don't have to do that here.
  915          */
  916         if ((error = ng_connect(hook, hook2)))
  917                 ng_rmnode(node2);
  918         return (error);
  919 }
  920 
  921 /*
  922  * Connect two nodes using the specified hooks
  923  */
  924 int
  925 ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
  926 {
  927         int     error;
  928         hook_p  hook;
  929         hook_p  hook2;
  930 
  931         if ((error = ng_add_hook(node, name, &hook)))
  932                 return (error);
  933         if ((error = ng_add_hook(node2, name2, &hook2))) {
  934                 ng_destroy_hook(hook);
  935                 return (error);
  936         }
  937         return (ng_connect(hook, hook2));
  938 }
  939 
  940 /*
  941  * Parse and verify a string of the form:  <NODE:><PATH>
  942  *
  943  * Such a string can refer to a specific node or a specific hook
  944  * on a specific node, depending on how you look at it. In the
  945  * latter case, the PATH component must not end in a dot.
  946  *
  947  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
  948  * of hook names separated by dots. This breaks out the original
  949  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
  950  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
  951  * the final hook component of <PATH>, if any, otherwise NULL.
  952  *
  953  * This returns -1 if the path is malformed. The char ** are optional.
  954  */
  955 
  956 int
  957 ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
  958 {
  959         char   *node, *path, *hook;
  960         int     k;
  961 
  962         /*
  963          * Extract absolute NODE, if any
  964          */
  965         for (path = addr; *path && *path != ':'; path++);
  966         if (*path) {
  967                 node = addr;    /* Here's the NODE */
  968                 *path++ = '\0'; /* Here's the PATH */
  969 
  970                 /* Node name must not be empty */
  971                 if (!*node)
  972                         return -1;
  973 
  974                 /* A name of "." is OK; otherwise '.' not allowed */
  975                 if (strcmp(node, ".") != 0) {
  976                         for (k = 0; node[k]; k++)
  977                                 if (node[k] == '.')
  978                                         return -1;
  979                 }
  980         } else {
  981                 node = NULL;    /* No absolute NODE */
  982                 path = addr;    /* Here's the PATH */
  983         }
  984 
  985         /* Snoop for illegal characters in PATH */
  986         for (k = 0; path[k]; k++)
  987                 if (path[k] == ':')
  988                         return -1;
  989 
  990         /* Check for no repeated dots in PATH */
  991         for (k = 0; path[k]; k++)
  992                 if (path[k] == '.' && path[k + 1] == '.')
  993                         return -1;
  994 
  995         /* Remove extra (degenerate) dots from beginning or end of PATH */
  996         if (path[0] == '.')
  997                 path++;
  998         if (*path && path[strlen(path) - 1] == '.')
  999                 path[strlen(path) - 1] = 0;
 1000 
 1001         /* If PATH has a dot, then we're not talking about a hook */
 1002         if (*path) {
 1003                 for (hook = path, k = 0; path[k]; k++)
 1004                         if (path[k] == '.') {
 1005                                 hook = NULL;
 1006                                 break;
 1007                         }
 1008         } else
 1009                 path = hook = NULL;
 1010 
 1011         /* Done */
 1012         if (nodep)
 1013                 *nodep = node;
 1014         if (pathp)
 1015                 *pathp = path;
 1016         if (hookp)
 1017                 *hookp = hook;
 1018         return (0);
 1019 }
 1020 
 1021 /*
 1022  * Given a path, which may be absolute or relative, and a starting node,
 1023  * return the destination node. Compute the "return address" if desired.
 1024  */
 1025 int
 1026 ng_path2node(node_p here, const char *address, node_p *destp, char **rtnp)
 1027 {
 1028         const   node_p start = here;
 1029         char    fullpath[NG_PATHLEN + 1];
 1030         char   *nodename, *path, pbuf[2];
 1031         node_p  node;
 1032         char   *cp;
 1033 
 1034         /* Initialize */
 1035         if (rtnp)
 1036                 *rtnp = NULL;
 1037         if (destp == NULL)
 1038                 return EINVAL;
 1039         *destp = NULL;
 1040 
 1041         /* Make a writable copy of address for ng_path_parse() */
 1042         strncpy(fullpath, address, sizeof(fullpath) - 1);
 1043         fullpath[sizeof(fullpath) - 1] = '\0';
 1044 
 1045         /* Parse out node and sequence of hooks */
 1046         if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
 1047                 TRAP_ERROR;
 1048                 return EINVAL;
 1049         }
 1050         if (path == NULL) {
 1051                 pbuf[0] = '.';  /* Needs to be writable */
 1052                 pbuf[1] = '\0';
 1053                 path = pbuf;
 1054         }
 1055 
 1056         /* For an absolute address, jump to the starting node */
 1057         if (nodename) {
 1058                 node = ng_findname(here, nodename);
 1059                 if (node == NULL) {
 1060                         TRAP_ERROR;
 1061                         return (ENOENT);
 1062                 }
 1063         } else
 1064                 node = here;
 1065 
 1066         /* Now follow the sequence of hooks */
 1067         for (cp = path; node != NULL && *cp != '\0'; ) {
 1068                 hook_p hook;
 1069                 char *segment;
 1070 
 1071                 /*
 1072                  * Break out the next path segment. Replace the dot we just
 1073                  * found with a NUL; "cp" points to the next segment (or the
 1074                  * NUL at the end).
 1075                  */
 1076                 for (segment = cp; *cp != '\0'; cp++) {
 1077                         if (*cp == '.') {
 1078                                 *cp++ = '\0';
 1079                                 break;
 1080                         }
 1081                 }
 1082 
 1083                 /* Empty segment */
 1084                 if (*segment == '\0')
 1085                         continue;
 1086 
 1087                 /* We have a segment, so look for a hook by that name */
 1088                 hook = ng_findhook(node, segment);
 1089 
 1090                 /* Can't get there from here... */
 1091                 if (hook == NULL
 1092                     || hook->peer == NULL
 1093                     || (hook->flags & HK_INVALID) != 0) {
 1094                         TRAP_ERROR;
 1095                         return (ENOENT);
 1096                 }
 1097 
 1098                 /* Hop on over to the next node */
 1099                 node = hook->peer->node;
 1100         }
 1101 
 1102         /* If node somehow missing, fail here (probably this is not needed) */
 1103         if (node == NULL) {
 1104                 TRAP_ERROR;
 1105                 return (ENXIO);
 1106         }
 1107 
 1108         /* Now compute return address, i.e., the path to the sender */
 1109         if (rtnp != NULL) {
 1110                 MALLOC(*rtnp, char *, NG_NODELEN + 2, M_NETGRAPH, M_WAITOK);
 1111                 if (*rtnp == NULL) {
 1112                         TRAP_ERROR;
 1113                         return (ENOMEM);
 1114                 }
 1115                 if (start->name != NULL)
 1116                         sprintf(*rtnp, "%s:", start->name);
 1117                 else
 1118                         sprintf(*rtnp, "[%x]:", ng_node2ID(start));
 1119         }
 1120 
 1121         /* Done */
 1122         *destp = node;
 1123         return (0);
 1124 }
 1125 
 1126 /*
 1127  * Call the appropriate message handler for the object.
 1128  * It is up to the message handler to free the message.
 1129  * If it's a generic message, handle it generically, otherwise
 1130  * call the type's message handler (if it exists)
 1131  * XXX (race). Remember that a queued message may reference a node
 1132  * or hook that has just been invalidated. It will exist
 1133  * as the queue code is holding a reference, but..
 1134  */
 1135 
 1136 #define CALL_MSG_HANDLER(error, node, msg, retaddr, resp)               \
 1137 do {                                                                    \
 1138         if((msg)->header.typecookie == NGM_GENERIC_COOKIE) {            \
 1139                 (error) = ng_generic_msg((node), (msg),                 \
 1140                                 (retaddr), (resp));                     \
 1141         } else {                                                        \
 1142                 if ((node)->type->rcvmsg != NULL) {                     \
 1143                         (error) = (*(node)->type->rcvmsg)((node),       \
 1144                                         (msg), (retaddr), (resp));      \
 1145                 } else {                                                \
 1146                         TRAP_ERROR;                                     \
 1147                         FREE((msg), M_NETGRAPH);                        \
 1148                         (error) = EINVAL;                               \
 1149                 }                                                       \
 1150         }                                                               \
 1151 } while (0)
 1152 
 1153 
 1154 /*
 1155  * Send a control message to a node
 1156  */
 1157 int
 1158 ng_send_msg(node_p here, struct ng_mesg *msg, const char *address,
 1159             struct ng_mesg **rptr)
 1160 {
 1161         node_p  dest = NULL;
 1162         char   *retaddr = NULL;
 1163         int     error;
 1164 
 1165         /* Find the target node */
 1166         error = ng_path2node(here, address, &dest, &retaddr);
 1167         if (error) {
 1168                 FREE(msg, M_NETGRAPH);
 1169                 return error;
 1170         }
 1171 
 1172         /* Make sure the resp field is null before we start */
 1173         if (rptr != NULL)
 1174                 *rptr = NULL;
 1175 
 1176         CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr);
 1177 
 1178         /* Make sure that if there is a response, it has the RESP bit set */
 1179         if ((error == 0) && rptr && *rptr)
 1180                 (*rptr)->header.flags |= NGF_RESP;
 1181 
 1182         /*
 1183          * If we had a return address it is up to us to free it. They should
 1184          * have taken a copy if they needed to make a delayed response.
 1185          */
 1186         if (retaddr)
 1187                 FREE(retaddr, M_NETGRAPH);
 1188         return (error);
 1189 }
 1190 
 1191 /*
 1192  * Implement the 'generic' control messages
 1193  */
 1194 static int
 1195 ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr,
 1196                struct ng_mesg **resp)
 1197 {
 1198         int error = 0;
 1199 
 1200         if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
 1201                 TRAP_ERROR;
 1202                 FREE(msg, M_NETGRAPH);
 1203                 return (EINVAL);
 1204         }
 1205         switch (msg->header.cmd) {
 1206         case NGM_SHUTDOWN:
 1207                 ng_rmnode(here);
 1208                 break;
 1209         case NGM_MKPEER:
 1210             {
 1211                 struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
 1212 
 1213                 if (msg->header.arglen != sizeof(*mkp)) {
 1214                         TRAP_ERROR;
 1215                         return (EINVAL);
 1216                 }
 1217                 mkp->type[sizeof(mkp->type) - 1] = '\0';
 1218                 mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
 1219                 mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
 1220                 error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
 1221                 break;
 1222             }
 1223         case NGM_CONNECT:
 1224             {
 1225                 struct ngm_connect *const con =
 1226                         (struct ngm_connect *) msg->data;
 1227                 node_p node2;
 1228 
 1229                 if (msg->header.arglen != sizeof(*con)) {
 1230                         TRAP_ERROR;
 1231                         return (EINVAL);
 1232                 }
 1233                 con->path[sizeof(con->path) - 1] = '\0';
 1234                 con->ourhook[sizeof(con->ourhook) - 1] = '\0';
 1235                 con->peerhook[sizeof(con->peerhook) - 1] = '\0';
 1236                 error = ng_path2node(here, con->path, &node2, NULL);
 1237                 if (error)
 1238                         break;
 1239                 error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
 1240                 break;
 1241             }
 1242         case NGM_NAME:
 1243             {
 1244                 struct ngm_name *const nam = (struct ngm_name *) msg->data;
 1245 
 1246                 if (msg->header.arglen != sizeof(*nam)) {
 1247                         TRAP_ERROR;
 1248                         return (EINVAL);
 1249                 }
 1250                 nam->name[sizeof(nam->name) - 1] = '\0';
 1251                 error = ng_name_node(here, nam->name);
 1252                 break;
 1253             }
 1254         case NGM_RMHOOK:
 1255             {
 1256                 struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
 1257                 hook_p hook;
 1258 
 1259                 if (msg->header.arglen != sizeof(*rmh)) {
 1260                         TRAP_ERROR;
 1261                         return (EINVAL);
 1262                 }
 1263                 rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
 1264                 if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
 1265                         ng_destroy_hook(hook);
 1266                 break;
 1267             }
 1268         case NGM_NODEINFO:
 1269             {
 1270                 struct nodeinfo *ni;
 1271                 struct ng_mesg *rp;
 1272 
 1273                 /* Get response struct */
 1274                 if (resp == NULL) {
 1275                         error = EINVAL;
 1276                         break;
 1277                 }
 1278                 NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT);
 1279                 if (rp == NULL) {
 1280                         error = ENOMEM;
 1281                         break;
 1282                 }
 1283 
 1284                 /* Fill in node info */
 1285                 ni = (struct nodeinfo *) rp->data;
 1286                 if (here->name != NULL)
 1287                         strncpy(ni->name, here->name, NG_NODELEN);
 1288                 strncpy(ni->type, here->type->name, NG_TYPELEN);
 1289                 ni->id = ng_node2ID(here);
 1290                 ni->hooks = here->numhooks;
 1291                 *resp = rp;
 1292                 break;
 1293             }
 1294         case NGM_LISTHOOKS:
 1295             {
 1296                 const int nhooks = here->numhooks;
 1297                 struct hooklist *hl;
 1298                 struct nodeinfo *ni;
 1299                 struct ng_mesg *rp;
 1300                 hook_p hook;
 1301 
 1302                 /* Get response struct */
 1303                 if (resp == NULL) {
 1304                         error = EINVAL;
 1305                         break;
 1306                 }
 1307                 NG_MKRESPONSE(rp, msg, sizeof(*hl)
 1308                     + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
 1309                 if (rp == NULL) {
 1310                         error = ENOMEM;
 1311                         break;
 1312                 }
 1313                 hl = (struct hooklist *) rp->data;
 1314                 ni = &hl->nodeinfo;
 1315 
 1316                 /* Fill in node info */
 1317                 if (here->name)
 1318                         strncpy(ni->name, here->name, NG_NODELEN);
 1319                 strncpy(ni->type, here->type->name, NG_TYPELEN);
 1320                 ni->id = ng_node2ID(here);
 1321 
 1322                 /* Cycle through the linked list of hooks */
 1323                 ni->hooks = 0;
 1324                 LIST_FOREACH(hook, &here->hooks, hooks) {
 1325                         struct linkinfo *const link = &hl->link[ni->hooks];
 1326 
 1327                         if (ni->hooks >= nhooks) {
 1328                                 log(LOG_ERR, "%s: number of %s changed\n",
 1329                                     __FUNCTION__, "hooks");
 1330                                 break;
 1331                         }
 1332                         if ((hook->flags & HK_INVALID) != 0)
 1333                                 continue;
 1334                         strncpy(link->ourhook, hook->name, NG_HOOKLEN);
 1335                         strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN);
 1336                         if (hook->peer->node->name != NULL)
 1337                                 strncpy(link->nodeinfo.name,
 1338                                     hook->peer->node->name, NG_NODELEN);
 1339                         strncpy(link->nodeinfo.type,
 1340                            hook->peer->node->type->name, NG_TYPELEN);
 1341                         link->nodeinfo.id = ng_node2ID(hook->peer->node);
 1342                         link->nodeinfo.hooks = hook->peer->node->numhooks;
 1343                         ni->hooks++;
 1344                 }
 1345                 *resp = rp;
 1346                 break;
 1347             }
 1348 
 1349         case NGM_LISTNAMES:
 1350         case NGM_LISTNODES:
 1351             {
 1352                 const int unnamed = (msg->header.cmd == NGM_LISTNODES);
 1353                 struct namelist *nl;
 1354                 struct ng_mesg *rp;
 1355                 node_p node;
 1356                 int num = 0;
 1357 
 1358                 if (resp == NULL) {
 1359                         error = EINVAL;
 1360                         break;
 1361                 }
 1362 
 1363                 /* Count number of nodes */
 1364                 LIST_FOREACH(node, &nodelist, nodes) {
 1365                         if (unnamed || node->name != NULL)
 1366                                 num++;
 1367                 }
 1368 
 1369                 /* Get response struct */
 1370                 if (resp == NULL) {
 1371                         error = EINVAL;
 1372                         break;
 1373                 }
 1374                 NG_MKRESPONSE(rp, msg, sizeof(*nl)
 1375                     + (num * sizeof(struct nodeinfo)), M_NOWAIT);
 1376                 if (rp == NULL) {
 1377                         error = ENOMEM;
 1378                         break;
 1379                 }
 1380                 nl = (struct namelist *) rp->data;
 1381 
 1382                 /* Cycle through the linked list of nodes */
 1383                 nl->numnames = 0;
 1384                 LIST_FOREACH(node, &nodelist, nodes) {
 1385                         struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
 1386 
 1387                         if (nl->numnames >= num) {
 1388                                 log(LOG_ERR, "%s: number of %s changed\n",
 1389                                     __FUNCTION__, "nodes");
 1390                                 break;
 1391                         }
 1392                         if ((node->flags & NG_INVALID) != 0)
 1393                                 continue;
 1394                         if (!unnamed && node->name == NULL)
 1395                                 continue;
 1396                         if (node->name != NULL)
 1397                                 strncpy(np->name, node->name, NG_NODELEN);
 1398                         strncpy(np->type, node->type->name, NG_TYPELEN);
 1399                         np->id = ng_node2ID(node);
 1400                         np->hooks = node->numhooks;
 1401                         nl->numnames++;
 1402                 }
 1403                 *resp = rp;
 1404                 break;
 1405             }
 1406 
 1407         case NGM_LISTTYPES:
 1408             {
 1409                 struct typelist *tl;
 1410                 struct ng_mesg *rp;
 1411                 struct ng_type *type;
 1412                 int num = 0;
 1413 
 1414                 if (resp == NULL) {
 1415                         error = EINVAL;
 1416                         break;
 1417                 }
 1418 
 1419                 /* Count number of types */
 1420                 LIST_FOREACH(type, &typelist, types)
 1421                         num++;
 1422 
 1423                 /* Get response struct */
 1424                 if (resp == NULL) {
 1425                         error = EINVAL;
 1426                         break;
 1427                 }
 1428                 NG_MKRESPONSE(rp, msg, sizeof(*tl)
 1429                     + (num * sizeof(struct typeinfo)), M_NOWAIT);
 1430                 if (rp == NULL) {
 1431                         error = ENOMEM;
 1432                         break;
 1433                 }
 1434                 tl = (struct typelist *) rp->data;
 1435 
 1436                 /* Cycle through the linked list of types */
 1437                 tl->numtypes = 0;
 1438                 LIST_FOREACH(type, &typelist, types) {
 1439                         struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
 1440 
 1441                         if (tl->numtypes >= num) {
 1442                                 log(LOG_ERR, "%s: number of %s changed\n",
 1443                                     __FUNCTION__, "types");
 1444                                 break;
 1445                         }
 1446                         strncpy(tp->type_name, type->name, NG_TYPELEN);
 1447                         tp->numnodes = type->refs;
 1448                         tl->numtypes++;
 1449                 }
 1450                 *resp = rp;
 1451                 break;
 1452             }
 1453 
 1454         case NGM_BINARY2ASCII:
 1455             {
 1456                 int bufSize = 2000;     /* XXX hard coded constant */
 1457                 const struct ng_parse_type *argstype;
 1458                 const struct ng_cmdlist *c;
 1459                 struct ng_mesg *rp, *binary, *ascii;
 1460 
 1461                 /* Data area must contain a valid netgraph message */
 1462                 binary = (struct ng_mesg *)msg->data;
 1463                 if (msg->header.arglen < sizeof(struct ng_mesg)
 1464                     || msg->header.arglen - sizeof(struct ng_mesg) 
 1465                       < binary->header.arglen) {
 1466                         error = EINVAL;
 1467                         break;
 1468                 }
 1469 
 1470                 /* Get a response message with lots of room */
 1471                 NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
 1472                 if (rp == NULL) {
 1473                         error = ENOMEM;
 1474                         break;
 1475                 }
 1476                 ascii = (struct ng_mesg *)rp->data;
 1477 
 1478                 /* Copy binary message header to response message payload */
 1479                 bcopy(binary, ascii, sizeof(*binary));
 1480 
 1481                 /* Find command by matching typecookie and command number */
 1482                 for (c = here->type->cmdlist;
 1483                     c != NULL && c->name != NULL; c++) {
 1484                         if (binary->header.typecookie == c->cookie
 1485                             && binary->header.cmd == c->cmd)
 1486                                 break;
 1487                 }
 1488                 if (c == NULL || c->name == NULL) {
 1489                         for (c = ng_generic_cmds; c->name != NULL; c++) {
 1490                                 if (binary->header.typecookie == c->cookie
 1491                                     && binary->header.cmd == c->cmd)
 1492                                         break;
 1493                         }
 1494                         if (c->name == NULL) {
 1495                                 FREE(rp, M_NETGRAPH);
 1496                                 error = ENOSYS;
 1497                                 break;
 1498                         }
 1499                 }
 1500 
 1501                 /* Convert command name to ASCII */
 1502                 snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
 1503                     "%s", c->name);
 1504 
 1505                 /* Convert command arguments to ASCII */
 1506                 argstype = (binary->header.flags & NGF_RESP) ?
 1507                     c->respType : c->mesgType;
 1508                 if (argstype == NULL)
 1509                         *ascii->data = '\0';
 1510                 else {
 1511                         if ((error = ng_unparse(argstype,
 1512                             (u_char *)binary->data,
 1513                             ascii->data, bufSize)) != 0) {
 1514                                 FREE(rp, M_NETGRAPH);
 1515                                 break;
 1516                         }
 1517                 }
 1518 
 1519                 /* Return the result as struct ng_mesg plus ASCII string */
 1520                 bufSize = strlen(ascii->data) + 1;
 1521                 ascii->header.arglen = bufSize;
 1522                 rp->header.arglen = sizeof(*ascii) + bufSize;
 1523                 *resp = rp;
 1524                 break;
 1525             }
 1526 
 1527         case NGM_ASCII2BINARY:
 1528             {
 1529                 int bufSize = 2000;     /* XXX hard coded constant */
 1530                 const struct ng_cmdlist *c;
 1531                 const struct ng_parse_type *argstype;
 1532                 struct ng_mesg *rp, *ascii, *binary;
 1533                 int off = 0;
 1534 
 1535                 /* Data area must contain at least a struct ng_mesg + '\0' */
 1536                 ascii = (struct ng_mesg *)msg->data;
 1537                 if (msg->header.arglen < sizeof(*ascii) + 1
 1538                     || ascii->header.arglen < 1
 1539                     || msg->header.arglen
 1540                       < sizeof(*ascii) + ascii->header.arglen) {
 1541                         error = EINVAL;
 1542                         break;
 1543                 }
 1544                 ascii->data[ascii->header.arglen - 1] = '\0';
 1545 
 1546                 /* Get a response message with lots of room */
 1547                 NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
 1548                 if (rp == NULL) {
 1549                         error = ENOMEM;
 1550                         break;
 1551                 }
 1552                 binary = (struct ng_mesg *)rp->data;
 1553 
 1554                 /* Copy ASCII message header to response message payload */
 1555                 bcopy(ascii, binary, sizeof(*ascii));
 1556 
 1557                 /* Find command by matching ASCII command string */
 1558                 for (c = here->type->cmdlist;
 1559                     c != NULL && c->name != NULL; c++) {
 1560                         if (strcmp(ascii->header.cmdstr, c->name) == 0)
 1561                                 break;
 1562                 }
 1563                 if (c == NULL || c->name == NULL) {
 1564                         for (c = ng_generic_cmds; c->name != NULL; c++) {
 1565                                 if (strcmp(ascii->header.cmdstr, c->name) == 0)
 1566                                         break;
 1567                         }
 1568                         if (c->name == NULL) {
 1569                                 FREE(rp, M_NETGRAPH);
 1570                                 error = ENOSYS;
 1571                                 break;
 1572                         }
 1573                 }
 1574 
 1575                 /* Convert command name to binary */
 1576                 binary->header.cmd = c->cmd;
 1577                 binary->header.typecookie = c->cookie;
 1578 
 1579                 /* Convert command arguments to binary */
 1580                 argstype = (binary->header.flags & NGF_RESP) ?
 1581                     c->respType : c->mesgType;
 1582                 if (argstype == NULL)
 1583                         bufSize = 0;
 1584                 else {
 1585                         if ((error = ng_parse(argstype, ascii->data,
 1586                             &off, (u_char *)binary->data, &bufSize)) != 0) {
 1587                                 FREE(rp, M_NETGRAPH);
 1588                                 break;
 1589                         }
 1590                 }
 1591 
 1592                 /* Return the result */
 1593                 binary->header.arglen = bufSize;
 1594                 rp->header.arglen = sizeof(*binary) + bufSize;
 1595                 *resp = rp;
 1596                 break;
 1597             }
 1598 
 1599         case NGM_TEXT_STATUS:
 1600                 /*
 1601                  * This one is tricky as it passes the command down to the
 1602                  * actual node, even though it is a generic type command.
 1603                  * This means we must assume that the msg is already freed
 1604                  * when control passes back to us.
 1605                  */
 1606                 if (resp == NULL) {
 1607                         error = EINVAL;
 1608                         break;
 1609                 }
 1610                 if (here->type->rcvmsg != NULL)
 1611                         return((*here->type->rcvmsg)(here, msg, retaddr, resp));
 1612                 /* Fall through if rcvmsg not supported */
 1613         default:
 1614                 TRAP_ERROR;
 1615                 error = EINVAL;
 1616         }
 1617         FREE(msg, M_NETGRAPH);
 1618         return (error);
 1619 }
 1620 
 1621 /*
 1622  * Send a data packet to a node. If the recipient has no
 1623  * 'receive data' method, then silently discard the packet.
 1624  */
 1625 int 
 1626 ng_send_data(hook_p hook, struct mbuf *m, meta_p meta)
 1627 {
 1628         int (*rcvdata)(hook_p, struct mbuf *, meta_p);
 1629         int error;
 1630 
 1631         CHECK_DATA_MBUF(m);
 1632         if (hook && (hook->flags & HK_INVALID) == 0) {
 1633                 rcvdata = hook->peer->node->type->rcvdata;
 1634                 if (rcvdata != NULL)
 1635                         error = (*rcvdata)(hook->peer, m, meta);
 1636                 else {
 1637                         error = 0;
 1638                         NG_FREE_DATA(m, meta);
 1639                 }
 1640         } else {
 1641                 TRAP_ERROR;
 1642                 error = ENOTCONN;
 1643                 NG_FREE_DATA(m, meta);
 1644         }
 1645         return (error);
 1646 }
 1647 
 1648 /*
 1649  * Send a queued data packet to a node. If the recipient has no
 1650  * 'receive queued data' method, then try the 'receive data' method above.
 1651  */
 1652 int 
 1653 ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta)
 1654 {
 1655         int (*rcvdataq)(hook_p, struct mbuf *, meta_p);
 1656         int error;
 1657 
 1658         CHECK_DATA_MBUF(m);
 1659         if (hook && (hook->flags & HK_INVALID) == 0) {
 1660                 rcvdataq = hook->peer->node->type->rcvdataq;
 1661                 if (rcvdataq != NULL)
 1662                         error = (*rcvdataq)(hook->peer, m, meta);
 1663                 else {
 1664                         error = ng_send_data(hook, m, meta);
 1665                 }
 1666         } else {
 1667                 TRAP_ERROR;
 1668                 error = ENOTCONN;
 1669                 NG_FREE_DATA(m, meta);
 1670         }
 1671         return (error);
 1672 }
 1673 
 1674 /*
 1675  * Copy a 'meta'.
 1676  *
 1677  * Returns new meta, or NULL if original meta is NULL or ENOMEM.
 1678  */
 1679 meta_p
 1680 ng_copy_meta(meta_p meta)
 1681 {
 1682         meta_p meta2;
 1683 
 1684         if (meta == NULL)
 1685                 return (NULL);
 1686         MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT);
 1687         if (meta2 == NULL)
 1688                 return (NULL);
 1689         meta2->allocated_len = meta->used_len;
 1690         bcopy(meta, meta2, meta->used_len);
 1691         return (meta2);
 1692 }
 1693 
 1694 /************************************************************************
 1695                         Module routines
 1696 ************************************************************************/
 1697 
 1698 /*
 1699  * Handle the loading/unloading of a netgraph node type module
 1700  */
 1701 int
 1702 ng_mod_event(module_t mod, int event, void *data)
 1703 {
 1704         struct ng_type *const type = data;
 1705         int s, error = 0;
 1706 
 1707         switch (event) {
 1708         case MOD_LOAD:
 1709 
 1710                 /* Register new netgraph node type */
 1711                 s = splnet();
 1712                 if ((error = ng_newtype(type)) != 0) {
 1713                         splx(s);
 1714                         break;
 1715                 }
 1716 
 1717                 /* Call type specific code */
 1718                 if (type->mod_event != NULL)
 1719                         if ((error = (*type->mod_event)(mod, event, data)) != 0)
 1720                                 LIST_REMOVE(type, types);
 1721                 splx(s);
 1722                 break;
 1723 
 1724         case MOD_UNLOAD:
 1725                 s = splnet();
 1726                 if (type->refs != 0)            /* make sure no nodes exist! */
 1727                         error = EBUSY;
 1728                 else {
 1729                         if (type->mod_event != NULL) {  /* check with type */
 1730                                 error = (*type->mod_event)(mod, event, data);
 1731                                 if (error != 0) {       /* type refuses.. */
 1732                                         splx(s);
 1733                                         break;
 1734                                 }
 1735                         }
 1736                         LIST_REMOVE(type, types);
 1737                 }
 1738                 splx(s);
 1739                 break;
 1740 
 1741         default:
 1742                 if (type->mod_event != NULL)
 1743                         error = (*type->mod_event)(mod, event, data);
 1744                 else
 1745                         error = 0;              /* XXX ? */
 1746                 break;
 1747         }
 1748         return (error);
 1749 }
 1750 
 1751 /*
 1752  * Handle loading and unloading for this code.
 1753  * The only thing we need to link into is the NETISR strucure.
 1754  */
 1755 static int
 1756 ngb_mod_event(module_t mod, int event, void *data)
 1757 {
 1758         int s, error = 0;
 1759 
 1760         switch (event) {
 1761         case MOD_LOAD:
 1762                 /* Register line discipline */
 1763                 s = splimp();
 1764                 error = register_netisr(NETISR_NETGRAPH, ngintr);
 1765                 splx(s);
 1766                 break;
 1767         case MOD_UNLOAD:
 1768                 /* You cant unload it because an interface may be using it.  */
 1769                 error = EBUSY;
 1770                 break;
 1771         default:
 1772                 error = EOPNOTSUPP;
 1773                 break;
 1774         }
 1775         return (error);
 1776 }
 1777 
 1778 static moduledata_t netgraph_mod = {
 1779         "netgraph",
 1780         ngb_mod_event,
 1781         (NULL)
 1782 };
 1783 DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
 1784 
 1785 /************************************************************************
 1786                         Queueing routines
 1787 ************************************************************************/
 1788 
 1789 /* The structure for queueing across ISR switches */
 1790 struct ng_queue_entry {
 1791         u_long  flags;
 1792         struct ng_queue_entry *next;
 1793         union {
 1794                 struct {
 1795                         hook_p          da_hook;        /*  target hook */
 1796                         struct mbuf     *da_m;
 1797                         meta_p          da_meta;
 1798                 } data;
 1799                 struct {
 1800                         struct ng_mesg  *msg_msg;
 1801                         node_p          msg_node;
 1802                         void            *msg_retaddr;
 1803                 } msg;
 1804         } body;
 1805 };
 1806 #define NGQF_DATA       0x01            /* the queue element is data */
 1807 #define NGQF_MESG       0x02            /* the queue element is a message */
 1808 
 1809 static struct ng_queue_entry   *ngqbase;        /* items to be unqueued */
 1810 static struct ng_queue_entry   *ngqlast;        /* last item queued */
 1811 static const int                ngqroom = 64;   /* max items to queue */
 1812 static int                      ngqsize;        /* number of items in queue */
 1813 
 1814 static struct ng_queue_entry   *ngqfree;        /* free ones */
 1815 static const int                ngqfreemax = 16;/* cache at most this many */
 1816 static int                      ngqfreesize;    /* number of cached entries */
 1817 
 1818 /*
 1819  * Get a queue entry
 1820  */
 1821 static struct ng_queue_entry *
 1822 ng_getqblk(void)
 1823 {
 1824         register struct ng_queue_entry *q;
 1825         int s;
 1826 
 1827         /* Could be guarding against tty ints or whatever */
 1828         s = splhigh();
 1829 
 1830         /* Try get a cached queue block, or else allocate a new one */
 1831         if ((q = ngqfree) == NULL) {
 1832                 splx(s);
 1833                 if (ngqsize < ngqroom) {        /* don't worry about races */
 1834                         MALLOC(q, struct ng_queue_entry *,
 1835                             sizeof(*q), M_NETGRAPH, M_NOWAIT);
 1836                 }
 1837         } else {
 1838                 ngqfree = q->next;
 1839                 ngqfreesize--;
 1840                 splx(s);
 1841         }
 1842         return (q);
 1843 }
 1844 
 1845 /*
 1846  * Release a queue entry
 1847  */
 1848 #define RETURN_QBLK(q)                                                  \
 1849 do {                                                                    \
 1850         int s;                                                          \
 1851         if (ngqfreesize < ngqfreemax) { /* don't worry about races */   \
 1852                 s = splhigh();                                          \
 1853                 (q)->next = ngqfree;                                    \
 1854                 ngqfree = (q);                                          \
 1855                 ngqfreesize++;                                          \
 1856                 splx(s);                                                \
 1857         } else {                                                        \
 1858                 FREE((q), M_NETGRAPH);                                  \
 1859         }                                                               \
 1860 } while (0)
 1861 
 1862 /*
 1863  * Running at a raised (but we don't know which) processor priority level,
 1864  * put the data onto a queue to be picked up by another PPL (probably splnet)
 1865  */
 1866 int
 1867 ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta)
 1868 {
 1869         struct ng_queue_entry *q;
 1870         int s;
 1871 
 1872         if (hook == NULL) {
 1873                 NG_FREE_DATA(m, meta);
 1874                 return (0);
 1875         }
 1876         if ((q = ng_getqblk()) == NULL) {
 1877                 NG_FREE_DATA(m, meta);
 1878                 return (ENOBUFS);
 1879         }
 1880 
 1881         /* Fill out the contents */
 1882         q->flags = NGQF_DATA;
 1883         q->next = NULL;
 1884         q->body.data.da_hook = hook;
 1885         q->body.data.da_m = m;
 1886         q->body.data.da_meta = meta;
 1887         s = splhigh();          /* protect refs and queue */
 1888         hook->refs++;           /* don't let it go away while on the queue */
 1889 
 1890         /* Put it on the queue */
 1891         if (ngqbase) {
 1892                 ngqlast->next = q;
 1893         } else {
 1894                 ngqbase = q;
 1895         }
 1896         ngqlast = q;
 1897         ngqsize++;
 1898         splx(s);
 1899 
 1900         /* Schedule software interrupt to handle it later */
 1901         schednetisr(NETISR_NETGRAPH);
 1902         return (0);
 1903 }
 1904 
 1905 /*
 1906  * Running at a raised (but we don't know which) processor priority level,
 1907  * put the msg onto a queue to be picked up by another PPL (probably splnet)
 1908  */
 1909 int
 1910 ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address)
 1911 {
 1912         register struct ng_queue_entry *q;
 1913         int     s;
 1914         node_p  dest = NULL;
 1915         char   *retaddr = NULL;
 1916         int     error;
 1917 
 1918         /* Find the target node. */
 1919         error = ng_path2node(here, address, &dest, &retaddr);
 1920         if (error) {
 1921                 FREE(msg, M_NETGRAPH);
 1922                 return (error);
 1923         }
 1924         if ((q = ng_getqblk()) == NULL) {
 1925                 FREE(msg, M_NETGRAPH);
 1926                 if (retaddr)
 1927                         FREE(retaddr, M_NETGRAPH);
 1928                 return (ENOBUFS);
 1929         }
 1930 
 1931         /* Fill out the contents */
 1932         q->flags = NGQF_MESG;
 1933         q->next = NULL;
 1934         q->body.msg.msg_node = dest;
 1935         q->body.msg.msg_msg = msg;
 1936         q->body.msg.msg_retaddr = retaddr;
 1937         s = splhigh();          /* protect refs and queue */
 1938         dest->refs++;           /* don't let it go away while on the queue */
 1939 
 1940         /* Put it on the queue */
 1941         if (ngqbase) {
 1942                 ngqlast->next = q;
 1943         } else {
 1944                 ngqbase = q;
 1945         }
 1946         ngqlast = q;
 1947         ngqsize++;
 1948         splx(s);
 1949 
 1950         /* Schedule software interrupt to handle it later */
 1951         schednetisr(NETISR_NETGRAPH);
 1952         return (0);
 1953 }
 1954 
 1955 /*
 1956  * Pick an item off the queue, process it, and dispose of the queue entry.
 1957  * Should be running at splnet.
 1958  */
 1959 static void
 1960 ngintr(void)
 1961 {
 1962         hook_p  hook;
 1963         struct ng_queue_entry *ngq;
 1964         struct mbuf *m;
 1965         meta_p  meta;
 1966         void   *retaddr;
 1967         struct ng_mesg *msg;
 1968         node_p  node;
 1969         int     error = 0;
 1970         int     s;
 1971 
 1972         while (1) {
 1973                 s = splhigh();
 1974                 if ((ngq = ngqbase)) {
 1975                         ngqbase = ngq->next;
 1976                         ngqsize--;
 1977                 }
 1978                 splx(s);
 1979                 if (ngq == NULL)
 1980                         return;
 1981                 switch (ngq->flags) {
 1982                 case NGQF_DATA:
 1983                         hook = ngq->body.data.da_hook;
 1984                         m = ngq->body.data.da_m;
 1985                         meta = ngq->body.data.da_meta;
 1986                         RETURN_QBLK(ngq);
 1987                         NG_SEND_DATAQ(error, hook, m, meta);
 1988                         ng_unref_hook(hook);
 1989                         break;
 1990                 case NGQF_MESG:
 1991                         node = ngq->body.msg.msg_node;
 1992                         msg = ngq->body.msg.msg_msg;
 1993                         retaddr = ngq->body.msg.msg_retaddr;
 1994                         RETURN_QBLK(ngq);
 1995                         if (node->flags & NG_INVALID) {
 1996                                 FREE(msg, M_NETGRAPH);
 1997                         } else {
 1998                                 CALL_MSG_HANDLER(error, node, msg,
 1999                                                  retaddr, NULL);
 2000                         }
 2001                         ng_unref(node);
 2002                         if (retaddr)
 2003                                 FREE(retaddr, M_NETGRAPH);
 2004                         break;
 2005                 default:
 2006                         RETURN_QBLK(ngq);
 2007                 }
 2008         }
 2009 }
 2010 
 2011 

Cache object: 1d1305980bd9b203ed993220f7f194e0


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