xref: /f-stack/lib/ff_ng_base.c (revision 8640edf1)
13b2bd0f6Slogwang /*-
23b2bd0f6Slogwang  * Copyright (c) 1996-1999 Whistle Communications, Inc.
33b2bd0f6Slogwang  * All rights reserved.
43b2bd0f6Slogwang  *
53b2bd0f6Slogwang  * Subject to the following obligations and disclaimer of warranty, use and
63b2bd0f6Slogwang  * redistribution of this software, in source or object code forms, with or
73b2bd0f6Slogwang  * without modifications are expressly permitted by Whistle Communications;
83b2bd0f6Slogwang  * provided, however, that:
93b2bd0f6Slogwang  * 1. Any and all reproductions of the source or object code must include the
103b2bd0f6Slogwang  *    copyright notice above and the following disclaimer of warranties; and
113b2bd0f6Slogwang  * 2. No rights are granted, in any manner or form, to use Whistle
123b2bd0f6Slogwang  *    Communications, Inc. trademarks, including the mark "WHISTLE
133b2bd0f6Slogwang  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
143b2bd0f6Slogwang  *    such appears in the above copyright notice or in the software.
153b2bd0f6Slogwang  *
163b2bd0f6Slogwang  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
173b2bd0f6Slogwang  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
183b2bd0f6Slogwang  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
193b2bd0f6Slogwang  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
203b2bd0f6Slogwang  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
213b2bd0f6Slogwang  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
223b2bd0f6Slogwang  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
233b2bd0f6Slogwang  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
243b2bd0f6Slogwang  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
253b2bd0f6Slogwang  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
263b2bd0f6Slogwang  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
273b2bd0f6Slogwang  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
283b2bd0f6Slogwang  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
293b2bd0f6Slogwang  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
303b2bd0f6Slogwang  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
313b2bd0f6Slogwang  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
323b2bd0f6Slogwang  * OF SUCH DAMAGE.
333b2bd0f6Slogwang  *
343b2bd0f6Slogwang  * Authors: Julian Elischer <[email protected]>
353b2bd0f6Slogwang  *          Archie Cobbs <[email protected]>
363b2bd0f6Slogwang  *
373b2bd0f6Slogwang  * $FreeBSD$
383b2bd0f6Slogwang  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
393b2bd0f6Slogwang  */
403b2bd0f6Slogwang 
413b2bd0f6Slogwang /*
423b2bd0f6Slogwang  * This file implements the base netgraph code.
433b2bd0f6Slogwang  */
443b2bd0f6Slogwang 
453b2bd0f6Slogwang #include <sys/param.h>
463b2bd0f6Slogwang #include <sys/systm.h>
473b2bd0f6Slogwang #include <sys/ctype.h>
483b2bd0f6Slogwang #include <sys/hash.h>
493b2bd0f6Slogwang #include <sys/kdb.h>
503b2bd0f6Slogwang #include <sys/kernel.h>
513b2bd0f6Slogwang #include <sys/kthread.h>
523b2bd0f6Slogwang #include <sys/ktr.h>
533b2bd0f6Slogwang #include <sys/limits.h>
543b2bd0f6Slogwang #include <sys/lock.h>
553b2bd0f6Slogwang #include <sys/malloc.h>
563b2bd0f6Slogwang #include <sys/mbuf.h>
573b2bd0f6Slogwang #include <sys/proc.h>
58*8640edf1Sfengbojiang #include <sys/epoch.h>
593b2bd0f6Slogwang #include <sys/queue.h>
603b2bd0f6Slogwang #include <sys/refcount.h>
613b2bd0f6Slogwang #include <sys/rwlock.h>
623b2bd0f6Slogwang #include <sys/smp.h>
633b2bd0f6Slogwang #include <sys/sysctl.h>
643b2bd0f6Slogwang #include <sys/syslog.h>
653b2bd0f6Slogwang #include <sys/unistd.h>
663b2bd0f6Slogwang #include <machine/cpu.h>
673b2bd0f6Slogwang #include <vm/uma.h>
683b2bd0f6Slogwang 
693b2bd0f6Slogwang #include <net/netisr.h>
703b2bd0f6Slogwang #include <net/vnet.h>
713b2bd0f6Slogwang 
723b2bd0f6Slogwang #include <netgraph/ng_message.h>
733b2bd0f6Slogwang #include <netgraph/netgraph.h>
743b2bd0f6Slogwang #include <netgraph/ng_parse.h>
753b2bd0f6Slogwang 
763b2bd0f6Slogwang MODULE_VERSION(netgraph, NG_ABI_VERSION);
773b2bd0f6Slogwang 
783b2bd0f6Slogwang /* Mutex to protect topology events. */
793b2bd0f6Slogwang static struct rwlock    ng_topo_lock;
803b2bd0f6Slogwang #define    TOPOLOGY_RLOCK()    rw_rlock(&ng_topo_lock)
813b2bd0f6Slogwang #define    TOPOLOGY_RUNLOCK()    rw_runlock(&ng_topo_lock)
823b2bd0f6Slogwang #define    TOPOLOGY_WLOCK()    rw_wlock(&ng_topo_lock)
833b2bd0f6Slogwang #define    TOPOLOGY_WUNLOCK()    rw_wunlock(&ng_topo_lock)
843b2bd0f6Slogwang #define    TOPOLOGY_NOTOWNED()    rw_assert(&ng_topo_lock, RA_UNLOCKED)
853b2bd0f6Slogwang 
863b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
873b2bd0f6Slogwang static struct mtx    ng_nodelist_mtx; /* protects global node/hook lists */
883b2bd0f6Slogwang static struct mtx    ngq_mtx;    /* protects the queue item list */
893b2bd0f6Slogwang 
903b2bd0f6Slogwang static SLIST_HEAD(, ng_node) ng_allnodes;
913b2bd0f6Slogwang static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
923b2bd0f6Slogwang static SLIST_HEAD(, ng_hook) ng_allhooks;
933b2bd0f6Slogwang static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
943b2bd0f6Slogwang 
953b2bd0f6Slogwang static void ng_dumpitems(void);
963b2bd0f6Slogwang static void ng_dumpnodes(void);
973b2bd0f6Slogwang static void ng_dumphooks(void);
983b2bd0f6Slogwang 
993b2bd0f6Slogwang #endif    /* NETGRAPH_DEBUG */
1003b2bd0f6Slogwang /*
1013b2bd0f6Slogwang  * DEAD versions of the structures.
1023b2bd0f6Slogwang  * In order to avoid races, it is sometimes necessary to point
1033b2bd0f6Slogwang  * at SOMETHING even though theoretically, the current entity is
1043b2bd0f6Slogwang  * INVALID. Use these to avoid these races.
1053b2bd0f6Slogwang  */
1063b2bd0f6Slogwang struct ng_type ng_deadtype = {
1073b2bd0f6Slogwang     NG_ABI_VERSION,
1083b2bd0f6Slogwang     "dead",
1093b2bd0f6Slogwang     NULL,    /* modevent */
1103b2bd0f6Slogwang     NULL,    /* constructor */
1113b2bd0f6Slogwang     NULL,    /* rcvmsg */
1123b2bd0f6Slogwang     NULL,    /* shutdown */
1133b2bd0f6Slogwang     NULL,    /* newhook */
1143b2bd0f6Slogwang     NULL,    /* findhook */
1153b2bd0f6Slogwang     NULL,    /* connect */
1163b2bd0f6Slogwang     NULL,    /* rcvdata */
1173b2bd0f6Slogwang     NULL,    /* disconnect */
1183b2bd0f6Slogwang     NULL,     /* cmdlist */
1193b2bd0f6Slogwang };
1203b2bd0f6Slogwang 
1213b2bd0f6Slogwang struct ng_node ng_deadnode = {
1223b2bd0f6Slogwang     "dead",
1233b2bd0f6Slogwang     &ng_deadtype,
1243b2bd0f6Slogwang     NGF_INVALID,
1253b2bd0f6Slogwang     0,    /* numhooks */
1263b2bd0f6Slogwang     NULL,    /* private */
1273b2bd0f6Slogwang     0,    /* ID */
1283b2bd0f6Slogwang     LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
1293b2bd0f6Slogwang     {},    /* all_nodes list entry */
1303b2bd0f6Slogwang     {},    /* id hashtable list entry */
1313b2bd0f6Slogwang     {    0,
1323b2bd0f6Slogwang         0,
1333b2bd0f6Slogwang         {}, /* should never use! (should hang) */
1343b2bd0f6Slogwang         {}, /* workqueue entry */
1353b2bd0f6Slogwang         STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
1363b2bd0f6Slogwang     },
1373b2bd0f6Slogwang     1,    /* refs */
1383b2bd0f6Slogwang     NULL,    /* vnet */
1393b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
1403b2bd0f6Slogwang     ND_MAGIC,
1413b2bd0f6Slogwang     __FILE__,
1423b2bd0f6Slogwang     __LINE__,
1433b2bd0f6Slogwang     {NULL}
1443b2bd0f6Slogwang #endif    /* NETGRAPH_DEBUG */
1453b2bd0f6Slogwang };
1463b2bd0f6Slogwang 
1473b2bd0f6Slogwang struct ng_hook ng_deadhook = {
1483b2bd0f6Slogwang     "dead",
1493b2bd0f6Slogwang     NULL,        /* private */
1503b2bd0f6Slogwang     HK_INVALID | HK_DEAD,
1513b2bd0f6Slogwang     0,        /* undefined data link type */
1523b2bd0f6Slogwang     &ng_deadhook,    /* Peer is self */
1533b2bd0f6Slogwang     &ng_deadnode,    /* attached to deadnode */
1543b2bd0f6Slogwang     {},        /* hooks list */
1553b2bd0f6Slogwang     NULL,        /* override rcvmsg() */
1563b2bd0f6Slogwang     NULL,        /* override rcvdata() */
1573b2bd0f6Slogwang     1,        /* refs always >= 1 */
1583b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
1593b2bd0f6Slogwang     HK_MAGIC,
1603b2bd0f6Slogwang     __FILE__,
1613b2bd0f6Slogwang     __LINE__,
1623b2bd0f6Slogwang     {NULL}
1633b2bd0f6Slogwang #endif    /* NETGRAPH_DEBUG */
1643b2bd0f6Slogwang };
1653b2bd0f6Slogwang 
1663b2bd0f6Slogwang /*
1673b2bd0f6Slogwang  * END DEAD STRUCTURES
1683b2bd0f6Slogwang  */
1693b2bd0f6Slogwang /* List nodes with unallocated work */
1703b2bd0f6Slogwang static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
1713b2bd0f6Slogwang static struct mtx    ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1723b2bd0f6Slogwang 
1733b2bd0f6Slogwang /* List of installed types */
1743b2bd0f6Slogwang static LIST_HEAD(, ng_type) ng_typelist;
1753b2bd0f6Slogwang static struct rwlock    ng_typelist_lock;
1763b2bd0f6Slogwang #define    TYPELIST_RLOCK()    rw_rlock(&ng_typelist_lock)
1773b2bd0f6Slogwang #define    TYPELIST_RUNLOCK()    rw_runlock(&ng_typelist_lock)
1783b2bd0f6Slogwang #define    TYPELIST_WLOCK()    rw_wlock(&ng_typelist_lock)
1793b2bd0f6Slogwang #define    TYPELIST_WUNLOCK()    rw_wunlock(&ng_typelist_lock)
1803b2bd0f6Slogwang 
1813b2bd0f6Slogwang /* Hash related definitions. */
1823b2bd0f6Slogwang LIST_HEAD(nodehash, ng_node);
183*8640edf1Sfengbojiang VNET_DEFINE_STATIC(struct nodehash *, ng_ID_hash);
184*8640edf1Sfengbojiang VNET_DEFINE_STATIC(u_long, ng_ID_hmask);
185*8640edf1Sfengbojiang VNET_DEFINE_STATIC(u_long, ng_nodes);
186*8640edf1Sfengbojiang VNET_DEFINE_STATIC(struct nodehash *, ng_name_hash);
187*8640edf1Sfengbojiang VNET_DEFINE_STATIC(u_long, ng_name_hmask);
188*8640edf1Sfengbojiang VNET_DEFINE_STATIC(u_long, ng_named_nodes);
1893b2bd0f6Slogwang #define    V_ng_ID_hash        VNET(ng_ID_hash)
1903b2bd0f6Slogwang #define    V_ng_ID_hmask        VNET(ng_ID_hmask)
1913b2bd0f6Slogwang #define    V_ng_nodes        VNET(ng_nodes)
1923b2bd0f6Slogwang #define    V_ng_name_hash        VNET(ng_name_hash)
1933b2bd0f6Slogwang #define    V_ng_name_hmask        VNET(ng_name_hmask)
1943b2bd0f6Slogwang #define    V_ng_named_nodes    VNET(ng_named_nodes)
1953b2bd0f6Slogwang 
1963b2bd0f6Slogwang static struct rwlock    ng_idhash_lock;
1973b2bd0f6Slogwang #define    IDHASH_RLOCK()        rw_rlock(&ng_idhash_lock)
1983b2bd0f6Slogwang #define    IDHASH_RUNLOCK()    rw_runlock(&ng_idhash_lock)
1993b2bd0f6Slogwang #define    IDHASH_WLOCK()        rw_wlock(&ng_idhash_lock)
2003b2bd0f6Slogwang #define    IDHASH_WUNLOCK()    rw_wunlock(&ng_idhash_lock)
2013b2bd0f6Slogwang 
2023b2bd0f6Slogwang /* Method to find a node.. used twice so do it here */
2033b2bd0f6Slogwang #define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1))
2043b2bd0f6Slogwang #define NG_IDHASH_FIND(ID, node)                    \
2053b2bd0f6Slogwang     do {                                 \
2063b2bd0f6Slogwang         rw_assert(&ng_idhash_lock, RA_LOCKED);            \
2073b2bd0f6Slogwang         LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],    \
2083b2bd0f6Slogwang                         nd_idnodes) {        \
2093b2bd0f6Slogwang             if (NG_NODE_IS_VALID(node)            \
2103b2bd0f6Slogwang             && (NG_NODE_ID(node) == ID)) {            \
2113b2bd0f6Slogwang                 break;                    \
2123b2bd0f6Slogwang             }                        \
2133b2bd0f6Slogwang         }                            \
2143b2bd0f6Slogwang     } while (0)
2153b2bd0f6Slogwang 
2163b2bd0f6Slogwang static struct rwlock    ng_namehash_lock;
2173b2bd0f6Slogwang #define    NAMEHASH_RLOCK()    rw_rlock(&ng_namehash_lock)
2183b2bd0f6Slogwang #define    NAMEHASH_RUNLOCK()    rw_runlock(&ng_namehash_lock)
2193b2bd0f6Slogwang #define    NAMEHASH_WLOCK()    rw_wlock(&ng_namehash_lock)
2203b2bd0f6Slogwang #define    NAMEHASH_WUNLOCK()    rw_wunlock(&ng_namehash_lock)
2213b2bd0f6Slogwang 
2223b2bd0f6Slogwang /* Internal functions */
2233b2bd0f6Slogwang static int    ng_add_hook(node_p node, const char *name, hook_p * hookp);
2243b2bd0f6Slogwang static int    ng_generic_msg(node_p here, item_p item, hook_p lasthook);
2253b2bd0f6Slogwang static ng_ID_t    ng_decodeidname(const char *name);
2263b2bd0f6Slogwang static int    ngb_mod_event(module_t mod, int event, void *data);
2273b2bd0f6Slogwang #ifndef FSTACK
2283b2bd0f6Slogwang static void    ng_worklist_add(node_p node);
2293b2bd0f6Slogwang static void    ngthread(void *);
2303b2bd0f6Slogwang #endif
2313b2bd0f6Slogwang static int    ng_apply_item(node_p node, item_p item, int rw);
2323b2bd0f6Slogwang #ifndef FSTACK
2333b2bd0f6Slogwang static void    ng_flush_input_queue(node_p node);
2343b2bd0f6Slogwang #endif
2353b2bd0f6Slogwang static node_p    ng_ID2noderef(ng_ID_t ID);
2363b2bd0f6Slogwang static int    ng_con_nodes(item_p item, node_p node, const char *name,
2373b2bd0f6Slogwang             node_p node2, const char *name2);
2383b2bd0f6Slogwang static int    ng_con_part2(node_p node, item_p item, hook_p hook);
2393b2bd0f6Slogwang static int    ng_con_part3(node_p node, item_p item, hook_p hook);
2403b2bd0f6Slogwang static int    ng_mkpeer(node_p node, const char *name, const char *name2,
2413b2bd0f6Slogwang             char *type);
2423b2bd0f6Slogwang static void    ng_name_rehash(void);
2433b2bd0f6Slogwang static void    ng_ID_rehash(void);
2443b2bd0f6Slogwang 
2453b2bd0f6Slogwang /* Imported, these used to be externally visible, some may go back. */
2463b2bd0f6Slogwang void    ng_destroy_hook(hook_p hook);
2473b2bd0f6Slogwang int    ng_path2noderef(node_p here, const char *path,
2483b2bd0f6Slogwang     node_p *dest, hook_p *lasthook);
2493b2bd0f6Slogwang int    ng_make_node(const char *type, node_p *nodepp);
2503b2bd0f6Slogwang int    ng_path_parse(char *addr, char **node, char **path, char **hook);
2513b2bd0f6Slogwang void    ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
2523b2bd0f6Slogwang void    ng_unname(node_p node);
2533b2bd0f6Slogwang 
2543b2bd0f6Slogwang /* Our own netgraph malloc type */
2553b2bd0f6Slogwang MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
2563b2bd0f6Slogwang MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
2573b2bd0f6Slogwang static MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
2583b2bd0f6Slogwang     "netgraph hook structures");
2593b2bd0f6Slogwang static MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
2603b2bd0f6Slogwang     "netgraph node structures");
2613b2bd0f6Slogwang static MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
2623b2bd0f6Slogwang     "netgraph item structures");
2633b2bd0f6Slogwang 
2643b2bd0f6Slogwang /* Should not be visible outside this file */
2653b2bd0f6Slogwang 
2663b2bd0f6Slogwang #define _NG_ALLOC_HOOK(hook) \
2673b2bd0f6Slogwang     hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
2683b2bd0f6Slogwang #define _NG_ALLOC_NODE(node) \
2693b2bd0f6Slogwang     node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
2703b2bd0f6Slogwang 
2713b2bd0f6Slogwang #define    NG_QUEUE_LOCK_INIT(n)            \
2723b2bd0f6Slogwang     mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2733b2bd0f6Slogwang #define    NG_QUEUE_LOCK(n)            \
2743b2bd0f6Slogwang     mtx_lock(&(n)->q_mtx)
2753b2bd0f6Slogwang #define    NG_QUEUE_UNLOCK(n)            \
2763b2bd0f6Slogwang     mtx_unlock(&(n)->q_mtx)
2773b2bd0f6Slogwang #define    NG_WORKLIST_LOCK_INIT()            \
2783b2bd0f6Slogwang     mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2793b2bd0f6Slogwang #define    NG_WORKLIST_LOCK()            \
2803b2bd0f6Slogwang     mtx_lock(&ng_worklist_mtx)
2813b2bd0f6Slogwang #define    NG_WORKLIST_UNLOCK()            \
2823b2bd0f6Slogwang     mtx_unlock(&ng_worklist_mtx)
2833b2bd0f6Slogwang #define    NG_WORKLIST_SLEEP()            \
2843b2bd0f6Slogwang     mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
2853b2bd0f6Slogwang #define    NG_WORKLIST_WAKEUP()            \
2863b2bd0f6Slogwang     wakeup_one(&ng_worklist)
2873b2bd0f6Slogwang 
2883b2bd0f6Slogwang #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
2893b2bd0f6Slogwang /*
2903b2bd0f6Slogwang  * In debug mode:
2913b2bd0f6Slogwang  * In an attempt to help track reference count screwups
2923b2bd0f6Slogwang  * we do not free objects back to the malloc system, but keep them
2933b2bd0f6Slogwang  * in a local cache where we can examine them and keep information safely
2943b2bd0f6Slogwang  * after they have been freed.
2953b2bd0f6Slogwang  * We use this scheme for nodes and hooks, and to some extent for items.
2963b2bd0f6Slogwang  */
2973b2bd0f6Slogwang static __inline hook_p
ng_alloc_hook(void)2983b2bd0f6Slogwang ng_alloc_hook(void)
2993b2bd0f6Slogwang {
3003b2bd0f6Slogwang     hook_p hook;
3013b2bd0f6Slogwang     SLIST_ENTRY(ng_hook) temp;
3023b2bd0f6Slogwang     mtx_lock(&ng_nodelist_mtx);
3033b2bd0f6Slogwang     hook = LIST_FIRST(&ng_freehooks);
3043b2bd0f6Slogwang     if (hook) {
3053b2bd0f6Slogwang         LIST_REMOVE(hook, hk_hooks);
3063b2bd0f6Slogwang         bcopy(&hook->hk_all, &temp, sizeof(temp));
3073b2bd0f6Slogwang         bzero(hook, sizeof(struct ng_hook));
3083b2bd0f6Slogwang         bcopy(&temp, &hook->hk_all, sizeof(temp));
3093b2bd0f6Slogwang         mtx_unlock(&ng_nodelist_mtx);
3103b2bd0f6Slogwang         hook->hk_magic = HK_MAGIC;
3113b2bd0f6Slogwang     } else {
3123b2bd0f6Slogwang         mtx_unlock(&ng_nodelist_mtx);
3133b2bd0f6Slogwang         _NG_ALLOC_HOOK(hook);
3143b2bd0f6Slogwang         if (hook) {
3153b2bd0f6Slogwang             hook->hk_magic = HK_MAGIC;
3163b2bd0f6Slogwang             mtx_lock(&ng_nodelist_mtx);
3173b2bd0f6Slogwang             SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
3183b2bd0f6Slogwang             mtx_unlock(&ng_nodelist_mtx);
3193b2bd0f6Slogwang         }
3203b2bd0f6Slogwang     }
3213b2bd0f6Slogwang     return (hook);
3223b2bd0f6Slogwang }
3233b2bd0f6Slogwang 
3243b2bd0f6Slogwang static __inline node_p
ng_alloc_node(void)3253b2bd0f6Slogwang ng_alloc_node(void)
3263b2bd0f6Slogwang {
3273b2bd0f6Slogwang     node_p node;
3283b2bd0f6Slogwang     SLIST_ENTRY(ng_node) temp;
3293b2bd0f6Slogwang     mtx_lock(&ng_nodelist_mtx);
3303b2bd0f6Slogwang     node = LIST_FIRST(&ng_freenodes);
3313b2bd0f6Slogwang     if (node) {
3323b2bd0f6Slogwang         LIST_REMOVE(node, nd_nodes);
3333b2bd0f6Slogwang         bcopy(&node->nd_all, &temp, sizeof(temp));
3343b2bd0f6Slogwang         bzero(node, sizeof(struct ng_node));
3353b2bd0f6Slogwang         bcopy(&temp, &node->nd_all, sizeof(temp));
3363b2bd0f6Slogwang         mtx_unlock(&ng_nodelist_mtx);
3373b2bd0f6Slogwang         node->nd_magic = ND_MAGIC;
3383b2bd0f6Slogwang     } else {
3393b2bd0f6Slogwang         mtx_unlock(&ng_nodelist_mtx);
3403b2bd0f6Slogwang         _NG_ALLOC_NODE(node);
3413b2bd0f6Slogwang         if (node) {
3423b2bd0f6Slogwang             node->nd_magic = ND_MAGIC;
3433b2bd0f6Slogwang             mtx_lock(&ng_nodelist_mtx);
3443b2bd0f6Slogwang             SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3453b2bd0f6Slogwang             mtx_unlock(&ng_nodelist_mtx);
3463b2bd0f6Slogwang         }
3473b2bd0f6Slogwang     }
3483b2bd0f6Slogwang     return (node);
3493b2bd0f6Slogwang }
3503b2bd0f6Slogwang 
3513b2bd0f6Slogwang #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
3523b2bd0f6Slogwang #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
3533b2bd0f6Slogwang 
3543b2bd0f6Slogwang #define NG_FREE_HOOK(hook)                        \
3553b2bd0f6Slogwang     do {                                \
3563b2bd0f6Slogwang         mtx_lock(&ng_nodelist_mtx);                \
3573b2bd0f6Slogwang         LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);    \
3583b2bd0f6Slogwang         hook->hk_magic = 0;                    \
3593b2bd0f6Slogwang         mtx_unlock(&ng_nodelist_mtx);                \
3603b2bd0f6Slogwang     } while (0)
3613b2bd0f6Slogwang 
3623b2bd0f6Slogwang #define NG_FREE_NODE(node)                        \
3633b2bd0f6Slogwang     do {                                \
3643b2bd0f6Slogwang         mtx_lock(&ng_nodelist_mtx);                \
3653b2bd0f6Slogwang         LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);    \
3663b2bd0f6Slogwang         node->nd_magic = 0;                    \
3673b2bd0f6Slogwang         mtx_unlock(&ng_nodelist_mtx);                \
3683b2bd0f6Slogwang     } while (0)
3693b2bd0f6Slogwang 
3703b2bd0f6Slogwang #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
3713b2bd0f6Slogwang 
3723b2bd0f6Slogwang #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
3733b2bd0f6Slogwang #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
3743b2bd0f6Slogwang 
3753b2bd0f6Slogwang #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
3763b2bd0f6Slogwang #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
3773b2bd0f6Slogwang 
3783b2bd0f6Slogwang #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
3793b2bd0f6Slogwang 
3803b2bd0f6Slogwang /* Set this to kdb_enter("X") to catch all errors as they occur */
3813b2bd0f6Slogwang #ifndef TRAP_ERROR
3823b2bd0f6Slogwang #define TRAP_ERROR()
3833b2bd0f6Slogwang #endif
3843b2bd0f6Slogwang 
385*8640edf1Sfengbojiang VNET_DEFINE_STATIC(ng_ID_t, nextID) = 1;
3863b2bd0f6Slogwang #define    V_nextID            VNET(nextID)
3873b2bd0f6Slogwang 
3883b2bd0f6Slogwang #ifdef INVARIANTS
3893b2bd0f6Slogwang #define CHECK_DATA_MBUF(m)    do {                    \
3903b2bd0f6Slogwang         struct mbuf *n;                        \
3913b2bd0f6Slogwang         int total;                        \
3923b2bd0f6Slogwang                                     \
3933b2bd0f6Slogwang         M_ASSERTPKTHDR(m);                    \
3943b2bd0f6Slogwang         for (total = 0, n = (m); n != NULL; n = n->m_next) {    \
3953b2bd0f6Slogwang             total += n->m_len;                \
3963b2bd0f6Slogwang             if (n->m_nextpkt != NULL)            \
3973b2bd0f6Slogwang                 panic("%s: m_nextpkt", __func__);    \
3983b2bd0f6Slogwang         }                            \
3993b2bd0f6Slogwang                                     \
4003b2bd0f6Slogwang         if ((m)->m_pkthdr.len != total) {            \
4013b2bd0f6Slogwang             panic("%s: %d != %d",                \
4023b2bd0f6Slogwang                 __func__, (m)->m_pkthdr.len, total);    \
4033b2bd0f6Slogwang         }                            \
4043b2bd0f6Slogwang     } while (0)
4053b2bd0f6Slogwang #else
4063b2bd0f6Slogwang #define CHECK_DATA_MBUF(m)
4073b2bd0f6Slogwang #endif
4083b2bd0f6Slogwang 
4093b2bd0f6Slogwang #define ERROUT(x)    do { error = (x); goto done; } while (0)
4103b2bd0f6Slogwang 
4113b2bd0f6Slogwang /************************************************************************
4123b2bd0f6Slogwang     Parse type definitions for generic messages
4133b2bd0f6Slogwang ************************************************************************/
4143b2bd0f6Slogwang 
4153b2bd0f6Slogwang /* Handy structure parse type defining macro */
4163b2bd0f6Slogwang #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)                \
4173b2bd0f6Slogwang static const struct ng_parse_struct_field                \
4183b2bd0f6Slogwang     ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;    \
4193b2bd0f6Slogwang static const struct ng_parse_type ng_generic_ ## lo ## _type = {    \
4203b2bd0f6Slogwang     &ng_parse_struct_type,                        \
4213b2bd0f6Slogwang     &ng_ ## lo ## _type_fields                    \
4223b2bd0f6Slogwang }
4233b2bd0f6Slogwang 
4243b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
4253b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
4263b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
4273b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
4283b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
4293b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
4303b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
4313b2bd0f6Slogwang 
4323b2bd0f6Slogwang /* Get length of an array when the length is stored as a 32 bit
4333b2bd0f6Slogwang    value immediately preceding the array -- as with struct namelist
4343b2bd0f6Slogwang    and struct typelist. */
4353b2bd0f6Slogwang static int
ng_generic_list_getLength(const struct ng_parse_type * type,const u_char * start,const u_char * buf)4363b2bd0f6Slogwang ng_generic_list_getLength(const struct ng_parse_type *type,
4373b2bd0f6Slogwang     const u_char *start, const u_char *buf)
4383b2bd0f6Slogwang {
4393b2bd0f6Slogwang     return *((const u_int32_t *)(buf - 4));
4403b2bd0f6Slogwang }
4413b2bd0f6Slogwang 
4423b2bd0f6Slogwang /* Get length of the array of struct linkinfo inside a struct hooklist */
4433b2bd0f6Slogwang static int
ng_generic_linkinfo_getLength(const struct ng_parse_type * type,const u_char * start,const u_char * buf)4443b2bd0f6Slogwang ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
4453b2bd0f6Slogwang     const u_char *start, const u_char *buf)
4463b2bd0f6Slogwang {
4473b2bd0f6Slogwang     const struct hooklist *hl = (const struct hooklist *)start;
4483b2bd0f6Slogwang 
4493b2bd0f6Slogwang     return hl->nodeinfo.hooks;
4503b2bd0f6Slogwang }
4513b2bd0f6Slogwang 
4523b2bd0f6Slogwang /* Array type for a variable length array of struct namelist */
4533b2bd0f6Slogwang static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
4543b2bd0f6Slogwang     &ng_generic_nodeinfo_type,
4553b2bd0f6Slogwang     &ng_generic_list_getLength
4563b2bd0f6Slogwang };
4573b2bd0f6Slogwang static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
4583b2bd0f6Slogwang     &ng_parse_array_type,
4593b2bd0f6Slogwang     &ng_nodeinfoarray_type_info
4603b2bd0f6Slogwang };
4613b2bd0f6Slogwang 
4623b2bd0f6Slogwang /* Array type for a variable length array of struct typelist */
4633b2bd0f6Slogwang static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
4643b2bd0f6Slogwang     &ng_generic_typeinfo_type,
4653b2bd0f6Slogwang     &ng_generic_list_getLength
4663b2bd0f6Slogwang };
4673b2bd0f6Slogwang static const struct ng_parse_type ng_generic_typeinfoarray_type = {
4683b2bd0f6Slogwang     &ng_parse_array_type,
4693b2bd0f6Slogwang     &ng_typeinfoarray_type_info
4703b2bd0f6Slogwang };
4713b2bd0f6Slogwang 
4723b2bd0f6Slogwang /* Array type for array of struct linkinfo in struct hooklist */
4733b2bd0f6Slogwang static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
4743b2bd0f6Slogwang     &ng_generic_linkinfo_type,
4753b2bd0f6Slogwang     &ng_generic_linkinfo_getLength
4763b2bd0f6Slogwang };
4773b2bd0f6Slogwang static const struct ng_parse_type ng_generic_linkinfo_array_type = {
4783b2bd0f6Slogwang     &ng_parse_array_type,
4793b2bd0f6Slogwang     &ng_generic_linkinfo_array_type_info
4803b2bd0f6Slogwang };
4813b2bd0f6Slogwang 
4823b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_typeinfoarray_type));
4833b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
4843b2bd0f6Slogwang     (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
4853b2bd0f6Slogwang DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
4863b2bd0f6Slogwang     (&ng_generic_nodeinfoarray_type));
4873b2bd0f6Slogwang 
4883b2bd0f6Slogwang /* List of commands and how to convert arguments to/from ASCII */
4893b2bd0f6Slogwang static const struct ng_cmdlist ng_generic_cmds[] = {
4903b2bd0f6Slogwang     {
4913b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
4923b2bd0f6Slogwang       NGM_SHUTDOWN,
4933b2bd0f6Slogwang       "shutdown",
4943b2bd0f6Slogwang       NULL,
4953b2bd0f6Slogwang       NULL
4963b2bd0f6Slogwang     },
4973b2bd0f6Slogwang     {
4983b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
4993b2bd0f6Slogwang       NGM_MKPEER,
5003b2bd0f6Slogwang       "mkpeer",
5013b2bd0f6Slogwang       &ng_generic_mkpeer_type,
5023b2bd0f6Slogwang       NULL
5033b2bd0f6Slogwang     },
5043b2bd0f6Slogwang     {
5053b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5063b2bd0f6Slogwang       NGM_CONNECT,
5073b2bd0f6Slogwang       "connect",
5083b2bd0f6Slogwang       &ng_generic_connect_type,
5093b2bd0f6Slogwang       NULL
5103b2bd0f6Slogwang     },
5113b2bd0f6Slogwang     {
5123b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5133b2bd0f6Slogwang       NGM_NAME,
5143b2bd0f6Slogwang       "name",
5153b2bd0f6Slogwang       &ng_generic_name_type,
5163b2bd0f6Slogwang       NULL
5173b2bd0f6Slogwang     },
5183b2bd0f6Slogwang     {
5193b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5203b2bd0f6Slogwang       NGM_RMHOOK,
5213b2bd0f6Slogwang       "rmhook",
5223b2bd0f6Slogwang       &ng_generic_rmhook_type,
5233b2bd0f6Slogwang       NULL
5243b2bd0f6Slogwang     },
5253b2bd0f6Slogwang     {
5263b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5273b2bd0f6Slogwang       NGM_NODEINFO,
5283b2bd0f6Slogwang       "nodeinfo",
5293b2bd0f6Slogwang       NULL,
5303b2bd0f6Slogwang       &ng_generic_nodeinfo_type
5313b2bd0f6Slogwang     },
5323b2bd0f6Slogwang     {
5333b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5343b2bd0f6Slogwang       NGM_LISTHOOKS,
5353b2bd0f6Slogwang       "listhooks",
5363b2bd0f6Slogwang       NULL,
5373b2bd0f6Slogwang       &ng_generic_hooklist_type
5383b2bd0f6Slogwang     },
5393b2bd0f6Slogwang     {
5403b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5413b2bd0f6Slogwang       NGM_LISTNAMES,
5423b2bd0f6Slogwang       "listnames",
5433b2bd0f6Slogwang       NULL,
5443b2bd0f6Slogwang       &ng_generic_listnodes_type    /* same as NGM_LISTNODES */
5453b2bd0f6Slogwang     },
5463b2bd0f6Slogwang     {
5473b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5483b2bd0f6Slogwang       NGM_LISTNODES,
5493b2bd0f6Slogwang       "listnodes",
5503b2bd0f6Slogwang       NULL,
5513b2bd0f6Slogwang       &ng_generic_listnodes_type
5523b2bd0f6Slogwang     },
5533b2bd0f6Slogwang     {
5543b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5553b2bd0f6Slogwang       NGM_LISTTYPES,
5563b2bd0f6Slogwang       "listtypes",
5573b2bd0f6Slogwang       NULL,
5583b2bd0f6Slogwang       &ng_generic_typelist_type
5593b2bd0f6Slogwang     },
5603b2bd0f6Slogwang     {
5613b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5623b2bd0f6Slogwang       NGM_TEXT_CONFIG,
5633b2bd0f6Slogwang       "textconfig",
5643b2bd0f6Slogwang       NULL,
5653b2bd0f6Slogwang       &ng_parse_string_type
5663b2bd0f6Slogwang     },
5673b2bd0f6Slogwang     {
5683b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5693b2bd0f6Slogwang       NGM_TEXT_STATUS,
5703b2bd0f6Slogwang       "textstatus",
5713b2bd0f6Slogwang       NULL,
5723b2bd0f6Slogwang       &ng_parse_string_type
5733b2bd0f6Slogwang     },
5743b2bd0f6Slogwang     {
5753b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5763b2bd0f6Slogwang       NGM_ASCII2BINARY,
5773b2bd0f6Slogwang       "ascii2binary",
5783b2bd0f6Slogwang       &ng_parse_ng_mesg_type,
5793b2bd0f6Slogwang       &ng_parse_ng_mesg_type
5803b2bd0f6Slogwang     },
5813b2bd0f6Slogwang     {
5823b2bd0f6Slogwang       NGM_GENERIC_COOKIE,
5833b2bd0f6Slogwang       NGM_BINARY2ASCII,
5843b2bd0f6Slogwang       "binary2ascii",
5853b2bd0f6Slogwang       &ng_parse_ng_mesg_type,
5863b2bd0f6Slogwang       &ng_parse_ng_mesg_type
5873b2bd0f6Slogwang     },
5883b2bd0f6Slogwang     { 0 }
5893b2bd0f6Slogwang };
5903b2bd0f6Slogwang 
5913b2bd0f6Slogwang /************************************************************************
5923b2bd0f6Slogwang             Node routines
5933b2bd0f6Slogwang ************************************************************************/
5943b2bd0f6Slogwang 
5953b2bd0f6Slogwang /*
5963b2bd0f6Slogwang  * Instantiate a node of the requested type
5973b2bd0f6Slogwang  */
5983b2bd0f6Slogwang int
ng_make_node(const char * typename,node_p * nodepp)5993b2bd0f6Slogwang ng_make_node(const char *typename, node_p *nodepp)
6003b2bd0f6Slogwang {
6013b2bd0f6Slogwang     struct ng_type *type;
6023b2bd0f6Slogwang     int    error;
6033b2bd0f6Slogwang 
6043b2bd0f6Slogwang     /* Check that the type makes sense */
6053b2bd0f6Slogwang     if (typename == NULL) {
6063b2bd0f6Slogwang         TRAP_ERROR();
6073b2bd0f6Slogwang         return (EINVAL);
6083b2bd0f6Slogwang     }
6093b2bd0f6Slogwang 
6103b2bd0f6Slogwang     /* Locate the node type. If we fail we return. Do not try to load
6113b2bd0f6Slogwang      * module.
6123b2bd0f6Slogwang      */
6133b2bd0f6Slogwang     if ((type = ng_findtype(typename)) == NULL)
6143b2bd0f6Slogwang         return (ENXIO);
6153b2bd0f6Slogwang 
6163b2bd0f6Slogwang     /*
6173b2bd0f6Slogwang      * If we have a constructor, then make the node and
6183b2bd0f6Slogwang      * call the constructor to do type specific initialisation.
6193b2bd0f6Slogwang      */
6203b2bd0f6Slogwang     if (type->constructor != NULL) {
6213b2bd0f6Slogwang         if ((error = ng_make_node_common(type, nodepp)) == 0) {
6223b2bd0f6Slogwang             if ((error = ((*type->constructor)(*nodepp))) != 0) {
6233b2bd0f6Slogwang                 NG_NODE_UNREF(*nodepp);
6243b2bd0f6Slogwang             }
6253b2bd0f6Slogwang         }
6263b2bd0f6Slogwang     } else {
6273b2bd0f6Slogwang         /*
6283b2bd0f6Slogwang          * Node has no constructor. We cannot ask for one
6293b2bd0f6Slogwang          * to be made. It must be brought into existence by
6303b2bd0f6Slogwang          * some external agency. The external agency should
6313b2bd0f6Slogwang          * call ng_make_node_common() directly to get the
6323b2bd0f6Slogwang          * netgraph part initialised.
6333b2bd0f6Slogwang          */
6343b2bd0f6Slogwang         TRAP_ERROR();
6353b2bd0f6Slogwang         error = EINVAL;
6363b2bd0f6Slogwang     }
6373b2bd0f6Slogwang     return (error);
6383b2bd0f6Slogwang }
6393b2bd0f6Slogwang 
6403b2bd0f6Slogwang /*
6413b2bd0f6Slogwang  * Generic node creation. Called by node initialisation for externally
6423b2bd0f6Slogwang  * instantiated nodes (e.g. hardware, sockets, etc ).
6433b2bd0f6Slogwang  * The returned node has a reference count of 1.
6443b2bd0f6Slogwang  */
6453b2bd0f6Slogwang int
ng_make_node_common(struct ng_type * type,node_p * nodepp)6463b2bd0f6Slogwang ng_make_node_common(struct ng_type *type, node_p *nodepp)
6473b2bd0f6Slogwang {
6483b2bd0f6Slogwang     node_p node;
6493b2bd0f6Slogwang 
6503b2bd0f6Slogwang     /* Require the node type to have been already installed */
6513b2bd0f6Slogwang     if (ng_findtype(type->name) == NULL) {
6523b2bd0f6Slogwang         TRAP_ERROR();
6533b2bd0f6Slogwang         return (EINVAL);
6543b2bd0f6Slogwang     }
6553b2bd0f6Slogwang 
6563b2bd0f6Slogwang     /* Make a node and try attach it to the type */
6573b2bd0f6Slogwang     NG_ALLOC_NODE(node);
6583b2bd0f6Slogwang     if (node == NULL) {
6593b2bd0f6Slogwang         TRAP_ERROR();
6603b2bd0f6Slogwang         return (ENOMEM);
6613b2bd0f6Slogwang     }
6623b2bd0f6Slogwang     node->nd_type = type;
6633b2bd0f6Slogwang #ifdef VIMAGE
6643b2bd0f6Slogwang     node->nd_vnet = curvnet;
6653b2bd0f6Slogwang #endif
6663b2bd0f6Slogwang     NG_NODE_REF(node);                /* note reference */
6673b2bd0f6Slogwang     type->refs++;
6683b2bd0f6Slogwang 
6693b2bd0f6Slogwang     NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6703b2bd0f6Slogwang     STAILQ_INIT(&node->nd_input_queue.queue);
6713b2bd0f6Slogwang     node->nd_input_queue.q_flags = 0;
6723b2bd0f6Slogwang 
6733b2bd0f6Slogwang     /* Initialize hook list for new node */
6743b2bd0f6Slogwang     LIST_INIT(&node->nd_hooks);
6753b2bd0f6Slogwang 
6763b2bd0f6Slogwang     /* Get an ID and put us in the hash chain. */
6773b2bd0f6Slogwang     IDHASH_WLOCK();
6783b2bd0f6Slogwang     for (;;) { /* wrap protection, even if silly */
6793b2bd0f6Slogwang         node_p node2 = NULL;
6803b2bd0f6Slogwang         node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6813b2bd0f6Slogwang 
6823b2bd0f6Slogwang         /* Is there a problem with the new number? */
6833b2bd0f6Slogwang         NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6843b2bd0f6Slogwang         if ((node->nd_ID != 0) && (node2 == NULL)) {
6853b2bd0f6Slogwang             break;
6863b2bd0f6Slogwang         }
6873b2bd0f6Slogwang     }
6883b2bd0f6Slogwang     V_ng_nodes++;
6893b2bd0f6Slogwang     if (V_ng_nodes * 2 > V_ng_ID_hmask)
6903b2bd0f6Slogwang         ng_ID_rehash();
6913b2bd0f6Slogwang     LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
6923b2bd0f6Slogwang         nd_idnodes);
6933b2bd0f6Slogwang     IDHASH_WUNLOCK();
6943b2bd0f6Slogwang 
6953b2bd0f6Slogwang     /* Done */
6963b2bd0f6Slogwang     *nodepp = node;
6973b2bd0f6Slogwang     return (0);
6983b2bd0f6Slogwang }
6993b2bd0f6Slogwang 
7003b2bd0f6Slogwang /*
7013b2bd0f6Slogwang  * Forceably start the shutdown process on a node. Either call
7023b2bd0f6Slogwang  * its shutdown method, or do the default shutdown if there is
7033b2bd0f6Slogwang  * no type-specific method.
7043b2bd0f6Slogwang  *
7053b2bd0f6Slogwang  * We can only be called from a shutdown message, so we know we have
7063b2bd0f6Slogwang  * a writer lock, and therefore exclusive access. It also means
7073b2bd0f6Slogwang  * that we should not be on the work queue, but we check anyhow.
7083b2bd0f6Slogwang  *
7093b2bd0f6Slogwang  * Persistent node types must have a type-specific method which
7103b2bd0f6Slogwang  * allocates a new node in which case, this one is irretrievably going away,
7113b2bd0f6Slogwang  * or cleans up anything it needs, and just makes the node valid again,
7123b2bd0f6Slogwang  * in which case we allow the node to survive.
7133b2bd0f6Slogwang  *
7143b2bd0f6Slogwang  * XXX We need to think of how to tell a persistent node that we
7153b2bd0f6Slogwang  * REALLY need to go away because the hardware has gone or we
7163b2bd0f6Slogwang  * are rebooting.... etc.
7173b2bd0f6Slogwang  */
7183b2bd0f6Slogwang void
ng_rmnode(node_p node,hook_p dummy1,void * dummy2,int dummy3)7193b2bd0f6Slogwang ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
7203b2bd0f6Slogwang {
7213b2bd0f6Slogwang     hook_p hook;
7223b2bd0f6Slogwang 
7233b2bd0f6Slogwang     /* Check if it's already shutting down */
7243b2bd0f6Slogwang     if ((node->nd_flags & NGF_CLOSING) != 0)
7253b2bd0f6Slogwang         return;
7263b2bd0f6Slogwang 
7273b2bd0f6Slogwang     if (node == &ng_deadnode) {
7283b2bd0f6Slogwang         printf ("shutdown called on deadnode\n");
7293b2bd0f6Slogwang         return;
7303b2bd0f6Slogwang     }
7313b2bd0f6Slogwang 
7323b2bd0f6Slogwang     /* Add an extra reference so it doesn't go away during this */
7333b2bd0f6Slogwang     NG_NODE_REF(node);
7343b2bd0f6Slogwang 
7353b2bd0f6Slogwang     /*
7363b2bd0f6Slogwang      * Mark it invalid so any newcomers know not to try use it
7373b2bd0f6Slogwang      * Also add our own mark so we can't recurse
7383b2bd0f6Slogwang      * note that NGF_INVALID does not do this as it's also set during
7393b2bd0f6Slogwang      * creation
7403b2bd0f6Slogwang      */
7413b2bd0f6Slogwang     node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7423b2bd0f6Slogwang 
7433b2bd0f6Slogwang     /* If node has its pre-shutdown method, then call it first*/
7443b2bd0f6Slogwang     if (node->nd_type && node->nd_type->close)
7453b2bd0f6Slogwang         (*node->nd_type->close)(node);
7463b2bd0f6Slogwang 
7473b2bd0f6Slogwang     /* Notify all remaining connected nodes to disconnect */
7483b2bd0f6Slogwang     while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7493b2bd0f6Slogwang         ng_destroy_hook(hook);
7503b2bd0f6Slogwang 
7513b2bd0f6Slogwang #ifndef FSTACK
7523b2bd0f6Slogwang     /*
7533b2bd0f6Slogwang      * Drain the input queue forceably.
7543b2bd0f6Slogwang      * it has no hooks so what's it going to do, bleed on someone?
7553b2bd0f6Slogwang      * Theoretically we came here from a queue entry that was added
7563b2bd0f6Slogwang      * Just before the queue was closed, so it should be empty anyway.
7573b2bd0f6Slogwang      * Also removes us from worklist if needed.
7583b2bd0f6Slogwang      */
7593b2bd0f6Slogwang     ng_flush_input_queue(node);
7603b2bd0f6Slogwang #endif
7613b2bd0f6Slogwang 
7623b2bd0f6Slogwang     /* Ask the type if it has anything to do in this case */
7633b2bd0f6Slogwang     if (node->nd_type && node->nd_type->shutdown) {
7643b2bd0f6Slogwang         (*node->nd_type->shutdown)(node);
7653b2bd0f6Slogwang         if (NG_NODE_IS_VALID(node)) {
7663b2bd0f6Slogwang             /*
7673b2bd0f6Slogwang              * Well, blow me down if the node code hasn't declared
7683b2bd0f6Slogwang              * that it doesn't want to die.
7693b2bd0f6Slogwang              * Presumably it is a persistent node.
7703b2bd0f6Slogwang              * If we REALLY want it to go away,
7713b2bd0f6Slogwang              *  e.g. hardware going away,
7723b2bd0f6Slogwang              * Our caller should set NGF_REALLY_DIE in nd_flags.
7733b2bd0f6Slogwang              */
7743b2bd0f6Slogwang             node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7753b2bd0f6Slogwang             NG_NODE_UNREF(node); /* Assume they still have theirs */
7763b2bd0f6Slogwang             return;
7773b2bd0f6Slogwang         }
7783b2bd0f6Slogwang     } else {                /* do the default thing */
7793b2bd0f6Slogwang         NG_NODE_UNREF(node);
7803b2bd0f6Slogwang     }
7813b2bd0f6Slogwang 
7823b2bd0f6Slogwang     ng_unname(node); /* basically a NOP these days */
7833b2bd0f6Slogwang 
7843b2bd0f6Slogwang     /*
7853b2bd0f6Slogwang      * Remove extra reference, possibly the last
7863b2bd0f6Slogwang      * Possible other holders of references may include
7873b2bd0f6Slogwang      * timeout callouts, but theoretically the node's supposed to
7883b2bd0f6Slogwang      * have cancelled them. Possibly hardware dependencies may
7893b2bd0f6Slogwang      * force a driver to 'linger' with a reference.
7903b2bd0f6Slogwang      */
7913b2bd0f6Slogwang     NG_NODE_UNREF(node);
7923b2bd0f6Slogwang }
7933b2bd0f6Slogwang 
7943b2bd0f6Slogwang /*
7953b2bd0f6Slogwang  * Remove a reference to the node, possibly the last.
7963b2bd0f6Slogwang  * deadnode always acts as it it were the last.
7973b2bd0f6Slogwang  */
7983b2bd0f6Slogwang void
ng_unref_node(node_p node)7993b2bd0f6Slogwang ng_unref_node(node_p node)
8003b2bd0f6Slogwang {
8013b2bd0f6Slogwang 
8023b2bd0f6Slogwang     if (node == &ng_deadnode)
8033b2bd0f6Slogwang         return;
8043b2bd0f6Slogwang 
8053b2bd0f6Slogwang     CURVNET_SET(node->nd_vnet);
8063b2bd0f6Slogwang 
8073b2bd0f6Slogwang     if (refcount_release(&node->nd_refs)) { /* we were the last */
8083b2bd0f6Slogwang 
8093b2bd0f6Slogwang         node->nd_type->refs--; /* XXX maybe should get types lock? */
8103b2bd0f6Slogwang         NAMEHASH_WLOCK();
8113b2bd0f6Slogwang         if (NG_NODE_HAS_NAME(node)) {
8123b2bd0f6Slogwang             V_ng_named_nodes--;
8133b2bd0f6Slogwang             LIST_REMOVE(node, nd_nodes);
8143b2bd0f6Slogwang         }
8153b2bd0f6Slogwang         NAMEHASH_WUNLOCK();
8163b2bd0f6Slogwang 
8173b2bd0f6Slogwang         IDHASH_WLOCK();
8183b2bd0f6Slogwang         V_ng_nodes--;
8193b2bd0f6Slogwang         LIST_REMOVE(node, nd_idnodes);
8203b2bd0f6Slogwang         IDHASH_WUNLOCK();
8213b2bd0f6Slogwang 
8223b2bd0f6Slogwang         mtx_destroy(&node->nd_input_queue.q_mtx);
8233b2bd0f6Slogwang         NG_FREE_NODE(node);
8243b2bd0f6Slogwang     }
8253b2bd0f6Slogwang     CURVNET_RESTORE();
8263b2bd0f6Slogwang }
8273b2bd0f6Slogwang 
8283b2bd0f6Slogwang /************************************************************************
8293b2bd0f6Slogwang             Node ID handling
8303b2bd0f6Slogwang ************************************************************************/
8313b2bd0f6Slogwang static node_p
ng_ID2noderef(ng_ID_t ID)8323b2bd0f6Slogwang ng_ID2noderef(ng_ID_t ID)
8333b2bd0f6Slogwang {
8343b2bd0f6Slogwang     node_p node;
8353b2bd0f6Slogwang 
8363b2bd0f6Slogwang     IDHASH_RLOCK();
8373b2bd0f6Slogwang     NG_IDHASH_FIND(ID, node);
8383b2bd0f6Slogwang     if (node)
8393b2bd0f6Slogwang         NG_NODE_REF(node);
8403b2bd0f6Slogwang     IDHASH_RUNLOCK();
8413b2bd0f6Slogwang     return(node);
8423b2bd0f6Slogwang }
8433b2bd0f6Slogwang 
8443b2bd0f6Slogwang ng_ID_t
ng_node2ID(node_p node)8453b2bd0f6Slogwang ng_node2ID(node_p node)
8463b2bd0f6Slogwang {
8473b2bd0f6Slogwang     return (node ? NG_NODE_ID(node) : 0);
8483b2bd0f6Slogwang }
8493b2bd0f6Slogwang 
8503b2bd0f6Slogwang /************************************************************************
8513b2bd0f6Slogwang             Node name handling
8523b2bd0f6Slogwang ************************************************************************/
8533b2bd0f6Slogwang 
8543b2bd0f6Slogwang /*
8553b2bd0f6Slogwang  * Assign a node a name.
8563b2bd0f6Slogwang  */
8573b2bd0f6Slogwang int
ng_name_node(node_p node,const char * name)8583b2bd0f6Slogwang ng_name_node(node_p node, const char *name)
8593b2bd0f6Slogwang {
8603b2bd0f6Slogwang     uint32_t hash;
8613b2bd0f6Slogwang     node_p node2;
8623b2bd0f6Slogwang     int i;
8633b2bd0f6Slogwang 
8643b2bd0f6Slogwang     /* Check the name is valid */
8653b2bd0f6Slogwang     for (i = 0; i < NG_NODESIZ; i++) {
8663b2bd0f6Slogwang         if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8673b2bd0f6Slogwang             break;
8683b2bd0f6Slogwang     }
8693b2bd0f6Slogwang     if (i == 0 || name[i] != '\0') {
8703b2bd0f6Slogwang         TRAP_ERROR();
8713b2bd0f6Slogwang         return (EINVAL);
8723b2bd0f6Slogwang     }
8733b2bd0f6Slogwang     if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8743b2bd0f6Slogwang         TRAP_ERROR();
8753b2bd0f6Slogwang         return (EINVAL);
8763b2bd0f6Slogwang     }
8773b2bd0f6Slogwang 
8783b2bd0f6Slogwang     NAMEHASH_WLOCK();
8793b2bd0f6Slogwang     if (V_ng_named_nodes * 2 > V_ng_name_hmask)
8803b2bd0f6Slogwang         ng_name_rehash();
8813b2bd0f6Slogwang 
8823b2bd0f6Slogwang     hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
8833b2bd0f6Slogwang     /* Check the name isn't already being used. */
8843b2bd0f6Slogwang     LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes)
8853b2bd0f6Slogwang         if (NG_NODE_IS_VALID(node2) &&
8863b2bd0f6Slogwang             (strcmp(NG_NODE_NAME(node2), name) == 0)) {
8873b2bd0f6Slogwang             NAMEHASH_WUNLOCK();
8883b2bd0f6Slogwang             return (EADDRINUSE);
8893b2bd0f6Slogwang         }
8903b2bd0f6Slogwang 
8913b2bd0f6Slogwang     if (NG_NODE_HAS_NAME(node))
8923b2bd0f6Slogwang         LIST_REMOVE(node, nd_nodes);
8933b2bd0f6Slogwang     else
8943b2bd0f6Slogwang         V_ng_named_nodes++;
8953b2bd0f6Slogwang     /* Copy it. */
8963b2bd0f6Slogwang     strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8973b2bd0f6Slogwang     /* Update name hash. */
8983b2bd0f6Slogwang     LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
8993b2bd0f6Slogwang     NAMEHASH_WUNLOCK();
9003b2bd0f6Slogwang 
9013b2bd0f6Slogwang     return (0);
9023b2bd0f6Slogwang }
9033b2bd0f6Slogwang 
9043b2bd0f6Slogwang /*
9053b2bd0f6Slogwang  * Find a node by absolute name. The name should NOT end with ':'
9063b2bd0f6Slogwang  * The name "." means "this node" and "[xxx]" means "the node
9073b2bd0f6Slogwang  * with ID (ie, at address) xxx".
9083b2bd0f6Slogwang  *
9093b2bd0f6Slogwang  * Returns the node if found, else NULL.
9103b2bd0f6Slogwang  * Eventually should add something faster than a sequential search.
9113b2bd0f6Slogwang  * Note it acquires a reference on the node so you can be sure it's still
9123b2bd0f6Slogwang  * there.
9133b2bd0f6Slogwang  */
9143b2bd0f6Slogwang node_p
ng_name2noderef(node_p here,const char * name)9153b2bd0f6Slogwang ng_name2noderef(node_p here, const char *name)
9163b2bd0f6Slogwang {
9173b2bd0f6Slogwang     node_p node;
9183b2bd0f6Slogwang     ng_ID_t temp;
9193b2bd0f6Slogwang     int    hash;
9203b2bd0f6Slogwang 
9213b2bd0f6Slogwang     /* "." means "this node" */
9223b2bd0f6Slogwang     if (strcmp(name, ".") == 0) {
9233b2bd0f6Slogwang         NG_NODE_REF(here);
9243b2bd0f6Slogwang         return(here);
9253b2bd0f6Slogwang     }
9263b2bd0f6Slogwang 
9273b2bd0f6Slogwang     /* Check for name-by-ID */
9283b2bd0f6Slogwang     if ((temp = ng_decodeidname(name)) != 0) {
9293b2bd0f6Slogwang         return (ng_ID2noderef(temp));
9303b2bd0f6Slogwang     }
9313b2bd0f6Slogwang 
9323b2bd0f6Slogwang     /* Find node by name. */
9333b2bd0f6Slogwang     hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
9343b2bd0f6Slogwang     NAMEHASH_RLOCK();
9353b2bd0f6Slogwang     LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes)
9363b2bd0f6Slogwang         if (NG_NODE_IS_VALID(node) &&
9373b2bd0f6Slogwang             (strcmp(NG_NODE_NAME(node), name) == 0)) {
9383b2bd0f6Slogwang             NG_NODE_REF(node);
9393b2bd0f6Slogwang             break;
9403b2bd0f6Slogwang         }
9413b2bd0f6Slogwang     NAMEHASH_RUNLOCK();
9423b2bd0f6Slogwang 
9433b2bd0f6Slogwang     return (node);
9443b2bd0f6Slogwang }
9453b2bd0f6Slogwang 
9463b2bd0f6Slogwang /*
9473b2bd0f6Slogwang  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
9483b2bd0f6Slogwang  * string is not valid, otherwise returns the value.
9493b2bd0f6Slogwang  */
9503b2bd0f6Slogwang static ng_ID_t
ng_decodeidname(const char * name)9513b2bd0f6Slogwang ng_decodeidname(const char *name)
9523b2bd0f6Slogwang {
9533b2bd0f6Slogwang     const int len = strlen(name);
9543b2bd0f6Slogwang     char *eptr;
9553b2bd0f6Slogwang     u_long val;
9563b2bd0f6Slogwang 
9573b2bd0f6Slogwang     /* Check for proper length, brackets, no leading junk */
9583b2bd0f6Slogwang     if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
9593b2bd0f6Slogwang         (!isxdigit(name[1])))
9603b2bd0f6Slogwang         return ((ng_ID_t)0);
9613b2bd0f6Slogwang 
9623b2bd0f6Slogwang     /* Decode number */
9633b2bd0f6Slogwang     val = strtoul(name + 1, &eptr, 16);
9643b2bd0f6Slogwang     if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
9653b2bd0f6Slogwang         return ((ng_ID_t)0);
9663b2bd0f6Slogwang 
9673b2bd0f6Slogwang     return ((ng_ID_t)val);
9683b2bd0f6Slogwang }
9693b2bd0f6Slogwang 
9703b2bd0f6Slogwang /*
9713b2bd0f6Slogwang  * Remove a name from a node. This should only be called
9723b2bd0f6Slogwang  * when shutting down and removing the node.
9733b2bd0f6Slogwang  */
9743b2bd0f6Slogwang void
ng_unname(node_p node)9753b2bd0f6Slogwang ng_unname(node_p node)
9763b2bd0f6Slogwang {
9773b2bd0f6Slogwang }
9783b2bd0f6Slogwang 
9793b2bd0f6Slogwang /*
9803b2bd0f6Slogwang  * Allocate a bigger name hash.
9813b2bd0f6Slogwang  */
9823b2bd0f6Slogwang static void
ng_name_rehash()9833b2bd0f6Slogwang ng_name_rehash()
9843b2bd0f6Slogwang {
9853b2bd0f6Slogwang     struct nodehash *new;
9863b2bd0f6Slogwang     uint32_t hash;
9873b2bd0f6Slogwang     u_long hmask;
9883b2bd0f6Slogwang     node_p node, node2;
9893b2bd0f6Slogwang     int i;
9903b2bd0f6Slogwang 
9913b2bd0f6Slogwang     new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
9923b2bd0f6Slogwang         HASH_NOWAIT);
9933b2bd0f6Slogwang     if (new == NULL)
9943b2bd0f6Slogwang         return;
9953b2bd0f6Slogwang 
9963b2bd0f6Slogwang     for (i = 0; i <= V_ng_name_hmask; i++)
9973b2bd0f6Slogwang         LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) {
9983b2bd0f6Slogwang #ifdef INVARIANTS
9993b2bd0f6Slogwang             LIST_REMOVE(node, nd_nodes);
10003b2bd0f6Slogwang #endif
10013b2bd0f6Slogwang             hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask;
10023b2bd0f6Slogwang             LIST_INSERT_HEAD(&new[hash], node, nd_nodes);
10033b2bd0f6Slogwang         }
10043b2bd0f6Slogwang 
10053b2bd0f6Slogwang     hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
10063b2bd0f6Slogwang     V_ng_name_hash = new;
10073b2bd0f6Slogwang     V_ng_name_hmask = hmask;
10083b2bd0f6Slogwang }
10093b2bd0f6Slogwang 
10103b2bd0f6Slogwang /*
10113b2bd0f6Slogwang  * Allocate a bigger ID hash.
10123b2bd0f6Slogwang  */
10133b2bd0f6Slogwang static void
ng_ID_rehash()10143b2bd0f6Slogwang ng_ID_rehash()
10153b2bd0f6Slogwang {
10163b2bd0f6Slogwang     struct nodehash *new;
10173b2bd0f6Slogwang     uint32_t hash;
10183b2bd0f6Slogwang     u_long hmask;
10193b2bd0f6Slogwang     node_p node, node2;
10203b2bd0f6Slogwang     int i;
10213b2bd0f6Slogwang 
10223b2bd0f6Slogwang     new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
10233b2bd0f6Slogwang         HASH_NOWAIT);
10243b2bd0f6Slogwang     if (new == NULL)
10253b2bd0f6Slogwang         return;
10263b2bd0f6Slogwang 
10273b2bd0f6Slogwang     for (i = 0; i <= V_ng_ID_hmask; i++)
10283b2bd0f6Slogwang         LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) {
10293b2bd0f6Slogwang #ifdef INVARIANTS
10303b2bd0f6Slogwang             LIST_REMOVE(node, nd_idnodes);
10313b2bd0f6Slogwang #endif
10323b2bd0f6Slogwang             hash = (node->nd_ID % (hmask + 1));
10333b2bd0f6Slogwang             LIST_INSERT_HEAD(&new[hash], node, nd_idnodes);
10343b2bd0f6Slogwang         }
10353b2bd0f6Slogwang 
10363b2bd0f6Slogwang     hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
10373b2bd0f6Slogwang     V_ng_ID_hash = new;
10383b2bd0f6Slogwang     V_ng_ID_hmask = hmask;
10393b2bd0f6Slogwang }
10403b2bd0f6Slogwang 
10413b2bd0f6Slogwang /************************************************************************
10423b2bd0f6Slogwang             Hook routines
10433b2bd0f6Slogwang  Names are not optional. Hooks are always connected, except for a
10443b2bd0f6Slogwang  brief moment within these routines. On invalidation or during creation
10453b2bd0f6Slogwang  they are connected to the 'dead' hook.
10463b2bd0f6Slogwang ************************************************************************/
10473b2bd0f6Slogwang 
10483b2bd0f6Slogwang /*
10493b2bd0f6Slogwang  * Remove a hook reference
10503b2bd0f6Slogwang  */
10513b2bd0f6Slogwang void
ng_unref_hook(hook_p hook)10523b2bd0f6Slogwang ng_unref_hook(hook_p hook)
10533b2bd0f6Slogwang {
10543b2bd0f6Slogwang 
10553b2bd0f6Slogwang     if (hook == &ng_deadhook)
10563b2bd0f6Slogwang         return;
10573b2bd0f6Slogwang 
10583b2bd0f6Slogwang     if (refcount_release(&hook->hk_refs)) { /* we were the last */
10593b2bd0f6Slogwang         if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
10603b2bd0f6Slogwang             _NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
10613b2bd0f6Slogwang         NG_FREE_HOOK(hook);
10623b2bd0f6Slogwang     }
10633b2bd0f6Slogwang }
10643b2bd0f6Slogwang 
10653b2bd0f6Slogwang /*
10663b2bd0f6Slogwang  * Add an unconnected hook to a node. Only used internally.
10673b2bd0f6Slogwang  * Assumes node is locked. (XXX not yet true )
10683b2bd0f6Slogwang  */
10693b2bd0f6Slogwang static int
ng_add_hook(node_p node,const char * name,hook_p * hookp)10703b2bd0f6Slogwang ng_add_hook(node_p node, const char *name, hook_p *hookp)
10713b2bd0f6Slogwang {
10723b2bd0f6Slogwang     hook_p hook;
10733b2bd0f6Slogwang     int error = 0;
10743b2bd0f6Slogwang 
10753b2bd0f6Slogwang     /* Check that the given name is good */
10763b2bd0f6Slogwang     if (name == NULL) {
10773b2bd0f6Slogwang         TRAP_ERROR();
10783b2bd0f6Slogwang         return (EINVAL);
10793b2bd0f6Slogwang     }
10803b2bd0f6Slogwang     if (ng_findhook(node, name) != NULL) {
10813b2bd0f6Slogwang         TRAP_ERROR();
10823b2bd0f6Slogwang         return (EEXIST);
10833b2bd0f6Slogwang     }
10843b2bd0f6Slogwang 
10853b2bd0f6Slogwang     /* Allocate the hook and link it up */
10863b2bd0f6Slogwang     NG_ALLOC_HOOK(hook);
10873b2bd0f6Slogwang     if (hook == NULL) {
10883b2bd0f6Slogwang         TRAP_ERROR();
10893b2bd0f6Slogwang         return (ENOMEM);
10903b2bd0f6Slogwang     }
10913b2bd0f6Slogwang     hook->hk_refs = 1;        /* add a reference for us to return */
10923b2bd0f6Slogwang     hook->hk_flags = HK_INVALID;
10933b2bd0f6Slogwang     hook->hk_peer = &ng_deadhook;    /* start off this way */
10943b2bd0f6Slogwang     hook->hk_node = node;
10953b2bd0f6Slogwang     NG_NODE_REF(node);        /* each hook counts as a reference */
10963b2bd0f6Slogwang 
10973b2bd0f6Slogwang     /* Set hook name */
10983b2bd0f6Slogwang     strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10993b2bd0f6Slogwang 
11003b2bd0f6Slogwang     /*
11013b2bd0f6Slogwang      * Check if the node type code has something to say about it
11023b2bd0f6Slogwang      * If it fails, the unref of the hook will also unref the node.
11033b2bd0f6Slogwang      */
11043b2bd0f6Slogwang     if (node->nd_type->newhook != NULL) {
11053b2bd0f6Slogwang         if ((error = (*node->nd_type->newhook)(node, hook, name))) {
11063b2bd0f6Slogwang             NG_HOOK_UNREF(hook);    /* this frees the hook */
11073b2bd0f6Slogwang             return (error);
11083b2bd0f6Slogwang         }
11093b2bd0f6Slogwang     }
11103b2bd0f6Slogwang     /*
11113b2bd0f6Slogwang      * The 'type' agrees so far, so go ahead and link it in.
11123b2bd0f6Slogwang      * We'll ask again later when we actually connect the hooks.
11133b2bd0f6Slogwang      */
11143b2bd0f6Slogwang     LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
11153b2bd0f6Slogwang     node->nd_numhooks++;
11163b2bd0f6Slogwang     NG_HOOK_REF(hook);    /* one for the node */
11173b2bd0f6Slogwang 
11183b2bd0f6Slogwang     if (hookp)
11193b2bd0f6Slogwang         *hookp = hook;
11203b2bd0f6Slogwang     return (0);
11213b2bd0f6Slogwang }
11223b2bd0f6Slogwang 
11233b2bd0f6Slogwang /*
11243b2bd0f6Slogwang  * Find a hook
11253b2bd0f6Slogwang  *
11263b2bd0f6Slogwang  * Node types may supply their own optimized routines for finding
11273b2bd0f6Slogwang  * hooks.  If none is supplied, we just do a linear search.
11283b2bd0f6Slogwang  * XXX Possibly we should add a reference to the hook?
11293b2bd0f6Slogwang  */
11303b2bd0f6Slogwang hook_p
ng_findhook(node_p node,const char * name)11313b2bd0f6Slogwang ng_findhook(node_p node, const char *name)
11323b2bd0f6Slogwang {
11333b2bd0f6Slogwang     hook_p hook;
11343b2bd0f6Slogwang 
11353b2bd0f6Slogwang     if (node->nd_type->findhook != NULL)
11363b2bd0f6Slogwang         return (*node->nd_type->findhook)(node, name);
11373b2bd0f6Slogwang     LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
11383b2bd0f6Slogwang         if (NG_HOOK_IS_VALID(hook) &&
11393b2bd0f6Slogwang             (strcmp(NG_HOOK_NAME(hook), name) == 0))
11403b2bd0f6Slogwang             return (hook);
11413b2bd0f6Slogwang     }
11423b2bd0f6Slogwang     return (NULL);
11433b2bd0f6Slogwang }
11443b2bd0f6Slogwang 
11453b2bd0f6Slogwang /*
11463b2bd0f6Slogwang  * Destroy a hook
11473b2bd0f6Slogwang  *
11483b2bd0f6Slogwang  * As hooks are always attached, this really destroys two hooks.
11493b2bd0f6Slogwang  * The one given, and the one attached to it. Disconnect the hooks
11503b2bd0f6Slogwang  * from each other first. We reconnect the peer hook to the 'dead'
11513b2bd0f6Slogwang  * hook so that it can still exist after we depart. We then
11523b2bd0f6Slogwang  * send the peer its own destroy message. This ensures that we only
11533b2bd0f6Slogwang  * interact with the peer's structures when it is locked processing that
11543b2bd0f6Slogwang  * message. We hold a reference to the peer hook so we are guaranteed that
11553b2bd0f6Slogwang  * the peer hook and node are still going to exist until
11563b2bd0f6Slogwang  * we are finished there as the hook holds a ref on the node.
11573b2bd0f6Slogwang  * We run this same code again on the peer hook, but that time it is already
11583b2bd0f6Slogwang  * attached to the 'dead' hook.
11593b2bd0f6Slogwang  *
11603b2bd0f6Slogwang  * This routine is called at all stages of hook creation
11613b2bd0f6Slogwang  * on error detection and must be able to handle any such stage.
11623b2bd0f6Slogwang  */
11633b2bd0f6Slogwang void
ng_destroy_hook(hook_p hook)11643b2bd0f6Slogwang ng_destroy_hook(hook_p hook)
11653b2bd0f6Slogwang {
11663b2bd0f6Slogwang     hook_p peer;
11673b2bd0f6Slogwang     node_p node;
11683b2bd0f6Slogwang 
11693b2bd0f6Slogwang     if (hook == &ng_deadhook) {    /* better safe than sorry */
11703b2bd0f6Slogwang         printf("ng_destroy_hook called on deadhook\n");
11713b2bd0f6Slogwang         return;
11723b2bd0f6Slogwang     }
11733b2bd0f6Slogwang 
11743b2bd0f6Slogwang     /*
11753b2bd0f6Slogwang      * Protect divorce process with mutex, to avoid races on
11763b2bd0f6Slogwang      * simultaneous disconnect.
11773b2bd0f6Slogwang      */
11783b2bd0f6Slogwang     TOPOLOGY_WLOCK();
11793b2bd0f6Slogwang 
11803b2bd0f6Slogwang     hook->hk_flags |= HK_INVALID;
11813b2bd0f6Slogwang 
11823b2bd0f6Slogwang     peer = NG_HOOK_PEER(hook);
11833b2bd0f6Slogwang     node = NG_HOOK_NODE(hook);
11843b2bd0f6Slogwang 
11853b2bd0f6Slogwang     if (peer && (peer != &ng_deadhook)) {
11863b2bd0f6Slogwang         /*
11873b2bd0f6Slogwang          * Set the peer to point to ng_deadhook
11883b2bd0f6Slogwang          * from this moment on we are effectively independent it.
1189*8640edf1Sfengbojiang          * send it an rmhook message of its own.
11903b2bd0f6Slogwang          */
11913b2bd0f6Slogwang         peer->hk_peer = &ng_deadhook;    /* They no longer know us */
11923b2bd0f6Slogwang         hook->hk_peer = &ng_deadhook;    /* Nor us, them */
11933b2bd0f6Slogwang         if (NG_HOOK_NODE(peer) == &ng_deadnode) {
11943b2bd0f6Slogwang             /*
11953b2bd0f6Slogwang              * If it's already divorced from a node,
11963b2bd0f6Slogwang              * just free it.
11973b2bd0f6Slogwang              */
11983b2bd0f6Slogwang             TOPOLOGY_WUNLOCK();
11993b2bd0f6Slogwang         } else {
12003b2bd0f6Slogwang             TOPOLOGY_WUNLOCK();
12013b2bd0f6Slogwang             ng_rmhook_self(peer);     /* Send it a surprise */
12023b2bd0f6Slogwang         }
12033b2bd0f6Slogwang         NG_HOOK_UNREF(peer);        /* account for peer link */
12043b2bd0f6Slogwang         NG_HOOK_UNREF(hook);        /* account for peer link */
12053b2bd0f6Slogwang     } else
12063b2bd0f6Slogwang         TOPOLOGY_WUNLOCK();
12073b2bd0f6Slogwang 
12083b2bd0f6Slogwang     TOPOLOGY_NOTOWNED();
12093b2bd0f6Slogwang 
12103b2bd0f6Slogwang     /*
12113b2bd0f6Slogwang      * Remove the hook from the node's list to avoid possible recursion
12123b2bd0f6Slogwang      * in case the disconnection results in node shutdown.
12133b2bd0f6Slogwang      */
12143b2bd0f6Slogwang     if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
12153b2bd0f6Slogwang         return;
12163b2bd0f6Slogwang     }
12173b2bd0f6Slogwang     LIST_REMOVE(hook, hk_hooks);
12183b2bd0f6Slogwang     node->nd_numhooks--;
12193b2bd0f6Slogwang     if (node->nd_type->disconnect) {
12203b2bd0f6Slogwang         /*
12213b2bd0f6Slogwang          * The type handler may elect to destroy the node so don't
12223b2bd0f6Slogwang          * trust its existence after this point. (except
12233b2bd0f6Slogwang          * that we still hold a reference on it. (which we
12243b2bd0f6Slogwang          * inherrited from the hook we are destroying)
12253b2bd0f6Slogwang          */
12263b2bd0f6Slogwang         (*node->nd_type->disconnect) (hook);
12273b2bd0f6Slogwang     }
12283b2bd0f6Slogwang 
12293b2bd0f6Slogwang     /*
12303b2bd0f6Slogwang      * Note that because we will point to ng_deadnode, the original node
12313b2bd0f6Slogwang      * is not decremented automatically so we do that manually.
12323b2bd0f6Slogwang      */
12333b2bd0f6Slogwang     _NG_HOOK_NODE(hook) = &ng_deadnode;
12343b2bd0f6Slogwang     NG_NODE_UNREF(node);    /* We no longer point to it so adjust count */
12353b2bd0f6Slogwang     NG_HOOK_UNREF(hook);    /* Account for linkage (in list) to node */
12363b2bd0f6Slogwang }
12373b2bd0f6Slogwang 
12383b2bd0f6Slogwang /*
12393b2bd0f6Slogwang  * Take two hooks on a node and merge the connection so that the given node
12403b2bd0f6Slogwang  * is effectively bypassed.
12413b2bd0f6Slogwang  */
12423b2bd0f6Slogwang int
ng_bypass(hook_p hook1,hook_p hook2)12433b2bd0f6Slogwang ng_bypass(hook_p hook1, hook_p hook2)
12443b2bd0f6Slogwang {
12453b2bd0f6Slogwang     if (hook1->hk_node != hook2->hk_node) {
12463b2bd0f6Slogwang         TRAP_ERROR();
12473b2bd0f6Slogwang         return (EINVAL);
12483b2bd0f6Slogwang     }
12493b2bd0f6Slogwang     TOPOLOGY_WLOCK();
12503b2bd0f6Slogwang     if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) {
12513b2bd0f6Slogwang         TOPOLOGY_WUNLOCK();
12523b2bd0f6Slogwang         return (EINVAL);
12533b2bd0f6Slogwang     }
12543b2bd0f6Slogwang     hook1->hk_peer->hk_peer = hook2->hk_peer;
12553b2bd0f6Slogwang     hook2->hk_peer->hk_peer = hook1->hk_peer;
12563b2bd0f6Slogwang 
12573b2bd0f6Slogwang     hook1->hk_peer = &ng_deadhook;
12583b2bd0f6Slogwang     hook2->hk_peer = &ng_deadhook;
12593b2bd0f6Slogwang     TOPOLOGY_WUNLOCK();
12603b2bd0f6Slogwang 
12613b2bd0f6Slogwang     NG_HOOK_UNREF(hook1);
12623b2bd0f6Slogwang     NG_HOOK_UNREF(hook2);
12633b2bd0f6Slogwang 
12643b2bd0f6Slogwang     /* XXX If we ever cache methods on hooks update them as well */
12653b2bd0f6Slogwang     ng_destroy_hook(hook1);
12663b2bd0f6Slogwang     ng_destroy_hook(hook2);
12673b2bd0f6Slogwang     return (0);
12683b2bd0f6Slogwang }
12693b2bd0f6Slogwang 
12703b2bd0f6Slogwang /*
12713b2bd0f6Slogwang  * Install a new netgraph type
12723b2bd0f6Slogwang  */
12733b2bd0f6Slogwang int
ng_newtype(struct ng_type * tp)12743b2bd0f6Slogwang ng_newtype(struct ng_type *tp)
12753b2bd0f6Slogwang {
12763b2bd0f6Slogwang     const size_t namelen = strlen(tp->name);
12773b2bd0f6Slogwang 
12783b2bd0f6Slogwang     /* Check version and type name fields */
12793b2bd0f6Slogwang     if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
12803b2bd0f6Slogwang         (namelen >= NG_TYPESIZ)) {
12813b2bd0f6Slogwang         TRAP_ERROR();
12823b2bd0f6Slogwang         if (tp->version != NG_ABI_VERSION) {
12833b2bd0f6Slogwang             printf("Netgraph: Node type rejected. ABI mismatch. "
12843b2bd0f6Slogwang                 "Suggest recompile\n");
12853b2bd0f6Slogwang         }
12863b2bd0f6Slogwang         return (EINVAL);
12873b2bd0f6Slogwang     }
12883b2bd0f6Slogwang 
12893b2bd0f6Slogwang     /* Check for name collision */
12903b2bd0f6Slogwang     if (ng_findtype(tp->name) != NULL) {
12913b2bd0f6Slogwang         TRAP_ERROR();
12923b2bd0f6Slogwang         return (EEXIST);
12933b2bd0f6Slogwang     }
12943b2bd0f6Slogwang 
12953b2bd0f6Slogwang     /* Link in new type */
12963b2bd0f6Slogwang     TYPELIST_WLOCK();
12973b2bd0f6Slogwang     LIST_INSERT_HEAD(&ng_typelist, tp, types);
12983b2bd0f6Slogwang     tp->refs = 1;    /* first ref is linked list */
12993b2bd0f6Slogwang     TYPELIST_WUNLOCK();
13003b2bd0f6Slogwang     return (0);
13013b2bd0f6Slogwang }
13023b2bd0f6Slogwang 
13033b2bd0f6Slogwang /*
13043b2bd0f6Slogwang  * unlink a netgraph type
13053b2bd0f6Slogwang  * If no examples exist
13063b2bd0f6Slogwang  */
13073b2bd0f6Slogwang int
ng_rmtype(struct ng_type * tp)13083b2bd0f6Slogwang ng_rmtype(struct ng_type *tp)
13093b2bd0f6Slogwang {
13103b2bd0f6Slogwang     /* Check for name collision */
13113b2bd0f6Slogwang     if (tp->refs != 1) {
13123b2bd0f6Slogwang         TRAP_ERROR();
13133b2bd0f6Slogwang         return (EBUSY);
13143b2bd0f6Slogwang     }
13153b2bd0f6Slogwang 
13163b2bd0f6Slogwang     /* Unlink type */
13173b2bd0f6Slogwang     TYPELIST_WLOCK();
13183b2bd0f6Slogwang     LIST_REMOVE(tp, types);
13193b2bd0f6Slogwang     TYPELIST_WUNLOCK();
13203b2bd0f6Slogwang     return (0);
13213b2bd0f6Slogwang }
13223b2bd0f6Slogwang 
13233b2bd0f6Slogwang /*
13243b2bd0f6Slogwang  * Look for a type of the name given
13253b2bd0f6Slogwang  */
13263b2bd0f6Slogwang struct ng_type *
ng_findtype(const char * typename)13273b2bd0f6Slogwang ng_findtype(const char *typename)
13283b2bd0f6Slogwang {
13293b2bd0f6Slogwang     struct ng_type *type;
13303b2bd0f6Slogwang 
13313b2bd0f6Slogwang     TYPELIST_RLOCK();
13323b2bd0f6Slogwang     LIST_FOREACH(type, &ng_typelist, types) {
13333b2bd0f6Slogwang         if (strcmp(type->name, typename) == 0)
13343b2bd0f6Slogwang             break;
13353b2bd0f6Slogwang     }
13363b2bd0f6Slogwang     TYPELIST_RUNLOCK();
13373b2bd0f6Slogwang     return (type);
13383b2bd0f6Slogwang }
13393b2bd0f6Slogwang 
13403b2bd0f6Slogwang /************************************************************************
13413b2bd0f6Slogwang             Composite routines
13423b2bd0f6Slogwang ************************************************************************/
13433b2bd0f6Slogwang /*
13443b2bd0f6Slogwang  * Connect two nodes using the specified hooks, using queued functions.
13453b2bd0f6Slogwang  */
13463b2bd0f6Slogwang static int
ng_con_part3(node_p node,item_p item,hook_p hook)13473b2bd0f6Slogwang ng_con_part3(node_p node, item_p item, hook_p hook)
13483b2bd0f6Slogwang {
13493b2bd0f6Slogwang     int    error = 0;
13503b2bd0f6Slogwang 
13513b2bd0f6Slogwang     /*
13523b2bd0f6Slogwang      * When we run, we know that the node 'node' is locked for us.
13533b2bd0f6Slogwang      * Our caller has a reference on the hook.
13543b2bd0f6Slogwang      * Our caller has a reference on the node.
13553b2bd0f6Slogwang      * (In this case our caller is ng_apply_item() ).
13563b2bd0f6Slogwang      * The peer hook has a reference on the hook.
13573b2bd0f6Slogwang      * We are all set up except for the final call to the node, and
13583b2bd0f6Slogwang      * the clearing of the INVALID flag.
13593b2bd0f6Slogwang      */
13603b2bd0f6Slogwang     if (NG_HOOK_NODE(hook) == &ng_deadnode) {
13613b2bd0f6Slogwang         /*
13623b2bd0f6Slogwang          * The node must have been freed again since we last visited
13633b2bd0f6Slogwang          * here. ng_destry_hook() has this effect but nothing else does.
13643b2bd0f6Slogwang          * We should just release our references and
13653b2bd0f6Slogwang          * free anything we can think of.
13663b2bd0f6Slogwang          * Since we know it's been destroyed, and it's our caller
13673b2bd0f6Slogwang          * that holds the references, just return.
13683b2bd0f6Slogwang          */
13693b2bd0f6Slogwang         ERROUT(ENOENT);
13703b2bd0f6Slogwang     }
13713b2bd0f6Slogwang     if (hook->hk_node->nd_type->connect) {
13723b2bd0f6Slogwang         if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13733b2bd0f6Slogwang             ng_destroy_hook(hook);    /* also zaps peer */
13743b2bd0f6Slogwang             printf("failed in ng_con_part3()\n");
13753b2bd0f6Slogwang             ERROUT(error);
13763b2bd0f6Slogwang         }
13773b2bd0f6Slogwang     }
13783b2bd0f6Slogwang     /*
13793b2bd0f6Slogwang      *  XXX this is wrong for SMP. Possibly we need
13803b2bd0f6Slogwang      * to separate out 'create' and 'invalid' flags.
13813b2bd0f6Slogwang      * should only set flags on hooks we have locked under our node.
13823b2bd0f6Slogwang      */
13833b2bd0f6Slogwang     hook->hk_flags &= ~HK_INVALID;
13843b2bd0f6Slogwang done:
13853b2bd0f6Slogwang     NG_FREE_ITEM(item);
13863b2bd0f6Slogwang     return (error);
13873b2bd0f6Slogwang }
13883b2bd0f6Slogwang 
13893b2bd0f6Slogwang static int
ng_con_part2(node_p node,item_p item,hook_p hook)13903b2bd0f6Slogwang ng_con_part2(node_p node, item_p item, hook_p hook)
13913b2bd0f6Slogwang {
13923b2bd0f6Slogwang     hook_p    peer;
13933b2bd0f6Slogwang     int    error = 0;
13943b2bd0f6Slogwang 
13953b2bd0f6Slogwang     /*
13963b2bd0f6Slogwang      * When we run, we know that the node 'node' is locked for us.
13973b2bd0f6Slogwang      * Our caller has a reference on the hook.
13983b2bd0f6Slogwang      * Our caller has a reference on the node.
13993b2bd0f6Slogwang      * (In this case our caller is ng_apply_item() ).
14003b2bd0f6Slogwang      * The peer hook has a reference on the hook.
14013b2bd0f6Slogwang      * our node pointer points to the 'dead' node.
14023b2bd0f6Slogwang      * First check the hook name is unique.
14033b2bd0f6Slogwang      * Should not happen because we checked before queueing this.
14043b2bd0f6Slogwang      */
14053b2bd0f6Slogwang     if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
14063b2bd0f6Slogwang         TRAP_ERROR();
14073b2bd0f6Slogwang         ng_destroy_hook(hook); /* should destroy peer too */
14083b2bd0f6Slogwang         printf("failed in ng_con_part2()\n");
14093b2bd0f6Slogwang         ERROUT(EEXIST);
14103b2bd0f6Slogwang     }
14113b2bd0f6Slogwang     /*
14123b2bd0f6Slogwang      * Check if the node type code has something to say about it
14133b2bd0f6Slogwang      * If it fails, the unref of the hook will also unref the attached node,
14143b2bd0f6Slogwang      * however since that node is 'ng_deadnode' this will do nothing.
14153b2bd0f6Slogwang      * The peer hook will also be destroyed.
14163b2bd0f6Slogwang      */
14173b2bd0f6Slogwang     if (node->nd_type->newhook != NULL) {
14183b2bd0f6Slogwang         if ((error = (*node->nd_type->newhook)(node, hook,
14193b2bd0f6Slogwang             hook->hk_name))) {
14203b2bd0f6Slogwang             ng_destroy_hook(hook); /* should destroy peer too */
14213b2bd0f6Slogwang             printf("failed in ng_con_part2()\n");
14223b2bd0f6Slogwang             ERROUT(error);
14233b2bd0f6Slogwang         }
14243b2bd0f6Slogwang     }
14253b2bd0f6Slogwang 
14263b2bd0f6Slogwang     /*
14273b2bd0f6Slogwang      * The 'type' agrees so far, so go ahead and link it in.
14283b2bd0f6Slogwang      * We'll ask again later when we actually connect the hooks.
14293b2bd0f6Slogwang      */
14303b2bd0f6Slogwang     hook->hk_node = node;        /* just overwrite ng_deadnode */
14313b2bd0f6Slogwang     NG_NODE_REF(node);        /* each hook counts as a reference */
14323b2bd0f6Slogwang     LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
14333b2bd0f6Slogwang     node->nd_numhooks++;
14343b2bd0f6Slogwang     NG_HOOK_REF(hook);    /* one for the node */
14353b2bd0f6Slogwang 
14363b2bd0f6Slogwang     /*
14373b2bd0f6Slogwang      * We now have a symmetrical situation, where both hooks have been
14383b2bd0f6Slogwang      * linked to their nodes, the newhook methods have been called
14393b2bd0f6Slogwang      * And the references are all correct. The hooks are still marked
14403b2bd0f6Slogwang      * as invalid, as we have not called the 'connect' methods
14413b2bd0f6Slogwang      * yet.
14423b2bd0f6Slogwang      * We can call the local one immediately as we have the
14433b2bd0f6Slogwang      * node locked, but we need to queue the remote one.
14443b2bd0f6Slogwang      */
14453b2bd0f6Slogwang     if (hook->hk_node->nd_type->connect) {
14463b2bd0f6Slogwang         if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
14473b2bd0f6Slogwang             ng_destroy_hook(hook);    /* also zaps peer */
14483b2bd0f6Slogwang             printf("failed in ng_con_part2(A)\n");
14493b2bd0f6Slogwang             ERROUT(error);
14503b2bd0f6Slogwang         }
14513b2bd0f6Slogwang     }
14523b2bd0f6Slogwang 
14533b2bd0f6Slogwang     /*
14543b2bd0f6Slogwang      * Acquire topo mutex to avoid race with ng_destroy_hook().
14553b2bd0f6Slogwang      */
14563b2bd0f6Slogwang     TOPOLOGY_RLOCK();
14573b2bd0f6Slogwang     peer = hook->hk_peer;
14583b2bd0f6Slogwang     if (peer == &ng_deadhook) {
14593b2bd0f6Slogwang         TOPOLOGY_RUNLOCK();
14603b2bd0f6Slogwang         printf("failed in ng_con_part2(B)\n");
14613b2bd0f6Slogwang         ng_destroy_hook(hook);
14623b2bd0f6Slogwang         ERROUT(ENOENT);
14633b2bd0f6Slogwang     }
14643b2bd0f6Slogwang     TOPOLOGY_RUNLOCK();
14653b2bd0f6Slogwang 
14663b2bd0f6Slogwang     if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
14673b2bd0f6Slogwang         NULL, 0, NG_REUSE_ITEM))) {
14683b2bd0f6Slogwang         printf("failed in ng_con_part2(C)\n");
14693b2bd0f6Slogwang         ng_destroy_hook(hook);    /* also zaps peer */
14703b2bd0f6Slogwang         return (error);        /* item was consumed. */
14713b2bd0f6Slogwang     }
14723b2bd0f6Slogwang     hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
14733b2bd0f6Slogwang     return (0);            /* item was consumed. */
14743b2bd0f6Slogwang done:
14753b2bd0f6Slogwang     NG_FREE_ITEM(item);
14763b2bd0f6Slogwang     return (error);
14773b2bd0f6Slogwang }
14783b2bd0f6Slogwang 
14793b2bd0f6Slogwang /*
14803b2bd0f6Slogwang  * Connect this node with another node. We assume that this node is
14813b2bd0f6Slogwang  * currently locked, as we are only called from an NGM_CONNECT message.
14823b2bd0f6Slogwang  */
14833b2bd0f6Slogwang static int
ng_con_nodes(item_p item,node_p node,const char * name,node_p node2,const char * name2)14843b2bd0f6Slogwang ng_con_nodes(item_p item, node_p node, const char *name,
14853b2bd0f6Slogwang     node_p node2, const char *name2)
14863b2bd0f6Slogwang {
14873b2bd0f6Slogwang     int    error;
14883b2bd0f6Slogwang     hook_p    hook;
14893b2bd0f6Slogwang     hook_p    hook2;
14903b2bd0f6Slogwang 
14913b2bd0f6Slogwang     if (ng_findhook(node2, name2) != NULL) {
14923b2bd0f6Slogwang         return(EEXIST);
14933b2bd0f6Slogwang     }
14943b2bd0f6Slogwang     if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
14953b2bd0f6Slogwang         return (error);
14963b2bd0f6Slogwang     /* Allocate the other hook and link it up */
14973b2bd0f6Slogwang     NG_ALLOC_HOOK(hook2);
14983b2bd0f6Slogwang     if (hook2 == NULL) {
14993b2bd0f6Slogwang         TRAP_ERROR();
15003b2bd0f6Slogwang         ng_destroy_hook(hook);    /* XXX check ref counts so far */
15013b2bd0f6Slogwang         NG_HOOK_UNREF(hook);    /* including our ref */
15023b2bd0f6Slogwang         return (ENOMEM);
15033b2bd0f6Slogwang     }
15043b2bd0f6Slogwang     hook2->hk_refs = 1;        /* start with a reference for us. */
15053b2bd0f6Slogwang     hook2->hk_flags = HK_INVALID;
15063b2bd0f6Slogwang     hook2->hk_peer = hook;        /* Link the two together */
15073b2bd0f6Slogwang     hook->hk_peer = hook2;
15083b2bd0f6Slogwang     NG_HOOK_REF(hook);        /* Add a ref for the peer to each*/
15093b2bd0f6Slogwang     NG_HOOK_REF(hook2);
15103b2bd0f6Slogwang     hook2->hk_node = &ng_deadnode;
15113b2bd0f6Slogwang     strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
15123b2bd0f6Slogwang 
15133b2bd0f6Slogwang     /*
15143b2bd0f6Slogwang      * Queue the function above.
15153b2bd0f6Slogwang      * Procesing continues in that function in the lock context of
15163b2bd0f6Slogwang      * the other node.
15173b2bd0f6Slogwang      */
15183b2bd0f6Slogwang     if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
15193b2bd0f6Slogwang         NG_NOFLAGS))) {
15203b2bd0f6Slogwang         printf("failed in ng_con_nodes(): %d\n", error);
15213b2bd0f6Slogwang         ng_destroy_hook(hook);    /* also zaps peer */
15223b2bd0f6Slogwang     }
15233b2bd0f6Slogwang 
15243b2bd0f6Slogwang     NG_HOOK_UNREF(hook);        /* Let each hook go if it wants to */
15253b2bd0f6Slogwang     NG_HOOK_UNREF(hook2);
15263b2bd0f6Slogwang     return (error);
15273b2bd0f6Slogwang }
15283b2bd0f6Slogwang 
15293b2bd0f6Slogwang /*
15303b2bd0f6Slogwang  * Make a peer and connect.
15313b2bd0f6Slogwang  * We assume that the local node is locked.
15323b2bd0f6Slogwang  * The new node probably doesn't need a lock until
15333b2bd0f6Slogwang  * it has a hook, because it cannot really have any work until then,
15343b2bd0f6Slogwang  * but we should think about it a bit more.
15353b2bd0f6Slogwang  *
15363b2bd0f6Slogwang  * The problem may come if the other node also fires up
15373b2bd0f6Slogwang  * some hardware or a timer or some other source of activation,
15383b2bd0f6Slogwang  * also it may already get a command msg via it's ID.
15393b2bd0f6Slogwang  *
15403b2bd0f6Slogwang  * We could use the same method as ng_con_nodes() but we'd have
15413b2bd0f6Slogwang  * to add ability to remove the node when failing. (Not hard, just
15423b2bd0f6Slogwang  * make arg1 point to the node to remove).
15433b2bd0f6Slogwang  * Unless of course we just ignore failure to connect and leave
15443b2bd0f6Slogwang  * an unconnected node?
15453b2bd0f6Slogwang  */
15463b2bd0f6Slogwang static int
ng_mkpeer(node_p node,const char * name,const char * name2,char * type)15473b2bd0f6Slogwang ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
15483b2bd0f6Slogwang {
15493b2bd0f6Slogwang     node_p    node2;
15503b2bd0f6Slogwang     hook_p    hook1, hook2;
15513b2bd0f6Slogwang     int    error;
15523b2bd0f6Slogwang 
15533b2bd0f6Slogwang     if ((error = ng_make_node(type, &node2))) {
15543b2bd0f6Slogwang         return (error);
15553b2bd0f6Slogwang     }
15563b2bd0f6Slogwang 
15573b2bd0f6Slogwang     if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
15583b2bd0f6Slogwang         ng_rmnode(node2, NULL, NULL, 0);
15593b2bd0f6Slogwang         return (error);
15603b2bd0f6Slogwang     }
15613b2bd0f6Slogwang 
15623b2bd0f6Slogwang     if ((error = ng_add_hook(node2, name2, &hook2))) {
15633b2bd0f6Slogwang         ng_rmnode(node2, NULL, NULL, 0);
15643b2bd0f6Slogwang         ng_destroy_hook(hook1);
15653b2bd0f6Slogwang         NG_HOOK_UNREF(hook1);
15663b2bd0f6Slogwang         return (error);
15673b2bd0f6Slogwang     }
15683b2bd0f6Slogwang 
15693b2bd0f6Slogwang     /*
15703b2bd0f6Slogwang      * Actually link the two hooks together.
15713b2bd0f6Slogwang      */
15723b2bd0f6Slogwang     hook1->hk_peer = hook2;
15733b2bd0f6Slogwang     hook2->hk_peer = hook1;
15743b2bd0f6Slogwang 
15753b2bd0f6Slogwang     /* Each hook is referenced by the other */
15763b2bd0f6Slogwang     NG_HOOK_REF(hook1);
15773b2bd0f6Slogwang     NG_HOOK_REF(hook2);
15783b2bd0f6Slogwang 
15793b2bd0f6Slogwang     /* Give each node the opportunity to veto the pending connection */
15803b2bd0f6Slogwang     if (hook1->hk_node->nd_type->connect) {
15813b2bd0f6Slogwang         error = (*hook1->hk_node->nd_type->connect) (hook1);
15823b2bd0f6Slogwang     }
15833b2bd0f6Slogwang 
15843b2bd0f6Slogwang     if ((error == 0) && hook2->hk_node->nd_type->connect) {
15853b2bd0f6Slogwang         error = (*hook2->hk_node->nd_type->connect) (hook2);
15863b2bd0f6Slogwang     }
15873b2bd0f6Slogwang 
15883b2bd0f6Slogwang     /*
15893b2bd0f6Slogwang      * drop the references we were holding on the two hooks.
15903b2bd0f6Slogwang      */
15913b2bd0f6Slogwang     if (error) {
15923b2bd0f6Slogwang         ng_destroy_hook(hook2);    /* also zaps hook1 */
15933b2bd0f6Slogwang         ng_rmnode(node2, NULL, NULL, 0);
15943b2bd0f6Slogwang     } else {
15953b2bd0f6Slogwang         /* As a last act, allow the hooks to be used */
15963b2bd0f6Slogwang         hook1->hk_flags &= ~HK_INVALID;
15973b2bd0f6Slogwang         hook2->hk_flags &= ~HK_INVALID;
15983b2bd0f6Slogwang     }
15993b2bd0f6Slogwang     NG_HOOK_UNREF(hook1);
16003b2bd0f6Slogwang     NG_HOOK_UNREF(hook2);
16013b2bd0f6Slogwang     return (error);
16023b2bd0f6Slogwang }
16033b2bd0f6Slogwang 
16043b2bd0f6Slogwang /************************************************************************
16053b2bd0f6Slogwang         Utility routines to send self messages
16063b2bd0f6Slogwang ************************************************************************/
16073b2bd0f6Slogwang 
16083b2bd0f6Slogwang /* Shut this node down as soon as everyone is clear of it */
16093b2bd0f6Slogwang /* Should add arg "immediately" to jump the queue */
16103b2bd0f6Slogwang int
ng_rmnode_self(node_p node)16113b2bd0f6Slogwang ng_rmnode_self(node_p node)
16123b2bd0f6Slogwang {
16133b2bd0f6Slogwang     int        error;
16143b2bd0f6Slogwang 
16153b2bd0f6Slogwang     if (node == &ng_deadnode)
16163b2bd0f6Slogwang         return (0);
16173b2bd0f6Slogwang     node->nd_flags |= NGF_INVALID;
16183b2bd0f6Slogwang     if (node->nd_flags & NGF_CLOSING)
16193b2bd0f6Slogwang         return (0);
16203b2bd0f6Slogwang 
16213b2bd0f6Slogwang     error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
16223b2bd0f6Slogwang     return (error);
16233b2bd0f6Slogwang }
16243b2bd0f6Slogwang 
16253b2bd0f6Slogwang static void
ng_rmhook_part2(node_p node,hook_p hook,void * arg1,int arg2)16263b2bd0f6Slogwang ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
16273b2bd0f6Slogwang {
16283b2bd0f6Slogwang     ng_destroy_hook(hook);
16293b2bd0f6Slogwang     return ;
16303b2bd0f6Slogwang }
16313b2bd0f6Slogwang 
16323b2bd0f6Slogwang int
ng_rmhook_self(hook_p hook)16333b2bd0f6Slogwang ng_rmhook_self(hook_p hook)
16343b2bd0f6Slogwang {
16353b2bd0f6Slogwang     int        error;
16363b2bd0f6Slogwang     node_p node = NG_HOOK_NODE(hook);
16373b2bd0f6Slogwang 
16383b2bd0f6Slogwang     if (node == &ng_deadnode)
16393b2bd0f6Slogwang         return (0);
16403b2bd0f6Slogwang 
16413b2bd0f6Slogwang     error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
16423b2bd0f6Slogwang     return (error);
16433b2bd0f6Slogwang }
16443b2bd0f6Slogwang 
16453b2bd0f6Slogwang /***********************************************************************
16463b2bd0f6Slogwang  * Parse and verify a string of the form:  <NODE:><PATH>
16473b2bd0f6Slogwang  *
16483b2bd0f6Slogwang  * Such a string can refer to a specific node or a specific hook
16493b2bd0f6Slogwang  * on a specific node, depending on how you look at it. In the
16503b2bd0f6Slogwang  * latter case, the PATH component must not end in a dot.
16513b2bd0f6Slogwang  *
16523b2bd0f6Slogwang  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
16533b2bd0f6Slogwang  * of hook names separated by dots. This breaks out the original
16543b2bd0f6Slogwang  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
16553b2bd0f6Slogwang  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
16563b2bd0f6Slogwang  * the final hook component of <PATH>, if any, otherwise NULL.
16573b2bd0f6Slogwang  *
16583b2bd0f6Slogwang  * This returns -1 if the path is malformed. The char ** are optional.
16593b2bd0f6Slogwang  ***********************************************************************/
16603b2bd0f6Slogwang int
ng_path_parse(char * addr,char ** nodep,char ** pathp,char ** hookp)16613b2bd0f6Slogwang ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
16623b2bd0f6Slogwang {
16633b2bd0f6Slogwang     char    *node, *path, *hook;
16643b2bd0f6Slogwang     int    k;
16653b2bd0f6Slogwang 
16663b2bd0f6Slogwang     /*
16673b2bd0f6Slogwang      * Extract absolute NODE, if any
16683b2bd0f6Slogwang      */
16693b2bd0f6Slogwang     for (path = addr; *path && *path != ':'; path++);
16703b2bd0f6Slogwang     if (*path) {
16713b2bd0f6Slogwang         node = addr;    /* Here's the NODE */
16723b2bd0f6Slogwang         *path++ = '\0';    /* Here's the PATH */
16733b2bd0f6Slogwang 
16743b2bd0f6Slogwang         /* Node name must not be empty */
16753b2bd0f6Slogwang         if (!*node)
16763b2bd0f6Slogwang             return -1;
16773b2bd0f6Slogwang 
16783b2bd0f6Slogwang         /* A name of "." is OK; otherwise '.' not allowed */
16793b2bd0f6Slogwang         if (strcmp(node, ".") != 0) {
16803b2bd0f6Slogwang             for (k = 0; node[k]; k++)
16813b2bd0f6Slogwang                 if (node[k] == '.')
16823b2bd0f6Slogwang                     return -1;
16833b2bd0f6Slogwang         }
16843b2bd0f6Slogwang     } else {
16853b2bd0f6Slogwang         node = NULL;    /* No absolute NODE */
16863b2bd0f6Slogwang         path = addr;    /* Here's the PATH */
16873b2bd0f6Slogwang     }
16883b2bd0f6Slogwang 
16893b2bd0f6Slogwang     /* Snoop for illegal characters in PATH */
16903b2bd0f6Slogwang     for (k = 0; path[k]; k++)
16913b2bd0f6Slogwang         if (path[k] == ':')
16923b2bd0f6Slogwang             return -1;
16933b2bd0f6Slogwang 
16943b2bd0f6Slogwang     /* Check for no repeated dots in PATH */
16953b2bd0f6Slogwang     for (k = 0; path[k]; k++)
16963b2bd0f6Slogwang         if (path[k] == '.' && path[k + 1] == '.')
16973b2bd0f6Slogwang             return -1;
16983b2bd0f6Slogwang 
16993b2bd0f6Slogwang     /* Remove extra (degenerate) dots from beginning or end of PATH */
17003b2bd0f6Slogwang     if (path[0] == '.')
17013b2bd0f6Slogwang         path++;
17023b2bd0f6Slogwang     if (*path && path[strlen(path) - 1] == '.')
17033b2bd0f6Slogwang         path[strlen(path) - 1] = 0;
17043b2bd0f6Slogwang 
17053b2bd0f6Slogwang     /* If PATH has a dot, then we're not talking about a hook */
17063b2bd0f6Slogwang     if (*path) {
17073b2bd0f6Slogwang         for (hook = path, k = 0; path[k]; k++)
17083b2bd0f6Slogwang             if (path[k] == '.') {
17093b2bd0f6Slogwang                 hook = NULL;
17103b2bd0f6Slogwang                 break;
17113b2bd0f6Slogwang             }
17123b2bd0f6Slogwang     } else
17133b2bd0f6Slogwang         path = hook = NULL;
17143b2bd0f6Slogwang 
17153b2bd0f6Slogwang     /* Done */
17163b2bd0f6Slogwang     if (nodep)
17173b2bd0f6Slogwang         *nodep = node;
17183b2bd0f6Slogwang     if (pathp)
17193b2bd0f6Slogwang         *pathp = path;
17203b2bd0f6Slogwang     if (hookp)
17213b2bd0f6Slogwang         *hookp = hook;
17223b2bd0f6Slogwang     return (0);
17233b2bd0f6Slogwang }
17243b2bd0f6Slogwang 
17253b2bd0f6Slogwang /*
17263b2bd0f6Slogwang  * Given a path, which may be absolute or relative, and a starting node,
17273b2bd0f6Slogwang  * return the destination node.
17283b2bd0f6Slogwang  */
17293b2bd0f6Slogwang int
ng_path2noderef(node_p here,const char * address,node_p * destp,hook_p * lasthook)17303b2bd0f6Slogwang ng_path2noderef(node_p here, const char *address, node_p *destp,
17313b2bd0f6Slogwang     hook_p *lasthook)
17323b2bd0f6Slogwang {
17333b2bd0f6Slogwang     char    fullpath[NG_PATHSIZ];
17343b2bd0f6Slogwang     char   *nodename, *path;
17353b2bd0f6Slogwang     node_p  node, oldnode;
17363b2bd0f6Slogwang 
17373b2bd0f6Slogwang     /* Initialize */
17383b2bd0f6Slogwang     if (destp == NULL) {
17393b2bd0f6Slogwang         TRAP_ERROR();
17403b2bd0f6Slogwang         return EINVAL;
17413b2bd0f6Slogwang     }
17423b2bd0f6Slogwang     *destp = NULL;
17433b2bd0f6Slogwang 
17443b2bd0f6Slogwang     /* Make a writable copy of address for ng_path_parse() */
17453b2bd0f6Slogwang     strncpy(fullpath, address, sizeof(fullpath) - 1);
17463b2bd0f6Slogwang     fullpath[sizeof(fullpath) - 1] = '\0';
17473b2bd0f6Slogwang 
17483b2bd0f6Slogwang     /* Parse out node and sequence of hooks */
17493b2bd0f6Slogwang     if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
17503b2bd0f6Slogwang         TRAP_ERROR();
17513b2bd0f6Slogwang         return EINVAL;
17523b2bd0f6Slogwang     }
17533b2bd0f6Slogwang 
17543b2bd0f6Slogwang     /*
17553b2bd0f6Slogwang      * For an absolute address, jump to the starting node.
17563b2bd0f6Slogwang      * Note that this holds a reference on the node for us.
17573b2bd0f6Slogwang      * Don't forget to drop the reference if we don't need it.
17583b2bd0f6Slogwang      */
17593b2bd0f6Slogwang     if (nodename) {
17603b2bd0f6Slogwang         node = ng_name2noderef(here, nodename);
17613b2bd0f6Slogwang         if (node == NULL) {
17623b2bd0f6Slogwang             TRAP_ERROR();
17633b2bd0f6Slogwang             return (ENOENT);
17643b2bd0f6Slogwang         }
17653b2bd0f6Slogwang     } else {
17663b2bd0f6Slogwang         if (here == NULL) {
17673b2bd0f6Slogwang             TRAP_ERROR();
17683b2bd0f6Slogwang             return (EINVAL);
17693b2bd0f6Slogwang         }
17703b2bd0f6Slogwang         node = here;
17713b2bd0f6Slogwang         NG_NODE_REF(node);
17723b2bd0f6Slogwang     }
17733b2bd0f6Slogwang 
17743b2bd0f6Slogwang     if (path == NULL) {
17753b2bd0f6Slogwang         if (lasthook != NULL)
17763b2bd0f6Slogwang             *lasthook = NULL;
17773b2bd0f6Slogwang         *destp = node;
17783b2bd0f6Slogwang         return (0);
17793b2bd0f6Slogwang     }
17803b2bd0f6Slogwang 
17813b2bd0f6Slogwang     /*
17823b2bd0f6Slogwang      * Now follow the sequence of hooks
17833b2bd0f6Slogwang      *
17843b2bd0f6Slogwang      * XXXGL: The path may demolish as we go the sequence, but if
17853b2bd0f6Slogwang      * we hold the topology mutex at critical places, then, I hope,
17863b2bd0f6Slogwang      * we would always have valid pointers in hand, although the
17873b2bd0f6Slogwang      * path behind us may no longer exist.
17883b2bd0f6Slogwang      */
17893b2bd0f6Slogwang     for (;;) {
17903b2bd0f6Slogwang         hook_p hook;
17913b2bd0f6Slogwang         char *segment;
17923b2bd0f6Slogwang 
17933b2bd0f6Slogwang         /*
17943b2bd0f6Slogwang          * Break out the next path segment. Replace the dot we just
17953b2bd0f6Slogwang          * found with a NUL; "path" points to the next segment (or the
17963b2bd0f6Slogwang          * NUL at the end).
17973b2bd0f6Slogwang          */
17983b2bd0f6Slogwang         for (segment = path; *path != '\0'; path++) {
17993b2bd0f6Slogwang             if (*path == '.') {
18003b2bd0f6Slogwang                 *path++ = '\0';
18013b2bd0f6Slogwang                 break;
18023b2bd0f6Slogwang             }
18033b2bd0f6Slogwang         }
18043b2bd0f6Slogwang 
18053b2bd0f6Slogwang         /* We have a segment, so look for a hook by that name */
18063b2bd0f6Slogwang         hook = ng_findhook(node, segment);
18073b2bd0f6Slogwang 
18083b2bd0f6Slogwang         TOPOLOGY_WLOCK();
18093b2bd0f6Slogwang         /* Can't get there from here... */
18103b2bd0f6Slogwang         if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
18113b2bd0f6Slogwang             NG_HOOK_NOT_VALID(hook) ||
18123b2bd0f6Slogwang             NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
18133b2bd0f6Slogwang             TRAP_ERROR();
18143b2bd0f6Slogwang             NG_NODE_UNREF(node);
18153b2bd0f6Slogwang             TOPOLOGY_WUNLOCK();
18163b2bd0f6Slogwang             return (ENOENT);
18173b2bd0f6Slogwang         }
18183b2bd0f6Slogwang 
18193b2bd0f6Slogwang         /*
18203b2bd0f6Slogwang          * Hop on over to the next node
18213b2bd0f6Slogwang          * XXX
18223b2bd0f6Slogwang          * Big race conditions here as hooks and nodes go away
18233b2bd0f6Slogwang          * *** Idea.. store an ng_ID_t in each hook and use that
18243b2bd0f6Slogwang          * instead of the direct hook in this crawl?
18253b2bd0f6Slogwang          */
18263b2bd0f6Slogwang         oldnode = node;
18273b2bd0f6Slogwang         if ((node = NG_PEER_NODE(hook)))
18283b2bd0f6Slogwang             NG_NODE_REF(node);    /* XXX RACE */
18293b2bd0f6Slogwang         NG_NODE_UNREF(oldnode);    /* XXX another race */
18303b2bd0f6Slogwang         if (NG_NODE_NOT_VALID(node)) {
18313b2bd0f6Slogwang             NG_NODE_UNREF(node);    /* XXX more races */
18323b2bd0f6Slogwang             TOPOLOGY_WUNLOCK();
18333b2bd0f6Slogwang             TRAP_ERROR();
18343b2bd0f6Slogwang             return (ENXIO);
18353b2bd0f6Slogwang         }
18363b2bd0f6Slogwang 
18373b2bd0f6Slogwang         if (*path == '\0') {
18383b2bd0f6Slogwang             if (lasthook != NULL) {
18393b2bd0f6Slogwang                 if (hook != NULL) {
18403b2bd0f6Slogwang                     *lasthook = NG_HOOK_PEER(hook);
18413b2bd0f6Slogwang                     NG_HOOK_REF(*lasthook);
18423b2bd0f6Slogwang                 } else
18433b2bd0f6Slogwang                     *lasthook = NULL;
18443b2bd0f6Slogwang             }
18453b2bd0f6Slogwang             TOPOLOGY_WUNLOCK();
18463b2bd0f6Slogwang             *destp = node;
18473b2bd0f6Slogwang             return (0);
18483b2bd0f6Slogwang         }
18493b2bd0f6Slogwang         TOPOLOGY_WUNLOCK();
18503b2bd0f6Slogwang     }
18513b2bd0f6Slogwang }
18523b2bd0f6Slogwang 
18533b2bd0f6Slogwang #ifndef FSTACK
18543b2bd0f6Slogwang /***************************************************************\
18553b2bd0f6Slogwang * Input queue handling.
18563b2bd0f6Slogwang * All activities are submitted to the node via the input queue
18573b2bd0f6Slogwang * which implements a multiple-reader/single-writer gate.
18583b2bd0f6Slogwang * Items which cannot be handled immediately are queued.
18593b2bd0f6Slogwang *
18603b2bd0f6Slogwang * read-write queue locking inline functions            *
18613b2bd0f6Slogwang \***************************************************************/
18623b2bd0f6Slogwang 
18633b2bd0f6Slogwang static __inline void    ng_queue_rw(node_p node, item_p  item, int rw);
18643b2bd0f6Slogwang static __inline item_p    ng_dequeue(node_p node, int *rw);
18653b2bd0f6Slogwang static __inline item_p    ng_acquire_read(node_p node, item_p  item);
18663b2bd0f6Slogwang static __inline item_p    ng_acquire_write(node_p node, item_p  item);
18673b2bd0f6Slogwang static __inline void    ng_leave_read(node_p node);
18683b2bd0f6Slogwang static __inline void    ng_leave_write(node_p node);
18693b2bd0f6Slogwang 
18703b2bd0f6Slogwang /*
18713b2bd0f6Slogwang  * Definition of the bits fields in the ng_queue flag word.
18723b2bd0f6Slogwang  * Defined here rather than in netgraph.h because no-one should fiddle
18733b2bd0f6Slogwang  * with them.
18743b2bd0f6Slogwang  *
18753b2bd0f6Slogwang  * The ordering here may be important! don't shuffle these.
18763b2bd0f6Slogwang  */
18773b2bd0f6Slogwang /*-
18783b2bd0f6Slogwang  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
18793b2bd0f6Slogwang                        |
18803b2bd0f6Slogwang                        V
18813b2bd0f6Slogwang +-------+-------+-------+-------+-------+-------+-------+-------+
18823b2bd0f6Slogwang   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
18833b2bd0f6Slogwang   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
18843b2bd0f6Slogwang   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
18853b2bd0f6Slogwang +-------+-------+-------+-------+-------+-------+-------+-------+
18863b2bd0f6Slogwang   \___________________________ ____________________________/ | |
18873b2bd0f6Slogwang                             V                                | |
18883b2bd0f6Slogwang                   [active reader count]                      | |
18893b2bd0f6Slogwang                                                              | |
18903b2bd0f6Slogwang             Operation Pending -------------------------------+ |
18913b2bd0f6Slogwang                                                                |
18923b2bd0f6Slogwang           Active Writer ---------------------------------------+
18933b2bd0f6Slogwang 
18943b2bd0f6Slogwang Node queue has such semantics:
18953b2bd0f6Slogwang - All flags modifications are atomic.
18963b2bd0f6Slogwang - Reader count can be incremented only if there is no writer or pending flags.
18973b2bd0f6Slogwang   As soon as this can't be done with single operation, it is implemented with
18983b2bd0f6Slogwang   spin loop and atomic_cmpset().
18993b2bd0f6Slogwang - Writer flag can be set only if there is no any bits set.
19003b2bd0f6Slogwang   It is implemented with atomic_cmpset().
19013b2bd0f6Slogwang - Pending flag can be set any time, but to avoid collision on queue processing
19023b2bd0f6Slogwang   all queue fields are protected by the mutex.
19033b2bd0f6Slogwang - Queue processing thread reads queue holding the mutex, but releases it while
19043b2bd0f6Slogwang   processing. When queue is empty pending flag is removed.
19053b2bd0f6Slogwang */
19063b2bd0f6Slogwang 
19073b2bd0f6Slogwang #define WRITER_ACTIVE    0x00000001
19083b2bd0f6Slogwang #define OP_PENDING    0x00000002
19093b2bd0f6Slogwang #define READER_INCREMENT 0x00000004
19103b2bd0f6Slogwang #define READER_MASK    0xfffffffc    /* Not valid if WRITER_ACTIVE is set */
19113b2bd0f6Slogwang #define SAFETY_BARRIER    0x00100000    /* 128K items queued should be enough */
19123b2bd0f6Slogwang 
19133b2bd0f6Slogwang /* Defines of more elaborate states on the queue */
19143b2bd0f6Slogwang /* Mask of bits a new read cares about */
19153b2bd0f6Slogwang #define NGQ_RMASK    (WRITER_ACTIVE|OP_PENDING)
19163b2bd0f6Slogwang 
19173b2bd0f6Slogwang /* Mask of bits a new write cares about */
19183b2bd0f6Slogwang #define NGQ_WMASK    (NGQ_RMASK|READER_MASK)
19193b2bd0f6Slogwang 
19203b2bd0f6Slogwang /* Test to decide if there is something on the queue. */
19213b2bd0f6Slogwang #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
19223b2bd0f6Slogwang 
19233b2bd0f6Slogwang /* How to decide what the next queued item is. */
19243b2bd0f6Slogwang #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
19253b2bd0f6Slogwang #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
19263b2bd0f6Slogwang 
19273b2bd0f6Slogwang /* Read the status to decide if the next item on the queue can now run. */
19283b2bd0f6Slogwang #define QUEUED_READER_CAN_PROCEED(QP)            \
19293b2bd0f6Slogwang         (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
19303b2bd0f6Slogwang #define QUEUED_WRITER_CAN_PROCEED(QP)            \
19313b2bd0f6Slogwang         (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
19323b2bd0f6Slogwang 
19333b2bd0f6Slogwang /* Is there a chance of getting ANY work off the queue? */
19343b2bd0f6Slogwang #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)                \
19353b2bd0f6Slogwang     ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :        \
19363b2bd0f6Slogwang                 QUEUED_WRITER_CAN_PROCEED(QP))
19373b2bd0f6Slogwang 
19383b2bd0f6Slogwang #define NGQRW_R 0
19393b2bd0f6Slogwang #define NGQRW_W 1
19403b2bd0f6Slogwang 
19413b2bd0f6Slogwang #define NGQ2_WORKQ    0x00000001
19423b2bd0f6Slogwang 
19433b2bd0f6Slogwang /*
19443b2bd0f6Slogwang  * Taking into account the current state of the queue and node, possibly take
19453b2bd0f6Slogwang  * the next entry off the queue and return it. Return NULL if there was
19463b2bd0f6Slogwang  * nothing we could return, either because there really was nothing there, or
19473b2bd0f6Slogwang  * because the node was in a state where it cannot yet process the next item
19483b2bd0f6Slogwang  * on the queue.
19493b2bd0f6Slogwang  */
19503b2bd0f6Slogwang static __inline item_p
ng_dequeue(node_p node,int * rw)19513b2bd0f6Slogwang ng_dequeue(node_p node, int *rw)
19523b2bd0f6Slogwang {
19533b2bd0f6Slogwang     item_p item;
19543b2bd0f6Slogwang     struct ng_queue *ngq = &node->nd_input_queue;
19553b2bd0f6Slogwang 
19563b2bd0f6Slogwang     /* This MUST be called with the mutex held. */
19573b2bd0f6Slogwang     mtx_assert(&ngq->q_mtx, MA_OWNED);
19583b2bd0f6Slogwang 
19593b2bd0f6Slogwang     /* If there is nothing queued, then just return. */
19603b2bd0f6Slogwang     if (!QUEUE_ACTIVE(ngq)) {
19613b2bd0f6Slogwang         CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
19623b2bd0f6Slogwang             "queue flags 0x%lx", __func__,
19633b2bd0f6Slogwang             node->nd_ID, node, ngq->q_flags);
19643b2bd0f6Slogwang         return (NULL);
19653b2bd0f6Slogwang     }
19663b2bd0f6Slogwang 
19673b2bd0f6Slogwang     /*
19683b2bd0f6Slogwang      * From here, we can assume there is a head item.
19693b2bd0f6Slogwang      * We need to find out what it is and if it can be dequeued, given
19703b2bd0f6Slogwang      * the current state of the node.
19713b2bd0f6Slogwang      */
19723b2bd0f6Slogwang     if (HEAD_IS_READER(ngq)) {
19733b2bd0f6Slogwang         while (1) {
19743b2bd0f6Slogwang             long t = ngq->q_flags;
19753b2bd0f6Slogwang             if (t & WRITER_ACTIVE) {
19763b2bd0f6Slogwang                 /* There is writer, reader can't proceed. */
19773b2bd0f6Slogwang                 CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
19783b2bd0f6Slogwang                     "reader can't proceed; queue flags 0x%lx",
19793b2bd0f6Slogwang                     __func__, node->nd_ID, node, t);
19803b2bd0f6Slogwang                 return (NULL);
19813b2bd0f6Slogwang             }
19823b2bd0f6Slogwang             if (atomic_cmpset_acq_int(&ngq->q_flags, t,
19833b2bd0f6Slogwang                 t + READER_INCREMENT))
19843b2bd0f6Slogwang                 break;
19853b2bd0f6Slogwang             cpu_spinwait();
19863b2bd0f6Slogwang         }
19873b2bd0f6Slogwang         /* We have got reader lock for the node. */
19883b2bd0f6Slogwang         *rw = NGQRW_R;
19893b2bd0f6Slogwang     } else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
19903b2bd0f6Slogwang         OP_PENDING + WRITER_ACTIVE)) {
19913b2bd0f6Slogwang         /* We have got writer lock for the node. */
19923b2bd0f6Slogwang         *rw = NGQRW_W;
19933b2bd0f6Slogwang     } else {
19943b2bd0f6Slogwang         /* There is somebody other, writer can't proceed. */
19953b2bd0f6Slogwang         CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
19963b2bd0f6Slogwang             "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
19973b2bd0f6Slogwang             ngq->q_flags);
19983b2bd0f6Slogwang         return (NULL);
19993b2bd0f6Slogwang     }
20003b2bd0f6Slogwang 
20013b2bd0f6Slogwang     /*
20023b2bd0f6Slogwang      * Now we dequeue the request (whatever it may be) and correct the
20033b2bd0f6Slogwang      * pending flags and the next and last pointers.
20043b2bd0f6Slogwang      */
20053b2bd0f6Slogwang     item = STAILQ_FIRST(&ngq->queue);
20063b2bd0f6Slogwang     STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
20073b2bd0f6Slogwang     if (STAILQ_EMPTY(&ngq->queue))
20083b2bd0f6Slogwang         atomic_clear_int(&ngq->q_flags, OP_PENDING);
20093b2bd0f6Slogwang     CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
20103b2bd0f6Slogwang         "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
20113b2bd0f6Slogwang         "READER", ngq->q_flags);
20123b2bd0f6Slogwang     return (item);
20133b2bd0f6Slogwang }
20143b2bd0f6Slogwang 
20153b2bd0f6Slogwang /*
20163b2bd0f6Slogwang  * Queue a packet to be picked up later by someone else.
20173b2bd0f6Slogwang  * If the queue could be run now, add node to the queue handler's worklist.
20183b2bd0f6Slogwang  */
20193b2bd0f6Slogwang static __inline void
ng_queue_rw(node_p node,item_p item,int rw)20203b2bd0f6Slogwang ng_queue_rw(node_p node, item_p  item, int rw)
20213b2bd0f6Slogwang {
20223b2bd0f6Slogwang     struct ng_queue *ngq = &node->nd_input_queue;
20233b2bd0f6Slogwang     if (rw == NGQRW_W)
20243b2bd0f6Slogwang         NGI_SET_WRITER(item);
20253b2bd0f6Slogwang     else
20263b2bd0f6Slogwang         NGI_SET_READER(item);
20273b2bd0f6Slogwang     item->depth = 1;
20283b2bd0f6Slogwang 
20293b2bd0f6Slogwang     NG_QUEUE_LOCK(ngq);
20303b2bd0f6Slogwang     /* Set OP_PENDING flag and enqueue the item. */
20313b2bd0f6Slogwang     atomic_set_int(&ngq->q_flags, OP_PENDING);
20323b2bd0f6Slogwang     STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
20333b2bd0f6Slogwang 
20343b2bd0f6Slogwang     CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
20353b2bd0f6Slogwang         node->nd_ID, node, item, rw ? "WRITER" : "READER" );
20363b2bd0f6Slogwang 
20373b2bd0f6Slogwang     /*
20383b2bd0f6Slogwang      * We can take the worklist lock with the node locked
20393b2bd0f6Slogwang      * BUT NOT THE REVERSE!
20403b2bd0f6Slogwang      */
20413b2bd0f6Slogwang     if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20423b2bd0f6Slogwang         ng_worklist_add(node);
20433b2bd0f6Slogwang     NG_QUEUE_UNLOCK(ngq);
20443b2bd0f6Slogwang }
20453b2bd0f6Slogwang 
20463b2bd0f6Slogwang /* Acquire reader lock on node. If node is busy, queue the packet. */
20473b2bd0f6Slogwang static __inline item_p
ng_acquire_read(node_p node,item_p item)20483b2bd0f6Slogwang ng_acquire_read(node_p node, item_p item)
20493b2bd0f6Slogwang {
20503b2bd0f6Slogwang     KASSERT(node != &ng_deadnode,
20513b2bd0f6Slogwang         ("%s: working on deadnode", __func__));
20523b2bd0f6Slogwang 
20533b2bd0f6Slogwang     /* Reader needs node without writer and pending items. */
20543b2bd0f6Slogwang     for (;;) {
20553b2bd0f6Slogwang         long t = node->nd_input_queue.q_flags;
20563b2bd0f6Slogwang         if (t & NGQ_RMASK)
20573b2bd0f6Slogwang             break; /* Node is not ready for reader. */
20583b2bd0f6Slogwang         if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
20593b2bd0f6Slogwang             t + READER_INCREMENT)) {
20603b2bd0f6Slogwang                 /* Successfully grabbed node */
20613b2bd0f6Slogwang             CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20623b2bd0f6Slogwang                 __func__, node->nd_ID, node, item);
20633b2bd0f6Slogwang             return (item);
20643b2bd0f6Slogwang         }
20653b2bd0f6Slogwang         cpu_spinwait();
20663b2bd0f6Slogwang     }
20673b2bd0f6Slogwang 
20683b2bd0f6Slogwang     /* Queue the request for later. */
20693b2bd0f6Slogwang     ng_queue_rw(node, item, NGQRW_R);
20703b2bd0f6Slogwang 
20713b2bd0f6Slogwang     return (NULL);
20723b2bd0f6Slogwang }
20733b2bd0f6Slogwang 
20743b2bd0f6Slogwang /* Acquire writer lock on node. If node is busy, queue the packet. */
20753b2bd0f6Slogwang static __inline item_p
ng_acquire_write(node_p node,item_p item)20763b2bd0f6Slogwang ng_acquire_write(node_p node, item_p item)
20773b2bd0f6Slogwang {
20783b2bd0f6Slogwang     KASSERT(node != &ng_deadnode,
20793b2bd0f6Slogwang         ("%s: working on deadnode", __func__));
20803b2bd0f6Slogwang 
20813b2bd0f6Slogwang     /* Writer needs completely idle node. */
20823b2bd0f6Slogwang     if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
20833b2bd0f6Slogwang         WRITER_ACTIVE)) {
20843b2bd0f6Slogwang             /* Successfully grabbed node */
20853b2bd0f6Slogwang         CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20863b2bd0f6Slogwang             __func__, node->nd_ID, node, item);
20873b2bd0f6Slogwang         return (item);
20883b2bd0f6Slogwang     }
20893b2bd0f6Slogwang 
20903b2bd0f6Slogwang     /* Queue the request for later. */
20913b2bd0f6Slogwang     ng_queue_rw(node, item, NGQRW_W);
20923b2bd0f6Slogwang 
20933b2bd0f6Slogwang     return (NULL);
20943b2bd0f6Slogwang }
20953b2bd0f6Slogwang 
20963b2bd0f6Slogwang #if 0
20973b2bd0f6Slogwang static __inline item_p
20983b2bd0f6Slogwang ng_upgrade_write(node_p node, item_p item)
20993b2bd0f6Slogwang {
21003b2bd0f6Slogwang     struct ng_queue *ngq = &node->nd_input_queue;
21013b2bd0f6Slogwang     KASSERT(node != &ng_deadnode,
21023b2bd0f6Slogwang         ("%s: working on deadnode", __func__));
21033b2bd0f6Slogwang 
21043b2bd0f6Slogwang     NGI_SET_WRITER(item);
21053b2bd0f6Slogwang 
21063b2bd0f6Slogwang     NG_QUEUE_LOCK(ngq);
21073b2bd0f6Slogwang 
21083b2bd0f6Slogwang     /*
21093b2bd0f6Slogwang      * There will never be no readers as we are there ourselves.
21103b2bd0f6Slogwang      * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
21113b2bd0f6Slogwang      * The caller we are running from will call ng_leave_read()
21123b2bd0f6Slogwang      * soon, so we must account for that. We must leave again with the
21133b2bd0f6Slogwang      * READER lock. If we find other readers, then
21143b2bd0f6Slogwang      * queue the request for later. However "later" may be rignt now
21153b2bd0f6Slogwang      * if there are no readers. We don't really care if there are queued
21163b2bd0f6Slogwang      * items as we will bypass them anyhow.
21173b2bd0f6Slogwang      */
21183b2bd0f6Slogwang     atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
21193b2bd0f6Slogwang     if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
21203b2bd0f6Slogwang         NG_QUEUE_UNLOCK(ngq);
21213b2bd0f6Slogwang 
21223b2bd0f6Slogwang         /* It's just us, act on the item. */
21233b2bd0f6Slogwang         /* will NOT drop writer lock when done */
21243b2bd0f6Slogwang         ng_apply_item(node, item, 0);
21253b2bd0f6Slogwang 
21263b2bd0f6Slogwang         /*
21273b2bd0f6Slogwang          * Having acted on the item, atomically
21283b2bd0f6Slogwang          * downgrade back to READER and finish up.
21293b2bd0f6Slogwang           */
21303b2bd0f6Slogwang         atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
21313b2bd0f6Slogwang 
21323b2bd0f6Slogwang         /* Our caller will call ng_leave_read() */
21333b2bd0f6Slogwang         return;
21343b2bd0f6Slogwang     }
21353b2bd0f6Slogwang     /*
21363b2bd0f6Slogwang      * It's not just us active, so queue us AT THE HEAD.
21373b2bd0f6Slogwang      * "Why?" I hear you ask.
21383b2bd0f6Slogwang      * Put us at the head of the queue as we've already been
21393b2bd0f6Slogwang      * through it once. If there is nothing else waiting,
21403b2bd0f6Slogwang      * set the correct flags.
21413b2bd0f6Slogwang      */
21423b2bd0f6Slogwang     if (STAILQ_EMPTY(&ngq->queue)) {
21433b2bd0f6Slogwang         /* We've gone from, 0 to 1 item in the queue */
21443b2bd0f6Slogwang         atomic_set_int(&ngq->q_flags, OP_PENDING);
21453b2bd0f6Slogwang 
21463b2bd0f6Slogwang         CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
21473b2bd0f6Slogwang             node->nd_ID, node);
21483b2bd0f6Slogwang     };
21493b2bd0f6Slogwang     STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
21503b2bd0f6Slogwang     CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
21513b2bd0f6Slogwang         __func__, node->nd_ID, node, item );
21523b2bd0f6Slogwang 
21533b2bd0f6Slogwang     /* Reverse what we did above. That downgrades us back to reader */
21543b2bd0f6Slogwang     atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
21553b2bd0f6Slogwang     if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
21563b2bd0f6Slogwang         ng_worklist_add(node);
21573b2bd0f6Slogwang     NG_QUEUE_UNLOCK(ngq);
21583b2bd0f6Slogwang 
21593b2bd0f6Slogwang     return;
21603b2bd0f6Slogwang }
21613b2bd0f6Slogwang #endif
21623b2bd0f6Slogwang 
21633b2bd0f6Slogwang /* Release reader lock. */
21643b2bd0f6Slogwang static __inline void
ng_leave_read(node_p node)21653b2bd0f6Slogwang ng_leave_read(node_p node)
21663b2bd0f6Slogwang {
21673b2bd0f6Slogwang     atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
21683b2bd0f6Slogwang }
21693b2bd0f6Slogwang 
21703b2bd0f6Slogwang /* Release writer lock. */
21713b2bd0f6Slogwang static __inline void
ng_leave_write(node_p node)21723b2bd0f6Slogwang ng_leave_write(node_p node)
21733b2bd0f6Slogwang {
21743b2bd0f6Slogwang     atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
21753b2bd0f6Slogwang }
21763b2bd0f6Slogwang 
21773b2bd0f6Slogwang /* Purge node queue. Called on node shutdown. */
21783b2bd0f6Slogwang static void
ng_flush_input_queue(node_p node)21793b2bd0f6Slogwang ng_flush_input_queue(node_p node)
21803b2bd0f6Slogwang {
21813b2bd0f6Slogwang     struct ng_queue *ngq = &node->nd_input_queue;
21823b2bd0f6Slogwang     item_p item;
21833b2bd0f6Slogwang 
21843b2bd0f6Slogwang     NG_QUEUE_LOCK(ngq);
21853b2bd0f6Slogwang     while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
21863b2bd0f6Slogwang         STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
21873b2bd0f6Slogwang         if (STAILQ_EMPTY(&ngq->queue))
21883b2bd0f6Slogwang             atomic_clear_int(&ngq->q_flags, OP_PENDING);
21893b2bd0f6Slogwang         NG_QUEUE_UNLOCK(ngq);
21903b2bd0f6Slogwang 
21913b2bd0f6Slogwang         /* If the item is supplying a callback, call it with an error */
21923b2bd0f6Slogwang         if (item->apply != NULL) {
21933b2bd0f6Slogwang             if (item->depth == 1)
21943b2bd0f6Slogwang                 item->apply->error = ENOENT;
21953b2bd0f6Slogwang             if (refcount_release(&item->apply->refs)) {
21963b2bd0f6Slogwang                 (*item->apply->apply)(item->apply->context,
21973b2bd0f6Slogwang                     item->apply->error);
21983b2bd0f6Slogwang             }
21993b2bd0f6Slogwang         }
22003b2bd0f6Slogwang         NG_FREE_ITEM(item);
22013b2bd0f6Slogwang         NG_QUEUE_LOCK(ngq);
22023b2bd0f6Slogwang     }
22033b2bd0f6Slogwang     NG_QUEUE_UNLOCK(ngq);
22043b2bd0f6Slogwang }
22053b2bd0f6Slogwang #endif
22063b2bd0f6Slogwang 
22073b2bd0f6Slogwang /***********************************************************************
22083b2bd0f6Slogwang * Externally visible method for sending or queueing messages or data.
22093b2bd0f6Slogwang ***********************************************************************/
22103b2bd0f6Slogwang 
22113b2bd0f6Slogwang /*
22123b2bd0f6Slogwang  * The module code should have filled out the item correctly by this stage:
22133b2bd0f6Slogwang  * Common:
22143b2bd0f6Slogwang  *    reference to destination node.
22153b2bd0f6Slogwang  *    Reference to destination rcv hook if relevant.
22163b2bd0f6Slogwang  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
22173b2bd0f6Slogwang  * Data:
22183b2bd0f6Slogwang  *    pointer to mbuf
22193b2bd0f6Slogwang  * Control_Message:
22203b2bd0f6Slogwang  *    pointer to msg.
22213b2bd0f6Slogwang  *    ID of original sender node. (return address)
22223b2bd0f6Slogwang  * Function:
22233b2bd0f6Slogwang  *    Function pointer
22243b2bd0f6Slogwang  *    void * argument
22253b2bd0f6Slogwang  *    integer argument
22263b2bd0f6Slogwang  *
22273b2bd0f6Slogwang  * The nodes have several routines and macros to help with this task:
22283b2bd0f6Slogwang  */
22293b2bd0f6Slogwang 
22303b2bd0f6Slogwang int
ng_snd_item(item_p item,int flags)22313b2bd0f6Slogwang ng_snd_item(item_p item, int flags)
22323b2bd0f6Slogwang {
22333b2bd0f6Slogwang     hook_p hook;
22343b2bd0f6Slogwang     node_p node;
22353b2bd0f6Slogwang #ifndef FSTACK
22363b2bd0f6Slogwang     int queue, rw;
22373b2bd0f6Slogwang     struct ng_queue *ngq;
22383b2bd0f6Slogwang #endif
22393b2bd0f6Slogwang     int error = 0;
22403b2bd0f6Slogwang 
22413b2bd0f6Slogwang     /* We are sending item, so it must be present! */
22423b2bd0f6Slogwang     KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
22433b2bd0f6Slogwang 
22443b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
22453b2bd0f6Slogwang     _ngi_check(item, __FILE__, __LINE__);
22463b2bd0f6Slogwang #endif
22473b2bd0f6Slogwang 
22483b2bd0f6Slogwang     /* Item was sent once more, postpone apply() call. */
22493b2bd0f6Slogwang     if (item->apply)
22503b2bd0f6Slogwang         refcount_acquire(&item->apply->refs);
22513b2bd0f6Slogwang 
22523b2bd0f6Slogwang     node = NGI_NODE(item);
22533b2bd0f6Slogwang     /* Node is never optional. */
22543b2bd0f6Slogwang     KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
22553b2bd0f6Slogwang 
22563b2bd0f6Slogwang     hook = NGI_HOOK(item);
22573b2bd0f6Slogwang     /* Valid hook and mbuf are mandatory for data. */
22583b2bd0f6Slogwang     if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
22593b2bd0f6Slogwang         KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
22603b2bd0f6Slogwang         if (NGI_M(item) == NULL)
22613b2bd0f6Slogwang             ERROUT(EINVAL);
22623b2bd0f6Slogwang         CHECK_DATA_MBUF(NGI_M(item));
22633b2bd0f6Slogwang     }
22643b2bd0f6Slogwang 
22653b2bd0f6Slogwang #ifndef FSTACK
22663b2bd0f6Slogwang     /*
22673b2bd0f6Slogwang      * If the item or the node specifies single threading, force
22683b2bd0f6Slogwang      * writer semantics. Similarly, the node may say one hook always
22693b2bd0f6Slogwang      * produces writers. These are overrides.
22703b2bd0f6Slogwang      */
22713b2bd0f6Slogwang     if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
22723b2bd0f6Slogwang         (node->nd_flags & NGF_FORCE_WRITER) ||
22733b2bd0f6Slogwang         (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
22743b2bd0f6Slogwang         rw = NGQRW_W;
22753b2bd0f6Slogwang     } else {
22763b2bd0f6Slogwang         rw = NGQRW_R;
22773b2bd0f6Slogwang     }
22783b2bd0f6Slogwang 
22793b2bd0f6Slogwang     /*
22803b2bd0f6Slogwang      * If sender or receiver requests queued delivery, or call graph
22813b2bd0f6Slogwang      * loops back from outbound to inbound path, or stack usage
22823b2bd0f6Slogwang      * level is dangerous - enqueue message.
22833b2bd0f6Slogwang      */
22843b2bd0f6Slogwang     if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
22853b2bd0f6Slogwang         queue = 1;
22863b2bd0f6Slogwang     } else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
22873b2bd0f6Slogwang         curthread->td_ng_outbound) {
22883b2bd0f6Slogwang         queue = 1;
22893b2bd0f6Slogwang     } else {
22903b2bd0f6Slogwang         queue = 0;
22913b2bd0f6Slogwang #ifdef GET_STACK_USAGE
22923b2bd0f6Slogwang         /*
22933b2bd0f6Slogwang          * Most of netgraph nodes have small stack consumption and
22943b2bd0f6Slogwang          * for them 25% of free stack space is more than enough.
22953b2bd0f6Slogwang          * Nodes/hooks with higher stack usage should be marked as
22963b2bd0f6Slogwang          * HI_STACK. For them 50% of stack will be guaranteed then.
22973b2bd0f6Slogwang          * XXX: Values 25% and 50% are completely empirical.
22983b2bd0f6Slogwang          */
22993b2bd0f6Slogwang         size_t    st, su, sl;
23003b2bd0f6Slogwang         GET_STACK_USAGE(st, su);
23013b2bd0f6Slogwang         sl = st - su;
23023b2bd0f6Slogwang         if ((sl * 4 < st) || ((sl * 2 < st) &&
23033b2bd0f6Slogwang             ((node->nd_flags & NGF_HI_STACK) || (hook &&
23043b2bd0f6Slogwang             (hook->hk_flags & HK_HI_STACK)))))
23053b2bd0f6Slogwang             queue = 1;
23063b2bd0f6Slogwang #endif
23073b2bd0f6Slogwang     }
23083b2bd0f6Slogwang 
23093b2bd0f6Slogwang     if (queue) {
23103b2bd0f6Slogwang         /* Put it on the queue for that node*/
23113b2bd0f6Slogwang         ng_queue_rw(node, item, rw);
23123b2bd0f6Slogwang         return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
23133b2bd0f6Slogwang     }
23143b2bd0f6Slogwang 
23153b2bd0f6Slogwang     /*
23163b2bd0f6Slogwang      * We already decided how we will be queueud or treated.
23173b2bd0f6Slogwang      * Try get the appropriate operating permission.
23183b2bd0f6Slogwang      */
23193b2bd0f6Slogwang      if (rw == NGQRW_R)
23203b2bd0f6Slogwang         item = ng_acquire_read(node, item);
23213b2bd0f6Slogwang     else
23223b2bd0f6Slogwang         item = ng_acquire_write(node, item);
23233b2bd0f6Slogwang 
23243b2bd0f6Slogwang     /* Item was queued while trying to get permission. */
23253b2bd0f6Slogwang     if (item == NULL)
23263b2bd0f6Slogwang         return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
23273b2bd0f6Slogwang #endif
23283b2bd0f6Slogwang 
23293b2bd0f6Slogwang     NGI_GET_NODE(item, node); /* zaps stored node */
23303b2bd0f6Slogwang 
23313b2bd0f6Slogwang     item->depth++;
23323b2bd0f6Slogwang #ifndef FSTACK
23333b2bd0f6Slogwang     error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
23343b2bd0f6Slogwang #else
23353b2bd0f6Slogwang     error = ng_apply_item(node, item, 0);
23363b2bd0f6Slogwang #endif
23373b2bd0f6Slogwang 
23383b2bd0f6Slogwang #ifndef FSTACK
23393b2bd0f6Slogwang     /* If something is waiting on queue and ready, schedule it. */
23403b2bd0f6Slogwang     ngq = &node->nd_input_queue;
23413b2bd0f6Slogwang     if (QUEUE_ACTIVE(ngq)) {
23423b2bd0f6Slogwang         NG_QUEUE_LOCK(ngq);
23433b2bd0f6Slogwang         if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
23443b2bd0f6Slogwang             ng_worklist_add(node);
23453b2bd0f6Slogwang         NG_QUEUE_UNLOCK(ngq);
23463b2bd0f6Slogwang     }
23473b2bd0f6Slogwang #endif
23483b2bd0f6Slogwang 
23493b2bd0f6Slogwang     /*
23503b2bd0f6Slogwang      * Node may go away as soon as we remove the reference.
23513b2bd0f6Slogwang      * Whatever we do, DO NOT access the node again!
23523b2bd0f6Slogwang      */
23533b2bd0f6Slogwang     NG_NODE_UNREF(node);
23543b2bd0f6Slogwang 
23553b2bd0f6Slogwang     return (error);
23563b2bd0f6Slogwang 
23573b2bd0f6Slogwang done:
23583b2bd0f6Slogwang     /* If was not sent, apply callback here. */
23593b2bd0f6Slogwang     if (item->apply != NULL) {
23603b2bd0f6Slogwang         if (item->depth == 0 && error != 0)
23613b2bd0f6Slogwang             item->apply->error = error;
23623b2bd0f6Slogwang         if (refcount_release(&item->apply->refs)) {
23633b2bd0f6Slogwang             (*item->apply->apply)(item->apply->context,
23643b2bd0f6Slogwang                 item->apply->error);
23653b2bd0f6Slogwang         }
23663b2bd0f6Slogwang     }
23673b2bd0f6Slogwang 
23683b2bd0f6Slogwang     NG_FREE_ITEM(item);
23693b2bd0f6Slogwang     return (error);
23703b2bd0f6Slogwang }
23713b2bd0f6Slogwang 
23723b2bd0f6Slogwang /*
23733b2bd0f6Slogwang  * We have an item that was possibly queued somewhere.
23743b2bd0f6Slogwang  * It should contain all the information needed
23753b2bd0f6Slogwang  * to run it on the appropriate node/hook.
23763b2bd0f6Slogwang  * If there is apply pointer and we own the last reference, call apply().
23773b2bd0f6Slogwang  */
23783b2bd0f6Slogwang static int
ng_apply_item(node_p node,item_p item,int rw)23793b2bd0f6Slogwang ng_apply_item(node_p node, item_p item, int rw)
23803b2bd0f6Slogwang {
23813b2bd0f6Slogwang     hook_p  hook;
23823b2bd0f6Slogwang     ng_rcvdata_t *rcvdata;
23833b2bd0f6Slogwang     ng_rcvmsg_t *rcvmsg;
23843b2bd0f6Slogwang     struct ng_apply_info *apply;
23853b2bd0f6Slogwang     int    error = 0, depth;
23863b2bd0f6Slogwang 
23873b2bd0f6Slogwang     /* Node and item are never optional. */
23883b2bd0f6Slogwang     KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
23893b2bd0f6Slogwang     KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
23903b2bd0f6Slogwang 
23913b2bd0f6Slogwang     NGI_GET_HOOK(item, hook); /* clears stored hook */
23923b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
23933b2bd0f6Slogwang     _ngi_check(item, __FILE__, __LINE__);
23943b2bd0f6Slogwang #endif
23953b2bd0f6Slogwang 
23963b2bd0f6Slogwang     apply = item->apply;
23973b2bd0f6Slogwang     depth = item->depth;
23983b2bd0f6Slogwang 
23993b2bd0f6Slogwang     switch (item->el_flags & NGQF_TYPE) {
24003b2bd0f6Slogwang     case NGQF_DATA:
24013b2bd0f6Slogwang         /*
24023b2bd0f6Slogwang          * Check things are still ok as when we were queued.
24033b2bd0f6Slogwang          */
24043b2bd0f6Slogwang         KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
24053b2bd0f6Slogwang         if (NG_HOOK_NOT_VALID(hook) ||
24063b2bd0f6Slogwang             NG_NODE_NOT_VALID(node)) {
24073b2bd0f6Slogwang             error = EIO;
24083b2bd0f6Slogwang             NG_FREE_ITEM(item);
24093b2bd0f6Slogwang             break;
24103b2bd0f6Slogwang         }
24113b2bd0f6Slogwang         /*
24123b2bd0f6Slogwang          * If no receive method, just silently drop it.
24133b2bd0f6Slogwang          * Give preference to the hook over-ride method.
24143b2bd0f6Slogwang          */
24153b2bd0f6Slogwang         if ((!(rcvdata = hook->hk_rcvdata)) &&
24163b2bd0f6Slogwang             (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
24173b2bd0f6Slogwang             error = 0;
24183b2bd0f6Slogwang             NG_FREE_ITEM(item);
24193b2bd0f6Slogwang             break;
24203b2bd0f6Slogwang         }
24213b2bd0f6Slogwang         error = (*rcvdata)(hook, item);
24223b2bd0f6Slogwang         break;
24233b2bd0f6Slogwang     case NGQF_MESG:
24243b2bd0f6Slogwang         if (hook && NG_HOOK_NOT_VALID(hook)) {
24253b2bd0f6Slogwang             /*
24263b2bd0f6Slogwang              * The hook has been zapped then we can't use it.
24273b2bd0f6Slogwang              * Immediately drop its reference.
24283b2bd0f6Slogwang              * The message may not need it.
24293b2bd0f6Slogwang              */
24303b2bd0f6Slogwang             NG_HOOK_UNREF(hook);
24313b2bd0f6Slogwang             hook = NULL;
24323b2bd0f6Slogwang         }
24333b2bd0f6Slogwang         /*
24343b2bd0f6Slogwang          * Similarly, if the node is a zombie there is
24353b2bd0f6Slogwang          * nothing we can do with it, drop everything.
24363b2bd0f6Slogwang          */
24373b2bd0f6Slogwang         if (NG_NODE_NOT_VALID(node)) {
24383b2bd0f6Slogwang             TRAP_ERROR();
24393b2bd0f6Slogwang             error = EINVAL;
24403b2bd0f6Slogwang             NG_FREE_ITEM(item);
24413b2bd0f6Slogwang             break;
24423b2bd0f6Slogwang         }
24433b2bd0f6Slogwang         /*
24443b2bd0f6Slogwang          * Call the appropriate message handler for the object.
24453b2bd0f6Slogwang          * It is up to the message handler to free the message.
24463b2bd0f6Slogwang          * If it's a generic message, handle it generically,
24473b2bd0f6Slogwang          * otherwise call the type's message handler (if it exists).
24483b2bd0f6Slogwang          * XXX (race). Remember that a queued message may
24493b2bd0f6Slogwang          * reference a node or hook that has just been
24503b2bd0f6Slogwang          * invalidated. It will exist as the queue code
24513b2bd0f6Slogwang          * is holding a reference, but..
24523b2bd0f6Slogwang          */
24533b2bd0f6Slogwang         if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
24543b2bd0f6Slogwang             ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
24553b2bd0f6Slogwang             error = ng_generic_msg(node, item, hook);
24563b2bd0f6Slogwang             break;
24573b2bd0f6Slogwang         }
24583b2bd0f6Slogwang         if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
24593b2bd0f6Slogwang             (!(rcvmsg = node->nd_type->rcvmsg))) {
24603b2bd0f6Slogwang             TRAP_ERROR();
24613b2bd0f6Slogwang             error = 0;
24623b2bd0f6Slogwang             NG_FREE_ITEM(item);
24633b2bd0f6Slogwang             break;
24643b2bd0f6Slogwang         }
24653b2bd0f6Slogwang         error = (*rcvmsg)(node, item, hook);
24663b2bd0f6Slogwang         break;
24673b2bd0f6Slogwang     case NGQF_FN:
24683b2bd0f6Slogwang     case NGQF_FN2:
24693b2bd0f6Slogwang         /*
24703b2bd0f6Slogwang          * In the case of the shutdown message we allow it to hit
24713b2bd0f6Slogwang          * even if the node is invalid.
24723b2bd0f6Slogwang          */
24733b2bd0f6Slogwang         if (NG_NODE_NOT_VALID(node) &&
24743b2bd0f6Slogwang             NGI_FN(item) != &ng_rmnode) {
24753b2bd0f6Slogwang             TRAP_ERROR();
24763b2bd0f6Slogwang             error = EINVAL;
24773b2bd0f6Slogwang             NG_FREE_ITEM(item);
24783b2bd0f6Slogwang             break;
24793b2bd0f6Slogwang         }
24803b2bd0f6Slogwang         /* Same is about some internal functions and invalid hook. */
24813b2bd0f6Slogwang         if (hook && NG_HOOK_NOT_VALID(hook) &&
24823b2bd0f6Slogwang             NGI_FN2(item) != &ng_con_part2 &&
24833b2bd0f6Slogwang             NGI_FN2(item) != &ng_con_part3 &&
24843b2bd0f6Slogwang             NGI_FN(item) != &ng_rmhook_part2) {
24853b2bd0f6Slogwang             TRAP_ERROR();
24863b2bd0f6Slogwang             error = EINVAL;
24873b2bd0f6Slogwang             NG_FREE_ITEM(item);
24883b2bd0f6Slogwang             break;
24893b2bd0f6Slogwang         }
24903b2bd0f6Slogwang 
24913b2bd0f6Slogwang         if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
24923b2bd0f6Slogwang             (*NGI_FN(item))(node, hook, NGI_ARG1(item),
24933b2bd0f6Slogwang                 NGI_ARG2(item));
24943b2bd0f6Slogwang             NG_FREE_ITEM(item);
24953b2bd0f6Slogwang         } else    /* it is NGQF_FN2 */
24963b2bd0f6Slogwang             error = (*NGI_FN2(item))(node, item, hook);
24973b2bd0f6Slogwang         break;
24983b2bd0f6Slogwang     }
24993b2bd0f6Slogwang     /*
25003b2bd0f6Slogwang      * We held references on some of the resources
25013b2bd0f6Slogwang      * that we took from the item. Now that we have
25023b2bd0f6Slogwang      * finished doing everything, drop those references.
25033b2bd0f6Slogwang      */
25043b2bd0f6Slogwang     if (hook)
25053b2bd0f6Slogwang         NG_HOOK_UNREF(hook);
25063b2bd0f6Slogwang 
25073b2bd0f6Slogwang #ifndef FSTACK
25083b2bd0f6Slogwang      if (rw == NGQRW_R)
25093b2bd0f6Slogwang         ng_leave_read(node);
25103b2bd0f6Slogwang     else
25113b2bd0f6Slogwang         ng_leave_write(node);
25123b2bd0f6Slogwang #endif
25133b2bd0f6Slogwang 
25143b2bd0f6Slogwang     /* Apply callback. */
25153b2bd0f6Slogwang     if (apply != NULL) {
25163b2bd0f6Slogwang         if (depth == 1 && error != 0)
25173b2bd0f6Slogwang             apply->error = error;
25183b2bd0f6Slogwang         if (refcount_release(&apply->refs))
25193b2bd0f6Slogwang             (*apply->apply)(apply->context, apply->error);
25203b2bd0f6Slogwang     }
25213b2bd0f6Slogwang 
25223b2bd0f6Slogwang     return (error);
25233b2bd0f6Slogwang }
25243b2bd0f6Slogwang 
25253b2bd0f6Slogwang /***********************************************************************
25263b2bd0f6Slogwang  * Implement the 'generic' control messages
25273b2bd0f6Slogwang  ***********************************************************************/
25283b2bd0f6Slogwang static int
ng_generic_msg(node_p here,item_p item,hook_p lasthook)25293b2bd0f6Slogwang ng_generic_msg(node_p here, item_p item, hook_p lasthook)
25303b2bd0f6Slogwang {
25313b2bd0f6Slogwang     int error = 0;
25323b2bd0f6Slogwang     struct ng_mesg *msg;
25333b2bd0f6Slogwang     struct ng_mesg *resp = NULL;
25343b2bd0f6Slogwang 
25353b2bd0f6Slogwang     NGI_GET_MSG(item, msg);
25363b2bd0f6Slogwang     if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
25373b2bd0f6Slogwang         TRAP_ERROR();
25383b2bd0f6Slogwang         error = EINVAL;
25393b2bd0f6Slogwang         goto out;
25403b2bd0f6Slogwang     }
25413b2bd0f6Slogwang     switch (msg->header.cmd) {
25423b2bd0f6Slogwang     case NGM_SHUTDOWN:
25433b2bd0f6Slogwang         ng_rmnode(here, NULL, NULL, 0);
25443b2bd0f6Slogwang         break;
25453b2bd0f6Slogwang     case NGM_MKPEER:
25463b2bd0f6Slogwang         {
25473b2bd0f6Slogwang         struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
25483b2bd0f6Slogwang 
25493b2bd0f6Slogwang         if (msg->header.arglen != sizeof(*mkp)) {
25503b2bd0f6Slogwang             TRAP_ERROR();
25513b2bd0f6Slogwang             error = EINVAL;
25523b2bd0f6Slogwang             break;
25533b2bd0f6Slogwang         }
25543b2bd0f6Slogwang         mkp->type[sizeof(mkp->type) - 1] = '\0';
25553b2bd0f6Slogwang         mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
25563b2bd0f6Slogwang         mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
25573b2bd0f6Slogwang         error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
25583b2bd0f6Slogwang         break;
25593b2bd0f6Slogwang         }
25603b2bd0f6Slogwang     case NGM_CONNECT:
25613b2bd0f6Slogwang         {
25623b2bd0f6Slogwang         struct ngm_connect *const con =
25633b2bd0f6Slogwang             (struct ngm_connect *) msg->data;
25643b2bd0f6Slogwang         node_p node2;
25653b2bd0f6Slogwang 
25663b2bd0f6Slogwang         if (msg->header.arglen != sizeof(*con)) {
25673b2bd0f6Slogwang             TRAP_ERROR();
25683b2bd0f6Slogwang             error = EINVAL;
25693b2bd0f6Slogwang             break;
25703b2bd0f6Slogwang         }
25713b2bd0f6Slogwang         con->path[sizeof(con->path) - 1] = '\0';
25723b2bd0f6Slogwang         con->ourhook[sizeof(con->ourhook) - 1] = '\0';
25733b2bd0f6Slogwang         con->peerhook[sizeof(con->peerhook) - 1] = '\0';
25743b2bd0f6Slogwang         /* Don't forget we get a reference.. */
25753b2bd0f6Slogwang         error = ng_path2noderef(here, con->path, &node2, NULL);
25763b2bd0f6Slogwang         if (error)
25773b2bd0f6Slogwang             break;
25783b2bd0f6Slogwang         error = ng_con_nodes(item, here, con->ourhook,
25793b2bd0f6Slogwang             node2, con->peerhook);
25803b2bd0f6Slogwang         NG_NODE_UNREF(node2);
25813b2bd0f6Slogwang         break;
25823b2bd0f6Slogwang         }
25833b2bd0f6Slogwang     case NGM_NAME:
25843b2bd0f6Slogwang         {
25853b2bd0f6Slogwang         struct ngm_name *const nam = (struct ngm_name *) msg->data;
25863b2bd0f6Slogwang 
25873b2bd0f6Slogwang         if (msg->header.arglen != sizeof(*nam)) {
25883b2bd0f6Slogwang             TRAP_ERROR();
25893b2bd0f6Slogwang             error = EINVAL;
25903b2bd0f6Slogwang             break;
25913b2bd0f6Slogwang         }
25923b2bd0f6Slogwang         nam->name[sizeof(nam->name) - 1] = '\0';
25933b2bd0f6Slogwang         error = ng_name_node(here, nam->name);
25943b2bd0f6Slogwang         break;
25953b2bd0f6Slogwang         }
25963b2bd0f6Slogwang     case NGM_RMHOOK:
25973b2bd0f6Slogwang         {
25983b2bd0f6Slogwang         struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
25993b2bd0f6Slogwang         hook_p hook;
26003b2bd0f6Slogwang 
26013b2bd0f6Slogwang         if (msg->header.arglen != sizeof(*rmh)) {
26023b2bd0f6Slogwang             TRAP_ERROR();
26033b2bd0f6Slogwang             error = EINVAL;
26043b2bd0f6Slogwang             break;
26053b2bd0f6Slogwang         }
26063b2bd0f6Slogwang         rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
26073b2bd0f6Slogwang         if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
26083b2bd0f6Slogwang             ng_destroy_hook(hook);
26093b2bd0f6Slogwang         break;
26103b2bd0f6Slogwang         }
26113b2bd0f6Slogwang     case NGM_NODEINFO:
26123b2bd0f6Slogwang         {
26133b2bd0f6Slogwang         struct nodeinfo *ni;
26143b2bd0f6Slogwang 
26153b2bd0f6Slogwang         NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
26163b2bd0f6Slogwang         if (resp == NULL) {
26173b2bd0f6Slogwang             error = ENOMEM;
26183b2bd0f6Slogwang             break;
26193b2bd0f6Slogwang         }
26203b2bd0f6Slogwang 
26213b2bd0f6Slogwang         /* Fill in node info */
26223b2bd0f6Slogwang         ni = (struct nodeinfo *) resp->data;
26233b2bd0f6Slogwang         if (NG_NODE_HAS_NAME(here))
26243b2bd0f6Slogwang             strcpy(ni->name, NG_NODE_NAME(here));
26253b2bd0f6Slogwang         strcpy(ni->type, here->nd_type->name);
26263b2bd0f6Slogwang         ni->id = ng_node2ID(here);
26273b2bd0f6Slogwang         ni->hooks = here->nd_numhooks;
26283b2bd0f6Slogwang         break;
26293b2bd0f6Slogwang         }
26303b2bd0f6Slogwang     case NGM_LISTHOOKS:
26313b2bd0f6Slogwang         {
26323b2bd0f6Slogwang         const int nhooks = here->nd_numhooks;
26333b2bd0f6Slogwang         struct hooklist *hl;
26343b2bd0f6Slogwang         struct nodeinfo *ni;
26353b2bd0f6Slogwang         hook_p hook;
26363b2bd0f6Slogwang 
26373b2bd0f6Slogwang         /* Get response struct */
26383b2bd0f6Slogwang         NG_MKRESPONSE(resp, msg, sizeof(*hl) +
26393b2bd0f6Slogwang             (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
26403b2bd0f6Slogwang         if (resp == NULL) {
26413b2bd0f6Slogwang             error = ENOMEM;
26423b2bd0f6Slogwang             break;
26433b2bd0f6Slogwang         }
26443b2bd0f6Slogwang         hl = (struct hooklist *) resp->data;
26453b2bd0f6Slogwang         ni = &hl->nodeinfo;
26463b2bd0f6Slogwang 
26473b2bd0f6Slogwang         /* Fill in node info */
26483b2bd0f6Slogwang         if (NG_NODE_HAS_NAME(here))
26493b2bd0f6Slogwang             strcpy(ni->name, NG_NODE_NAME(here));
26503b2bd0f6Slogwang         strcpy(ni->type, here->nd_type->name);
26513b2bd0f6Slogwang         ni->id = ng_node2ID(here);
26523b2bd0f6Slogwang 
26533b2bd0f6Slogwang         /* Cycle through the linked list of hooks */
26543b2bd0f6Slogwang         ni->hooks = 0;
26553b2bd0f6Slogwang         LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
26563b2bd0f6Slogwang             struct linkinfo *const link = &hl->link[ni->hooks];
26573b2bd0f6Slogwang 
26583b2bd0f6Slogwang             if (ni->hooks >= nhooks) {
26593b2bd0f6Slogwang                 log(LOG_ERR, "%s: number of %s changed\n",
26603b2bd0f6Slogwang                     __func__, "hooks");
26613b2bd0f6Slogwang                 break;
26623b2bd0f6Slogwang             }
26633b2bd0f6Slogwang             if (NG_HOOK_NOT_VALID(hook))
26643b2bd0f6Slogwang                 continue;
26653b2bd0f6Slogwang             strcpy(link->ourhook, NG_HOOK_NAME(hook));
26663b2bd0f6Slogwang             strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
26673b2bd0f6Slogwang             if (NG_PEER_NODE_NAME(hook)[0] != '\0')
26683b2bd0f6Slogwang                 strcpy(link->nodeinfo.name,
26693b2bd0f6Slogwang                     NG_PEER_NODE_NAME(hook));
26703b2bd0f6Slogwang             strcpy(link->nodeinfo.type,
26713b2bd0f6Slogwang                NG_PEER_NODE(hook)->nd_type->name);
26723b2bd0f6Slogwang             link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
26733b2bd0f6Slogwang             link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
26743b2bd0f6Slogwang             ni->hooks++;
26753b2bd0f6Slogwang         }
26763b2bd0f6Slogwang         break;
26773b2bd0f6Slogwang         }
26783b2bd0f6Slogwang 
26793b2bd0f6Slogwang     case NGM_LISTNODES:
26803b2bd0f6Slogwang         {
26813b2bd0f6Slogwang         struct namelist *nl;
26823b2bd0f6Slogwang         node_p node;
26833b2bd0f6Slogwang         int i;
26843b2bd0f6Slogwang 
26853b2bd0f6Slogwang         IDHASH_RLOCK();
26863b2bd0f6Slogwang         /* Get response struct. */
26873b2bd0f6Slogwang         NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2688*8640edf1Sfengbojiang             (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
26893b2bd0f6Slogwang         if (resp == NULL) {
26903b2bd0f6Slogwang             IDHASH_RUNLOCK();
26913b2bd0f6Slogwang             error = ENOMEM;
26923b2bd0f6Slogwang             break;
26933b2bd0f6Slogwang         }
26943b2bd0f6Slogwang         nl = (struct namelist *) resp->data;
26953b2bd0f6Slogwang 
26963b2bd0f6Slogwang         /* Cycle through the lists of nodes. */
26973b2bd0f6Slogwang         nl->numnames = 0;
26983b2bd0f6Slogwang         for (i = 0; i <= V_ng_ID_hmask; i++) {
26993b2bd0f6Slogwang             LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
27003b2bd0f6Slogwang                 struct nodeinfo *const np =
27013b2bd0f6Slogwang                     &nl->nodeinfo[nl->numnames];
27023b2bd0f6Slogwang 
27033b2bd0f6Slogwang                 if (NG_NODE_NOT_VALID(node))
27043b2bd0f6Slogwang                     continue;
27053b2bd0f6Slogwang                 if (NG_NODE_HAS_NAME(node))
27063b2bd0f6Slogwang                     strcpy(np->name, NG_NODE_NAME(node));
27073b2bd0f6Slogwang                 strcpy(np->type, node->nd_type->name);
27083b2bd0f6Slogwang                 np->id = ng_node2ID(node);
27093b2bd0f6Slogwang                 np->hooks = node->nd_numhooks;
27103b2bd0f6Slogwang                 KASSERT(nl->numnames < V_ng_nodes,
27113b2bd0f6Slogwang                     ("%s: no space", __func__));
27123b2bd0f6Slogwang                 nl->numnames++;
27133b2bd0f6Slogwang             }
27143b2bd0f6Slogwang         }
27153b2bd0f6Slogwang         IDHASH_RUNLOCK();
27163b2bd0f6Slogwang         break;
27173b2bd0f6Slogwang         }
27183b2bd0f6Slogwang     case NGM_LISTNAMES:
27193b2bd0f6Slogwang         {
27203b2bd0f6Slogwang         struct namelist *nl;
27213b2bd0f6Slogwang         node_p node;
27223b2bd0f6Slogwang         int i;
27233b2bd0f6Slogwang 
27243b2bd0f6Slogwang         NAMEHASH_RLOCK();
27253b2bd0f6Slogwang         /* Get response struct. */
27263b2bd0f6Slogwang         NG_MKRESPONSE(resp, msg, sizeof(*nl) +
27273b2bd0f6Slogwang             (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
27283b2bd0f6Slogwang         if (resp == NULL) {
27293b2bd0f6Slogwang             NAMEHASH_RUNLOCK();
27303b2bd0f6Slogwang             error = ENOMEM;
27313b2bd0f6Slogwang             break;
27323b2bd0f6Slogwang         }
27333b2bd0f6Slogwang         nl = (struct namelist *) resp->data;
27343b2bd0f6Slogwang 
27353b2bd0f6Slogwang         /* Cycle through the lists of nodes. */
27363b2bd0f6Slogwang         nl->numnames = 0;
27373b2bd0f6Slogwang         for (i = 0; i <= V_ng_name_hmask; i++) {
27383b2bd0f6Slogwang             LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
27393b2bd0f6Slogwang                 struct nodeinfo *const np =
27403b2bd0f6Slogwang                     &nl->nodeinfo[nl->numnames];
27413b2bd0f6Slogwang 
27423b2bd0f6Slogwang                 if (NG_NODE_NOT_VALID(node))
27433b2bd0f6Slogwang                     continue;
27443b2bd0f6Slogwang                 strcpy(np->name, NG_NODE_NAME(node));
27453b2bd0f6Slogwang                 strcpy(np->type, node->nd_type->name);
27463b2bd0f6Slogwang                 np->id = ng_node2ID(node);
27473b2bd0f6Slogwang                 np->hooks = node->nd_numhooks;
27483b2bd0f6Slogwang                 KASSERT(nl->numnames < V_ng_named_nodes,
27493b2bd0f6Slogwang                     ("%s: no space", __func__));
27503b2bd0f6Slogwang                 nl->numnames++;
27513b2bd0f6Slogwang             }
27523b2bd0f6Slogwang         }
27533b2bd0f6Slogwang         NAMEHASH_RUNLOCK();
27543b2bd0f6Slogwang         break;
27553b2bd0f6Slogwang         }
27563b2bd0f6Slogwang 
27573b2bd0f6Slogwang     case NGM_LISTTYPES:
27583b2bd0f6Slogwang         {
27593b2bd0f6Slogwang         struct typelist *tl;
27603b2bd0f6Slogwang         struct ng_type *type;
27613b2bd0f6Slogwang         int num = 0;
27623b2bd0f6Slogwang 
27633b2bd0f6Slogwang         TYPELIST_RLOCK();
27643b2bd0f6Slogwang         /* Count number of types */
27653b2bd0f6Slogwang         LIST_FOREACH(type, &ng_typelist, types)
27663b2bd0f6Slogwang             num++;
27673b2bd0f6Slogwang 
27683b2bd0f6Slogwang         /* Get response struct */
27693b2bd0f6Slogwang         NG_MKRESPONSE(resp, msg, sizeof(*tl) +
27703b2bd0f6Slogwang             (num * sizeof(struct typeinfo)), M_NOWAIT);
27713b2bd0f6Slogwang         if (resp == NULL) {
27723b2bd0f6Slogwang             TYPELIST_RUNLOCK();
27733b2bd0f6Slogwang             error = ENOMEM;
27743b2bd0f6Slogwang             break;
27753b2bd0f6Slogwang         }
27763b2bd0f6Slogwang         tl = (struct typelist *) resp->data;
27773b2bd0f6Slogwang 
27783b2bd0f6Slogwang         /* Cycle through the linked list of types */
27793b2bd0f6Slogwang         tl->numtypes = 0;
27803b2bd0f6Slogwang         LIST_FOREACH(type, &ng_typelist, types) {
27813b2bd0f6Slogwang             struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
27823b2bd0f6Slogwang 
27833b2bd0f6Slogwang             strcpy(tp->type_name, type->name);
27843b2bd0f6Slogwang             tp->numnodes = type->refs - 1; /* don't count list */
27853b2bd0f6Slogwang             KASSERT(tl->numtypes < num, ("%s: no space", __func__));
27863b2bd0f6Slogwang             tl->numtypes++;
27873b2bd0f6Slogwang         }
27883b2bd0f6Slogwang         TYPELIST_RUNLOCK();
27893b2bd0f6Slogwang         break;
27903b2bd0f6Slogwang         }
27913b2bd0f6Slogwang 
27923b2bd0f6Slogwang     case NGM_BINARY2ASCII:
27933b2bd0f6Slogwang         {
27943b2bd0f6Slogwang         int bufSize = 20 * 1024;    /* XXX hard coded constant */
27953b2bd0f6Slogwang         const struct ng_parse_type *argstype;
27963b2bd0f6Slogwang         const struct ng_cmdlist *c;
27973b2bd0f6Slogwang         struct ng_mesg *binary, *ascii;
27983b2bd0f6Slogwang 
27993b2bd0f6Slogwang         /* Data area must contain a valid netgraph message */
28003b2bd0f6Slogwang         binary = (struct ng_mesg *)msg->data;
28013b2bd0f6Slogwang         if (msg->header.arglen < sizeof(struct ng_mesg) ||
28023b2bd0f6Slogwang             (msg->header.arglen - sizeof(struct ng_mesg) <
28033b2bd0f6Slogwang             binary->header.arglen)) {
28043b2bd0f6Slogwang             TRAP_ERROR();
28053b2bd0f6Slogwang             error = EINVAL;
28063b2bd0f6Slogwang             break;
28073b2bd0f6Slogwang         }
28083b2bd0f6Slogwang 
28093b2bd0f6Slogwang         /* Get a response message with lots of room */
28103b2bd0f6Slogwang         NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
28113b2bd0f6Slogwang         if (resp == NULL) {
28123b2bd0f6Slogwang             error = ENOMEM;
28133b2bd0f6Slogwang             break;
28143b2bd0f6Slogwang         }
28153b2bd0f6Slogwang         ascii = (struct ng_mesg *)resp->data;
28163b2bd0f6Slogwang 
28173b2bd0f6Slogwang         /* Copy binary message header to response message payload */
28183b2bd0f6Slogwang         bcopy(binary, ascii, sizeof(*binary));
28193b2bd0f6Slogwang 
28203b2bd0f6Slogwang         /* Find command by matching typecookie and command number */
28213b2bd0f6Slogwang         for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
28223b2bd0f6Slogwang             c++) {
28233b2bd0f6Slogwang             if (binary->header.typecookie == c->cookie &&
28243b2bd0f6Slogwang                 binary->header.cmd == c->cmd)
28253b2bd0f6Slogwang                 break;
28263b2bd0f6Slogwang         }
28273b2bd0f6Slogwang         if (c == NULL || c->name == NULL) {
28283b2bd0f6Slogwang             for (c = ng_generic_cmds; c->name != NULL; c++) {
28293b2bd0f6Slogwang                 if (binary->header.typecookie == c->cookie &&
28303b2bd0f6Slogwang                     binary->header.cmd == c->cmd)
28313b2bd0f6Slogwang                     break;
28323b2bd0f6Slogwang             }
28333b2bd0f6Slogwang             if (c->name == NULL) {
28343b2bd0f6Slogwang                 NG_FREE_MSG(resp);
28353b2bd0f6Slogwang                 error = ENOSYS;
28363b2bd0f6Slogwang                 break;
28373b2bd0f6Slogwang             }
28383b2bd0f6Slogwang         }
28393b2bd0f6Slogwang 
28403b2bd0f6Slogwang         /* Convert command name to ASCII */
28413b2bd0f6Slogwang         snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
28423b2bd0f6Slogwang             "%s", c->name);
28433b2bd0f6Slogwang 
28443b2bd0f6Slogwang         /* Convert command arguments to ASCII */
28453b2bd0f6Slogwang         argstype = (binary->header.flags & NGF_RESP) ?
28463b2bd0f6Slogwang             c->respType : c->mesgType;
28473b2bd0f6Slogwang         if (argstype == NULL) {
28483b2bd0f6Slogwang             *ascii->data = '\0';
28493b2bd0f6Slogwang         } else {
28503b2bd0f6Slogwang             if ((error = ng_unparse(argstype,
28513b2bd0f6Slogwang                 (u_char *)binary->data,
28523b2bd0f6Slogwang                 ascii->data, bufSize)) != 0) {
28533b2bd0f6Slogwang                 NG_FREE_MSG(resp);
28543b2bd0f6Slogwang                 break;
28553b2bd0f6Slogwang             }
28563b2bd0f6Slogwang         }
28573b2bd0f6Slogwang 
28583b2bd0f6Slogwang         /* Return the result as struct ng_mesg plus ASCII string */
28593b2bd0f6Slogwang         bufSize = strlen(ascii->data) + 1;
28603b2bd0f6Slogwang         ascii->header.arglen = bufSize;
28613b2bd0f6Slogwang         resp->header.arglen = sizeof(*ascii) + bufSize;
28623b2bd0f6Slogwang         break;
28633b2bd0f6Slogwang         }
28643b2bd0f6Slogwang 
28653b2bd0f6Slogwang     case NGM_ASCII2BINARY:
28663b2bd0f6Slogwang         {
28673b2bd0f6Slogwang         int bufSize = 20 * 1024;    /* XXX hard coded constant */
28683b2bd0f6Slogwang         const struct ng_cmdlist *c;
28693b2bd0f6Slogwang         const struct ng_parse_type *argstype;
28703b2bd0f6Slogwang         struct ng_mesg *ascii, *binary;
28713b2bd0f6Slogwang         int off = 0;
28723b2bd0f6Slogwang 
28733b2bd0f6Slogwang         /* Data area must contain at least a struct ng_mesg + '\0' */
28743b2bd0f6Slogwang         ascii = (struct ng_mesg *)msg->data;
28753b2bd0f6Slogwang         if ((msg->header.arglen < sizeof(*ascii) + 1) ||
28763b2bd0f6Slogwang             (ascii->header.arglen < 1) ||
28773b2bd0f6Slogwang             (msg->header.arglen < sizeof(*ascii) +
28783b2bd0f6Slogwang             ascii->header.arglen)) {
28793b2bd0f6Slogwang             TRAP_ERROR();
28803b2bd0f6Slogwang             error = EINVAL;
28813b2bd0f6Slogwang             break;
28823b2bd0f6Slogwang         }
28833b2bd0f6Slogwang         ascii->data[ascii->header.arglen - 1] = '\0';
28843b2bd0f6Slogwang 
28853b2bd0f6Slogwang         /* Get a response message with lots of room */
28863b2bd0f6Slogwang         NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
28873b2bd0f6Slogwang         if (resp == NULL) {
28883b2bd0f6Slogwang             error = ENOMEM;
28893b2bd0f6Slogwang             break;
28903b2bd0f6Slogwang         }
28913b2bd0f6Slogwang         binary = (struct ng_mesg *)resp->data;
28923b2bd0f6Slogwang 
28933b2bd0f6Slogwang         /* Copy ASCII message header to response message payload */
28943b2bd0f6Slogwang         bcopy(ascii, binary, sizeof(*ascii));
28953b2bd0f6Slogwang 
28963b2bd0f6Slogwang         /* Find command by matching ASCII command string */
28973b2bd0f6Slogwang         for (c = here->nd_type->cmdlist;
28983b2bd0f6Slogwang             c != NULL && c->name != NULL; c++) {
28993b2bd0f6Slogwang             if (strcmp(ascii->header.cmdstr, c->name) == 0)
29003b2bd0f6Slogwang                 break;
29013b2bd0f6Slogwang         }
29023b2bd0f6Slogwang         if (c == NULL || c->name == NULL) {
29033b2bd0f6Slogwang             for (c = ng_generic_cmds; c->name != NULL; c++) {
29043b2bd0f6Slogwang                 if (strcmp(ascii->header.cmdstr, c->name) == 0)
29053b2bd0f6Slogwang                     break;
29063b2bd0f6Slogwang             }
29073b2bd0f6Slogwang             if (c->name == NULL) {
29083b2bd0f6Slogwang                 NG_FREE_MSG(resp);
29093b2bd0f6Slogwang                 error = ENOSYS;
29103b2bd0f6Slogwang                 break;
29113b2bd0f6Slogwang             }
29123b2bd0f6Slogwang         }
29133b2bd0f6Slogwang 
29143b2bd0f6Slogwang         /* Convert command name to binary */
29153b2bd0f6Slogwang         binary->header.cmd = c->cmd;
29163b2bd0f6Slogwang         binary->header.typecookie = c->cookie;
29173b2bd0f6Slogwang 
29183b2bd0f6Slogwang         /* Convert command arguments to binary */
29193b2bd0f6Slogwang         argstype = (binary->header.flags & NGF_RESP) ?
29203b2bd0f6Slogwang             c->respType : c->mesgType;
29213b2bd0f6Slogwang         if (argstype == NULL) {
29223b2bd0f6Slogwang             bufSize = 0;
29233b2bd0f6Slogwang         } else {
29243b2bd0f6Slogwang             if ((error = ng_parse(argstype, ascii->data, &off,
29253b2bd0f6Slogwang                 (u_char *)binary->data, &bufSize)) != 0) {
29263b2bd0f6Slogwang                 NG_FREE_MSG(resp);
29273b2bd0f6Slogwang                 break;
29283b2bd0f6Slogwang             }
29293b2bd0f6Slogwang         }
29303b2bd0f6Slogwang 
29313b2bd0f6Slogwang         /* Return the result */
29323b2bd0f6Slogwang         binary->header.arglen = bufSize;
29333b2bd0f6Slogwang         resp->header.arglen = sizeof(*binary) + bufSize;
29343b2bd0f6Slogwang         break;
29353b2bd0f6Slogwang         }
29363b2bd0f6Slogwang 
29373b2bd0f6Slogwang     case NGM_TEXT_CONFIG:
29383b2bd0f6Slogwang     case NGM_TEXT_STATUS:
29393b2bd0f6Slogwang         /*
29403b2bd0f6Slogwang          * This one is tricky as it passes the command down to the
29413b2bd0f6Slogwang          * actual node, even though it is a generic type command.
29423b2bd0f6Slogwang          * This means we must assume that the item/msg is already freed
29433b2bd0f6Slogwang          * when control passes back to us.
29443b2bd0f6Slogwang          */
29453b2bd0f6Slogwang         if (here->nd_type->rcvmsg != NULL) {
29463b2bd0f6Slogwang             NGI_MSG(item) = msg; /* put it back as we found it */
29473b2bd0f6Slogwang             return((*here->nd_type->rcvmsg)(here, item, lasthook));
29483b2bd0f6Slogwang         }
29493b2bd0f6Slogwang         /* Fall through if rcvmsg not supported */
29503b2bd0f6Slogwang     default:
29513b2bd0f6Slogwang         TRAP_ERROR();
29523b2bd0f6Slogwang         error = EINVAL;
29533b2bd0f6Slogwang     }
29543b2bd0f6Slogwang     /*
29553b2bd0f6Slogwang      * Sometimes a generic message may be statically allocated
29563b2bd0f6Slogwang      * to avoid problems with allocating when in tight memory situations.
29573b2bd0f6Slogwang      * Don't free it if it is so.
29583b2bd0f6Slogwang      * I break them apart here, because erros may cause a free if the item
29593b2bd0f6Slogwang      * in which case we'd be doing it twice.
29603b2bd0f6Slogwang      * they are kept together above, to simplify freeing.
29613b2bd0f6Slogwang      */
29623b2bd0f6Slogwang out:
29633b2bd0f6Slogwang     NG_RESPOND_MSG(error, here, item, resp);
29643b2bd0f6Slogwang     NG_FREE_MSG(msg);
29653b2bd0f6Slogwang     return (error);
29663b2bd0f6Slogwang }
29673b2bd0f6Slogwang 
29683b2bd0f6Slogwang /************************************************************************
29693b2bd0f6Slogwang             Queue element get/free routines
29703b2bd0f6Slogwang ************************************************************************/
29713b2bd0f6Slogwang 
29723b2bd0f6Slogwang uma_zone_t            ng_qzone;
29733b2bd0f6Slogwang uma_zone_t            ng_qdzone;
29743b2bd0f6Slogwang #ifndef FSTACK
29753b2bd0f6Slogwang static int            numthreads = 0; /* number of queue threads */
29763b2bd0f6Slogwang #endif
29773b2bd0f6Slogwang static int            maxalloc = 4096;/* limit the damage of a leak */
29783b2bd0f6Slogwang static int            maxdata = 4096;    /* limit the damage of a DoS */
29793b2bd0f6Slogwang 
29803b2bd0f6Slogwang #ifndef FSTACK
29813b2bd0f6Slogwang SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
29823b2bd0f6Slogwang     0, "Number of queue processing threads");
29833b2bd0f6Slogwang #endif
29843b2bd0f6Slogwang SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
29853b2bd0f6Slogwang     0, "Maximum number of non-data queue items to allocate");
29863b2bd0f6Slogwang SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
29873b2bd0f6Slogwang     0, "Maximum number of data queue items to allocate");
29883b2bd0f6Slogwang 
29893b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
29903b2bd0f6Slogwang static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
29913b2bd0f6Slogwang static int allocated;    /* number of items malloc'd */
29923b2bd0f6Slogwang #endif
29933b2bd0f6Slogwang 
29943b2bd0f6Slogwang /*
29953b2bd0f6Slogwang  * Get a queue entry.
29963b2bd0f6Slogwang  * This is usually called when a packet first enters netgraph.
29973b2bd0f6Slogwang  * By definition, this is usually from an interrupt, or from a user.
29983b2bd0f6Slogwang  * Users are not so important, but try be quick for the times that it's
29993b2bd0f6Slogwang  * an interrupt.
30003b2bd0f6Slogwang  */
30013b2bd0f6Slogwang static __inline item_p
ng_alloc_item(int type,int flags)30023b2bd0f6Slogwang ng_alloc_item(int type, int flags)
30033b2bd0f6Slogwang {
30043b2bd0f6Slogwang     item_p item;
30053b2bd0f6Slogwang 
30063b2bd0f6Slogwang     KASSERT(((type & ~NGQF_TYPE) == 0),
30073b2bd0f6Slogwang         ("%s: incorrect item type: %d", __func__, type));
30083b2bd0f6Slogwang 
30093b2bd0f6Slogwang     item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
30103b2bd0f6Slogwang         ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
30113b2bd0f6Slogwang 
30123b2bd0f6Slogwang     if (item) {
30133b2bd0f6Slogwang         item->el_flags = type;
30143b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
30153b2bd0f6Slogwang         mtx_lock(&ngq_mtx);
30163b2bd0f6Slogwang         TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
30173b2bd0f6Slogwang         allocated++;
30183b2bd0f6Slogwang         mtx_unlock(&ngq_mtx);
30193b2bd0f6Slogwang #endif
30203b2bd0f6Slogwang     }
30213b2bd0f6Slogwang 
30223b2bd0f6Slogwang     return (item);
30233b2bd0f6Slogwang }
30243b2bd0f6Slogwang 
30253b2bd0f6Slogwang /*
30263b2bd0f6Slogwang  * Release a queue entry
30273b2bd0f6Slogwang  */
30283b2bd0f6Slogwang void
ng_free_item(item_p item)30293b2bd0f6Slogwang ng_free_item(item_p item)
30303b2bd0f6Slogwang {
30313b2bd0f6Slogwang     /*
3032*8640edf1Sfengbojiang      * The item may hold resources on its own. We need to free
30333b2bd0f6Slogwang      * these before we can free the item. What they are depends upon
30343b2bd0f6Slogwang      * what kind of item it is. it is important that nodes zero
30353b2bd0f6Slogwang      * out pointers to resources that they remove from the item
30363b2bd0f6Slogwang      * or we release them again here.
30373b2bd0f6Slogwang      */
30383b2bd0f6Slogwang     switch (item->el_flags & NGQF_TYPE) {
30393b2bd0f6Slogwang     case NGQF_DATA:
30403b2bd0f6Slogwang         /* If we have an mbuf still attached.. */
30413b2bd0f6Slogwang         NG_FREE_M(_NGI_M(item));
30423b2bd0f6Slogwang         break;
30433b2bd0f6Slogwang     case NGQF_MESG:
30443b2bd0f6Slogwang         _NGI_RETADDR(item) = 0;
30453b2bd0f6Slogwang         NG_FREE_MSG(_NGI_MSG(item));
30463b2bd0f6Slogwang         break;
30473b2bd0f6Slogwang     case NGQF_FN:
30483b2bd0f6Slogwang     case NGQF_FN2:
30493b2bd0f6Slogwang         /* nothing to free really, */
30503b2bd0f6Slogwang         _NGI_FN(item) = NULL;
30513b2bd0f6Slogwang         _NGI_ARG1(item) = NULL;
30523b2bd0f6Slogwang         _NGI_ARG2(item) = 0;
30533b2bd0f6Slogwang         break;
30543b2bd0f6Slogwang     }
30553b2bd0f6Slogwang     /* If we still have a node or hook referenced... */
30563b2bd0f6Slogwang     _NGI_CLR_NODE(item);
30573b2bd0f6Slogwang     _NGI_CLR_HOOK(item);
30583b2bd0f6Slogwang 
30593b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
30603b2bd0f6Slogwang     mtx_lock(&ngq_mtx);
30613b2bd0f6Slogwang     TAILQ_REMOVE(&ng_itemlist, item, all);
30623b2bd0f6Slogwang     allocated--;
30633b2bd0f6Slogwang     mtx_unlock(&ngq_mtx);
30643b2bd0f6Slogwang #endif
30653b2bd0f6Slogwang     uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
30663b2bd0f6Slogwang         ng_qdzone : ng_qzone, item);
30673b2bd0f6Slogwang }
30683b2bd0f6Slogwang 
30693b2bd0f6Slogwang /*
30703b2bd0f6Slogwang  * Change type of the queue entry.
30713b2bd0f6Slogwang  * Possibly reallocates it from another UMA zone.
30723b2bd0f6Slogwang  */
30733b2bd0f6Slogwang static __inline item_p
ng_realloc_item(item_p pitem,int type,int flags)30743b2bd0f6Slogwang ng_realloc_item(item_p pitem, int type, int flags)
30753b2bd0f6Slogwang {
30763b2bd0f6Slogwang     item_p item;
30773b2bd0f6Slogwang     int from, to;
30783b2bd0f6Slogwang 
30793b2bd0f6Slogwang     KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
30803b2bd0f6Slogwang     KASSERT(((type & ~NGQF_TYPE) == 0),
30813b2bd0f6Slogwang         ("%s: incorrect item type: %d", __func__, type));
30823b2bd0f6Slogwang 
30833b2bd0f6Slogwang     from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
30843b2bd0f6Slogwang     to = (type == NGQF_DATA);
30853b2bd0f6Slogwang     if (from != to) {
30863b2bd0f6Slogwang         /* If reallocation is required do it and copy item. */
30873b2bd0f6Slogwang         if ((item = ng_alloc_item(type, flags)) == NULL) {
30883b2bd0f6Slogwang             ng_free_item(pitem);
30893b2bd0f6Slogwang             return (NULL);
30903b2bd0f6Slogwang         }
30913b2bd0f6Slogwang         *item = *pitem;
30923b2bd0f6Slogwang         ng_free_item(pitem);
30933b2bd0f6Slogwang     } else
30943b2bd0f6Slogwang         item = pitem;
30953b2bd0f6Slogwang     item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
30963b2bd0f6Slogwang 
30973b2bd0f6Slogwang     return (item);
30983b2bd0f6Slogwang }
30993b2bd0f6Slogwang 
31003b2bd0f6Slogwang /************************************************************************
31013b2bd0f6Slogwang             Module routines
31023b2bd0f6Slogwang ************************************************************************/
31033b2bd0f6Slogwang 
31043b2bd0f6Slogwang /*
31053b2bd0f6Slogwang  * Handle the loading/unloading of a netgraph node type module
31063b2bd0f6Slogwang  */
31073b2bd0f6Slogwang int
ng_mod_event(module_t mod,int event,void * data)31083b2bd0f6Slogwang ng_mod_event(module_t mod, int event, void *data)
31093b2bd0f6Slogwang {
31103b2bd0f6Slogwang     struct ng_type *const type = data;
31113b2bd0f6Slogwang     int error = 0;
31123b2bd0f6Slogwang 
31133b2bd0f6Slogwang     switch (event) {
31143b2bd0f6Slogwang     case MOD_LOAD:
31153b2bd0f6Slogwang 
31163b2bd0f6Slogwang         /* Register new netgraph node type */
31173b2bd0f6Slogwang         if ((error = ng_newtype(type)) != 0)
31183b2bd0f6Slogwang             break;
31193b2bd0f6Slogwang 
31203b2bd0f6Slogwang         /* Call type specific code */
31213b2bd0f6Slogwang         if (type->mod_event != NULL)
31223b2bd0f6Slogwang             if ((error = (*type->mod_event)(mod, event, data))) {
31233b2bd0f6Slogwang                 TYPELIST_WLOCK();
31243b2bd0f6Slogwang                 type->refs--;    /* undo it */
31253b2bd0f6Slogwang                 LIST_REMOVE(type, types);
31263b2bd0f6Slogwang                 TYPELIST_WUNLOCK();
31273b2bd0f6Slogwang             }
31283b2bd0f6Slogwang         break;
31293b2bd0f6Slogwang 
31303b2bd0f6Slogwang     case MOD_UNLOAD:
31313b2bd0f6Slogwang         if (type->refs > 1) {        /* make sure no nodes exist! */
31323b2bd0f6Slogwang             error = EBUSY;
31333b2bd0f6Slogwang         } else {
31343b2bd0f6Slogwang             if (type->refs == 0) /* failed load, nothing to undo */
31353b2bd0f6Slogwang                 break;
31363b2bd0f6Slogwang             if (type->mod_event != NULL) {    /* check with type */
31373b2bd0f6Slogwang                 error = (*type->mod_event)(mod, event, data);
31383b2bd0f6Slogwang                 if (error != 0)    /* type refuses.. */
31393b2bd0f6Slogwang                     break;
31403b2bd0f6Slogwang             }
31413b2bd0f6Slogwang             TYPELIST_WLOCK();
31423b2bd0f6Slogwang             LIST_REMOVE(type, types);
31433b2bd0f6Slogwang             TYPELIST_WUNLOCK();
31443b2bd0f6Slogwang         }
31453b2bd0f6Slogwang         break;
31463b2bd0f6Slogwang 
31473b2bd0f6Slogwang     default:
31483b2bd0f6Slogwang         if (type->mod_event != NULL)
31493b2bd0f6Slogwang             error = (*type->mod_event)(mod, event, data);
31503b2bd0f6Slogwang         else
31513b2bd0f6Slogwang             error = EOPNOTSUPP;        /* XXX ? */
31523b2bd0f6Slogwang         break;
31533b2bd0f6Slogwang     }
31543b2bd0f6Slogwang     return (error);
31553b2bd0f6Slogwang }
31563b2bd0f6Slogwang 
31573b2bd0f6Slogwang static void
vnet_netgraph_init(const void * unused __unused)31583b2bd0f6Slogwang vnet_netgraph_init(const void *unused __unused)
31593b2bd0f6Slogwang {
31603b2bd0f6Slogwang 
31613b2bd0f6Slogwang     /* We start with small hashes, but they can grow. */
31623b2bd0f6Slogwang     V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask);
31633b2bd0f6Slogwang     V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask);
31643b2bd0f6Slogwang }
31653b2bd0f6Slogwang VNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
31663b2bd0f6Slogwang     vnet_netgraph_init, NULL);
31673b2bd0f6Slogwang 
31683b2bd0f6Slogwang #ifdef VIMAGE
31693b2bd0f6Slogwang static void
vnet_netgraph_uninit(const void * unused __unused)31703b2bd0f6Slogwang vnet_netgraph_uninit(const void *unused __unused)
31713b2bd0f6Slogwang {
31723b2bd0f6Slogwang     node_p node = NULL, last_killed = NULL;
31733b2bd0f6Slogwang     int i;
31743b2bd0f6Slogwang 
31753b2bd0f6Slogwang     do {
31763b2bd0f6Slogwang         /* Find a node to kill */
31773b2bd0f6Slogwang         IDHASH_RLOCK();
31783b2bd0f6Slogwang         for (i = 0; i <= V_ng_ID_hmask; i++) {
31793b2bd0f6Slogwang             LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
31803b2bd0f6Slogwang                 if (node != &ng_deadnode) {
31813b2bd0f6Slogwang                     NG_NODE_REF(node);
31823b2bd0f6Slogwang                     break;
31833b2bd0f6Slogwang                 }
31843b2bd0f6Slogwang             }
31853b2bd0f6Slogwang             if (node != NULL)
31863b2bd0f6Slogwang                 break;
31873b2bd0f6Slogwang         }
31883b2bd0f6Slogwang         IDHASH_RUNLOCK();
31893b2bd0f6Slogwang 
31903b2bd0f6Slogwang         /* Attempt to kill it only if it is a regular node */
31913b2bd0f6Slogwang         if (node != NULL) {
31923b2bd0f6Slogwang             if (node == last_killed) {
31933b2bd0f6Slogwang                 if (node->nd_flags & NGF_REALLY_DIE)
31943b2bd0f6Slogwang                     panic("ng node %s won't die",
31953b2bd0f6Slogwang                         node->nd_name);
3196*8640edf1Sfengbojiang                 /* The node persisted itself.  Try again. */
31973b2bd0f6Slogwang                 node->nd_flags |= NGF_REALLY_DIE;
31983b2bd0f6Slogwang             }
31993b2bd0f6Slogwang             ng_rmnode(node, NULL, NULL, 0);
32003b2bd0f6Slogwang             NG_NODE_UNREF(node);
32013b2bd0f6Slogwang             last_killed = node;
32023b2bd0f6Slogwang         }
32033b2bd0f6Slogwang     } while (node != NULL);
32043b2bd0f6Slogwang 
32053b2bd0f6Slogwang     hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
32063b2bd0f6Slogwang     hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask);
32073b2bd0f6Slogwang }
32083b2bd0f6Slogwang VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
32093b2bd0f6Slogwang     vnet_netgraph_uninit, NULL);
32103b2bd0f6Slogwang #endif /* VIMAGE */
32113b2bd0f6Slogwang 
32123b2bd0f6Slogwang /*
32133b2bd0f6Slogwang  * Handle loading and unloading for this code.
32143b2bd0f6Slogwang  * The only thing we need to link into is the NETISR strucure.
32153b2bd0f6Slogwang  */
32163b2bd0f6Slogwang static int
ngb_mod_event(module_t mod,int event,void * data)32173b2bd0f6Slogwang ngb_mod_event(module_t mod, int event, void *data)
32183b2bd0f6Slogwang {
32193b2bd0f6Slogwang #ifndef FSTACK
32203b2bd0f6Slogwang     struct proc *p;
32213b2bd0f6Slogwang     struct thread *td;
32223b2bd0f6Slogwang #endif
32233b2bd0f6Slogwang     int i, error = 0;
32243b2bd0f6Slogwang 
32253b2bd0f6Slogwang     switch (event) {
32263b2bd0f6Slogwang     case MOD_LOAD:
32273b2bd0f6Slogwang         /* Initialize everything. */
32283b2bd0f6Slogwang         NG_WORKLIST_LOCK_INIT();
32293b2bd0f6Slogwang         rw_init(&ng_typelist_lock, "netgraph types");
32303b2bd0f6Slogwang         rw_init(&ng_idhash_lock, "netgraph idhash");
32313b2bd0f6Slogwang         rw_init(&ng_namehash_lock, "netgraph namehash");
32323b2bd0f6Slogwang         rw_init(&ng_topo_lock, "netgraph topology mutex");
32333b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
32343b2bd0f6Slogwang         mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
32353b2bd0f6Slogwang             MTX_DEF);
32363b2bd0f6Slogwang         mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
32373b2bd0f6Slogwang             MTX_DEF);
32383b2bd0f6Slogwang #endif
32393b2bd0f6Slogwang         ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
32403b2bd0f6Slogwang             NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
32413b2bd0f6Slogwang         uma_zone_set_max(ng_qzone, maxalloc);
32423b2bd0f6Slogwang         ng_qdzone = uma_zcreate("NetGraph data items",
32433b2bd0f6Slogwang             sizeof(struct ng_item), NULL, NULL, NULL, NULL,
32443b2bd0f6Slogwang             UMA_ALIGN_CACHE, 0);
32453b2bd0f6Slogwang         uma_zone_set_max(ng_qdzone, maxdata);
32463b2bd0f6Slogwang #ifndef FSTACK
32473b2bd0f6Slogwang         /* Autoconfigure number of threads. */
32483b2bd0f6Slogwang         if (numthreads <= 0)
32493b2bd0f6Slogwang             numthreads = mp_ncpus;
32503b2bd0f6Slogwang         /* Create threads. */
32513b2bd0f6Slogwang             p = NULL; /* start with no process */
32523b2bd0f6Slogwang         for (i = 0; i < numthreads; i++) {
32533b2bd0f6Slogwang             if (kproc_kthread_add(ngthread, NULL, &p, &td,
32543b2bd0f6Slogwang                 RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
32553b2bd0f6Slogwang                 numthreads = i;
32563b2bd0f6Slogwang                 break;
32573b2bd0f6Slogwang             }
32583b2bd0f6Slogwang         }
32593b2bd0f6Slogwang #endif
32603b2bd0f6Slogwang         break;
32613b2bd0f6Slogwang     case MOD_UNLOAD:
32623b2bd0f6Slogwang         /* You can't unload it because an interface may be using it. */
32633b2bd0f6Slogwang         error = EBUSY;
32643b2bd0f6Slogwang         break;
32653b2bd0f6Slogwang     default:
32663b2bd0f6Slogwang         error = EOPNOTSUPP;
32673b2bd0f6Slogwang         break;
32683b2bd0f6Slogwang     }
32693b2bd0f6Slogwang     return (error);
32703b2bd0f6Slogwang }
32713b2bd0f6Slogwang 
32723b2bd0f6Slogwang static moduledata_t netgraph_mod = {
32733b2bd0f6Slogwang     "netgraph",
32743b2bd0f6Slogwang     ngb_mod_event,
32753b2bd0f6Slogwang     (NULL)
32763b2bd0f6Slogwang };
32773b2bd0f6Slogwang DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST);
3278*8640edf1Sfengbojiang SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
3279*8640edf1Sfengbojiang     "netgraph Family");
32803b2bd0f6Slogwang SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,"");
32813b2bd0f6Slogwang SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, "");
32823b2bd0f6Slogwang 
32833b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
32843b2bd0f6Slogwang void
dumphook(hook_p hook,char * file,int line)32853b2bd0f6Slogwang dumphook (hook_p hook, char *file, int line)
32863b2bd0f6Slogwang {
32873b2bd0f6Slogwang     printf("hook: name %s, %d refs, Last touched:\n",
32883b2bd0f6Slogwang         _NG_HOOK_NAME(hook), hook->hk_refs);
32893b2bd0f6Slogwang     printf("    Last active @ %s, line %d\n",
32903b2bd0f6Slogwang         hook->lastfile, hook->lastline);
32913b2bd0f6Slogwang     if (line) {
32923b2bd0f6Slogwang         printf(" problem discovered at file %s, line %d\n", file, line);
32933b2bd0f6Slogwang #ifdef KDB
32943b2bd0f6Slogwang         kdb_backtrace();
32953b2bd0f6Slogwang #endif
32963b2bd0f6Slogwang     }
32973b2bd0f6Slogwang }
32983b2bd0f6Slogwang 
32993b2bd0f6Slogwang void
dumpnode(node_p node,char * file,int line)33003b2bd0f6Slogwang dumpnode(node_p node, char *file, int line)
33013b2bd0f6Slogwang {
33023b2bd0f6Slogwang     printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
33033b2bd0f6Slogwang         _NG_NODE_ID(node), node->nd_type->name,
33043b2bd0f6Slogwang         node->nd_numhooks, node->nd_flags,
33053b2bd0f6Slogwang         node->nd_refs, node->nd_name);
33063b2bd0f6Slogwang     printf("    Last active @ %s, line %d\n",
33073b2bd0f6Slogwang         node->lastfile, node->lastline);
33083b2bd0f6Slogwang     if (line) {
33093b2bd0f6Slogwang         printf(" problem discovered at file %s, line %d\n", file, line);
33103b2bd0f6Slogwang #ifdef KDB
33113b2bd0f6Slogwang         kdb_backtrace();
33123b2bd0f6Slogwang #endif
33133b2bd0f6Slogwang     }
33143b2bd0f6Slogwang }
33153b2bd0f6Slogwang 
33163b2bd0f6Slogwang void
dumpitem(item_p item,char * file,int line)33173b2bd0f6Slogwang dumpitem(item_p item, char *file, int line)
33183b2bd0f6Slogwang {
33193b2bd0f6Slogwang     printf(" ACTIVE item, last used at %s, line %d",
33203b2bd0f6Slogwang         item->lastfile, item->lastline);
33213b2bd0f6Slogwang     switch(item->el_flags & NGQF_TYPE) {
33223b2bd0f6Slogwang     case NGQF_DATA:
33233b2bd0f6Slogwang         printf(" - [data]\n");
33243b2bd0f6Slogwang         break;
33253b2bd0f6Slogwang     case NGQF_MESG:
33263b2bd0f6Slogwang         printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
33273b2bd0f6Slogwang         break;
33283b2bd0f6Slogwang     case NGQF_FN:
33293b2bd0f6Slogwang         printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
33303b2bd0f6Slogwang             _NGI_FN(item),
33313b2bd0f6Slogwang             _NGI_NODE(item),
33323b2bd0f6Slogwang             _NGI_HOOK(item),
33333b2bd0f6Slogwang             item->body.fn.fn_arg1,
33343b2bd0f6Slogwang             item->body.fn.fn_arg2,
33353b2bd0f6Slogwang             item->body.fn.fn_arg2);
33363b2bd0f6Slogwang         break;
33373b2bd0f6Slogwang     case NGQF_FN2:
33383b2bd0f6Slogwang         printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
33393b2bd0f6Slogwang             _NGI_FN2(item),
33403b2bd0f6Slogwang             _NGI_NODE(item),
33413b2bd0f6Slogwang             _NGI_HOOK(item),
33423b2bd0f6Slogwang             item->body.fn.fn_arg1,
33433b2bd0f6Slogwang             item->body.fn.fn_arg2,
33443b2bd0f6Slogwang             item->body.fn.fn_arg2);
33453b2bd0f6Slogwang         break;
33463b2bd0f6Slogwang     }
33473b2bd0f6Slogwang     if (line) {
33483b2bd0f6Slogwang         printf(" problem discovered at file %s, line %d\n", file, line);
33493b2bd0f6Slogwang         if (_NGI_NODE(item)) {
33503b2bd0f6Slogwang             printf("node %p ([%x])\n",
33513b2bd0f6Slogwang                 _NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
33523b2bd0f6Slogwang         }
33533b2bd0f6Slogwang     }
33543b2bd0f6Slogwang }
33553b2bd0f6Slogwang 
33563b2bd0f6Slogwang static void
ng_dumpitems(void)33573b2bd0f6Slogwang ng_dumpitems(void)
33583b2bd0f6Slogwang {
33593b2bd0f6Slogwang     item_p item;
33603b2bd0f6Slogwang     int i = 1;
33613b2bd0f6Slogwang     TAILQ_FOREACH(item, &ng_itemlist, all) {
33623b2bd0f6Slogwang         printf("[%d] ", i++);
33633b2bd0f6Slogwang         dumpitem(item, NULL, 0);
33643b2bd0f6Slogwang     }
33653b2bd0f6Slogwang }
33663b2bd0f6Slogwang 
33673b2bd0f6Slogwang static void
ng_dumpnodes(void)33683b2bd0f6Slogwang ng_dumpnodes(void)
33693b2bd0f6Slogwang {
33703b2bd0f6Slogwang     node_p node;
33713b2bd0f6Slogwang     int i = 1;
33723b2bd0f6Slogwang     mtx_lock(&ng_nodelist_mtx);
33733b2bd0f6Slogwang     SLIST_FOREACH(node, &ng_allnodes, nd_all) {
33743b2bd0f6Slogwang         printf("[%d] ", i++);
33753b2bd0f6Slogwang         dumpnode(node, NULL, 0);
33763b2bd0f6Slogwang     }
33773b2bd0f6Slogwang     mtx_unlock(&ng_nodelist_mtx);
33783b2bd0f6Slogwang }
33793b2bd0f6Slogwang 
33803b2bd0f6Slogwang static void
ng_dumphooks(void)33813b2bd0f6Slogwang ng_dumphooks(void)
33823b2bd0f6Slogwang {
33833b2bd0f6Slogwang     hook_p hook;
33843b2bd0f6Slogwang     int i = 1;
33853b2bd0f6Slogwang     mtx_lock(&ng_nodelist_mtx);
33863b2bd0f6Slogwang     SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
33873b2bd0f6Slogwang         printf("[%d] ", i++);
33883b2bd0f6Slogwang         dumphook(hook, NULL, 0);
33893b2bd0f6Slogwang     }
33903b2bd0f6Slogwang     mtx_unlock(&ng_nodelist_mtx);
33913b2bd0f6Slogwang }
33923b2bd0f6Slogwang 
33933b2bd0f6Slogwang static int
sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)33943b2bd0f6Slogwang sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
33953b2bd0f6Slogwang {
33963b2bd0f6Slogwang     int error;
33973b2bd0f6Slogwang     int val;
33983b2bd0f6Slogwang     int i;
33993b2bd0f6Slogwang 
34003b2bd0f6Slogwang     val = allocated;
34013b2bd0f6Slogwang     i = 1;
34023b2bd0f6Slogwang     error = sysctl_handle_int(oidp, &val, 0, req);
34033b2bd0f6Slogwang     if (error != 0 || req->newptr == NULL)
34043b2bd0f6Slogwang         return (error);
34053b2bd0f6Slogwang     if (val == 42) {
34063b2bd0f6Slogwang         ng_dumpitems();
34073b2bd0f6Slogwang         ng_dumpnodes();
34083b2bd0f6Slogwang         ng_dumphooks();
34093b2bd0f6Slogwang     }
34103b2bd0f6Slogwang     return (0);
34113b2bd0f6Slogwang }
34123b2bd0f6Slogwang 
3413*8640edf1Sfengbojiang SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items,
3414*8640edf1Sfengbojiang     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, sizeof(int),
3415*8640edf1Sfengbojiang     sysctl_debug_ng_dump_items, "I",
3416*8640edf1Sfengbojiang     "Number of allocated items");
34173b2bd0f6Slogwang #endif    /* NETGRAPH_DEBUG */
34183b2bd0f6Slogwang 
34193b2bd0f6Slogwang #ifndef FSTACK
34203b2bd0f6Slogwang /***********************************************************************
34213b2bd0f6Slogwang * Worklist routines
34223b2bd0f6Slogwang **********************************************************************/
34233b2bd0f6Slogwang /*
34243b2bd0f6Slogwang  * Pick a node off the list of nodes with work,
34253b2bd0f6Slogwang  * try get an item to process off it. Remove the node from the list.
34263b2bd0f6Slogwang  */
34273b2bd0f6Slogwang static void
ngthread(void * arg)34283b2bd0f6Slogwang ngthread(void *arg)
34293b2bd0f6Slogwang {
34303b2bd0f6Slogwang     for (;;) {
3431*8640edf1Sfengbojiang         struct epoch_tracker et;
34323b2bd0f6Slogwang         node_p  node;
34333b2bd0f6Slogwang 
34343b2bd0f6Slogwang         /* Get node from the worklist. */
34353b2bd0f6Slogwang         NG_WORKLIST_LOCK();
34363b2bd0f6Slogwang         while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
34373b2bd0f6Slogwang             NG_WORKLIST_SLEEP();
34383b2bd0f6Slogwang         STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
34393b2bd0f6Slogwang         NG_WORKLIST_UNLOCK();
34403b2bd0f6Slogwang         CURVNET_SET(node->nd_vnet);
34413b2bd0f6Slogwang         CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
34423b2bd0f6Slogwang             __func__, node->nd_ID, node);
34433b2bd0f6Slogwang         /*
34443b2bd0f6Slogwang          * We have the node. We also take over the reference
34453b2bd0f6Slogwang          * that the list had on it.
34463b2bd0f6Slogwang          * Now process as much as you can, until it won't
34473b2bd0f6Slogwang          * let you have another item off the queue.
34483b2bd0f6Slogwang          * All this time, keep the reference
34493b2bd0f6Slogwang          * that lets us be sure that the node still exists.
34503b2bd0f6Slogwang          * Let the reference go at the last minute.
34513b2bd0f6Slogwang          */
3452*8640edf1Sfengbojiang         NET_EPOCH_ENTER(et);
34533b2bd0f6Slogwang         for (;;) {
34543b2bd0f6Slogwang             item_p item;
34553b2bd0f6Slogwang             int rw;
34563b2bd0f6Slogwang 
34573b2bd0f6Slogwang             NG_QUEUE_LOCK(&node->nd_input_queue);
34583b2bd0f6Slogwang             item = ng_dequeue(node, &rw);
34593b2bd0f6Slogwang             if (item == NULL) {
34603b2bd0f6Slogwang                 node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
34613b2bd0f6Slogwang                 NG_QUEUE_UNLOCK(&node->nd_input_queue);
34623b2bd0f6Slogwang                 break; /* go look for another node */
34633b2bd0f6Slogwang             } else {
34643b2bd0f6Slogwang                 NG_QUEUE_UNLOCK(&node->nd_input_queue);
34653b2bd0f6Slogwang                 NGI_GET_NODE(item, node); /* zaps stored node */
34663b2bd0f6Slogwang                 ng_apply_item(node, item, rw);
34673b2bd0f6Slogwang                 NG_NODE_UNREF(node);
34683b2bd0f6Slogwang             }
34693b2bd0f6Slogwang         }
3470*8640edf1Sfengbojiang         NET_EPOCH_EXIT(et);
34713b2bd0f6Slogwang         NG_NODE_UNREF(node);
34723b2bd0f6Slogwang         CURVNET_RESTORE();
34733b2bd0f6Slogwang     }
34743b2bd0f6Slogwang }
34753b2bd0f6Slogwang 
34763b2bd0f6Slogwang /*
34773b2bd0f6Slogwang  * XXX
34783b2bd0f6Slogwang  * It's posible that a debugging NG_NODE_REF may need
34793b2bd0f6Slogwang  * to be outside the mutex zone
34803b2bd0f6Slogwang  */
34813b2bd0f6Slogwang static void
ng_worklist_add(node_p node)34823b2bd0f6Slogwang ng_worklist_add(node_p node)
34833b2bd0f6Slogwang {
34843b2bd0f6Slogwang 
34853b2bd0f6Slogwang     mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
34863b2bd0f6Slogwang 
34873b2bd0f6Slogwang     if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
34883b2bd0f6Slogwang         /*
34893b2bd0f6Slogwang          * If we are not already on the work queue,
34903b2bd0f6Slogwang          * then put us on.
34913b2bd0f6Slogwang          */
34923b2bd0f6Slogwang         node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
34933b2bd0f6Slogwang         NG_NODE_REF(node); /* XXX safe in mutex? */
34943b2bd0f6Slogwang         NG_WORKLIST_LOCK();
34953b2bd0f6Slogwang         STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
34963b2bd0f6Slogwang         NG_WORKLIST_UNLOCK();
34973b2bd0f6Slogwang         CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
34983b2bd0f6Slogwang             node->nd_ID, node);
34993b2bd0f6Slogwang         NG_WORKLIST_WAKEUP();
35003b2bd0f6Slogwang     } else {
35013b2bd0f6Slogwang         CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
35023b2bd0f6Slogwang             __func__, node->nd_ID, node);
35033b2bd0f6Slogwang     }
35043b2bd0f6Slogwang }
35053b2bd0f6Slogwang #endif
35063b2bd0f6Slogwang 
35073b2bd0f6Slogwang /***********************************************************************
35083b2bd0f6Slogwang * Externally useable functions to set up a queue item ready for sending
35093b2bd0f6Slogwang ***********************************************************************/
35103b2bd0f6Slogwang 
35113b2bd0f6Slogwang #ifdef    NETGRAPH_DEBUG
35123b2bd0f6Slogwang #define    ITEM_DEBUG_CHECKS                        \
35133b2bd0f6Slogwang     do {                                \
35143b2bd0f6Slogwang         if (NGI_NODE(item) ) {                    \
35153b2bd0f6Slogwang             printf("item already has node");        \
35163b2bd0f6Slogwang             kdb_enter(KDB_WHY_NETGRAPH, "has node");    \
35173b2bd0f6Slogwang             NGI_CLR_NODE(item);                \
35183b2bd0f6Slogwang         }                            \
35193b2bd0f6Slogwang         if (NGI_HOOK(item) ) {                    \
35203b2bd0f6Slogwang             printf("item already has hook");        \
35213b2bd0f6Slogwang             kdb_enter(KDB_WHY_NETGRAPH, "has hook");    \
35223b2bd0f6Slogwang             NGI_CLR_HOOK(item);                \
35233b2bd0f6Slogwang         }                            \
35243b2bd0f6Slogwang     } while (0)
35253b2bd0f6Slogwang #else
35263b2bd0f6Slogwang #define ITEM_DEBUG_CHECKS
35273b2bd0f6Slogwang #endif
35283b2bd0f6Slogwang 
35293b2bd0f6Slogwang /*
35303b2bd0f6Slogwang  * Put mbuf into the item.
35313b2bd0f6Slogwang  * Hook and node references will be removed when the item is dequeued.
35323b2bd0f6Slogwang  * (or equivalent)
35333b2bd0f6Slogwang  * (XXX) Unsafe because no reference held by peer on remote node.
35343b2bd0f6Slogwang  * remote node might go away in this timescale.
35353b2bd0f6Slogwang  * We know the hooks can't go away because that would require getting
35363b2bd0f6Slogwang  * a writer item on both nodes and we must have at least a  reader
35373b2bd0f6Slogwang  * here to be able to do this.
35383b2bd0f6Slogwang  * Note that the hook loaded is the REMOTE hook.
35393b2bd0f6Slogwang  *
35403b2bd0f6Slogwang  * This is possibly in the critical path for new data.
35413b2bd0f6Slogwang  */
35423b2bd0f6Slogwang item_p
ng_package_data(struct mbuf * m,int flags)35433b2bd0f6Slogwang ng_package_data(struct mbuf *m, int flags)
35443b2bd0f6Slogwang {
35453b2bd0f6Slogwang     item_p item;
35463b2bd0f6Slogwang 
35473b2bd0f6Slogwang     if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
35483b2bd0f6Slogwang         NG_FREE_M(m);
35493b2bd0f6Slogwang         return (NULL);
35503b2bd0f6Slogwang     }
35513b2bd0f6Slogwang     ITEM_DEBUG_CHECKS;
35523b2bd0f6Slogwang     item->el_flags |= NGQF_READER;
35533b2bd0f6Slogwang     NGI_M(item) = m;
35543b2bd0f6Slogwang     return (item);
35553b2bd0f6Slogwang }
35563b2bd0f6Slogwang 
35573b2bd0f6Slogwang /*
35583b2bd0f6Slogwang  * Allocate a queue item and put items into it..
35593b2bd0f6Slogwang  * Evaluate the address as this will be needed to queue it and
35603b2bd0f6Slogwang  * to work out what some of the fields should be.
35613b2bd0f6Slogwang  * Hook and node references will be removed when the item is dequeued.
35623b2bd0f6Slogwang  * (or equivalent)
35633b2bd0f6Slogwang  */
35643b2bd0f6Slogwang item_p
ng_package_msg(struct ng_mesg * msg,int flags)35653b2bd0f6Slogwang ng_package_msg(struct ng_mesg *msg, int flags)
35663b2bd0f6Slogwang {
35673b2bd0f6Slogwang     item_p item;
35683b2bd0f6Slogwang 
35693b2bd0f6Slogwang     if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
35703b2bd0f6Slogwang         NG_FREE_MSG(msg);
35713b2bd0f6Slogwang         return (NULL);
35723b2bd0f6Slogwang     }
35733b2bd0f6Slogwang     ITEM_DEBUG_CHECKS;
35743b2bd0f6Slogwang     /* Messages items count as writers unless explicitly exempted. */
35753b2bd0f6Slogwang     if (msg->header.cmd & NGM_READONLY)
35763b2bd0f6Slogwang         item->el_flags |= NGQF_READER;
35773b2bd0f6Slogwang     else
35783b2bd0f6Slogwang         item->el_flags |= NGQF_WRITER;
35793b2bd0f6Slogwang     /*
35803b2bd0f6Slogwang      * Set the current lasthook into the queue item
35813b2bd0f6Slogwang      */
35823b2bd0f6Slogwang     NGI_MSG(item) = msg;
35833b2bd0f6Slogwang     NGI_RETADDR(item) = 0;
35843b2bd0f6Slogwang     return (item);
35853b2bd0f6Slogwang }
35863b2bd0f6Slogwang 
35873b2bd0f6Slogwang #define SET_RETADDR(item, here, retaddr)                \
35883b2bd0f6Slogwang     do {    /* Data or fn items don't have retaddrs */        \
35893b2bd0f6Slogwang         if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {    \
35903b2bd0f6Slogwang             if (retaddr) {                    \
35913b2bd0f6Slogwang                 NGI_RETADDR(item) = retaddr;        \
35923b2bd0f6Slogwang             } else {                    \
35933b2bd0f6Slogwang                 /*                    \
35943b2bd0f6Slogwang                  * The old return address should be ok.    \
35953b2bd0f6Slogwang                  * If there isn't one, use the address    \
35963b2bd0f6Slogwang                  * here.                \
35973b2bd0f6Slogwang                  */                    \
35983b2bd0f6Slogwang                 if (NGI_RETADDR(item) == 0) {        \
35993b2bd0f6Slogwang                     NGI_RETADDR(item)        \
36003b2bd0f6Slogwang                         = ng_node2ID(here);    \
36013b2bd0f6Slogwang                 }                    \
36023b2bd0f6Slogwang             }                        \
36033b2bd0f6Slogwang         }                            \
36043b2bd0f6Slogwang     } while (0)
36053b2bd0f6Slogwang 
36063b2bd0f6Slogwang int
ng_address_hook(node_p here,item_p item,hook_p hook,ng_ID_t retaddr)36073b2bd0f6Slogwang ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
36083b2bd0f6Slogwang {
36093b2bd0f6Slogwang     hook_p peer;
36103b2bd0f6Slogwang     node_p peernode;
36113b2bd0f6Slogwang     ITEM_DEBUG_CHECKS;
36123b2bd0f6Slogwang     /*
36133b2bd0f6Slogwang      * Quick sanity check..
3614*8640edf1Sfengbojiang      * Since a hook holds a reference on its node, once we know
36153b2bd0f6Slogwang      * that the peer is still connected (even if invalid,) we know
36163b2bd0f6Slogwang      * that the peer node is present, though maybe invalid.
36173b2bd0f6Slogwang      */
36183b2bd0f6Slogwang     TOPOLOGY_RLOCK();
36193b2bd0f6Slogwang     if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
36203b2bd0f6Slogwang         NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
36213b2bd0f6Slogwang         NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
36223b2bd0f6Slogwang         NG_FREE_ITEM(item);
36233b2bd0f6Slogwang         TRAP_ERROR();
36243b2bd0f6Slogwang         TOPOLOGY_RUNLOCK();
36253b2bd0f6Slogwang         return (ENETDOWN);
36263b2bd0f6Slogwang     }
36273b2bd0f6Slogwang 
36283b2bd0f6Slogwang     /*
36293b2bd0f6Slogwang      * Transfer our interest to the other (peer) end.
36303b2bd0f6Slogwang      */
36313b2bd0f6Slogwang     NG_HOOK_REF(peer);
36323b2bd0f6Slogwang     NG_NODE_REF(peernode);
36333b2bd0f6Slogwang     NGI_SET_HOOK(item, peer);
36343b2bd0f6Slogwang     NGI_SET_NODE(item, peernode);
36353b2bd0f6Slogwang     SET_RETADDR(item, here, retaddr);
36363b2bd0f6Slogwang 
36373b2bd0f6Slogwang     TOPOLOGY_RUNLOCK();
36383b2bd0f6Slogwang 
36393b2bd0f6Slogwang     return (0);
36403b2bd0f6Slogwang }
36413b2bd0f6Slogwang 
36423b2bd0f6Slogwang int
ng_address_path(node_p here,item_p item,const char * address,ng_ID_t retaddr)36433b2bd0f6Slogwang ng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
36443b2bd0f6Slogwang {
36453b2bd0f6Slogwang     node_p    dest = NULL;
36463b2bd0f6Slogwang     hook_p    hook = NULL;
36473b2bd0f6Slogwang     int    error;
36483b2bd0f6Slogwang 
36493b2bd0f6Slogwang     ITEM_DEBUG_CHECKS;
36503b2bd0f6Slogwang     /*
36513b2bd0f6Slogwang      * Note that ng_path2noderef increments the reference count
36523b2bd0f6Slogwang      * on the node for us if it finds one. So we don't have to.
36533b2bd0f6Slogwang      */
36543b2bd0f6Slogwang     error = ng_path2noderef(here, address, &dest, &hook);
36553b2bd0f6Slogwang     if (error) {
36563b2bd0f6Slogwang         NG_FREE_ITEM(item);
36573b2bd0f6Slogwang         return (error);
36583b2bd0f6Slogwang     }
36593b2bd0f6Slogwang     NGI_SET_NODE(item, dest);
36603b2bd0f6Slogwang     if (hook)
36613b2bd0f6Slogwang         NGI_SET_HOOK(item, hook);
36623b2bd0f6Slogwang 
36633b2bd0f6Slogwang     SET_RETADDR(item, here, retaddr);
36643b2bd0f6Slogwang     return (0);
36653b2bd0f6Slogwang }
36663b2bd0f6Slogwang 
36673b2bd0f6Slogwang int
ng_address_ID(node_p here,item_p item,ng_ID_t ID,ng_ID_t retaddr)36683b2bd0f6Slogwang ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
36693b2bd0f6Slogwang {
36703b2bd0f6Slogwang     node_p dest;
36713b2bd0f6Slogwang 
36723b2bd0f6Slogwang     ITEM_DEBUG_CHECKS;
36733b2bd0f6Slogwang     /*
36743b2bd0f6Slogwang      * Find the target node.
36753b2bd0f6Slogwang      */
36763b2bd0f6Slogwang     dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
36773b2bd0f6Slogwang     if (dest == NULL) {
36783b2bd0f6Slogwang         NG_FREE_ITEM(item);
36793b2bd0f6Slogwang         TRAP_ERROR();
36803b2bd0f6Slogwang         return(EINVAL);
36813b2bd0f6Slogwang     }
36823b2bd0f6Slogwang     /* Fill out the contents */
36833b2bd0f6Slogwang     NGI_SET_NODE(item, dest);
36843b2bd0f6Slogwang     NGI_CLR_HOOK(item);
36853b2bd0f6Slogwang     SET_RETADDR(item, here, retaddr);
36863b2bd0f6Slogwang     return (0);
36873b2bd0f6Slogwang }
36883b2bd0f6Slogwang 
36893b2bd0f6Slogwang /*
36903b2bd0f6Slogwang  * special case to send a message to self (e.g. destroy node)
36913b2bd0f6Slogwang  * Possibly indicate an arrival hook too.
36923b2bd0f6Slogwang  * Useful for removing that hook :-)
36933b2bd0f6Slogwang  */
36943b2bd0f6Slogwang item_p
ng_package_msg_self(node_p here,hook_p hook,struct ng_mesg * msg)36953b2bd0f6Slogwang ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
36963b2bd0f6Slogwang {
36973b2bd0f6Slogwang     item_p item;
36983b2bd0f6Slogwang 
36993b2bd0f6Slogwang     /*
37003b2bd0f6Slogwang      * Find the target node.
37013b2bd0f6Slogwang      * If there is a HOOK argument, then use that in preference
37023b2bd0f6Slogwang      * to the address.
37033b2bd0f6Slogwang      */
37043b2bd0f6Slogwang     if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
37053b2bd0f6Slogwang         NG_FREE_MSG(msg);
37063b2bd0f6Slogwang         return (NULL);
37073b2bd0f6Slogwang     }
37083b2bd0f6Slogwang 
37093b2bd0f6Slogwang     /* Fill out the contents */
37103b2bd0f6Slogwang     item->el_flags |= NGQF_WRITER;
37113b2bd0f6Slogwang     NG_NODE_REF(here);
37123b2bd0f6Slogwang     NGI_SET_NODE(item, here);
37133b2bd0f6Slogwang     if (hook) {
37143b2bd0f6Slogwang         NG_HOOK_REF(hook);
37153b2bd0f6Slogwang         NGI_SET_HOOK(item, hook);
37163b2bd0f6Slogwang     }
37173b2bd0f6Slogwang     NGI_MSG(item) = msg;
37183b2bd0f6Slogwang     NGI_RETADDR(item) = ng_node2ID(here);
37193b2bd0f6Slogwang     return (item);
37203b2bd0f6Slogwang }
37213b2bd0f6Slogwang 
37223b2bd0f6Slogwang /*
37233b2bd0f6Slogwang  * Send ng_item_fn function call to the specified node.
37243b2bd0f6Slogwang  */
37253b2bd0f6Slogwang 
37263b2bd0f6Slogwang int
ng_send_fn(node_p node,hook_p hook,ng_item_fn * fn,void * arg1,int arg2)37273b2bd0f6Slogwang ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
37283b2bd0f6Slogwang {
37293b2bd0f6Slogwang 
37303b2bd0f6Slogwang     return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
37313b2bd0f6Slogwang }
37323b2bd0f6Slogwang 
37333b2bd0f6Slogwang int
ng_send_fn1(node_p node,hook_p hook,ng_item_fn * fn,void * arg1,int arg2,int flags)37343b2bd0f6Slogwang ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
37353b2bd0f6Slogwang     int flags)
37363b2bd0f6Slogwang {
37373b2bd0f6Slogwang     item_p item;
37383b2bd0f6Slogwang 
37393b2bd0f6Slogwang     if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
37403b2bd0f6Slogwang         return (ENOMEM);
37413b2bd0f6Slogwang     }
37423b2bd0f6Slogwang     item->el_flags |= NGQF_WRITER;
37433b2bd0f6Slogwang     NG_NODE_REF(node); /* and one for the item */
37443b2bd0f6Slogwang     NGI_SET_NODE(item, node);
37453b2bd0f6Slogwang     if (hook) {
37463b2bd0f6Slogwang         NG_HOOK_REF(hook);
37473b2bd0f6Slogwang         NGI_SET_HOOK(item, hook);
37483b2bd0f6Slogwang     }
37493b2bd0f6Slogwang     NGI_FN(item) = fn;
37503b2bd0f6Slogwang     NGI_ARG1(item) = arg1;
37513b2bd0f6Slogwang     NGI_ARG2(item) = arg2;
37523b2bd0f6Slogwang     return(ng_snd_item(item, flags));
37533b2bd0f6Slogwang }
37543b2bd0f6Slogwang 
37553b2bd0f6Slogwang /*
37563b2bd0f6Slogwang  * Send ng_item_fn2 function call to the specified node.
37573b2bd0f6Slogwang  *
37583b2bd0f6Slogwang  * If an optional pitem parameter is supplied, its apply
37593b2bd0f6Slogwang  * callback will be copied to the new item. If also NG_REUSE_ITEM
37603b2bd0f6Slogwang  * flag is set, no new item will be allocated, but pitem will
37613b2bd0f6Slogwang  * be used.
37623b2bd0f6Slogwang  */
37633b2bd0f6Slogwang int
ng_send_fn2(node_p node,hook_p hook,item_p pitem,ng_item_fn2 * fn,void * arg1,int arg2,int flags)37643b2bd0f6Slogwang ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
37653b2bd0f6Slogwang     int arg2, int flags)
37663b2bd0f6Slogwang {
37673b2bd0f6Slogwang     item_p item;
37683b2bd0f6Slogwang 
37693b2bd0f6Slogwang     KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
37703b2bd0f6Slogwang         ("%s: NG_REUSE_ITEM but no pitem", __func__));
37713b2bd0f6Slogwang 
37723b2bd0f6Slogwang     /*
37733b2bd0f6Slogwang      * Allocate a new item if no supplied or
37743b2bd0f6Slogwang      * if we can't use supplied one.
37753b2bd0f6Slogwang      */
37763b2bd0f6Slogwang     if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
37773b2bd0f6Slogwang         if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
37783b2bd0f6Slogwang             return (ENOMEM);
37793b2bd0f6Slogwang         if (pitem != NULL)
37803b2bd0f6Slogwang             item->apply = pitem->apply;
37813b2bd0f6Slogwang     } else {
37823b2bd0f6Slogwang         if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
37833b2bd0f6Slogwang             return (ENOMEM);
37843b2bd0f6Slogwang     }
37853b2bd0f6Slogwang 
37863b2bd0f6Slogwang     item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
37873b2bd0f6Slogwang     NG_NODE_REF(node); /* and one for the item */
37883b2bd0f6Slogwang     NGI_SET_NODE(item, node);
37893b2bd0f6Slogwang     if (hook) {
37903b2bd0f6Slogwang         NG_HOOK_REF(hook);
37913b2bd0f6Slogwang         NGI_SET_HOOK(item, hook);
37923b2bd0f6Slogwang     }
37933b2bd0f6Slogwang     NGI_FN2(item) = fn;
37943b2bd0f6Slogwang     NGI_ARG1(item) = arg1;
37953b2bd0f6Slogwang     NGI_ARG2(item) = arg2;
37963b2bd0f6Slogwang     return(ng_snd_item(item, flags));
37973b2bd0f6Slogwang }
37983b2bd0f6Slogwang 
37993b2bd0f6Slogwang /*
38003b2bd0f6Slogwang  * Official timeout routines for Netgraph nodes.
38013b2bd0f6Slogwang  */
38023b2bd0f6Slogwang static void
ng_callout_trampoline(void * arg)38033b2bd0f6Slogwang ng_callout_trampoline(void *arg)
38043b2bd0f6Slogwang {
3805*8640edf1Sfengbojiang     struct epoch_tracker et;
38063b2bd0f6Slogwang     item_p item = arg;
38073b2bd0f6Slogwang 
3808*8640edf1Sfengbojiang     NET_EPOCH_ENTER(et);
38093b2bd0f6Slogwang     CURVNET_SET(NGI_NODE(item)->nd_vnet);
38103b2bd0f6Slogwang     ng_snd_item(item, 0);
38113b2bd0f6Slogwang     CURVNET_RESTORE();
3812*8640edf1Sfengbojiang     NET_EPOCH_EXIT(et);
38133b2bd0f6Slogwang }
38143b2bd0f6Slogwang 
38153b2bd0f6Slogwang int
ng_callout(struct callout * c,node_p node,hook_p hook,int ticks,ng_item_fn * fn,void * arg1,int arg2)38163b2bd0f6Slogwang ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
38173b2bd0f6Slogwang     ng_item_fn *fn, void * arg1, int arg2)
38183b2bd0f6Slogwang {
38193b2bd0f6Slogwang     item_p item, oitem;
38203b2bd0f6Slogwang 
38213b2bd0f6Slogwang     if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
38223b2bd0f6Slogwang         return (ENOMEM);
38233b2bd0f6Slogwang 
38243b2bd0f6Slogwang     item->el_flags |= NGQF_WRITER;
38253b2bd0f6Slogwang     NG_NODE_REF(node);        /* and one for the item */
38263b2bd0f6Slogwang     NGI_SET_NODE(item, node);
38273b2bd0f6Slogwang     if (hook) {
38283b2bd0f6Slogwang         NG_HOOK_REF(hook);
38293b2bd0f6Slogwang         NGI_SET_HOOK(item, hook);
38303b2bd0f6Slogwang     }
38313b2bd0f6Slogwang     NGI_FN(item) = fn;
38323b2bd0f6Slogwang     NGI_ARG1(item) = arg1;
38333b2bd0f6Slogwang     NGI_ARG2(item) = arg2;
38343b2bd0f6Slogwang     oitem = c->c_arg;
38353b2bd0f6Slogwang     if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
38363b2bd0f6Slogwang         oitem != NULL)
38373b2bd0f6Slogwang         NG_FREE_ITEM(oitem);
38383b2bd0f6Slogwang     return (0);
38393b2bd0f6Slogwang }
38403b2bd0f6Slogwang 
3841*8640edf1Sfengbojiang /* A special modified version of callout_stop() */
38423b2bd0f6Slogwang int
ng_uncallout(struct callout * c,node_p node)38433b2bd0f6Slogwang ng_uncallout(struct callout *c, node_p node)
38443b2bd0f6Slogwang {
38453b2bd0f6Slogwang     item_p item;
38463b2bd0f6Slogwang     int rval;
38473b2bd0f6Slogwang 
38483b2bd0f6Slogwang     KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
38493b2bd0f6Slogwang     KASSERT(node != NULL, ("ng_uncallout: NULL node"));
38503b2bd0f6Slogwang 
38513b2bd0f6Slogwang     rval = callout_stop(c);
38523b2bd0f6Slogwang     item = c->c_arg;
38533b2bd0f6Slogwang     /* Do an extra check */
38543b2bd0f6Slogwang     if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
3855*8640edf1Sfengbojiang         (item != NULL) && (NGI_NODE(item) == node)) {
38563b2bd0f6Slogwang         /*
38573b2bd0f6Slogwang          * We successfully removed it from the queue before it ran
38583b2bd0f6Slogwang          * So now we need to unreference everything that was
38593b2bd0f6Slogwang          * given extra references. (NG_FREE_ITEM does this).
38603b2bd0f6Slogwang          */
38613b2bd0f6Slogwang         NG_FREE_ITEM(item);
38623b2bd0f6Slogwang     }
38633b2bd0f6Slogwang     c->c_arg = NULL;
38643b2bd0f6Slogwang 
3865*8640edf1Sfengbojiang     /*
3866*8640edf1Sfengbojiang      * Callers only want to know if the callout was cancelled and
3867*8640edf1Sfengbojiang      * not draining or stopped.
3868*8640edf1Sfengbojiang      */
3869*8640edf1Sfengbojiang     return (rval > 0);
38703b2bd0f6Slogwang }
38713b2bd0f6Slogwang 
38723b2bd0f6Slogwang /*
38733b2bd0f6Slogwang  * Set the address, if none given, give the node here.
38743b2bd0f6Slogwang  */
38753b2bd0f6Slogwang void
ng_replace_retaddr(node_p here,item_p item,ng_ID_t retaddr)38763b2bd0f6Slogwang ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
38773b2bd0f6Slogwang {
38783b2bd0f6Slogwang     if (retaddr) {
38793b2bd0f6Slogwang         NGI_RETADDR(item) = retaddr;
38803b2bd0f6Slogwang     } else {
38813b2bd0f6Slogwang         /*
38823b2bd0f6Slogwang          * The old return address should be ok.
38833b2bd0f6Slogwang          * If there isn't one, use the address here.
38843b2bd0f6Slogwang          */
38853b2bd0f6Slogwang         NGI_RETADDR(item) = ng_node2ID(here);
38863b2bd0f6Slogwang     }
38873b2bd0f6Slogwang }
3888