xref: /freebsd/sys/netgraph/ng_base.c (revision 8805f3d7be2ce01a2f80c6a33b4cd84e6632a81d)
1c398230bSWarner Losh /*-
24cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
34cf49a43SJulian Elischer  * All rights reserved.
44cf49a43SJulian Elischer  *
54cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
64cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
74cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
84cf49a43SJulian Elischer  * provided, however, that:
94cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
104cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
114cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
124cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
134cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
144cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
154cf49a43SJulian Elischer  *
164cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
174cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
184cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
194cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
204cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
214cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
224cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
234cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
244cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
254cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
264cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
274cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
284cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
294cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
304cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
314cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
324cf49a43SJulian Elischer  * OF SUCH DAMAGE.
334cf49a43SJulian Elischer  *
34cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
35cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
364cf49a43SJulian Elischer  *
374cf49a43SJulian Elischer  * $FreeBSD$
384cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
394cf49a43SJulian Elischer  */
404cf49a43SJulian Elischer 
414cf49a43SJulian Elischer /*
424cf49a43SJulian Elischer  * This file implements the base netgraph code.
434cf49a43SJulian Elischer  */
444cf49a43SJulian Elischer 
454cf49a43SJulian Elischer #include <sys/param.h>
46b99a7379SGleb Smirnoff #include <sys/systm.h>
4777a58296SGleb Smirnoff #include <sys/ctype.h>
48687adb70SGleb Smirnoff #include <sys/hash.h>
49f33ca0c9SMarcel Moolenaar #include <sys/kdb.h>
504cf49a43SJulian Elischer #include <sys/kernel.h>
5119afcd98SGleb Smirnoff #include <sys/kthread.h>
523b33fbe7SGleb Smirnoff #include <sys/ktr.h>
53104a9b7eSAlexander Kabaev #include <sys/limits.h>
54c4282b74SGleb Smirnoff #include <sys/lock.h>
554cf49a43SJulian Elischer #include <sys/malloc.h>
564cf49a43SJulian Elischer #include <sys/mbuf.h>
5719afcd98SGleb Smirnoff #include <sys/proc.h>
5877a58296SGleb Smirnoff #include <sys/queue.h>
5919afcd98SGleb Smirnoff #include <sys/refcount.h>
6019afcd98SGleb Smirnoff #include <sys/rwlock.h>
6119afcd98SGleb Smirnoff #include <sys/smp.h>
62bfa7e882SJulian Elischer #include <sys/sysctl.h>
6377a58296SGleb Smirnoff #include <sys/syslog.h>
64f2fbb838SAlexander Motin #include <sys/unistd.h>
65394cb30aSAlexander Motin #include <machine/cpu.h>
668ec07310SGleb Smirnoff #include <vm/uma.h>
674cf49a43SJulian Elischer 
684cf49a43SJulian Elischer #include <net/netisr.h>
69eddfbb76SRobert Watson #include <net/vnet.h>
704cf49a43SJulian Elischer 
714cf49a43SJulian Elischer #include <netgraph/ng_message.h>
724cf49a43SJulian Elischer #include <netgraph/netgraph.h>
73f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
744cf49a43SJulian Elischer 
759d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION);
7699ff8176SPeter Wemm 
77ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */
78d2fd0788SAlexander V. Chernikov static struct rwlock	ng_topo_lock;
79d2fd0788SAlexander V. Chernikov #define	TOPOLOGY_RLOCK()	rw_rlock(&ng_topo_lock)
80d2fd0788SAlexander V. Chernikov #define	TOPOLOGY_RUNLOCK()	rw_runlock(&ng_topo_lock)
81d2fd0788SAlexander V. Chernikov #define	TOPOLOGY_WLOCK()	rw_wlock(&ng_topo_lock)
82d2fd0788SAlexander V. Chernikov #define	TOPOLOGY_WUNLOCK()	rw_wunlock(&ng_topo_lock)
83d2fd0788SAlexander V. Chernikov #define	TOPOLOGY_NOTOWNED()	rw_assert(&ng_topo_lock, RA_UNLOCKED)
84ac5dd141SGleb Smirnoff 
8530400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
86cfea3f85SAlexander Motin static struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
871489164fSGleb Smirnoff static struct mtx	ngq_mtx;	/* protects the queue item list */
8830400f03SJulian Elischer 
8930400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
9030400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
9130400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
9230400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
9330400f03SJulian Elischer 
9430400f03SJulian Elischer static void ng_dumpitems(void);
9530400f03SJulian Elischer static void ng_dumpnodes(void);
9630400f03SJulian Elischer static void ng_dumphooks(void);
9730400f03SJulian Elischer 
9830400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
99954c4772SJulian Elischer /*
100954c4772SJulian Elischer  * DEAD versions of the structures.
101a2098feaSGabor Kovesdan  * In order to avoid races, it is sometimes necessary to point
102954c4772SJulian Elischer  * at SOMETHING even though theoretically, the current entity is
103954c4772SJulian Elischer  * INVALID. Use these to avoid these races.
104954c4772SJulian Elischer  */
105954c4772SJulian Elischer struct ng_type ng_deadtype = {
106954c4772SJulian Elischer 	NG_ABI_VERSION,
107954c4772SJulian Elischer 	"dead",
108954c4772SJulian Elischer 	NULL,	/* modevent */
109954c4772SJulian Elischer 	NULL,	/* constructor */
110954c4772SJulian Elischer 	NULL,	/* rcvmsg */
111954c4772SJulian Elischer 	NULL,	/* shutdown */
112954c4772SJulian Elischer 	NULL,	/* newhook */
113954c4772SJulian Elischer 	NULL,	/* findhook */
114954c4772SJulian Elischer 	NULL,	/* connect */
115954c4772SJulian Elischer 	NULL,	/* rcvdata */
116954c4772SJulian Elischer 	NULL,	/* disconnect */
117954c4772SJulian Elischer 	NULL, 	/* cmdlist */
118954c4772SJulian Elischer };
11930400f03SJulian Elischer 
120954c4772SJulian Elischer struct ng_node ng_deadnode = {
121954c4772SJulian Elischer 	"dead",
122954c4772SJulian Elischer 	&ng_deadtype,
123be4252b3SJulian Elischer 	NGF_INVALID,
124954c4772SJulian Elischer 	0,	/* numhooks */
125954c4772SJulian Elischer 	NULL,	/* private */
126954c4772SJulian Elischer 	0,	/* ID */
12713e403fdSAntoine Brodin 	LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
128954c4772SJulian Elischer 	{},	/* all_nodes list entry */
129954c4772SJulian Elischer 	{},	/* id hashtable list entry */
130954c4772SJulian Elischer 	{	0,
1319852972bSAlexander Motin 		0,
132954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
1339852972bSAlexander Motin 		{}, /* workqueue entry */
1349852972bSAlexander Motin 		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
135954c4772SJulian Elischer 	},
1369852972bSAlexander Motin 	1,	/* refs */
137a40b7874SMarko Zec 	NULL,	/* vnet */
138954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
139954c4772SJulian Elischer 	ND_MAGIC,
140954c4772SJulian Elischer 	__FILE__,
141954c4772SJulian Elischer 	__LINE__,
142954c4772SJulian Elischer 	{NULL}
143954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
144954c4772SJulian Elischer };
145954c4772SJulian Elischer 
146954c4772SJulian Elischer struct ng_hook ng_deadhook = {
147954c4772SJulian Elischer 	"dead",
148954c4772SJulian Elischer 	NULL,		/* private */
149954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
150e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
151954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
152954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
153954c4772SJulian Elischer 	{},		/* hooks list */
154c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
155c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1569852972bSAlexander Motin 	1,		/* refs always >= 1 */
157954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
158954c4772SJulian Elischer 	HK_MAGIC,
159954c4772SJulian Elischer 	__FILE__,
160954c4772SJulian Elischer 	__LINE__,
161954c4772SJulian Elischer 	{NULL}
162954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
163954c4772SJulian Elischer };
164954c4772SJulian Elischer 
165954c4772SJulian Elischer /*
166954c4772SJulian Elischer  * END DEAD STRUCTURES
167954c4772SJulian Elischer  */
168069154d5SJulian Elischer /* List nodes with unallocated work */
1699852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
170b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1714cf49a43SJulian Elischer 
1724cf49a43SJulian Elischer /* List of installed types */
173069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
174c4282b74SGleb Smirnoff static struct rwlock	ng_typelist_lock;
175c4282b74SGleb Smirnoff #define	TYPELIST_RLOCK()	rw_rlock(&ng_typelist_lock)
176c4282b74SGleb Smirnoff #define	TYPELIST_RUNLOCK()	rw_runlock(&ng_typelist_lock)
177c4282b74SGleb Smirnoff #define	TYPELIST_WLOCK()	rw_wlock(&ng_typelist_lock)
178c4282b74SGleb Smirnoff #define	TYPELIST_WUNLOCK()	rw_wunlock(&ng_typelist_lock)
1794cf49a43SJulian Elischer 
180687adb70SGleb Smirnoff /* Hash related definitions. */
181687adb70SGleb Smirnoff LIST_HEAD(nodehash, ng_node);
1825f901c92SAndrew Turner VNET_DEFINE_STATIC(struct nodehash *, ng_ID_hash);
1835f901c92SAndrew Turner VNET_DEFINE_STATIC(u_long, ng_ID_hmask);
1845f901c92SAndrew Turner VNET_DEFINE_STATIC(u_long, ng_nodes);
1855f901c92SAndrew Turner VNET_DEFINE_STATIC(struct nodehash *, ng_name_hash);
1865f901c92SAndrew Turner VNET_DEFINE_STATIC(u_long, ng_name_hmask);
1875f901c92SAndrew Turner VNET_DEFINE_STATIC(u_long, ng_named_nodes);
1881e77c105SRobert Watson #define	V_ng_ID_hash		VNET(ng_ID_hash)
189687adb70SGleb Smirnoff #define	V_ng_ID_hmask		VNET(ng_ID_hmask)
190687adb70SGleb Smirnoff #define	V_ng_nodes		VNET(ng_nodes)
191687adb70SGleb Smirnoff #define	V_ng_name_hash		VNET(ng_name_hash)
192687adb70SGleb Smirnoff #define	V_ng_name_hmask		VNET(ng_name_hmask)
193687adb70SGleb Smirnoff #define	V_ng_named_nodes	VNET(ng_named_nodes)
194eddfbb76SRobert Watson 
195c4282b74SGleb Smirnoff static struct rwlock	ng_idhash_lock;
196c4282b74SGleb Smirnoff #define	IDHASH_RLOCK()		rw_rlock(&ng_idhash_lock)
197c4282b74SGleb Smirnoff #define	IDHASH_RUNLOCK()	rw_runlock(&ng_idhash_lock)
198c4282b74SGleb Smirnoff #define	IDHASH_WLOCK()		rw_wlock(&ng_idhash_lock)
199c4282b74SGleb Smirnoff #define	IDHASH_WUNLOCK()	rw_wunlock(&ng_idhash_lock)
200c4282b74SGleb Smirnoff 
2010f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
202687adb70SGleb Smirnoff #define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1))
2030f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
2040f150d04SJulian Elischer 	do { 								\
205c4282b74SGleb Smirnoff 		rw_assert(&ng_idhash_lock, RA_LOCKED);			\
206603724d3SBjoern A. Zeeb 		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
2070f150d04SJulian Elischer 						nd_idnodes) {		\
2080f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
2090f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
2100f150d04SJulian Elischer 				break;					\
2110f150d04SJulian Elischer 			}						\
2120f150d04SJulian Elischer 		}							\
2130f150d04SJulian Elischer 	} while (0)
214069154d5SJulian Elischer 
215c4282b74SGleb Smirnoff static struct rwlock	ng_namehash_lock;
216c4282b74SGleb Smirnoff #define	NAMEHASH_RLOCK()	rw_rlock(&ng_namehash_lock)
217c4282b74SGleb Smirnoff #define	NAMEHASH_RUNLOCK()	rw_runlock(&ng_namehash_lock)
218c4282b74SGleb Smirnoff #define	NAMEHASH_WLOCK()	rw_wlock(&ng_namehash_lock)
219c4282b74SGleb Smirnoff #define	NAMEHASH_WUNLOCK()	rw_wunlock(&ng_namehash_lock)
220dc90cad9SJulian Elischer 
2214cf49a43SJulian Elischer /* Internal functions */
2224cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
223069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
224dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2254cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
226394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
227f2fbb838SAlexander Motin static void	ngthread(void *);
22827757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2299852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
230069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
231e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
232e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
233e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
234e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
235687adb70SGleb Smirnoff static int	ng_mkpeer(node_p node, const char *name, const char *name2,
236687adb70SGleb Smirnoff 		    char *type);
237687adb70SGleb Smirnoff static void	ng_name_rehash(void);
238687adb70SGleb Smirnoff static void	ng_ID_rehash(void);
239069154d5SJulian Elischer 
2404c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */
241069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
242069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
243069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
244069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
245069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
2461acb27c6SJulian Elischer void	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
24730400f03SJulian Elischer void	ng_unname(node_p node);
248069154d5SJulian Elischer 
2494cf49a43SJulian Elischer /* Our own netgraph malloc type */
2504cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
251069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
252d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
253d745c852SEd Schouten     "netgraph hook structures");
254d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
255d745c852SEd Schouten     "netgraph node structures");
256d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
257d745c852SEd Schouten     "netgraph item structures");
258069154d5SJulian Elischer 
259069154d5SJulian Elischer /* Should not be visible outside this file */
26030400f03SJulian Elischer 
26130400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
2621ede983cSDag-Erling Smørgrav 	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
26330400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
2641ede983cSDag-Erling Smørgrav 	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
26530400f03SJulian Elischer 
2662c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2674abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2682c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2694abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2702c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2714abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2722c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2734abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2742c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2754abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2762c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2774abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
278f2fbb838SAlexander Motin #define	NG_WORKLIST_SLEEP()			\
279f2fbb838SAlexander Motin 	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
280f2fbb838SAlexander Motin #define	NG_WORKLIST_WAKEUP()			\
281f2fbb838SAlexander Motin 	wakeup_one(&ng_worklist)
2822c8dda8dSWojciech A. Koszek 
28330400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
28430400f03SJulian Elischer /*
28530400f03SJulian Elischer  * In debug mode:
28630400f03SJulian Elischer  * In an attempt to help track reference count screwups
28730400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
28830400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
28930400f03SJulian Elischer  * after they have been freed.
29030400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
29130400f03SJulian Elischer  */
29230400f03SJulian Elischer static __inline hook_p
29330400f03SJulian Elischer ng_alloc_hook(void)
29430400f03SJulian Elischer {
29530400f03SJulian Elischer 	hook_p hook;
29630400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2979ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
29830400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
29930400f03SJulian Elischer 	if (hook) {
30030400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
30130400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
30230400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
30330400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
3049ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30530400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
30630400f03SJulian Elischer 	} else {
3079ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30830400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
30930400f03SJulian Elischer 		if (hook) {
31030400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
3119ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
31230400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
3139ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
31430400f03SJulian Elischer 		}
31530400f03SJulian Elischer 	}
31630400f03SJulian Elischer 	return (hook);
31730400f03SJulian Elischer }
31830400f03SJulian Elischer 
31930400f03SJulian Elischer static __inline node_p
32030400f03SJulian Elischer ng_alloc_node(void)
32130400f03SJulian Elischer {
32230400f03SJulian Elischer 	node_p node;
32330400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
3249ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
32530400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
32630400f03SJulian Elischer 	if (node) {
32730400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
32830400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
32930400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
33030400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3319ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
33230400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
33330400f03SJulian Elischer 	} else {
3349ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
33530400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
33630400f03SJulian Elischer 		if (node) {
33730400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3389ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
33930400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3409ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
34130400f03SJulian Elischer 		}
34230400f03SJulian Elischer 	}
34330400f03SJulian Elischer 	return (node);
34430400f03SJulian Elischer }
34530400f03SJulian Elischer 
34630400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
34730400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
34830400f03SJulian Elischer 
34930400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
35030400f03SJulian Elischer 	do {								\
3519ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);				\
35230400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
35330400f03SJulian Elischer 		hook->hk_magic = 0;					\
3549ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);				\
35530400f03SJulian Elischer 	} while (0)
35630400f03SJulian Elischer 
35730400f03SJulian Elischer #define NG_FREE_NODE(node)						\
35830400f03SJulian Elischer 	do {								\
3599ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);				\
36030400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
36130400f03SJulian Elischer 		node->nd_magic = 0;					\
3629ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);				\
36330400f03SJulian Elischer 	} while (0)
36430400f03SJulian Elischer 
36530400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
36630400f03SJulian Elischer 
36730400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
36830400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
36930400f03SJulian Elischer 
3701ede983cSDag-Erling Smørgrav #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
3711ede983cSDag-Erling Smørgrav #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
37230400f03SJulian Elischer 
37330400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
37430400f03SJulian Elischer 
375f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3764cf49a43SJulian Elischer #ifndef TRAP_ERROR
3776b795970SJulian Elischer #define TRAP_ERROR()
3784cf49a43SJulian Elischer #endif
3794cf49a43SJulian Elischer 
3805f901c92SAndrew Turner VNET_DEFINE_STATIC(ng_ID_t, nextID) = 1;
3811e77c105SRobert Watson #define	V_nextID			VNET(nextID)
382dc90cad9SJulian Elischer 
383b2da83c2SArchie Cobbs #ifdef INVARIANTS
384b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
385b2da83c2SArchie Cobbs 		struct mbuf *n;						\
386b2da83c2SArchie Cobbs 		int total;						\
387b2da83c2SArchie Cobbs 									\
388fe584538SDag-Erling Smørgrav 		M_ASSERTPKTHDR(m);					\
389b32cfb32SGleb Smirnoff 		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
390b2da83c2SArchie Cobbs 			total += n->m_len;				\
391b32cfb32SGleb Smirnoff 			if (n->m_nextpkt != NULL)			\
392b32cfb32SGleb Smirnoff 				panic("%s: m_nextpkt", __func__);	\
393b32cfb32SGleb Smirnoff 		}							\
394ba5b359aSGleb Smirnoff 									\
395b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
396b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
3976e551fb6SDavid E. O'Brien 			    __func__, (m)->m_pkthdr.len, total);	\
398b2da83c2SArchie Cobbs 		}							\
399b2da83c2SArchie Cobbs 	} while (0)
400b2da83c2SArchie Cobbs #else
401b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
402b2da83c2SArchie Cobbs #endif
403b2da83c2SArchie Cobbs 
404e088dd4cSAlexander Motin #define ERROUT(x)	do { error = (x); goto done; } while (0)
405dc90cad9SJulian Elischer 
4064cf49a43SJulian Elischer /************************************************************************
407f8307e12SArchie Cobbs 	Parse type definitions for generic messages
408f8307e12SArchie Cobbs ************************************************************************/
409f8307e12SArchie Cobbs 
410f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
411f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
412f0184ff8SArchie Cobbs static const struct ng_parse_struct_field				\
413f0184ff8SArchie Cobbs 	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
414f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
415f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
416f0184ff8SArchie Cobbs 	&ng_ ## lo ## _type_fields					\
417f8307e12SArchie Cobbs }
418f8307e12SArchie Cobbs 
419f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
420f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
421f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
422f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
423f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
424f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
425f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
426f8307e12SArchie Cobbs 
427f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
428d7d97eb0SJeroen Ruigrok van der Werven    value immediately preceding the array -- as with struct namelist
429f8307e12SArchie Cobbs    and struct typelist. */
430f8307e12SArchie Cobbs static int
431f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
432f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
433f8307e12SArchie Cobbs {
434f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
435f8307e12SArchie Cobbs }
436f8307e12SArchie Cobbs 
437f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
438f8307e12SArchie Cobbs static int
439f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
440f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
441f8307e12SArchie Cobbs {
442f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
443f8307e12SArchie Cobbs 
444f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
445f8307e12SArchie Cobbs }
446f8307e12SArchie Cobbs 
447f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
448f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
449f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
450f8307e12SArchie Cobbs 	&ng_generic_list_getLength
451f8307e12SArchie Cobbs };
452f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
453f8307e12SArchie Cobbs 	&ng_parse_array_type,
454f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
455f8307e12SArchie Cobbs };
456f8307e12SArchie Cobbs 
457f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
458f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
459f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
460f8307e12SArchie Cobbs 	&ng_generic_list_getLength
461f8307e12SArchie Cobbs };
462f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
463f8307e12SArchie Cobbs 	&ng_parse_array_type,
464f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
465f8307e12SArchie Cobbs };
466f8307e12SArchie Cobbs 
467f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
468f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
469f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
470f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
471f8307e12SArchie Cobbs };
472f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
473f8307e12SArchie Cobbs 	&ng_parse_array_type,
474f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
475f8307e12SArchie Cobbs };
476f8307e12SArchie Cobbs 
4775caf0d56SGleb Smirnoff DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_typeinfoarray_type));
478f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
479f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
480f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
481f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
482f8307e12SArchie Cobbs 
483f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
484f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
485f8307e12SArchie Cobbs 	{
486f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
487f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
488f8307e12SArchie Cobbs 	  "shutdown",
489f8307e12SArchie Cobbs 	  NULL,
490f8307e12SArchie Cobbs 	  NULL
491f8307e12SArchie Cobbs 	},
492f8307e12SArchie Cobbs 	{
493f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
494f8307e12SArchie Cobbs 	  NGM_MKPEER,
495f8307e12SArchie Cobbs 	  "mkpeer",
496f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
497f8307e12SArchie Cobbs 	  NULL
498f8307e12SArchie Cobbs 	},
499f8307e12SArchie Cobbs 	{
500f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
501f8307e12SArchie Cobbs 	  NGM_CONNECT,
502f8307e12SArchie Cobbs 	  "connect",
503f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
504f8307e12SArchie Cobbs 	  NULL
505f8307e12SArchie Cobbs 	},
506f8307e12SArchie Cobbs 	{
507f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
508f8307e12SArchie Cobbs 	  NGM_NAME,
509f8307e12SArchie Cobbs 	  "name",
510f8307e12SArchie Cobbs 	  &ng_generic_name_type,
511f8307e12SArchie Cobbs 	  NULL
512f8307e12SArchie Cobbs 	},
513f8307e12SArchie Cobbs 	{
514f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
515f8307e12SArchie Cobbs 	  NGM_RMHOOK,
516f8307e12SArchie Cobbs 	  "rmhook",
517f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
518f8307e12SArchie Cobbs 	  NULL
519f8307e12SArchie Cobbs 	},
520f8307e12SArchie Cobbs 	{
521f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
522f8307e12SArchie Cobbs 	  NGM_NODEINFO,
523f8307e12SArchie Cobbs 	  "nodeinfo",
524f8307e12SArchie Cobbs 	  NULL,
525f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
526f8307e12SArchie Cobbs 	},
527f8307e12SArchie Cobbs 	{
528f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
529f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
530f8307e12SArchie Cobbs 	  "listhooks",
531f8307e12SArchie Cobbs 	  NULL,
532f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
533f8307e12SArchie Cobbs 	},
534f8307e12SArchie Cobbs 	{
535f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
536f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
537f8307e12SArchie Cobbs 	  "listnames",
538f8307e12SArchie Cobbs 	  NULL,
539f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
540f8307e12SArchie Cobbs 	},
541f8307e12SArchie Cobbs 	{
542f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
543f8307e12SArchie Cobbs 	  NGM_LISTNODES,
544f8307e12SArchie Cobbs 	  "listnodes",
545f8307e12SArchie Cobbs 	  NULL,
546f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
547f8307e12SArchie Cobbs 	},
548f8307e12SArchie Cobbs 	{
549f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
550f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
551f8307e12SArchie Cobbs 	  "listtypes",
552f8307e12SArchie Cobbs 	  NULL,
5535caf0d56SGleb Smirnoff 	  &ng_generic_typelist_type
554f8307e12SArchie Cobbs 	},
555f8307e12SArchie Cobbs 	{
556f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
5577095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
5587095e097SPoul-Henning Kamp 	  "textconfig",
5597095e097SPoul-Henning Kamp 	  NULL,
5607095e097SPoul-Henning Kamp 	  &ng_parse_string_type
5617095e097SPoul-Henning Kamp 	},
5627095e097SPoul-Henning Kamp 	{
5637095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
564f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
565f8307e12SArchie Cobbs 	  "textstatus",
566f8307e12SArchie Cobbs 	  NULL,
567f8307e12SArchie Cobbs 	  &ng_parse_string_type
568f8307e12SArchie Cobbs 	},
569f8307e12SArchie Cobbs 	{
570f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
571f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
572f8307e12SArchie Cobbs 	  "ascii2binary",
573f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
574f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
575f8307e12SArchie Cobbs 	},
576f8307e12SArchie Cobbs 	{
577f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
578f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
579f8307e12SArchie Cobbs 	  "binary2ascii",
580f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
581f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
582f8307e12SArchie Cobbs 	},
583f8307e12SArchie Cobbs 	{ 0 }
584f8307e12SArchie Cobbs };
585f8307e12SArchie Cobbs 
586f8307e12SArchie Cobbs /************************************************************************
5874cf49a43SJulian Elischer 			Node routines
5884cf49a43SJulian Elischer ************************************************************************/
5894cf49a43SJulian Elischer 
5904cf49a43SJulian Elischer /*
5914cf49a43SJulian Elischer  * Instantiate a node of the requested type
5924cf49a43SJulian Elischer  */
5934cf49a43SJulian Elischer int
5944cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
5954cf49a43SJulian Elischer {
5964cf49a43SJulian Elischer 	struct ng_type *type;
597069154d5SJulian Elischer 	int	error;
5984cf49a43SJulian Elischer 
5994cf49a43SJulian Elischer 	/* Check that the type makes sense */
6004cf49a43SJulian Elischer 	if (typename == NULL) {
6016b795970SJulian Elischer 		TRAP_ERROR();
6024cf49a43SJulian Elischer 		return (EINVAL);
6034cf49a43SJulian Elischer 	}
6044cf49a43SJulian Elischer 
6057610f574SGleb Smirnoff 	/* Locate the node type. If we fail we return. Do not try to load
6067610f574SGleb Smirnoff 	 * module.
6077610f574SGleb Smirnoff 	 */
6084cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL)
6094cf49a43SJulian Elischer 		return (ENXIO);
6104cf49a43SJulian Elischer 
611069154d5SJulian Elischer 	/*
612069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
613069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
614069154d5SJulian Elischer 	 */
615069154d5SJulian Elischer 	if (type->constructor != NULL) {
616069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
6175633ca71SGleb Smirnoff 			if ((error = ((*type->constructor)(*nodepp))) != 0) {
61830400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
619069154d5SJulian Elischer 			}
620069154d5SJulian Elischer 		}
621069154d5SJulian Elischer 	} else {
622069154d5SJulian Elischer 		/*
623069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
62464efc707SRobert Watson 		 * to be made. It must be brought into existence by
625954c4772SJulian Elischer 		 * some external agency. The external agency should
626069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
627069154d5SJulian Elischer 		 * netgraph part initialised.
628069154d5SJulian Elischer 		 */
6296b795970SJulian Elischer 		TRAP_ERROR();
630069154d5SJulian Elischer 		error = EINVAL;
631069154d5SJulian Elischer 	}
632069154d5SJulian Elischer 	return (error);
6334cf49a43SJulian Elischer }
6344cf49a43SJulian Elischer 
6354cf49a43SJulian Elischer /*
636069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
637069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
6384cf49a43SJulian Elischer  * The returned node has a reference count of 1.
6394cf49a43SJulian Elischer  */
6404cf49a43SJulian Elischer int
6414cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
6424cf49a43SJulian Elischer {
6434cf49a43SJulian Elischer 	node_p node;
6444cf49a43SJulian Elischer 
6454cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6464cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6476b795970SJulian Elischer 		TRAP_ERROR();
6484cf49a43SJulian Elischer 		return (EINVAL);
6494cf49a43SJulian Elischer 	}
6504cf49a43SJulian Elischer 
6514cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
65230400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6534cf49a43SJulian Elischer 	if (node == NULL) {
6546b795970SJulian Elischer 		TRAP_ERROR();
6554cf49a43SJulian Elischer 		return (ENOMEM);
6564cf49a43SJulian Elischer 	}
65730400f03SJulian Elischer 	node->nd_type = type;
658bc29160dSMarko Zec #ifdef VIMAGE
659bc29160dSMarko Zec 	node->nd_vnet = curvnet;
660bc29160dSMarko Zec #endif
66130400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6624cf49a43SJulian Elischer 	type->refs++;
6634cf49a43SJulian Elischer 
6642c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6659852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
66630400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6674cf49a43SJulian Elischer 
6684cf49a43SJulian Elischer 	/* Initialize hook list for new node */
66930400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6704cf49a43SJulian Elischer 
671687adb70SGleb Smirnoff 	/* Get an ID and put us in the hash chain. */
672c4282b74SGleb Smirnoff 	IDHASH_WLOCK();
67330400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
674069154d5SJulian Elischer 		node_p node2 = NULL;
675ac957cd2SJulian Elischer 		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6760f150d04SJulian Elischer 
67730400f03SJulian Elischer 		/* Is there a problem with the new number? */
6780f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6790f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
68030400f03SJulian Elischer 			break;
681069154d5SJulian Elischer 		}
68230400f03SJulian Elischer 	}
683687adb70SGleb Smirnoff 	V_ng_nodes++;
684687adb70SGleb Smirnoff 	if (V_ng_nodes * 2 > V_ng_ID_hmask)
685687adb70SGleb Smirnoff 		ng_ID_rehash();
6864bd1b557SGleb Smirnoff 	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
6874bd1b557SGleb Smirnoff 	    nd_idnodes);
688c4282b74SGleb Smirnoff 	IDHASH_WUNLOCK();
689dc90cad9SJulian Elischer 
6904cf49a43SJulian Elischer 	/* Done */
6914cf49a43SJulian Elischer 	*nodepp = node;
6924cf49a43SJulian Elischer 	return (0);
6934cf49a43SJulian Elischer }
6944cf49a43SJulian Elischer 
6954cf49a43SJulian Elischer /*
6964cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
69764efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6984cf49a43SJulian Elischer  * no type-specific method.
6994cf49a43SJulian Elischer  *
70064efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
7013e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
7023e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
703069154d5SJulian Elischer  *
704069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
70564efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
7063e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
7073e4084c8SJulian Elischer  * in which case we allow the node to survive.
7083e4084c8SJulian Elischer  *
70964efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
7103e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
7113e4084c8SJulian Elischer  * are rebooting.... etc.
7124cf49a43SJulian Elischer  */
7134cf49a43SJulian Elischer void
7141acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
7154cf49a43SJulian Elischer {
7163e4084c8SJulian Elischer 	hook_p hook;
7173e4084c8SJulian Elischer 
7184cf49a43SJulian Elischer 	/* Check if it's already shutting down */
719be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
7204cf49a43SJulian Elischer 		return;
7214cf49a43SJulian Elischer 
7221acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
7231acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
7241acb27c6SJulian Elischer 		return;
7251acb27c6SJulian Elischer 	}
7261acb27c6SJulian Elischer 
7274cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
72830400f03SJulian Elischer 	NG_NODE_REF(node);
7294cf49a43SJulian Elischer 
73030400f03SJulian Elischer 	/*
73130400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
73230400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
733be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
73430400f03SJulian Elischer 	 * creation
73530400f03SJulian Elischer 	 */
736be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7374cf49a43SJulian Elischer 
738991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
739991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
740991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
741991fc65aSJulian Elischer 
7423e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7433e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7443e4084c8SJulian Elischer 		ng_destroy_hook(hook);
74530400f03SJulian Elischer 
746069154d5SJulian Elischer 	/*
747069154d5SJulian Elischer 	 * Drain the input queue forceably.
74830400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
74930400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
75030400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
751b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
752069154d5SJulian Elischer 	 */
7539852972bSAlexander Motin 	ng_flush_input_queue(node);
754069154d5SJulian Elischer 
755069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
75630400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
75730400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
75830400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
75930400f03SJulian Elischer 			/*
76030400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
76130400f03SJulian Elischer 			 * that it doesn't want to die.
762053359b7SPedro F. Giffuni 			 * Presumably it is a persistent node.
7631acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7641acb27c6SJulian Elischer 			 *  e.g. hardware going away,
765be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
76630400f03SJulian Elischer 			 */
767be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7681acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
76930400f03SJulian Elischer 			return;
7704cf49a43SJulian Elischer 		}
7711acb27c6SJulian Elischer 	} else {				/* do the default thing */
7721acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7731acb27c6SJulian Elischer 	}
7744cf49a43SJulian Elischer 
77530400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
77630400f03SJulian Elischer 
77730400f03SJulian Elischer 	/*
77830400f03SJulian Elischer 	 * Remove extra reference, possibly the last
77930400f03SJulian Elischer 	 * Possible other holders of references may include
78030400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
78130400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
78230400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
78330400f03SJulian Elischer 	 */
78430400f03SJulian Elischer 	NG_NODE_UNREF(node);
7854cf49a43SJulian Elischer }
7864cf49a43SJulian Elischer 
7875951069aSJulian Elischer /*
7885951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7895951069aSJulian Elischer  * deadnode always acts as it it were the last.
7905951069aSJulian Elischer  */
7913fbdf774SGleb Smirnoff void
79230400f03SJulian Elischer ng_unref_node(node_p node)
7934cf49a43SJulian Elischer {
7946b795970SJulian Elischer 
7953fbdf774SGleb Smirnoff 	if (node == &ng_deadnode)
7963fbdf774SGleb Smirnoff 		return;
7976b795970SJulian Elischer 
798719fb725SCraig Rodrigues 	CURVNET_SET(node->nd_vnet);
799719fb725SCraig Rodrigues 
8003fbdf774SGleb Smirnoff 	if (refcount_release(&node->nd_refs)) { /* we were the last */
801069154d5SJulian Elischer 
80230400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
803c4282b74SGleb Smirnoff 		NAMEHASH_WLOCK();
804687adb70SGleb Smirnoff 		if (NG_NODE_HAS_NAME(node)) {
805687adb70SGleb Smirnoff 			V_ng_named_nodes--;
80630400f03SJulian Elischer 			LIST_REMOVE(node, nd_nodes);
807687adb70SGleb Smirnoff 		}
808c4282b74SGleb Smirnoff 		NAMEHASH_WUNLOCK();
809069154d5SJulian Elischer 
810c4282b74SGleb Smirnoff 		IDHASH_WLOCK();
811687adb70SGleb Smirnoff 		V_ng_nodes--;
81230400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
813c4282b74SGleb Smirnoff 		IDHASH_WUNLOCK();
814069154d5SJulian Elischer 
81512574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
816069154d5SJulian Elischer 		NG_FREE_NODE(node);
8174cf49a43SJulian Elischer 	}
818719fb725SCraig Rodrigues 	CURVNET_RESTORE();
8194cf49a43SJulian Elischer }
8204cf49a43SJulian Elischer 
8214cf49a43SJulian Elischer /************************************************************************
822dc90cad9SJulian Elischer 			Node ID handling
823dc90cad9SJulian Elischer ************************************************************************/
824dc90cad9SJulian Elischer static node_p
825069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
826dc90cad9SJulian Elischer {
82730400f03SJulian Elischer 	node_p node;
828687adb70SGleb Smirnoff 
829c4282b74SGleb Smirnoff 	IDHASH_RLOCK();
8300f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
83130400f03SJulian Elischer 	if (node)
83230400f03SJulian Elischer 		NG_NODE_REF(node);
833c4282b74SGleb Smirnoff 	IDHASH_RUNLOCK();
83430400f03SJulian Elischer 	return(node);
835dc90cad9SJulian Elischer }
836dc90cad9SJulian Elischer 
837dc90cad9SJulian Elischer ng_ID_t
838dc90cad9SJulian Elischer ng_node2ID(node_p node)
839dc90cad9SJulian Elischer {
84070de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
841dc90cad9SJulian Elischer }
842dc90cad9SJulian Elischer 
843dc90cad9SJulian Elischer /************************************************************************
8444cf49a43SJulian Elischer 			Node name handling
8454cf49a43SJulian Elischer ************************************************************************/
8464cf49a43SJulian Elischer 
8474cf49a43SJulian Elischer /*
8484bd1b557SGleb Smirnoff  * Assign a node a name.
8494cf49a43SJulian Elischer  */
8504cf49a43SJulian Elischer int
8514cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8524cf49a43SJulian Elischer {
853687adb70SGleb Smirnoff 	uint32_t hash;
854069154d5SJulian Elischer 	node_p node2;
855687adb70SGleb Smirnoff 	int i;
8564cf49a43SJulian Elischer 
8574cf49a43SJulian Elischer 	/* Check the name is valid */
85887e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8594cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8604cf49a43SJulian Elischer 			break;
8614cf49a43SJulian Elischer 	}
8624cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8636b795970SJulian Elischer 		TRAP_ERROR();
8644cf49a43SJulian Elischer 		return (EINVAL);
8654cf49a43SJulian Elischer 	}
866dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8676b795970SJulian Elischer 		TRAP_ERROR();
8684cf49a43SJulian Elischer 		return (EINVAL);
8694cf49a43SJulian Elischer 	}
8704cf49a43SJulian Elischer 
871687adb70SGleb Smirnoff 	NAMEHASH_WLOCK();
872687adb70SGleb Smirnoff 	if (V_ng_named_nodes * 2 > V_ng_name_hmask)
873687adb70SGleb Smirnoff 		ng_name_rehash();
874687adb70SGleb Smirnoff 
875687adb70SGleb Smirnoff 	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
876687adb70SGleb Smirnoff 	/* Check the name isn't already being used. */
877687adb70SGleb Smirnoff 	LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes)
878687adb70SGleb Smirnoff 		if (NG_NODE_IS_VALID(node2) &&
879687adb70SGleb Smirnoff 		    (strcmp(NG_NODE_NAME(node2), name) == 0)) {
880687adb70SGleb Smirnoff 			NAMEHASH_WUNLOCK();
8814cf49a43SJulian Elischer 			return (EADDRINUSE);
8824cf49a43SJulian Elischer 		}
8834cf49a43SJulian Elischer 
884687adb70SGleb Smirnoff 	if (NG_NODE_HAS_NAME(node))
885cfea3f85SAlexander Motin 		LIST_REMOVE(node, nd_nodes);
886687adb70SGleb Smirnoff 	else
887687adb70SGleb Smirnoff 		V_ng_named_nodes++;
888687adb70SGleb Smirnoff 	/* Copy it. */
889687adb70SGleb Smirnoff 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
890687adb70SGleb Smirnoff 	/* Update name hash. */
891603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
892c4282b74SGleb Smirnoff 	NAMEHASH_WUNLOCK();
893cfea3f85SAlexander Motin 
8944cf49a43SJulian Elischer 	return (0);
8954cf49a43SJulian Elischer }
8964cf49a43SJulian Elischer 
8974cf49a43SJulian Elischer /*
8984cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8994cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
9004cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
9014cf49a43SJulian Elischer  *
9024cf49a43SJulian Elischer  * Returns the node if found, else NULL.
903069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
904e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
905e1e8f51bSRobert Watson  * there.
9064cf49a43SJulian Elischer  */
9074cf49a43SJulian Elischer node_p
908069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
9094cf49a43SJulian Elischer {
910dc90cad9SJulian Elischer 	node_p node;
911dc90cad9SJulian Elischer 	ng_ID_t temp;
912cfea3f85SAlexander Motin 	int	hash;
9134cf49a43SJulian Elischer 
9144cf49a43SJulian Elischer 	/* "." means "this node" */
915069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
91630400f03SJulian Elischer 		NG_NODE_REF(here);
917069154d5SJulian Elischer 		return(here);
918069154d5SJulian Elischer 	}
9194cf49a43SJulian Elischer 
9204cf49a43SJulian Elischer 	/* Check for name-by-ID */
921dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
922069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
9234cf49a43SJulian Elischer 	}
9244cf49a43SJulian Elischer 
925687adb70SGleb Smirnoff 	/* Find node by name. */
926687adb70SGleb Smirnoff 	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
927c4282b74SGleb Smirnoff 	NAMEHASH_RLOCK();
928c4282b74SGleb Smirnoff 	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes)
929cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
930cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
931c4282b74SGleb Smirnoff 			NG_NODE_REF(node);
9324cf49a43SJulian Elischer 			break;
9334cf49a43SJulian Elischer 		}
934c4282b74SGleb Smirnoff 	NAMEHASH_RUNLOCK();
935c4282b74SGleb Smirnoff 
9364cf49a43SJulian Elischer 	return (node);
9374cf49a43SJulian Elischer }
9384cf49a43SJulian Elischer 
9394cf49a43SJulian Elischer /*
9409d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
941dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9424cf49a43SJulian Elischer  */
943dc90cad9SJulian Elischer static ng_ID_t
9444cf49a43SJulian Elischer ng_decodeidname(const char *name)
9454cf49a43SJulian Elischer {
9462b70adcbSArchie Cobbs 	const int len = strlen(name);
94725792ef3SArchie Cobbs 	char *eptr;
9482b70adcbSArchie Cobbs 	u_long val;
9494cf49a43SJulian Elischer 
9502b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
9514bd1b557SGleb Smirnoff 	if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
9524bd1b557SGleb Smirnoff 	    (!isxdigit(name[1])))
95370de87f2SJulian Elischer 		return ((ng_ID_t)0);
9544cf49a43SJulian Elischer 
9552b70adcbSArchie Cobbs 	/* Decode number */
9562b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
9574bd1b557SGleb Smirnoff 	if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
95812f035e0SJulian Elischer 		return ((ng_ID_t)0);
9594bd1b557SGleb Smirnoff 
9604bd1b557SGleb Smirnoff 	return ((ng_ID_t)val);
9614cf49a43SJulian Elischer }
9624cf49a43SJulian Elischer 
9634cf49a43SJulian Elischer /*
9644cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9654cf49a43SJulian Elischer  * when shutting down and removing the node.
9664cf49a43SJulian Elischer  */
9674cf49a43SJulian Elischer void
9684cf49a43SJulian Elischer ng_unname(node_p node)
9694cf49a43SJulian Elischer {
9704cf49a43SJulian Elischer }
9714cf49a43SJulian Elischer 
972687adb70SGleb Smirnoff /*
973687adb70SGleb Smirnoff  * Allocate a bigger name hash.
974687adb70SGleb Smirnoff  */
975687adb70SGleb Smirnoff static void
976687adb70SGleb Smirnoff ng_name_rehash()
977687adb70SGleb Smirnoff {
978687adb70SGleb Smirnoff 	struct nodehash *new;
979687adb70SGleb Smirnoff 	uint32_t hash;
980687adb70SGleb Smirnoff 	u_long hmask;
981687adb70SGleb Smirnoff 	node_p node, node2;
982687adb70SGleb Smirnoff 	int i;
983687adb70SGleb Smirnoff 
984687adb70SGleb Smirnoff 	new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
985687adb70SGleb Smirnoff 	    HASH_NOWAIT);
986687adb70SGleb Smirnoff 	if (new == NULL)
987687adb70SGleb Smirnoff 		return;
988687adb70SGleb Smirnoff 
989687adb70SGleb Smirnoff 	for (i = 0; i <= V_ng_name_hmask; i++)
990687adb70SGleb Smirnoff 		LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) {
991687adb70SGleb Smirnoff #ifdef INVARIANTS
992687adb70SGleb Smirnoff 			LIST_REMOVE(node, nd_nodes);
993687adb70SGleb Smirnoff #endif
994687adb70SGleb Smirnoff 			hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask;
995687adb70SGleb Smirnoff 			LIST_INSERT_HEAD(&new[hash], node, nd_nodes);
996687adb70SGleb Smirnoff 		}
997687adb70SGleb Smirnoff 
998687adb70SGleb Smirnoff 	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
999687adb70SGleb Smirnoff 	V_ng_name_hash = new;
1000687adb70SGleb Smirnoff 	V_ng_name_hmask = hmask;
1001687adb70SGleb Smirnoff }
1002687adb70SGleb Smirnoff 
1003687adb70SGleb Smirnoff /*
1004687adb70SGleb Smirnoff  * Allocate a bigger ID hash.
1005687adb70SGleb Smirnoff  */
1006687adb70SGleb Smirnoff static void
1007687adb70SGleb Smirnoff ng_ID_rehash()
1008687adb70SGleb Smirnoff {
1009687adb70SGleb Smirnoff 	struct nodehash *new;
1010687adb70SGleb Smirnoff 	uint32_t hash;
1011687adb70SGleb Smirnoff 	u_long hmask;
1012687adb70SGleb Smirnoff 	node_p node, node2;
1013687adb70SGleb Smirnoff 	int i;
1014687adb70SGleb Smirnoff 
1015687adb70SGleb Smirnoff 	new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
1016687adb70SGleb Smirnoff 	    HASH_NOWAIT);
1017687adb70SGleb Smirnoff 	if (new == NULL)
1018687adb70SGleb Smirnoff 		return;
1019687adb70SGleb Smirnoff 
1020687adb70SGleb Smirnoff 	for (i = 0; i <= V_ng_ID_hmask; i++)
1021687adb70SGleb Smirnoff 		LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) {
1022687adb70SGleb Smirnoff #ifdef INVARIANTS
1023687adb70SGleb Smirnoff 			LIST_REMOVE(node, nd_idnodes);
1024687adb70SGleb Smirnoff #endif
1025687adb70SGleb Smirnoff 			hash = (node->nd_ID % (hmask + 1));
1026687adb70SGleb Smirnoff 			LIST_INSERT_HEAD(&new[hash], node, nd_idnodes);
1027687adb70SGleb Smirnoff 		}
1028687adb70SGleb Smirnoff 
1029687adb70SGleb Smirnoff 	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
1030687adb70SGleb Smirnoff 	V_ng_ID_hash = new;
1031687adb70SGleb Smirnoff 	V_ng_ID_hmask = hmask;
1032687adb70SGleb Smirnoff }
1033687adb70SGleb Smirnoff 
10344cf49a43SJulian Elischer /************************************************************************
10354cf49a43SJulian Elischer 			Hook routines
10364cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
10373e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
10383e4084c8SJulian Elischer  they are connected to the 'dead' hook.
10394cf49a43SJulian Elischer ************************************************************************/
10404cf49a43SJulian Elischer 
10414cf49a43SJulian Elischer /*
10424cf49a43SJulian Elischer  * Remove a hook reference
10434cf49a43SJulian Elischer  */
104430400f03SJulian Elischer void
10454cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
10464cf49a43SJulian Elischer {
10476b795970SJulian Elischer 
10483fbdf774SGleb Smirnoff 	if (hook == &ng_deadhook)
10496b795970SJulian Elischer 		return;
1050018fe3d1SAlexander Motin 
10513fbdf774SGleb Smirnoff 	if (refcount_release(&hook->hk_refs)) { /* we were the last */
1052f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
10536b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
1054069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
1055069154d5SJulian Elischer 	}
10564cf49a43SJulian Elischer }
10574cf49a43SJulian Elischer 
10584cf49a43SJulian Elischer /*
10594cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
10603e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
10614cf49a43SJulian Elischer  */
10624cf49a43SJulian Elischer static int
10634cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
10644cf49a43SJulian Elischer {
10654cf49a43SJulian Elischer 	hook_p hook;
10664cf49a43SJulian Elischer 	int error = 0;
10674cf49a43SJulian Elischer 
10684cf49a43SJulian Elischer 	/* Check that the given name is good */
10694cf49a43SJulian Elischer 	if (name == NULL) {
10706b795970SJulian Elischer 		TRAP_ERROR();
10714cf49a43SJulian Elischer 		return (EINVAL);
10724cf49a43SJulian Elischer 	}
1073899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
10746b795970SJulian Elischer 		TRAP_ERROR();
10754cf49a43SJulian Elischer 		return (EEXIST);
10764cf49a43SJulian Elischer 	}
10774cf49a43SJulian Elischer 
10784cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
107930400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
10804cf49a43SJulian Elischer 	if (hook == NULL) {
10816b795970SJulian Elischer 		TRAP_ERROR();
10824cf49a43SJulian Elischer 		return (ENOMEM);
10834cf49a43SJulian Elischer 	}
10843e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
108530400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
10863e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
108730400f03SJulian Elischer 	hook->hk_node = node;
108830400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
10894cf49a43SJulian Elischer 
10903e4084c8SJulian Elischer 	/* Set hook name */
109187e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10923e4084c8SJulian Elischer 
10933e4084c8SJulian Elischer 	/*
10943e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10953e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10963e4084c8SJulian Elischer 	 */
1097954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1098954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
109930400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1100069154d5SJulian Elischer 			return (error);
1101069154d5SJulian Elischer 		}
1102954c4772SJulian Elischer 	}
11034cf49a43SJulian Elischer 	/*
11044cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
11054cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
11064cf49a43SJulian Elischer 	 */
110730400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
110830400f03SJulian Elischer 	node->nd_numhooks++;
11093e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
11104cf49a43SJulian Elischer 
11114cf49a43SJulian Elischer 	if (hookp)
11124cf49a43SJulian Elischer 		*hookp = hook;
11133e4084c8SJulian Elischer 	return (0);
11144cf49a43SJulian Elischer }
11154cf49a43SJulian Elischer 
11164cf49a43SJulian Elischer /*
1117899e9c4eSArchie Cobbs  * Find a hook
1118899e9c4eSArchie Cobbs  *
1119899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1120899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
11213e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1122899e9c4eSArchie Cobbs  */
1123899e9c4eSArchie Cobbs hook_p
1124899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1125899e9c4eSArchie Cobbs {
1126899e9c4eSArchie Cobbs 	hook_p hook;
1127899e9c4eSArchie Cobbs 
112830400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
112930400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
113030400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
11314bd1b557SGleb Smirnoff 		if (NG_HOOK_IS_VALID(hook) &&
11324bd1b557SGleb Smirnoff 		    (strcmp(NG_HOOK_NAME(hook), name) == 0))
1133899e9c4eSArchie Cobbs 			return (hook);
1134899e9c4eSArchie Cobbs 	}
1135899e9c4eSArchie Cobbs 	return (NULL);
1136899e9c4eSArchie Cobbs }
1137899e9c4eSArchie Cobbs 
1138899e9c4eSArchie Cobbs /*
11394cf49a43SJulian Elischer  * Destroy a hook
11404cf49a43SJulian Elischer  *
11414cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
11424cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
11433e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
11443e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
11453e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
11463e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
11473e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
11483e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
11493e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
11503e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
11513e4084c8SJulian Elischer  * attached to the 'dead' hook.
11526b795970SJulian Elischer  *
11536b795970SJulian Elischer  * This routine is called at all stages of hook creation
11546b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
11554cf49a43SJulian Elischer  */
11564cf49a43SJulian Elischer void
11574cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
11584cf49a43SJulian Elischer {
1159ac5dd141SGleb Smirnoff 	hook_p peer;
1160ac5dd141SGleb Smirnoff 	node_p node;
11614cf49a43SJulian Elischer 
11626b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
11636b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
11646b795970SJulian Elischer 		return;
11656b795970SJulian Elischer 	}
1166ac5dd141SGleb Smirnoff 
1167ac5dd141SGleb Smirnoff 	/*
1168ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1169ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1170ac5dd141SGleb Smirnoff 	 */
1171d2fd0788SAlexander V. Chernikov 	TOPOLOGY_WLOCK();
1172ac5dd141SGleb Smirnoff 
1173ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1174ac5dd141SGleb Smirnoff 
1175ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1176ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1177ac5dd141SGleb Smirnoff 
11783e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
11793e4084c8SJulian Elischer 		/*
11803e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
11813e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
118228323addSBryan Drewery 		 * send it an rmhook message of its own.
11833e4084c8SJulian Elischer 		 */
11843e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
11853e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
11866b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
11876b795970SJulian Elischer 			/*
11886b795970SJulian Elischer 			 * If it's already divorced from a node,
11896b795970SJulian Elischer 			 * just free it.
11906b795970SJulian Elischer 			 */
1191d2fd0788SAlexander V. Chernikov 			TOPOLOGY_WUNLOCK();
11926b795970SJulian Elischer 		} else {
1193d2fd0788SAlexander V. Chernikov 			TOPOLOGY_WUNLOCK();
11946b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11956b795970SJulian Elischer 		}
119652fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
119752fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1198ac5dd141SGleb Smirnoff 	} else
1199d2fd0788SAlexander V. Chernikov 		TOPOLOGY_WUNLOCK();
1200ac5dd141SGleb Smirnoff 
1201d2fd0788SAlexander V. Chernikov 	TOPOLOGY_NOTOWNED();
12024cf49a43SJulian Elischer 
12034cf49a43SJulian Elischer 	/*
12044cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
12054cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
12064cf49a43SJulian Elischer 	 */
12076b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
12086b795970SJulian Elischer 		return;
12096b795970SJulian Elischer 	}
121030400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
121130400f03SJulian Elischer 	node->nd_numhooks--;
121230400f03SJulian Elischer 	if (node->nd_type->disconnect) {
12134cf49a43SJulian Elischer 		/*
12146b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
121564efc707SRobert Watson 		 * trust its existence after this point. (except
12166b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
12176b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
12184cf49a43SJulian Elischer 		 */
121930400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
12204cf49a43SJulian Elischer 	}
12216b795970SJulian Elischer 
12226b795970SJulian Elischer 	/*
12236b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
12246b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
12256b795970SJulian Elischer 	 */
12266b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
12276b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
12286b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
12294cf49a43SJulian Elischer }
12304cf49a43SJulian Elischer 
12314cf49a43SJulian Elischer /*
12324cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
12334cf49a43SJulian Elischer  * is effectively bypassed.
12344cf49a43SJulian Elischer  */
12354cf49a43SJulian Elischer int
12364cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
12374cf49a43SJulian Elischer {
123830400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
12396b795970SJulian Elischer 		TRAP_ERROR();
12404cf49a43SJulian Elischer 		return (EINVAL);
124130400f03SJulian Elischer 	}
1242d2fd0788SAlexander V. Chernikov 	TOPOLOGY_WLOCK();
1243c3189b3fSGleb Smirnoff 	if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) {
1244d2fd0788SAlexander V. Chernikov 		TOPOLOGY_WUNLOCK();
1245c3189b3fSGleb Smirnoff 		return (EINVAL);
1246c3189b3fSGleb Smirnoff 	}
124730400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
124830400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
12494cf49a43SJulian Elischer 
12503e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
12513e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
1252d2fd0788SAlexander V. Chernikov 	TOPOLOGY_WUNLOCK();
12533e4084c8SJulian Elischer 
1254cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1255cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1256cf3254aaSGleb Smirnoff 
12574cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
12584cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
12594cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
12604cf49a43SJulian Elischer 	return (0);
12614cf49a43SJulian Elischer }
12624cf49a43SJulian Elischer 
12634cf49a43SJulian Elischer /*
12644cf49a43SJulian Elischer  * Install a new netgraph type
12654cf49a43SJulian Elischer  */
12664cf49a43SJulian Elischer int
12674cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
12684cf49a43SJulian Elischer {
12694cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
12704cf49a43SJulian Elischer 
12714cf49a43SJulian Elischer 	/* Check version and type name fields */
12724bd1b557SGleb Smirnoff 	if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
12734bd1b557SGleb Smirnoff 	    (namelen >= NG_TYPESIZ)) {
12746b795970SJulian Elischer 		TRAP_ERROR();
12758ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
12764bd1b557SGleb Smirnoff 			printf("Netgraph: Node type rejected. ABI mismatch. "
12774bd1b557SGleb Smirnoff 			    "Suggest recompile\n");
12788ed370fdSJulian Elischer 		}
12794cf49a43SJulian Elischer 		return (EINVAL);
12804cf49a43SJulian Elischer 	}
12814cf49a43SJulian Elischer 
12824cf49a43SJulian Elischer 	/* Check for name collision */
12834cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
12846b795970SJulian Elischer 		TRAP_ERROR();
12854cf49a43SJulian Elischer 		return (EEXIST);
12864cf49a43SJulian Elischer 	}
12874cf49a43SJulian Elischer 
1288069154d5SJulian Elischer 	/* Link in new type */
1289c4282b74SGleb Smirnoff 	TYPELIST_WLOCK();
1290069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1291c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
1292c4282b74SGleb Smirnoff 	TYPELIST_WUNLOCK();
12934cf49a43SJulian Elischer 	return (0);
12944cf49a43SJulian Elischer }
12954cf49a43SJulian Elischer 
12964cf49a43SJulian Elischer /*
1297c31b4a53SJulian Elischer  * unlink a netgraph type
1298c31b4a53SJulian Elischer  * If no examples exist
1299c31b4a53SJulian Elischer  */
1300c31b4a53SJulian Elischer int
1301c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1302c31b4a53SJulian Elischer {
1303c31b4a53SJulian Elischer 	/* Check for name collision */
1304c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1305c31b4a53SJulian Elischer 		TRAP_ERROR();
1306c31b4a53SJulian Elischer 		return (EBUSY);
1307c31b4a53SJulian Elischer 	}
1308c31b4a53SJulian Elischer 
1309c31b4a53SJulian Elischer 	/* Unlink type */
1310c4282b74SGleb Smirnoff 	TYPELIST_WLOCK();
1311c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1312c4282b74SGleb Smirnoff 	TYPELIST_WUNLOCK();
1313c31b4a53SJulian Elischer 	return (0);
1314c31b4a53SJulian Elischer }
1315c31b4a53SJulian Elischer 
1316c31b4a53SJulian Elischer /*
13174cf49a43SJulian Elischer  * Look for a type of the name given
13184cf49a43SJulian Elischer  */
13194cf49a43SJulian Elischer struct ng_type *
13204cf49a43SJulian Elischer ng_findtype(const char *typename)
13214cf49a43SJulian Elischer {
13224cf49a43SJulian Elischer 	struct ng_type *type;
13234cf49a43SJulian Elischer 
1324c4282b74SGleb Smirnoff 	TYPELIST_RLOCK();
1325069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
13264cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
13274cf49a43SJulian Elischer 			break;
13284cf49a43SJulian Elischer 	}
1329c4282b74SGleb Smirnoff 	TYPELIST_RUNLOCK();
13304cf49a43SJulian Elischer 	return (type);
13314cf49a43SJulian Elischer }
13324cf49a43SJulian Elischer 
13334cf49a43SJulian Elischer /************************************************************************
13344cf49a43SJulian Elischer 			Composite routines
13354cf49a43SJulian Elischer ************************************************************************/
13364cf49a43SJulian Elischer /*
13376b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
13384cf49a43SJulian Elischer  */
1339e088dd4cSAlexander Motin static int
1340e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
13414cf49a43SJulian Elischer {
1342e088dd4cSAlexander Motin 	int	error = 0;
13434cf49a43SJulian Elischer 
13446b795970SJulian Elischer 	/*
13456b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
13466b795970SJulian Elischer 	 * Our caller has a reference on the hook.
13476b795970SJulian Elischer 	 * Our caller has a reference on the node.
13486b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13496b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13501acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
13511acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
13526b795970SJulian Elischer 	 */
13536b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
13546b795970SJulian Elischer 		/*
13556b795970SJulian Elischer 		 * The node must have been freed again since we last visited
13566b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
13576b795970SJulian Elischer 		 * We should just release our references and
13586b795970SJulian Elischer 		 * free anything we can think of.
13596b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
13606b795970SJulian Elischer 		 * that holds the references, just return.
13616b795970SJulian Elischer 		 */
1362e088dd4cSAlexander Motin 		ERROUT(ENOENT);
13636b795970SJulian Elischer 	}
13646b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1365e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13666b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13671acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1368e088dd4cSAlexander Motin 			ERROUT(error);
13694cf49a43SJulian Elischer 		}
13706b795970SJulian Elischer 	}
13716b795970SJulian Elischer 	/*
13726b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
13736b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
13746b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
13756b795970SJulian Elischer 	 */
13766b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1377e088dd4cSAlexander Motin done:
1378e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1379e088dd4cSAlexander Motin 	return (error);
13806b795970SJulian Elischer }
13816b795970SJulian Elischer 
1382e088dd4cSAlexander Motin static int
1383e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
13846b795970SJulian Elischer {
1385ac5dd141SGleb Smirnoff 	hook_p	peer;
1386e088dd4cSAlexander Motin 	int	error = 0;
13876b795970SJulian Elischer 
13886b795970SJulian Elischer 	/*
13896b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
13906b795970SJulian Elischer 	 * Our caller has a reference on the hook.
13916b795970SJulian Elischer 	 * Our caller has a reference on the node.
13926b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13936b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13946b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
13956b795970SJulian Elischer 	 * First check the hook name is unique.
13961acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13976b795970SJulian Elischer 	 */
13986b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13996b795970SJulian Elischer 		TRAP_ERROR();
14006b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
14011acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1402e088dd4cSAlexander Motin 		ERROUT(EEXIST);
14036b795970SJulian Elischer 	}
14046b795970SJulian Elischer 	/*
14056b795970SJulian Elischer 	 * Check if the node type code has something to say about it
14066b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
14076b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
14086b795970SJulian Elischer 	 * The peer hook will also be destroyed.
14096b795970SJulian Elischer 	 */
14106b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1411e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1412e088dd4cSAlexander Motin 		    hook->hk_name))) {
14136b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
14141acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1415e088dd4cSAlexander Motin 			ERROUT(error);
14166b795970SJulian Elischer 		}
14176b795970SJulian Elischer 	}
14186b795970SJulian Elischer 
14196b795970SJulian Elischer 	/*
14206b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
14216b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
14226b795970SJulian Elischer 	 */
14236b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
14246b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
14256b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
14266b795970SJulian Elischer 	node->nd_numhooks++;
14276b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
14286b795970SJulian Elischer 
14296b795970SJulian Elischer 	/*
143064efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
14315951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
14326b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
14336b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
14346b795970SJulian Elischer 	 * yet.
143564efc707SRobert Watson 	 * We can call the local one immediately as we have the
14366b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
14376b795970SJulian Elischer 	 */
14386b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1439e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
14406b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
14411acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1442e088dd4cSAlexander Motin 			ERROUT(error);
14436b795970SJulian Elischer 		}
14446b795970SJulian Elischer 	}
1445ac5dd141SGleb Smirnoff 
1446ac5dd141SGleb Smirnoff 	/*
1447ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1448ac5dd141SGleb Smirnoff 	 */
1449d2fd0788SAlexander V. Chernikov 	TOPOLOGY_RLOCK();
1450ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1451ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1452d2fd0788SAlexander V. Chernikov 		TOPOLOGY_RUNLOCK();
1453ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1454ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1455e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1456ac5dd141SGleb Smirnoff 	}
1457d2fd0788SAlexander V. Chernikov 	TOPOLOGY_RUNLOCK();
1458ac5dd141SGleb Smirnoff 
1459b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1460b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1461ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
14621acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1463e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
14641acb27c6SJulian Elischer 	}
14656b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1466e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1467e088dd4cSAlexander Motin done:
1468e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1469e088dd4cSAlexander Motin 	return (error);
14704cf49a43SJulian Elischer }
14714cf49a43SJulian Elischer 
14724cf49a43SJulian Elischer /*
14736b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
14746b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
14754cf49a43SJulian Elischer  */
14766b795970SJulian Elischer static int
1477e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1478e088dd4cSAlexander Motin     node_p node2, const char *name2)
14794cf49a43SJulian Elischer {
14804cf49a43SJulian Elischer 	int	error;
14814cf49a43SJulian Elischer 	hook_p	hook;
14824cf49a43SJulian Elischer 	hook_p	hook2;
14834cf49a43SJulian Elischer 
14841acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
14851acb27c6SJulian Elischer 		return(EEXIST);
14861acb27c6SJulian Elischer 	}
14873e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
14884cf49a43SJulian Elischer 		return (error);
14896b795970SJulian Elischer 	/* Allocate the other hook and link it up */
14906b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
149119724144SGleb Smirnoff 	if (hook2 == NULL) {
14926b795970SJulian Elischer 		TRAP_ERROR();
14936b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
14946b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
14956b795970SJulian Elischer 		return (ENOMEM);
14966b795970SJulian Elischer 	}
14976b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14986b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14996b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
15006b795970SJulian Elischer 	hook->hk_peer = hook2;
15016b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
15026b795970SJulian Elischer 	NG_HOOK_REF(hook2);
15036b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
150487e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
15056b795970SJulian Elischer 
15066b795970SJulian Elischer 	/*
15076b795970SJulian Elischer 	 * Queue the function above.
15086b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
15096b795970SJulian Elischer 	 * the other node.
15106b795970SJulian Elischer 	 */
1511b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1512b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
15133fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
15143fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
15153fb87c24SAlexander Motin 	}
15166b795970SJulian Elischer 
15176b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
15186b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
15193fb87c24SAlexander Motin 	return (error);
15204cf49a43SJulian Elischer }
15216b795970SJulian Elischer 
15226b795970SJulian Elischer /*
15236b795970SJulian Elischer  * Make a peer and connect.
15246b795970SJulian Elischer  * We assume that the local node is locked.
15256b795970SJulian Elischer  * The new node probably doesn't need a lock until
15266b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
15276b795970SJulian Elischer  * but we should think about it a bit more.
15286b795970SJulian Elischer  *
15296b795970SJulian Elischer  * The problem may come if the other node also fires up
15306b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
15316b795970SJulian Elischer  * also it may already get a command msg via it's ID.
15326b795970SJulian Elischer  *
15336b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
15346b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
15356b795970SJulian Elischer  * make arg1 point to the node to remove).
15366b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
15376b795970SJulian Elischer  * an unconnected node?
15386b795970SJulian Elischer  */
15396b795970SJulian Elischer static int
15406b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
15416b795970SJulian Elischer {
15426b795970SJulian Elischer 	node_p	node2;
15434c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
15446b795970SJulian Elischer 	int	error;
15456b795970SJulian Elischer 
15466b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
15476b795970SJulian Elischer 		return (error);
15486b795970SJulian Elischer 	}
15496b795970SJulian Elischer 
15506b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
15511acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15526b795970SJulian Elischer 		return (error);
15536b795970SJulian Elischer 	}
15546b795970SJulian Elischer 
15556b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
15561acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15576b795970SJulian Elischer 		ng_destroy_hook(hook1);
15586b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
15596b795970SJulian Elischer 		return (error);
15606b795970SJulian Elischer 	}
15616b795970SJulian Elischer 
15626b795970SJulian Elischer 	/*
15636b795970SJulian Elischer 	 * Actually link the two hooks together.
15646b795970SJulian Elischer 	 */
15656b795970SJulian Elischer 	hook1->hk_peer = hook2;
15666b795970SJulian Elischer 	hook2->hk_peer = hook1;
15676b795970SJulian Elischer 
15686b795970SJulian Elischer 	/* Each hook is referenced by the other */
15696b795970SJulian Elischer 	NG_HOOK_REF(hook1);
15706b795970SJulian Elischer 	NG_HOOK_REF(hook2);
15716b795970SJulian Elischer 
15726b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
15736b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
15746b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
15756b795970SJulian Elischer 	}
15766b795970SJulian Elischer 
15776b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
15786b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
15796b795970SJulian Elischer 
15806b795970SJulian Elischer 	}
15813e4084c8SJulian Elischer 
15823e4084c8SJulian Elischer 	/*
15833e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
15843e4084c8SJulian Elischer 	 */
15856b795970SJulian Elischer 	if (error) {
15866b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
15871acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15886b795970SJulian Elischer 	} else {
15896b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
15906b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
15916b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
15926b795970SJulian Elischer 	}
15936b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
15943e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
15953e4084c8SJulian Elischer 	return (error);
15964cf49a43SJulian Elischer }
15976b795970SJulian Elischer 
1598069154d5SJulian Elischer /************************************************************************
1599069154d5SJulian Elischer 		Utility routines to send self messages
1600069154d5SJulian Elischer ************************************************************************/
1601069154d5SJulian Elischer 
16021acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
160364efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1604069154d5SJulian Elischer int
160515cea89fSAlexander Motin ng_rmnode_self(node_p node)
1606069154d5SJulian Elischer {
16071acb27c6SJulian Elischer 	int		error;
16084cf49a43SJulian Elischer 
16091acb27c6SJulian Elischer 	if (node == &ng_deadnode)
16101acb27c6SJulian Elischer 		return (0);
1611be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1612be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
16131acb27c6SJulian Elischer 		return (0);
1614069154d5SJulian Elischer 
161515cea89fSAlexander Motin 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
16161acb27c6SJulian Elischer 	return (error);
1617069154d5SJulian Elischer }
1618069154d5SJulian Elischer 
16191acb27c6SJulian Elischer static void
16206b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
16216b795970SJulian Elischer {
16226b795970SJulian Elischer 	ng_destroy_hook(hook);
16231acb27c6SJulian Elischer 	return ;
16246b795970SJulian Elischer }
16256b795970SJulian Elischer 
1626954c4772SJulian Elischer int
1627954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1628954c4772SJulian Elischer {
16296b795970SJulian Elischer 	int		error;
1630954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1631954c4772SJulian Elischer 
16326b795970SJulian Elischer 	if (node == &ng_deadnode)
16336b795970SJulian Elischer 		return (0);
16346b795970SJulian Elischer 
16356b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
16366b795970SJulian Elischer 	return (error);
1637954c4772SJulian Elischer }
1638954c4772SJulian Elischer 
1639069154d5SJulian Elischer /***********************************************************************
16404cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
16414cf49a43SJulian Elischer  *
16424cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
16434cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
16444cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
16454cf49a43SJulian Elischer  *
16464cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
16474cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
16484cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
16494cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
16504cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
16514cf49a43SJulian Elischer  *
16524cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1653069154d5SJulian Elischer  ***********************************************************************/
16544cf49a43SJulian Elischer int
16554cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
16564cf49a43SJulian Elischer {
16574cf49a43SJulian Elischer 	char	*node, *path, *hook;
16584cf49a43SJulian Elischer 	int	k;
16594cf49a43SJulian Elischer 
16604cf49a43SJulian Elischer 	/*
16614cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
16624cf49a43SJulian Elischer 	 */
16634cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
16644cf49a43SJulian Elischer 	if (*path) {
16654cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
16664cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
16674cf49a43SJulian Elischer 
16684cf49a43SJulian Elischer 		/* Node name must not be empty */
16694cf49a43SJulian Elischer 		if (!*node)
16704cf49a43SJulian Elischer 			return -1;
16714cf49a43SJulian Elischer 
16724cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
16734cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
16744cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
16754cf49a43SJulian Elischer 				if (node[k] == '.')
16764cf49a43SJulian Elischer 					return -1;
16774cf49a43SJulian Elischer 		}
16784cf49a43SJulian Elischer 	} else {
16794cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
16804cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
16814cf49a43SJulian Elischer 	}
16824cf49a43SJulian Elischer 
16834cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
16844cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16854cf49a43SJulian Elischer 		if (path[k] == ':')
16864cf49a43SJulian Elischer 			return -1;
16874cf49a43SJulian Elischer 
16884cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
16894cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16904cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
16914cf49a43SJulian Elischer 			return -1;
16924cf49a43SJulian Elischer 
16934cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
16944cf49a43SJulian Elischer 	if (path[0] == '.')
16954cf49a43SJulian Elischer 		path++;
16964cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16974cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16984cf49a43SJulian Elischer 
16994cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
17004cf49a43SJulian Elischer 	if (*path) {
17014cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
17024cf49a43SJulian Elischer 			if (path[k] == '.') {
17034cf49a43SJulian Elischer 				hook = NULL;
17044cf49a43SJulian Elischer 				break;
17054cf49a43SJulian Elischer 			}
17064cf49a43SJulian Elischer 	} else
17074cf49a43SJulian Elischer 		path = hook = NULL;
17084cf49a43SJulian Elischer 
17094cf49a43SJulian Elischer 	/* Done */
17104cf49a43SJulian Elischer 	if (nodep)
17114cf49a43SJulian Elischer 		*nodep = node;
17124cf49a43SJulian Elischer 	if (pathp)
17134cf49a43SJulian Elischer 		*pathp = path;
17144cf49a43SJulian Elischer 	if (hookp)
17154cf49a43SJulian Elischer 		*hookp = hook;
17164cf49a43SJulian Elischer 	return (0);
17174cf49a43SJulian Elischer }
17184cf49a43SJulian Elischer 
17194cf49a43SJulian Elischer /*
17204cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1721069154d5SJulian Elischer  * return the destination node.
17224cf49a43SJulian Elischer  */
17234cf49a43SJulian Elischer int
17244bd1b557SGleb Smirnoff ng_path2noderef(node_p here, const char *address, node_p *destp,
17254bd1b557SGleb Smirnoff     hook_p *lasthook)
17264cf49a43SJulian Elischer {
172787e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
1728a7da736aSGleb Smirnoff 	char   *nodename, *path;
1729069154d5SJulian Elischer 	node_p  node, oldnode;
17304cf49a43SJulian Elischer 
17314cf49a43SJulian Elischer 	/* Initialize */
173230400f03SJulian Elischer 	if (destp == NULL) {
17336b795970SJulian Elischer 		TRAP_ERROR();
17344cf49a43SJulian Elischer 		return EINVAL;
173530400f03SJulian Elischer 	}
17364cf49a43SJulian Elischer 	*destp = NULL;
17374cf49a43SJulian Elischer 
17384cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
17394cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
17404cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
17414cf49a43SJulian Elischer 
17424cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
17434cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
17446b795970SJulian Elischer 		TRAP_ERROR();
17454cf49a43SJulian Elischer 		return EINVAL;
17464cf49a43SJulian Elischer 	}
17474cf49a43SJulian Elischer 
1748069154d5SJulian Elischer 	/*
1749069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1750069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1751069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1752069154d5SJulian Elischer 	 */
17534cf49a43SJulian Elischer 	if (nodename) {
1754069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
17554cf49a43SJulian Elischer 		if (node == NULL) {
17566b795970SJulian Elischer 			TRAP_ERROR();
17574cf49a43SJulian Elischer 			return (ENOENT);
17584cf49a43SJulian Elischer 		}
1759069154d5SJulian Elischer 	} else {
1760069154d5SJulian Elischer 		if (here == NULL) {
17616b795970SJulian Elischer 			TRAP_ERROR();
1762069154d5SJulian Elischer 			return (EINVAL);
1763069154d5SJulian Elischer 		}
17644cf49a43SJulian Elischer 		node = here;
176530400f03SJulian Elischer 		NG_NODE_REF(node);
1766069154d5SJulian Elischer 	}
17674cf49a43SJulian Elischer 
1768a7da736aSGleb Smirnoff 	if (path == NULL) {
1769a7da736aSGleb Smirnoff 		if (lasthook != NULL)
1770a7da736aSGleb Smirnoff 			*lasthook = NULL;
1771a7da736aSGleb Smirnoff 		*destp = node;
1772a7da736aSGleb Smirnoff 		return (0);
1773a7da736aSGleb Smirnoff 	}
1774a7da736aSGleb Smirnoff 
1775069154d5SJulian Elischer 	/*
1776069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1777a7da736aSGleb Smirnoff 	 *
1778a7da736aSGleb Smirnoff 	 * XXXGL: The path may demolish as we go the sequence, but if
1779a7da736aSGleb Smirnoff 	 * we hold the topology mutex at critical places, then, I hope,
1780a7da736aSGleb Smirnoff 	 * we would always have valid pointers in hand, although the
1781a7da736aSGleb Smirnoff 	 * path behind us may no longer exist.
1782069154d5SJulian Elischer 	 */
1783a7da736aSGleb Smirnoff 	for (;;) {
1784a7da736aSGleb Smirnoff 		hook_p hook;
17854cf49a43SJulian Elischer 		char *segment;
17864cf49a43SJulian Elischer 
17874cf49a43SJulian Elischer 		/*
17884cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
1789a7da736aSGleb Smirnoff 		 * found with a NUL; "path" points to the next segment (or the
17904cf49a43SJulian Elischer 		 * NUL at the end).
17914cf49a43SJulian Elischer 		 */
1792a7da736aSGleb Smirnoff 		for (segment = path; *path != '\0'; path++) {
1793a7da736aSGleb Smirnoff 			if (*path == '.') {
1794a7da736aSGleb Smirnoff 				*path++ = '\0';
17954cf49a43SJulian Elischer 				break;
17964cf49a43SJulian Elischer 			}
17974cf49a43SJulian Elischer 		}
17984cf49a43SJulian Elischer 
17994cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1800899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
18014cf49a43SJulian Elischer 
1802d2fd0788SAlexander V. Chernikov 		TOPOLOGY_WLOCK();
18034cf49a43SJulian Elischer 		/* Can't get there from here... */
18044bd1b557SGleb Smirnoff 		if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
18054bd1b557SGleb Smirnoff 		    NG_HOOK_NOT_VALID(hook) ||
18064bd1b557SGleb Smirnoff 		    NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
18076b795970SJulian Elischer 			TRAP_ERROR();
180830400f03SJulian Elischer 			NG_NODE_UNREF(node);
1809d2fd0788SAlexander V. Chernikov 			TOPOLOGY_WUNLOCK();
18104cf49a43SJulian Elischer 			return (ENOENT);
18114cf49a43SJulian Elischer 		}
18124cf49a43SJulian Elischer 
1813069154d5SJulian Elischer 		/*
1814069154d5SJulian Elischer 		 * Hop on over to the next node
1815069154d5SJulian Elischer 		 * XXX
1816069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1817069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1818069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1819069154d5SJulian Elischer 		 */
1820069154d5SJulian Elischer 		oldnode = node;
182130400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
182230400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
182330400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
182430400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
182530400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1826d2fd0788SAlexander V. Chernikov 			TOPOLOGY_WUNLOCK();
18276b795970SJulian Elischer 			TRAP_ERROR();
18284cf49a43SJulian Elischer 			return (ENXIO);
18294cf49a43SJulian Elischer 		}
18304cf49a43SJulian Elischer 
1831a7da736aSGleb Smirnoff 		if (*path == '\0') {
1832a7da736aSGleb Smirnoff 			if (lasthook != NULL) {
1833a7da736aSGleb Smirnoff 				if (hook != NULL) {
1834a7da736aSGleb Smirnoff 					*lasthook = NG_HOOK_PEER(hook);
1835a7da736aSGleb Smirnoff 					NG_HOOK_REF(*lasthook);
1836a7da736aSGleb Smirnoff 				} else
1837a7da736aSGleb Smirnoff 					*lasthook = NULL;
1838a7da736aSGleb Smirnoff 			}
1839d2fd0788SAlexander V. Chernikov 			TOPOLOGY_WUNLOCK();
18404cf49a43SJulian Elischer 			*destp = node;
1841069154d5SJulian Elischer 			return (0);
1842069154d5SJulian Elischer 		}
1843d2fd0788SAlexander V. Chernikov 		TOPOLOGY_WUNLOCK();
1844a7da736aSGleb Smirnoff 	}
1845a7da736aSGleb Smirnoff }
1846069154d5SJulian Elischer 
1847069154d5SJulian Elischer /***************************************************************\
1848069154d5SJulian Elischer * Input queue handling.
1849069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1850069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
185164efc707SRobert Watson * Items which cannot be handled immediately are queued.
1852069154d5SJulian Elischer *
1853069154d5SJulian Elischer * read-write queue locking inline functions			*
1854069154d5SJulian Elischer \***************************************************************/
1855069154d5SJulian Elischer 
18569852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
18579852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
18589852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
18599852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
18609852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
18619852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1862069154d5SJulian Elischer 
1863069154d5SJulian Elischer /*
1864069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1865069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1866069154d5SJulian Elischer  * with them.
1867069154d5SJulian Elischer  *
1868b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1869069154d5SJulian Elischer  */
1870069154d5SJulian Elischer /*-
1871069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1872069154d5SJulian Elischer                        |
1873069154d5SJulian Elischer                        V
1874069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18754be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
18764be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
18774be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1878069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18794be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
18804be59335SGleb Smirnoff                             V                                | |
18814be59335SGleb Smirnoff                   [active reader count]                      | |
1882069154d5SJulian Elischer                                                              | |
18834be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1884069154d5SJulian Elischer                                                                |
18854be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1886b57a7965SJulian Elischer 
18878f9ac44aSAlexander Motin Node queue has such semantics:
18888f9ac44aSAlexander Motin - All flags modifications are atomic.
18898f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18908f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18918f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18928f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18938f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18948f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18958f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18968f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18978f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1898069154d5SJulian Elischer */
18998f9ac44aSAlexander Motin 
19004be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
19014be59335SGleb Smirnoff #define OP_PENDING	0x00000002
19024be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
19034be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
19044be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1905b57a7965SJulian Elischer 
1906b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
19074be59335SGleb Smirnoff /* Mask of bits a new read cares about */
19084be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1909b57a7965SJulian Elischer 
19104be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1911b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1912b57a7965SJulian Elischer 
19134be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
19144be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
19154be59335SGleb Smirnoff 
19164be59335SGleb Smirnoff /* How to decide what the next queued item is. */
19179852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
19189852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
19194be59335SGleb Smirnoff 
19204be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
19214be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
19224be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
19234be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
19244be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1925b57a7965SJulian Elischer 
1926b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
19274be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
19284be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1929394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
19304be59335SGleb Smirnoff 
1931714fb865SGleb Smirnoff #define NGQRW_R 0
1932714fb865SGleb Smirnoff #define NGQRW_W 1
1933714fb865SGleb Smirnoff 
19349852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
19359852972bSAlexander Motin 
1936069154d5SJulian Elischer /*
1937069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1938069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1939069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1940069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1941069154d5SJulian Elischer  * on the queue.
1942069154d5SJulian Elischer  */
1943069154d5SJulian Elischer static __inline item_p
19449852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1945069154d5SJulian Elischer {
1946069154d5SJulian Elischer 	item_p item;
19479852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1948b57a7965SJulian Elischer 
19498f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1950d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
19518f9ac44aSAlexander Motin 
19528f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
19534be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
19542955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
19552955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
19569852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19574be59335SGleb Smirnoff 		return (NULL);
19584be59335SGleb Smirnoff 	}
1959d58e678fSGleb Smirnoff 
19604be59335SGleb Smirnoff 	/*
19614be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
19624be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
19634be59335SGleb Smirnoff 	 * the current state of the node.
19644be59335SGleb Smirnoff 	 */
19654be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1966394cb30aSAlexander Motin 		while (1) {
1967394cb30aSAlexander Motin 			long t = ngq->q_flags;
1968394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
19698f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
19704bd1b557SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
19714bd1b557SGleb Smirnoff 				    "reader can't proceed; queue flags 0x%lx",
19724bd1b557SGleb Smirnoff 				    __func__, node->nd_ID, node, t);
19734be59335SGleb Smirnoff 				return (NULL);
19744be59335SGleb Smirnoff 			}
19759852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1976394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1977394cb30aSAlexander Motin 				break;
1978394cb30aSAlexander Motin 			cpu_spinwait();
1979394cb30aSAlexander Motin 		}
19808f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1981714fb865SGleb Smirnoff 		*rw = NGQRW_R;
19829852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1983394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19848f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1985714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1986069154d5SJulian Elischer 	} else {
19878f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
19884bd1b557SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
19894bd1b557SGleb Smirnoff 		    "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
19904bd1b557SGleb Smirnoff 		    ngq->q_flags);
19914be59335SGleb Smirnoff 		return (NULL);
19924cf49a43SJulian Elischer 	}
19934cf49a43SJulian Elischer 
19944cf49a43SJulian Elischer 	/*
1995069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1996069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19974cf49a43SJulian Elischer 	 */
19989852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19999852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
20009852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
20019852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
20024bd1b557SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
20034bd1b557SGleb Smirnoff 	    "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
20044bd1b557SGleb Smirnoff 	    "READER", ngq->q_flags);
2005069154d5SJulian Elischer 	return (item);
2006069154d5SJulian Elischer }
2007859a4d16SJulian Elischer 
2008859a4d16SJulian Elischer /*
20098f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
20108f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
2011859a4d16SJulian Elischer  */
2012069154d5SJulian Elischer static __inline void
20139852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
2014069154d5SJulian Elischer {
20159852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20164be59335SGleb Smirnoff 	if (rw == NGQRW_W)
20174be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
20184be59335SGleb Smirnoff 	else
20194be59335SGleb Smirnoff 		NGI_SET_READER(item);
20203fabe28bSRyan Stone 	item->depth = 1;
2021394cb30aSAlexander Motin 
2022394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2023394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
20249852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
20259852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
2026394cb30aSAlexander Motin 
20272955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
20289852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
20294be59335SGleb Smirnoff 
20304be59335SGleb Smirnoff 	/*
20314be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
20324be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
20334be59335SGleb Smirnoff 	 */
20344be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20359852972bSAlexander Motin 		ng_worklist_add(node);
2036394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2037069154d5SJulian Elischer }
2038069154d5SJulian Elischer 
20398f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
2040069154d5SJulian Elischer static __inline item_p
20419852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
2042069154d5SJulian Elischer {
20439852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2044ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
2045069154d5SJulian Elischer 
2046394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
20474bd1b557SGleb Smirnoff 	for (;;) {
20489852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
2049394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
2050394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
20514bd1b557SGleb Smirnoff 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
20524bd1b557SGleb Smirnoff 		    t + READER_INCREMENT)) {
2053069154d5SJulian Elischer 	    		/* Successfully grabbed node */
2054394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20559852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
2056069154d5SJulian Elischer 			return (item);
2057069154d5SJulian Elischer 		}
2058394cb30aSAlexander Motin 		cpu_spinwait();
205974b8d63dSPedro F. Giffuni 	}
2060069154d5SJulian Elischer 
2061394cb30aSAlexander Motin 	/* Queue the request for later. */
20629852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
2063f912c0f7SGleb Smirnoff 
2064f912c0f7SGleb Smirnoff 	return (NULL);
2065069154d5SJulian Elischer }
2066069154d5SJulian Elischer 
20678f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
2068069154d5SJulian Elischer static __inline item_p
20699852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
2070069154d5SJulian Elischer {
20719852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2072ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
2073ac5dd141SGleb Smirnoff 
2074394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
20754bd1b557SGleb Smirnoff 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
20764bd1b557SGleb Smirnoff 	    WRITER_ACTIVE)) {
2077394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
20782955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20799852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
2080069154d5SJulian Elischer 		return (item);
2081069154d5SJulian Elischer 	}
2082069154d5SJulian Elischer 
2083394cb30aSAlexander Motin 	/* Queue the request for later. */
20849852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
2085f912c0f7SGleb Smirnoff 
2086f912c0f7SGleb Smirnoff 	return (NULL);
2087069154d5SJulian Elischer }
2088069154d5SJulian Elischer 
2089262dfd7fSJulian Elischer #if 0
2090262dfd7fSJulian Elischer static __inline item_p
20919852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
2092262dfd7fSJulian Elischer {
20939852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20949852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2095262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2096262dfd7fSJulian Elischer 
2097262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2098262dfd7fSJulian Elischer 
20999852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2100262dfd7fSJulian Elischer 
2101262dfd7fSJulian Elischer 	/*
2102262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2103262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2104262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2105262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2106262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2107262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2108262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2109262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2110262dfd7fSJulian Elischer 	 */
21119852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
21129852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
21139852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2114262dfd7fSJulian Elischer 
2115262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2116262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2117262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2118262dfd7fSJulian Elischer 
2119262dfd7fSJulian Elischer 		/*
2120262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
21214bd1b557SGleb Smirnoff 		 * downgrade back to READER and finish up.
2122262dfd7fSJulian Elischer 	 	 */
21234bd1b557SGleb Smirnoff 		atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2124262dfd7fSJulian Elischer 
2125262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2126262dfd7fSJulian Elischer 		return;
2127262dfd7fSJulian Elischer 	}
2128262dfd7fSJulian Elischer 	/*
2129262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2130262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2131262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2132262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2133262dfd7fSJulian Elischer 	 * set the correct flags.
2134262dfd7fSJulian Elischer 	 */
21359852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2136262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
21379852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2138262dfd7fSJulian Elischer 
2139262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
21409852972bSAlexander Motin 		    node->nd_ID, node);
2141262dfd7fSJulian Elischer 	};
21429852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
21439852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
21449852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2145262dfd7fSJulian Elischer 
2146262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
21479852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2148394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
21499852972bSAlexander Motin 		ng_worklist_add(node);
21509852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2151262dfd7fSJulian Elischer 
2152262dfd7fSJulian Elischer 	return;
2153262dfd7fSJulian Elischer }
2154262dfd7fSJulian Elischer #endif
2155262dfd7fSJulian Elischer 
21568f9ac44aSAlexander Motin /* Release reader lock. */
2157069154d5SJulian Elischer static __inline void
21589852972bSAlexander Motin ng_leave_read(node_p node)
2159069154d5SJulian Elischer {
21609852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2161069154d5SJulian Elischer }
2162069154d5SJulian Elischer 
21638f9ac44aSAlexander Motin /* Release writer lock. */
2164069154d5SJulian Elischer static __inline void
21659852972bSAlexander Motin ng_leave_write(node_p node)
2166069154d5SJulian Elischer {
21679852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2168069154d5SJulian Elischer }
2169069154d5SJulian Elischer 
21708f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2171069154d5SJulian Elischer static void
21729852972bSAlexander Motin ng_flush_input_queue(node_p node)
2173069154d5SJulian Elischer {
21749852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2175069154d5SJulian Elischer 	item_p item;
2176069154d5SJulian Elischer 
21772c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
21789852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
21799852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
21809852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
21819852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
21822c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21834be59335SGleb Smirnoff 
21844be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
218510e87318SAlexander Motin 		if (item->apply != NULL) {
218610e87318SAlexander Motin 			if (item->depth == 1)
218710e87318SAlexander Motin 				item->apply->error = ENOENT;
218810e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
218910e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
219010e87318SAlexander Motin 				    item->apply->error);
219110e87318SAlexander Motin 			}
2192eb2405ddSGleb Smirnoff 		}
2193505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21942c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2195069154d5SJulian Elischer 	}
21962c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2197069154d5SJulian Elischer }
2198069154d5SJulian Elischer 
2199069154d5SJulian Elischer /***********************************************************************
2200069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2201069154d5SJulian Elischer ***********************************************************************/
2202069154d5SJulian Elischer 
2203069154d5SJulian Elischer /*
22041acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2205069154d5SJulian Elischer  * Common:
2206069154d5SJulian Elischer  *    reference to destination node.
2207069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2208e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2209069154d5SJulian Elischer  * Data:
2210069154d5SJulian Elischer  *    pointer to mbuf
2211069154d5SJulian Elischer  * Control_Message:
2212069154d5SJulian Elischer  *    pointer to msg.
2213069154d5SJulian Elischer  *    ID of original sender node. (return address)
22141acb27c6SJulian Elischer  * Function:
22151acb27c6SJulian Elischer  *    Function pointer
22161acb27c6SJulian Elischer  *    void * argument
22171acb27c6SJulian Elischer  *    integer argument
2218069154d5SJulian Elischer  *
2219069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2220069154d5SJulian Elischer  */
2221069154d5SJulian Elischer 
2222069154d5SJulian Elischer int
222342282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2224069154d5SJulian Elischer {
2225e088dd4cSAlexander Motin 	hook_p hook;
2226e088dd4cSAlexander Motin 	node_p node;
222742282202SGleb Smirnoff 	int queue, rw;
2228e088dd4cSAlexander Motin 	struct ng_queue *ngq;
222932b33288SGleb Smirnoff 	int error = 0;
2230069154d5SJulian Elischer 
2231f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2232f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2233e088dd4cSAlexander Motin 
2234e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2235e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2236e088dd4cSAlexander Motin #endif
2237e088dd4cSAlexander Motin 
2238f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2239e088dd4cSAlexander Motin 	if (item->apply)
2240e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2241e088dd4cSAlexander Motin 
2242e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2243f50597f5SAlexander Motin 	/* Node is never optional. */
2244f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2245e088dd4cSAlexander Motin 
2246e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2247f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2248f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2249f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
225010204449SJulian Elischer 		if (NGI_M(item) == NULL)
2251e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2252069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
22536f683eeeSGleb Smirnoff 	}
22546f683eeeSGleb Smirnoff 
2255069154d5SJulian Elischer 	/*
2256f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2257f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2258f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2259069154d5SJulian Elischer 	 */
2260db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2261f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2262f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2263069154d5SJulian Elischer 		rw = NGQRW_W;
2264f50597f5SAlexander Motin 	} else {
2265f50597f5SAlexander Motin 		rw = NGQRW_R;
2266f50597f5SAlexander Motin 	}
22676f683eeeSGleb Smirnoff 
226881a253a4SAlexander Motin 	/*
2269f089869fSMarko Zec 	 * If sender or receiver requests queued delivery, or call graph
2270f089869fSMarko Zec 	 * loops back from outbound to inbound path, or stack usage
227181a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
227281a253a4SAlexander Motin 	 */
227381a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
227481a253a4SAlexander Motin 		queue = 1;
2275f089869fSMarko Zec 	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2276f089869fSMarko Zec 	    curthread->td_ng_outbound) {
2277f089869fSMarko Zec 		queue = 1;
2278f50597f5SAlexander Motin 	} else {
2279f50597f5SAlexander Motin 		queue = 0;
228081a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2281d4529f98SAlexander Motin 		/*
2282942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2283f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2284d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2285f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2286f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2287d4529f98SAlexander Motin 		 */
2288f50597f5SAlexander Motin 		size_t	st, su, sl;
228981a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2290f50597f5SAlexander Motin 		sl = st - su;
22914bd1b557SGleb Smirnoff 		if ((sl * 4 < st) || ((sl * 2 < st) &&
22924bd1b557SGleb Smirnoff 		    ((node->nd_flags & NGF_HI_STACK) || (hook &&
22934bd1b557SGleb Smirnoff 		    (hook->hk_flags & HK_HI_STACK)))))
229481a253a4SAlexander Motin 			queue = 1;
229581a253a4SAlexander Motin #endif
2296f50597f5SAlexander Motin 	}
229781a253a4SAlexander Motin 
2298069154d5SJulian Elischer 	if (queue) {
2299394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
23009852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2301f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2302069154d5SJulian Elischer 	}
2303069154d5SJulian Elischer 
2304069154d5SJulian Elischer 	/*
2305069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2306069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2307069154d5SJulian Elischer 	 */
230832b33288SGleb Smirnoff  	if (rw == NGQRW_R)
23099852972bSAlexander Motin 		item = ng_acquire_read(node, item);
231032b33288SGleb Smirnoff 	else
23119852972bSAlexander Motin 		item = ng_acquire_write(node, item);
23124cf49a43SJulian Elischer 
2313f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2314f50597f5SAlexander Motin 	if (item == NULL)
2315f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2316069154d5SJulian Elischer 
23175951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
23185951069aSJulian Elischer 
231910e87318SAlexander Motin 	item->depth++;
232027757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2321069154d5SJulian Elischer 
2322394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
23239852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2324394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
23252c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2326394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
23279852972bSAlexander Motin 			ng_worklist_add(node);
23282c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2329394cb30aSAlexander Motin 	}
2330394cb30aSAlexander Motin 
2331394cb30aSAlexander Motin 	/*
2332394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2333394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2334394cb30aSAlexander Motin 	 */
2335394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2336069154d5SJulian Elischer 
23371acb27c6SJulian Elischer 	return (error);
2338e088dd4cSAlexander Motin 
2339e088dd4cSAlexander Motin done:
2340f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
234110e87318SAlexander Motin 	if (item->apply != NULL) {
234210e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
234310e87318SAlexander Motin 			item->apply->error = error;
234410e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
234510e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
234610e87318SAlexander Motin 			    item->apply->error);
234710e87318SAlexander Motin 		}
234810e87318SAlexander Motin 	}
2349e72a98f4SAlexander Motin 
2350e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2351e088dd4cSAlexander Motin 	return (error);
2352069154d5SJulian Elischer }
2353069154d5SJulian Elischer 
2354069154d5SJulian Elischer /*
2355069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2356069154d5SJulian Elischer  * It should contain all the information needed
2357069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2358e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
23594cf49a43SJulian Elischer  */
236027757487SGleb Smirnoff static int
2361714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2362069154d5SJulian Elischer {
2363069154d5SJulian Elischer 	hook_p  hook;
2364069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2365c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2366e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
236710e87318SAlexander Motin 	int	error = 0, depth;
2368069154d5SJulian Elischer 
2369f50597f5SAlexander Motin 	/* Node and item are never optional. */
2370f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2371f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2372f50597f5SAlexander Motin 
23731acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
237430400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2375069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2376069154d5SJulian Elischer #endif
23778afe16d5SGleb Smirnoff 
23788afe16d5SGleb Smirnoff 	apply = item->apply;
237910e87318SAlexander Motin 	depth = item->depth;
23808afe16d5SGleb Smirnoff 
23816b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2382069154d5SJulian Elischer 	case NGQF_DATA:
2383069154d5SJulian Elischer 		/*
2384069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2385069154d5SJulian Elischer 		 */
2386f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2387f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2388f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2389a54a69d7SJulian Elischer 			error = EIO;
2390069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2391c4b5eea4SJulian Elischer 			break;
2392069154d5SJulian Elischer 		}
2393c4b5eea4SJulian Elischer 		/*
2394c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
23954bd1b557SGleb Smirnoff 		 * Give preference to the hook over-ride method.
2396c4b5eea4SJulian Elischer 		 */
23974bd1b557SGleb Smirnoff 		if ((!(rcvdata = hook->hk_rcvdata)) &&
23984bd1b557SGleb Smirnoff 		    (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2399c4b5eea4SJulian Elischer 			error = 0;
2400c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2401c4b5eea4SJulian Elischer 			break;
2402c4b5eea4SJulian Elischer 		}
2403a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2404069154d5SJulian Elischer 		break;
2405069154d5SJulian Elischer 	case NGQF_MESG:
2406e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2407069154d5SJulian Elischer 			/*
2408e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2409e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2410069154d5SJulian Elischer 			 * The message may not need it.
2411069154d5SJulian Elischer 			 */
241230400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2413069154d5SJulian Elischer 			hook = NULL;
2414069154d5SJulian Elischer 		}
2415069154d5SJulian Elischer 		/*
2416069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2417069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2418069154d5SJulian Elischer 		 */
241930400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
24206b795970SJulian Elischer 			TRAP_ERROR();
2421a54a69d7SJulian Elischer 			error = EINVAL;
2422069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2423e72a98f4SAlexander Motin 			break;
2424e72a98f4SAlexander Motin 		}
2425069154d5SJulian Elischer 		/*
2426069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2427069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2428069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2429e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2430069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2431069154d5SJulian Elischer 		 * reference a node or hook that has just been
2432069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2433069154d5SJulian Elischer 		 * is holding a reference, but..
2434069154d5SJulian Elischer 		 */
2435e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2436e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2437a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2438c4b5eea4SJulian Elischer 			break;
2439c4b5eea4SJulian Elischer 		}
2440e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2441e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
24426b795970SJulian Elischer 			TRAP_ERROR();
2443a54a69d7SJulian Elischer 			error = 0;
2444069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2445c4b5eea4SJulian Elischer 			break;
2446069154d5SJulian Elischer 		}
2447a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2448069154d5SJulian Elischer 		break;
24496b795970SJulian Elischer 	case NGQF_FN:
2450e088dd4cSAlexander Motin 	case NGQF_FN2:
2451e088dd4cSAlexander Motin 		/*
245274c9119dSAlexander Motin 		 * In the case of the shutdown message we allow it to hit
2453e088dd4cSAlexander Motin 		 * even if the node is invalid.
2454e088dd4cSAlexander Motin 		 */
245574c9119dSAlexander Motin 		if (NG_NODE_NOT_VALID(node) &&
245674c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmnode) {
2457e088dd4cSAlexander Motin 			TRAP_ERROR();
2458e088dd4cSAlexander Motin 			error = EINVAL;
2459e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2460e088dd4cSAlexander Motin 			break;
2461e088dd4cSAlexander Motin 		}
246274c9119dSAlexander Motin 		/* Same is about some internal functions and invalid hook. */
246374c9119dSAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook) &&
246474c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part2 &&
246574c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part3 &&
246674c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmhook_part2) {
246774c9119dSAlexander Motin 			TRAP_ERROR();
246874c9119dSAlexander Motin 			error = EINVAL;
246974c9119dSAlexander Motin 			NG_FREE_ITEM(item);
247074c9119dSAlexander Motin 			break;
247174c9119dSAlexander Motin 		}
247274c9119dSAlexander Motin 
2473b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2474b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2475b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2476b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2477b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2478e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2479e088dd4cSAlexander Motin 		break;
2480069154d5SJulian Elischer 	}
2481069154d5SJulian Elischer 	/*
2482069154d5SJulian Elischer 	 * We held references on some of the resources
2483069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2484069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2485069154d5SJulian Elischer 	 */
2486e72a98f4SAlexander Motin 	if (hook)
248730400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2488069154d5SJulian Elischer 
2489f50597f5SAlexander Motin  	if (rw == NGQRW_R)
24909852972bSAlexander Motin 		ng_leave_read(node);
2491f50597f5SAlexander Motin 	else
24929852972bSAlexander Motin 		ng_leave_write(node);
24938afe16d5SGleb Smirnoff 
24948afe16d5SGleb Smirnoff 	/* Apply callback. */
249510e87318SAlexander Motin 	if (apply != NULL) {
249610e87318SAlexander Motin 		if (depth == 1 && error != 0)
249710e87318SAlexander Motin 			apply->error = error;
249810e87318SAlexander Motin 		if (refcount_release(&apply->refs))
249910e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
250010e87318SAlexander Motin 	}
25018afe16d5SGleb Smirnoff 
250227757487SGleb Smirnoff 	return (error);
2503069154d5SJulian Elischer }
2504069154d5SJulian Elischer 
2505069154d5SJulian Elischer /***********************************************************************
2506069154d5SJulian Elischer  * Implement the 'generic' control messages
2507069154d5SJulian Elischer  ***********************************************************************/
2508069154d5SJulian Elischer static int
2509069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
25104cf49a43SJulian Elischer {
25114cf49a43SJulian Elischer 	int error = 0;
2512069154d5SJulian Elischer 	struct ng_mesg *msg;
2513069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
25144cf49a43SJulian Elischer 
2515069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
25164cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
25176b795970SJulian Elischer 		TRAP_ERROR();
2518069154d5SJulian Elischer 		error = EINVAL;
2519069154d5SJulian Elischer 		goto out;
25204cf49a43SJulian Elischer 	}
25214cf49a43SJulian Elischer 	switch (msg->header.cmd) {
25224cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
25231acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
25244cf49a43SJulian Elischer 		break;
25254cf49a43SJulian Elischer 	case NGM_MKPEER:
25264cf49a43SJulian Elischer 	    {
25274cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
25284cf49a43SJulian Elischer 
25294cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
25306b795970SJulian Elischer 			TRAP_ERROR();
2531069154d5SJulian Elischer 			error = EINVAL;
2532069154d5SJulian Elischer 			break;
25334cf49a43SJulian Elischer 		}
25344cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
25354cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
25364cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
25374cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
25384cf49a43SJulian Elischer 		break;
25394cf49a43SJulian Elischer 	    }
25404cf49a43SJulian Elischer 	case NGM_CONNECT:
25414cf49a43SJulian Elischer 	    {
25424cf49a43SJulian Elischer 		struct ngm_connect *const con =
25434cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
25444cf49a43SJulian Elischer 		node_p node2;
25454cf49a43SJulian Elischer 
25464cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
25476b795970SJulian Elischer 			TRAP_ERROR();
2548069154d5SJulian Elischer 			error = EINVAL;
2549069154d5SJulian Elischer 			break;
25504cf49a43SJulian Elischer 		}
25514cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
25524cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
25534cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2554069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2555069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
25564cf49a43SJulian Elischer 		if (error)
25574cf49a43SJulian Elischer 			break;
2558e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2559e088dd4cSAlexander Motin 		    node2, con->peerhook);
256030400f03SJulian Elischer 		NG_NODE_UNREF(node2);
25614cf49a43SJulian Elischer 		break;
25624cf49a43SJulian Elischer 	    }
25634cf49a43SJulian Elischer 	case NGM_NAME:
25644cf49a43SJulian Elischer 	    {
25654cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
25664cf49a43SJulian Elischer 
25674cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
25686b795970SJulian Elischer 			TRAP_ERROR();
2569069154d5SJulian Elischer 			error = EINVAL;
2570069154d5SJulian Elischer 			break;
25714cf49a43SJulian Elischer 		}
25724cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
25734cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
25744cf49a43SJulian Elischer 		break;
25754cf49a43SJulian Elischer 	    }
25764cf49a43SJulian Elischer 	case NGM_RMHOOK:
25774cf49a43SJulian Elischer 	    {
25784cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
25794cf49a43SJulian Elischer 		hook_p hook;
25804cf49a43SJulian Elischer 
25814cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
25826b795970SJulian Elischer 			TRAP_ERROR();
2583069154d5SJulian Elischer 			error = EINVAL;
2584069154d5SJulian Elischer 			break;
25854cf49a43SJulian Elischer 		}
25864cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2587899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
25884cf49a43SJulian Elischer 			ng_destroy_hook(hook);
25894cf49a43SJulian Elischer 		break;
25904cf49a43SJulian Elischer 	    }
25914cf49a43SJulian Elischer 	case NGM_NODEINFO:
25924cf49a43SJulian Elischer 	    {
25934cf49a43SJulian Elischer 		struct nodeinfo *ni;
25944cf49a43SJulian Elischer 
2595069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25964cf49a43SJulian Elischer 		if (resp == NULL) {
25974cf49a43SJulian Elischer 			error = ENOMEM;
25984cf49a43SJulian Elischer 			break;
25994cf49a43SJulian Elischer 		}
26004cf49a43SJulian Elischer 
26014cf49a43SJulian Elischer 		/* Fill in node info */
2602069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
260330400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
260487e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
260587e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2606dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
260730400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
26084cf49a43SJulian Elischer 		break;
26094cf49a43SJulian Elischer 	    }
26104cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
26114cf49a43SJulian Elischer 	    {
261230400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
26134cf49a43SJulian Elischer 		struct hooklist *hl;
26144cf49a43SJulian Elischer 		struct nodeinfo *ni;
26154cf49a43SJulian Elischer 		hook_p hook;
26164cf49a43SJulian Elischer 
26174cf49a43SJulian Elischer 		/* Get response struct */
26184bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*hl) +
26194bd1b557SGleb Smirnoff 		    (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2620069154d5SJulian Elischer 		if (resp == NULL) {
26214cf49a43SJulian Elischer 			error = ENOMEM;
26224cf49a43SJulian Elischer 			break;
26234cf49a43SJulian Elischer 		}
2624069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
26254cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
26264cf49a43SJulian Elischer 
26274cf49a43SJulian Elischer 		/* Fill in node info */
262830400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
262987e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
263087e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2631dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
26324cf49a43SJulian Elischer 
26334cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
26344cf49a43SJulian Elischer 		ni->hooks = 0;
263530400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
26364cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
26374cf49a43SJulian Elischer 
26384cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
26394cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26406e551fb6SDavid E. O'Brien 				    __func__, "hooks");
26414cf49a43SJulian Elischer 				break;
26424cf49a43SJulian Elischer 			}
264330400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
26444cf49a43SJulian Elischer 				continue;
264587e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
264687e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
264730400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
264887e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
264987e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
265087e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
265187e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
265230400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
265330400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
26544cf49a43SJulian Elischer 			ni->hooks++;
26554cf49a43SJulian Elischer 		}
26564cf49a43SJulian Elischer 		break;
26574cf49a43SJulian Elischer 	    }
26584cf49a43SJulian Elischer 
26594cf49a43SJulian Elischer 	case NGM_LISTNODES:
26604cf49a43SJulian Elischer 	    {
26614cf49a43SJulian Elischer 		struct namelist *nl;
26624cf49a43SJulian Elischer 		node_p node;
2663687adb70SGleb Smirnoff 		int i;
2664687adb70SGleb Smirnoff 
2665687adb70SGleb Smirnoff 		IDHASH_RLOCK();
2666687adb70SGleb Smirnoff 		/* Get response struct. */
2667687adb70SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2668*8805f3d7SAlexander Motin 		    (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
2669687adb70SGleb Smirnoff 		if (resp == NULL) {
2670687adb70SGleb Smirnoff 			IDHASH_RUNLOCK();
2671687adb70SGleb Smirnoff 			error = ENOMEM;
2672687adb70SGleb Smirnoff 			break;
2673687adb70SGleb Smirnoff 		}
2674687adb70SGleb Smirnoff 		nl = (struct namelist *) resp->data;
2675687adb70SGleb Smirnoff 
2676687adb70SGleb Smirnoff 		/* Cycle through the lists of nodes. */
2677687adb70SGleb Smirnoff 		nl->numnames = 0;
2678687adb70SGleb Smirnoff 		for (i = 0; i <= V_ng_ID_hmask; i++) {
2679687adb70SGleb Smirnoff 			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
2680687adb70SGleb Smirnoff 				struct nodeinfo *const np =
2681687adb70SGleb Smirnoff 				    &nl->nodeinfo[nl->numnames];
2682687adb70SGleb Smirnoff 
2683687adb70SGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2684687adb70SGleb Smirnoff 					continue;
2685687adb70SGleb Smirnoff 				if (NG_NODE_HAS_NAME(node))
2686687adb70SGleb Smirnoff 					strcpy(np->name, NG_NODE_NAME(node));
2687687adb70SGleb Smirnoff 				strcpy(np->type, node->nd_type->name);
2688687adb70SGleb Smirnoff 				np->id = ng_node2ID(node);
2689687adb70SGleb Smirnoff 				np->hooks = node->nd_numhooks;
2690687adb70SGleb Smirnoff 				KASSERT(nl->numnames < V_ng_nodes,
2691687adb70SGleb Smirnoff 				    ("%s: no space", __func__));
2692687adb70SGleb Smirnoff 				nl->numnames++;
2693687adb70SGleb Smirnoff 			}
2694687adb70SGleb Smirnoff 		}
2695687adb70SGleb Smirnoff 		IDHASH_RUNLOCK();
2696687adb70SGleb Smirnoff 		break;
2697687adb70SGleb Smirnoff 	    }
2698687adb70SGleb Smirnoff 	case NGM_LISTNAMES:
2699687adb70SGleb Smirnoff 	    {
2700687adb70SGleb Smirnoff 		struct namelist *nl;
2701687adb70SGleb Smirnoff 		node_p node;
2702687adb70SGleb Smirnoff 		int i;
27034cf49a43SJulian Elischer 
2704c4282b74SGleb Smirnoff 		NAMEHASH_RLOCK();
2705687adb70SGleb Smirnoff 		/* Get response struct. */
27064bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2707687adb70SGleb Smirnoff 		    (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
2708069154d5SJulian Elischer 		if (resp == NULL) {
2709c4282b74SGleb Smirnoff 			NAMEHASH_RUNLOCK();
27104cf49a43SJulian Elischer 			error = ENOMEM;
27114cf49a43SJulian Elischer 			break;
27124cf49a43SJulian Elischer 		}
2713069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
27144cf49a43SJulian Elischer 
2715687adb70SGleb Smirnoff 		/* Cycle through the lists of nodes. */
27164cf49a43SJulian Elischer 		nl->numnames = 0;
2717687adb70SGleb Smirnoff 		for (i = 0; i <= V_ng_name_hmask; i++) {
2718603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2719cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2720cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
27214cf49a43SJulian Elischer 
2722b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2723b96baf0aSGleb Smirnoff 					continue;
272487e2c66aSHartmut Brandt 				strcpy(np->name, NG_NODE_NAME(node));
272587e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2726dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
272730400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
2728687adb70SGleb Smirnoff 				KASSERT(nl->numnames < V_ng_named_nodes,
2729687adb70SGleb Smirnoff 				    ("%s: no space", __func__));
27304cf49a43SJulian Elischer 				nl->numnames++;
27314cf49a43SJulian Elischer 			}
2732cfea3f85SAlexander Motin 		}
2733c4282b74SGleb Smirnoff 		NAMEHASH_RUNLOCK();
27344cf49a43SJulian Elischer 		break;
27354cf49a43SJulian Elischer 	    }
27364cf49a43SJulian Elischer 
27374cf49a43SJulian Elischer 	case NGM_LISTTYPES:
27384cf49a43SJulian Elischer 	    {
27394cf49a43SJulian Elischer 		struct typelist *tl;
27404cf49a43SJulian Elischer 		struct ng_type *type;
27414cf49a43SJulian Elischer 		int num = 0;
27424cf49a43SJulian Elischer 
2743c4282b74SGleb Smirnoff 		TYPELIST_RLOCK();
27444cf49a43SJulian Elischer 		/* Count number of types */
2745c4282b74SGleb Smirnoff 		LIST_FOREACH(type, &ng_typelist, types)
27464cf49a43SJulian Elischer 			num++;
27474cf49a43SJulian Elischer 
27484cf49a43SJulian Elischer 		/* Get response struct */
27494bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*tl) +
27504bd1b557SGleb Smirnoff 		    (num * sizeof(struct typeinfo)), M_NOWAIT);
2751069154d5SJulian Elischer 		if (resp == NULL) {
2752c4282b74SGleb Smirnoff 			TYPELIST_RUNLOCK();
27534cf49a43SJulian Elischer 			error = ENOMEM;
27544cf49a43SJulian Elischer 			break;
27554cf49a43SJulian Elischer 		}
2756069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
27574cf49a43SJulian Elischer 
27584cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
27594cf49a43SJulian Elischer 		tl->numtypes = 0;
2760069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
27614cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
27624cf49a43SJulian Elischer 
276387e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2764c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
2765c4282b74SGleb Smirnoff 			KASSERT(tl->numtypes < num, ("%s: no space", __func__));
27664cf49a43SJulian Elischer 			tl->numtypes++;
27674cf49a43SJulian Elischer 		}
2768c4282b74SGleb Smirnoff 		TYPELIST_RUNLOCK();
27694cf49a43SJulian Elischer 		break;
27704cf49a43SJulian Elischer 	    }
27714cf49a43SJulian Elischer 
2772f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2773f8307e12SArchie Cobbs 	    {
27747133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2775f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2776f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2777069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2778f8307e12SArchie Cobbs 
2779f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2780f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
27814c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
27824c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
27834c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
27846b795970SJulian Elischer 			TRAP_ERROR();
2785f8307e12SArchie Cobbs 			error = EINVAL;
2786f8307e12SArchie Cobbs 			break;
2787f8307e12SArchie Cobbs 		}
2788f8307e12SArchie Cobbs 
2789f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2790069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2791069154d5SJulian Elischer 		if (resp == NULL) {
2792f8307e12SArchie Cobbs 			error = ENOMEM;
2793f8307e12SArchie Cobbs 			break;
2794f8307e12SArchie Cobbs 		}
2795069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2796f8307e12SArchie Cobbs 
2797f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2798f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2799f8307e12SArchie Cobbs 
2800f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
28014bd1b557SGleb Smirnoff 		for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
28024bd1b557SGleb Smirnoff 		    c++) {
28034bd1b557SGleb Smirnoff 			if (binary->header.typecookie == c->cookie &&
28044bd1b557SGleb Smirnoff 			    binary->header.cmd == c->cmd)
2805f8307e12SArchie Cobbs 				break;
2806f8307e12SArchie Cobbs 		}
2807f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2808f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
28094bd1b557SGleb Smirnoff 				if (binary->header.typecookie == c->cookie &&
28104bd1b557SGleb Smirnoff 				    binary->header.cmd == c->cmd)
2811f8307e12SArchie Cobbs 					break;
2812f8307e12SArchie Cobbs 			}
2813f8307e12SArchie Cobbs 			if (c->name == NULL) {
2814069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2815f8307e12SArchie Cobbs 				error = ENOSYS;
2816f8307e12SArchie Cobbs 				break;
2817f8307e12SArchie Cobbs 			}
2818f8307e12SArchie Cobbs 		}
2819f8307e12SArchie Cobbs 
2820f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2821f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2822f8307e12SArchie Cobbs 		    "%s", c->name);
2823f8307e12SArchie Cobbs 
2824f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2825f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2826f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
282770de87f2SJulian Elischer 		if (argstype == NULL) {
2828f8307e12SArchie Cobbs 			*ascii->data = '\0';
282970de87f2SJulian Elischer 		} else {
2830f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2831f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2832f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2833069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2834f8307e12SArchie Cobbs 				break;
2835f8307e12SArchie Cobbs 			}
2836f8307e12SArchie Cobbs 		}
2837f8307e12SArchie Cobbs 
2838f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2839f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2840f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2841069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2842f8307e12SArchie Cobbs 		break;
2843f8307e12SArchie Cobbs 	    }
2844f8307e12SArchie Cobbs 
2845f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2846f8307e12SArchie Cobbs 	    {
284798a5a343SMarko Zec 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2848f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2849f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2850069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
285152ec4a03SArchie Cobbs 		int off = 0;
2852f8307e12SArchie Cobbs 
2853f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2854f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
28554c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
28564c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
28574c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
28584c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
28596b795970SJulian Elischer 			TRAP_ERROR();
2860f8307e12SArchie Cobbs 			error = EINVAL;
2861f8307e12SArchie Cobbs 			break;
2862f8307e12SArchie Cobbs 		}
2863f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2864f8307e12SArchie Cobbs 
2865f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2866069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2867069154d5SJulian Elischer 		if (resp == NULL) {
2868f8307e12SArchie Cobbs 			error = ENOMEM;
2869f8307e12SArchie Cobbs 			break;
2870f8307e12SArchie Cobbs 		}
2871069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2872f8307e12SArchie Cobbs 
2873f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2874f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2875f8307e12SArchie Cobbs 
2876f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
287730400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2878f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2879f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2880f8307e12SArchie Cobbs 				break;
2881f8307e12SArchie Cobbs 		}
2882f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2883f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2884f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2885f8307e12SArchie Cobbs 					break;
2886f8307e12SArchie Cobbs 			}
2887f8307e12SArchie Cobbs 			if (c->name == NULL) {
2888069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2889f8307e12SArchie Cobbs 				error = ENOSYS;
2890f8307e12SArchie Cobbs 				break;
2891f8307e12SArchie Cobbs 			}
2892f8307e12SArchie Cobbs 		}
2893f8307e12SArchie Cobbs 
2894f8307e12SArchie Cobbs 		/* Convert command name to binary */
2895f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2896f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2897f8307e12SArchie Cobbs 
2898f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2899f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2900f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
290170de87f2SJulian Elischer 		if (argstype == NULL) {
2902f8307e12SArchie Cobbs 			bufSize = 0;
290370de87f2SJulian Elischer 		} else {
29044bd1b557SGleb Smirnoff 			if ((error = ng_parse(argstype, ascii->data, &off,
29054bd1b557SGleb Smirnoff 			    (u_char *)binary->data, &bufSize)) != 0) {
2906069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2907f8307e12SArchie Cobbs 				break;
2908f8307e12SArchie Cobbs 			}
2909f8307e12SArchie Cobbs 		}
2910f8307e12SArchie Cobbs 
2911f8307e12SArchie Cobbs 		/* Return the result */
2912f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2913069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2914f8307e12SArchie Cobbs 		break;
2915f8307e12SArchie Cobbs 	    }
2916f8307e12SArchie Cobbs 
29177095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
29184cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
29194cf49a43SJulian Elischer 		/*
29204cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
29214cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2922069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
29234cf49a43SJulian Elischer 		 * when control passes back to us.
29244cf49a43SJulian Elischer 		 */
292530400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2926069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
292730400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
29284cf49a43SJulian Elischer 		}
29294cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
29304cf49a43SJulian Elischer 	default:
29316b795970SJulian Elischer 		TRAP_ERROR();
29324cf49a43SJulian Elischer 		error = EINVAL;
29334cf49a43SJulian Elischer 	}
2934069154d5SJulian Elischer 	/*
2935069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
29364bd1b557SGleb Smirnoff 	 * to avoid problems with allocating when in tight memory situations.
2937069154d5SJulian Elischer 	 * Don't free it if it is so.
2938053359b7SPedro F. Giffuni 	 * I break them apart here, because erros may cause a free if the item
2939069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2940069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2941069154d5SJulian Elischer 	 */
2942069154d5SJulian Elischer out:
2943069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2944069154d5SJulian Elischer 	NG_FREE_MSG(msg);
29454cf49a43SJulian Elischer 	return (error);
29464cf49a43SJulian Elischer }
29474cf49a43SJulian Elischer 
29484cf49a43SJulian Elischer /************************************************************************
29498253c060SGleb Smirnoff 			Queue element get/free routines
29508253c060SGleb Smirnoff ************************************************************************/
29518253c060SGleb Smirnoff 
29528253c060SGleb Smirnoff uma_zone_t			ng_qzone;
29536aa6d011SAlexander Motin uma_zone_t			ng_qdzone;
2954f2fbb838SAlexander Motin static int			numthreads = 0; /* number of queue threads */
2955ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
29568d8e595eSGleb Smirnoff static int			maxdata = 4096;	/* limit the damage of a DoS */
29578253c060SGleb Smirnoff 
2958f2fbb838SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2959f2fbb838SAlexander Motin     0, "Number of queue processing threads");
29608253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
29616aa6d011SAlexander Motin     0, "Maximum number of non-data queue items to allocate");
29626aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
29636aa6d011SAlexander Motin     0, "Maximum number of data queue items to allocate");
29648253c060SGleb Smirnoff 
29658253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29668253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
29678253c060SGleb Smirnoff static int allocated;	/* number of items malloc'd */
29688253c060SGleb Smirnoff #endif
29698253c060SGleb Smirnoff 
29708253c060SGleb Smirnoff /*
29718253c060SGleb Smirnoff  * Get a queue entry.
29728253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
29738253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
29748253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
29758253c060SGleb Smirnoff  * an interrupt.
29768253c060SGleb Smirnoff  */
29778253c060SGleb Smirnoff static __inline item_p
29786aa6d011SAlexander Motin ng_alloc_item(int type, int flags)
29798253c060SGleb Smirnoff {
29806aa6d011SAlexander Motin 	item_p item;
29818253c060SGleb Smirnoff 
29826aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29836aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
298442282202SGleb Smirnoff 
29856aa6d011SAlexander Motin 	item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
29866aa6d011SAlexander Motin 	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
29878253c060SGleb Smirnoff 
29888253c060SGleb Smirnoff 	if (item) {
29896aa6d011SAlexander Motin 		item->el_flags = type;
29906aa6d011SAlexander Motin #ifdef	NETGRAPH_DEBUG
29918253c060SGleb Smirnoff 		mtx_lock(&ngq_mtx);
29928253c060SGleb Smirnoff 		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
29938253c060SGleb Smirnoff 		allocated++;
29948253c060SGleb Smirnoff 		mtx_unlock(&ngq_mtx);
29958253c060SGleb Smirnoff #endif
29966aa6d011SAlexander Motin 	}
29978253c060SGleb Smirnoff 
29988253c060SGleb Smirnoff 	return (item);
29998253c060SGleb Smirnoff }
30008253c060SGleb Smirnoff 
30018253c060SGleb Smirnoff /*
30028253c060SGleb Smirnoff  * Release a queue entry
30038253c060SGleb Smirnoff  */
30048253c060SGleb Smirnoff void
30058253c060SGleb Smirnoff ng_free_item(item_p item)
30068253c060SGleb Smirnoff {
30078253c060SGleb Smirnoff 	/*
300828323addSBryan Drewery 	 * The item may hold resources on its own. We need to free
30098253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
30108253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
30118253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
30128253c060SGleb Smirnoff 	 * or we release them again here.
30138253c060SGleb Smirnoff 	 */
30148253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
30158253c060SGleb Smirnoff 	case NGQF_DATA:
30168253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
30178253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
30188253c060SGleb Smirnoff 		break;
30198253c060SGleb Smirnoff 	case NGQF_MESG:
30208253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
30218253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
30228253c060SGleb Smirnoff 		break;
30238253c060SGleb Smirnoff 	case NGQF_FN:
3024e088dd4cSAlexander Motin 	case NGQF_FN2:
30258253c060SGleb Smirnoff 		/* nothing to free really, */
30268253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
30278253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
30288253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
30298253c060SGleb Smirnoff 		break;
30308253c060SGleb Smirnoff 	}
30318253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
30328253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
30338253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
30348253c060SGleb Smirnoff 
30358253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
30368253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
30378253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
30388253c060SGleb Smirnoff 	allocated--;
30398253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
30408253c060SGleb Smirnoff #endif
30416aa6d011SAlexander Motin 	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
30426aa6d011SAlexander Motin 	    ng_qdzone : ng_qzone, item);
30436aa6d011SAlexander Motin }
30446aa6d011SAlexander Motin 
30456aa6d011SAlexander Motin /*
30466aa6d011SAlexander Motin  * Change type of the queue entry.
30476aa6d011SAlexander Motin  * Possibly reallocates it from another UMA zone.
30486aa6d011SAlexander Motin  */
30496aa6d011SAlexander Motin static __inline item_p
30506aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags)
30516aa6d011SAlexander Motin {
30526aa6d011SAlexander Motin 	item_p item;
30536aa6d011SAlexander Motin 	int from, to;
30546aa6d011SAlexander Motin 
30556aa6d011SAlexander Motin 	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
30566aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
30576aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
30586aa6d011SAlexander Motin 
30596aa6d011SAlexander Motin 	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
30606aa6d011SAlexander Motin 	to = (type == NGQF_DATA);
30616aa6d011SAlexander Motin 	if (from != to) {
30626aa6d011SAlexander Motin 		/* If reallocation is required do it and copy item. */
30636aa6d011SAlexander Motin 		if ((item = ng_alloc_item(type, flags)) == NULL) {
30646aa6d011SAlexander Motin 			ng_free_item(pitem);
30656aa6d011SAlexander Motin 			return (NULL);
30666aa6d011SAlexander Motin 		}
30676aa6d011SAlexander Motin 		*item = *pitem;
30686aa6d011SAlexander Motin 		ng_free_item(pitem);
30696aa6d011SAlexander Motin 	} else
30706aa6d011SAlexander Motin 		item = pitem;
30716aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
30726aa6d011SAlexander Motin 
30736aa6d011SAlexander Motin 	return (item);
30748253c060SGleb Smirnoff }
30758253c060SGleb Smirnoff 
30768253c060SGleb Smirnoff /************************************************************************
30774cf49a43SJulian Elischer 			Module routines
30784cf49a43SJulian Elischer ************************************************************************/
30794cf49a43SJulian Elischer 
30804cf49a43SJulian Elischer /*
30814cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
30824cf49a43SJulian Elischer  */
30834cf49a43SJulian Elischer int
30844cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
30854cf49a43SJulian Elischer {
30864cf49a43SJulian Elischer 	struct ng_type *const type = data;
30874bd1b557SGleb Smirnoff 	int error = 0;
30884cf49a43SJulian Elischer 
30894cf49a43SJulian Elischer 	switch (event) {
30904cf49a43SJulian Elischer 	case MOD_LOAD:
30914cf49a43SJulian Elischer 
30924cf49a43SJulian Elischer 		/* Register new netgraph node type */
30934bd1b557SGleb Smirnoff 		if ((error = ng_newtype(type)) != 0)
30944cf49a43SJulian Elischer 			break;
30954cf49a43SJulian Elischer 
30964cf49a43SJulian Elischer 		/* Call type specific code */
30974cf49a43SJulian Elischer 		if (type->mod_event != NULL)
3098069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
3099c4282b74SGleb Smirnoff 				TYPELIST_WLOCK();
3100c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
31014cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
3102c4282b74SGleb Smirnoff 				TYPELIST_WUNLOCK();
3103069154d5SJulian Elischer 			}
31044cf49a43SJulian Elischer 		break;
31054cf49a43SJulian Elischer 
31064cf49a43SJulian Elischer 	case MOD_UNLOAD:
3107c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
31084cf49a43SJulian Elischer 			error = EBUSY;
3109c73b94a2SJulian Elischer 		} else {
31104bd1b557SGleb Smirnoff 			if (type->refs == 0) /* failed load, nothing to undo */
3111c73b94a2SJulian Elischer 				break;
31124cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
31134cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
31144bd1b557SGleb Smirnoff 				if (error != 0)	/* type refuses.. */
31154cf49a43SJulian Elischer 					break;
31164cf49a43SJulian Elischer 			}
3117c4282b74SGleb Smirnoff 			TYPELIST_WLOCK();
31184cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
3119c4282b74SGleb Smirnoff 			TYPELIST_WUNLOCK();
31204cf49a43SJulian Elischer 		}
31214cf49a43SJulian Elischer 		break;
31224cf49a43SJulian Elischer 
31234cf49a43SJulian Elischer 	default:
31244cf49a43SJulian Elischer 		if (type->mod_event != NULL)
31254cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
31264cf49a43SJulian Elischer 		else
31273e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
31284cf49a43SJulian Elischer 		break;
31294cf49a43SJulian Elischer 	}
31304cf49a43SJulian Elischer 	return (error);
31314cf49a43SJulian Elischer }
31324cf49a43SJulian Elischer 
3133687adb70SGleb Smirnoff static void
3134687adb70SGleb Smirnoff vnet_netgraph_init(const void *unused __unused)
3135687adb70SGleb Smirnoff {
3136687adb70SGleb Smirnoff 
3137687adb70SGleb Smirnoff 	/* We start with small hashes, but they can grow. */
3138687adb70SGleb Smirnoff 	V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask);
3139687adb70SGleb Smirnoff 	V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask);
3140687adb70SGleb Smirnoff }
3141687adb70SGleb Smirnoff VNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3142687adb70SGleb Smirnoff     vnet_netgraph_init, NULL);
3143687adb70SGleb Smirnoff 
3144eddfbb76SRobert Watson #ifdef VIMAGE
3145d0728d71SRobert Watson static void
3146d0728d71SRobert Watson vnet_netgraph_uninit(const void *unused __unused)
3147bc29160dSMarko Zec {
3148a3f93b72SMarko Zec 	node_p node = NULL, last_killed = NULL;
3149a3f93b72SMarko Zec 	int i;
3150bc29160dSMarko Zec 
3151a3f93b72SMarko Zec 	do {
3152a3f93b72SMarko Zec 		/* Find a node to kill */
3153687adb70SGleb Smirnoff 		IDHASH_RLOCK();
3154687adb70SGleb Smirnoff 		for (i = 0; i <= V_ng_ID_hmask; i++) {
3155687adb70SGleb Smirnoff 			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
3156a3f93b72SMarko Zec 				if (node != &ng_deadnode) {
3157a3f93b72SMarko Zec 					NG_NODE_REF(node);
3158a3f93b72SMarko Zec 					break;
3159a3f93b72SMarko Zec 				}
3160a3f93b72SMarko Zec 			}
3161a3f93b72SMarko Zec 			if (node != NULL)
3162a3f93b72SMarko Zec 				break;
3163a3f93b72SMarko Zec 		}
3164687adb70SGleb Smirnoff 		IDHASH_RUNLOCK();
3165a3f93b72SMarko Zec 
3166a3f93b72SMarko Zec 		/* Attempt to kill it only if it is a regular node */
3167a3f93b72SMarko Zec 		if (node != NULL) {
3168bc29160dSMarko Zec 			if (node == last_killed) {
3169bc29160dSMarko Zec 				/* This should never happen */
31704bd1b557SGleb Smirnoff 				printf("ng node %s needs NGF_REALLY_DIE\n",
31714bd1b557SGleb Smirnoff 				    node->nd_name);
3172a3f93b72SMarko Zec 				if (node->nd_flags & NGF_REALLY_DIE)
3173a3f93b72SMarko Zec 					panic("ng node %s won't die",
3174a3f93b72SMarko Zec 					    node->nd_name);
3175bc29160dSMarko Zec 				node->nd_flags |= NGF_REALLY_DIE;
3176bc29160dSMarko Zec 			}
3177bc29160dSMarko Zec 			ng_rmnode(node, NULL, NULL, 0);
3178a3f93b72SMarko Zec 			NG_NODE_UNREF(node);
3179bc29160dSMarko Zec 			last_killed = node;
3180bc29160dSMarko Zec 		}
3181a3f93b72SMarko Zec 	} while (node != NULL);
3182687adb70SGleb Smirnoff 
3183687adb70SGleb Smirnoff 	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
3184687adb70SGleb Smirnoff 	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask);
3185bc29160dSMarko Zec }
3186320d00eeSGleb Smirnoff VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3187d0728d71SRobert Watson     vnet_netgraph_uninit, NULL);
3188bc29160dSMarko Zec #endif /* VIMAGE */
3189bc29160dSMarko Zec 
31904cf49a43SJulian Elischer /*
31914cf49a43SJulian Elischer  * Handle loading and unloading for this code.
31924cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
31934cf49a43SJulian Elischer  */
31944cf49a43SJulian Elischer static int
31954cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
31964cf49a43SJulian Elischer {
3197f2fbb838SAlexander Motin 	struct proc *p;
3198f2fbb838SAlexander Motin 	struct thread *td;
3199f2fbb838SAlexander Motin 	int i, error = 0;
32004cf49a43SJulian Elischer 
32014cf49a43SJulian Elischer 	switch (event) {
32024cf49a43SJulian Elischer 	case MOD_LOAD:
32031489164fSGleb Smirnoff 		/* Initialize everything. */
32042c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3205c4282b74SGleb Smirnoff 		rw_init(&ng_typelist_lock, "netgraph types");
3206c4282b74SGleb Smirnoff 		rw_init(&ng_idhash_lock, "netgraph idhash");
3207c4282b74SGleb Smirnoff 		rw_init(&ng_namehash_lock, "netgraph namehash");
3208d2fd0788SAlexander V. Chernikov 		rw_init(&ng_topo_lock, "netgraph topology mutex");
32091489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3210cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3211cfea3f85SAlexander Motin 		    MTX_DEF);
32121489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3213efd8e7c9SDon Lewis 		    MTX_DEF);
32141489164fSGleb Smirnoff #endif
32151489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
321677a117caSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
32171489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
32184bd1b557SGleb Smirnoff 		ng_qdzone = uma_zcreate("NetGraph data items",
32194bd1b557SGleb Smirnoff 		    sizeof(struct ng_item), NULL, NULL, NULL, NULL,
322077a117caSGleb Smirnoff 		    UMA_ALIGN_CACHE, 0);
32216aa6d011SAlexander Motin 		uma_zone_set_max(ng_qdzone, maxdata);
3222f2fbb838SAlexander Motin 		/* Autoconfigure number of threads. */
3223f2fbb838SAlexander Motin 		if (numthreads <= 0)
3224f2fbb838SAlexander Motin 			numthreads = mp_ncpus;
3225f2fbb838SAlexander Motin 		/* Create threads. */
3226f2fbb838SAlexander Motin     		p = NULL; /* start with no process */
3227f2fbb838SAlexander Motin 		for (i = 0; i < numthreads; i++) {
3228f2fbb838SAlexander Motin 			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3229f2fbb838SAlexander Motin 			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3230f2fbb838SAlexander Motin 				numthreads = i;
3231f2fbb838SAlexander Motin 				break;
3232f2fbb838SAlexander Motin 			}
3233f2fbb838SAlexander Motin 		}
32344cf49a43SJulian Elischer 		break;
32354cf49a43SJulian Elischer 	case MOD_UNLOAD:
323664efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
32374cf49a43SJulian Elischer 		error = EBUSY;
32384cf49a43SJulian Elischer 		break;
32394cf49a43SJulian Elischer 	default:
32404cf49a43SJulian Elischer 		error = EOPNOTSUPP;
32414cf49a43SJulian Elischer 		break;
32424cf49a43SJulian Elischer 	}
32434cf49a43SJulian Elischer 	return (error);
32444cf49a43SJulian Elischer }
32454cf49a43SJulian Elischer 
32464cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
32474cf49a43SJulian Elischer 	"netgraph",
32484cf49a43SJulian Elischer 	ngb_mod_event,
32494cf49a43SJulian Elischer 	(NULL)
32504cf49a43SJulian Elischer };
3251320d00eeSGleb Smirnoff DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST);
3252bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3253f0188618SHans Petter Selasky SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,"");
3254f0188618SHans Petter Selasky SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, "");
32554cf49a43SJulian Elischer 
325630400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
325730400f03SJulian Elischer void
325830400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
325930400f03SJulian Elischer {
326030400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
326130400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
326230400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
326330400f03SJulian Elischer 		hook->lastfile, hook->lastline);
326430400f03SJulian Elischer 	if (line) {
326530400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
3266e5fe87b3SGleb Smirnoff #ifdef KDB
3267e5fe87b3SGleb Smirnoff 		kdb_backtrace();
3268e5fe87b3SGleb Smirnoff #endif
326930400f03SJulian Elischer 	}
327030400f03SJulian Elischer }
327130400f03SJulian Elischer 
327230400f03SJulian Elischer void
327330400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
327430400f03SJulian Elischer {
327530400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
32766b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
327730400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
327830400f03SJulian Elischer 		node->nd_refs, node->nd_name);
327930400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
328030400f03SJulian Elischer 		node->lastfile, node->lastline);
328130400f03SJulian Elischer 	if (line) {
328230400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
3283e5fe87b3SGleb Smirnoff #ifdef KDB
3284e5fe87b3SGleb Smirnoff 		kdb_backtrace();
3285e5fe87b3SGleb Smirnoff #endif
328630400f03SJulian Elischer 	}
328730400f03SJulian Elischer }
328830400f03SJulian Elischer 
3289069154d5SJulian Elischer void
3290069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3291069154d5SJulian Elischer {
3292069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3293069154d5SJulian Elischer 		item->lastfile, item->lastline);
32946b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
32956b795970SJulian Elischer 	case NGQF_DATA:
3296069154d5SJulian Elischer 		printf(" - [data]\n");
32976b795970SJulian Elischer 		break;
32986b795970SJulian Elischer 	case NGQF_MESG:
32996b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
33006b795970SJulian Elischer 		break;
33016b795970SJulian Elischer 	case NGQF_FN:
3302857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3303857304e6SRuslan Ermilov 			_NGI_FN(item),
3304857304e6SRuslan Ermilov 			_NGI_NODE(item),
3305857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3306857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3307857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3308857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3309857304e6SRuslan Ermilov 		break;
3310e088dd4cSAlexander Motin 	case NGQF_FN2:
3311eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3312857304e6SRuslan Ermilov 			_NGI_FN2(item),
33136064e568SGleb Smirnoff 			_NGI_NODE(item),
33146064e568SGleb Smirnoff 			_NGI_HOOK(item),
33156b795970SJulian Elischer 			item->body.fn.fn_arg1,
33166b795970SJulian Elischer 			item->body.fn.fn_arg2,
33176b795970SJulian Elischer 			item->body.fn.fn_arg2);
33186b795970SJulian Elischer 		break;
3319069154d5SJulian Elischer 	}
332030400f03SJulian Elischer 	if (line) {
3321069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
33226064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
332330400f03SJulian Elischer 			printf("node %p ([%x])\n",
33246064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3325069154d5SJulian Elischer 		}
332630400f03SJulian Elischer 	}
332730400f03SJulian Elischer }
332830400f03SJulian Elischer 
332930400f03SJulian Elischer static void
333030400f03SJulian Elischer ng_dumpitems(void)
333130400f03SJulian Elischer {
333230400f03SJulian Elischer 	item_p item;
333330400f03SJulian Elischer 	int i = 1;
333430400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
333530400f03SJulian Elischer 		printf("[%d] ", i++);
333630400f03SJulian Elischer 		dumpitem(item, NULL, 0);
333730400f03SJulian Elischer 	}
333830400f03SJulian Elischer }
333930400f03SJulian Elischer 
334030400f03SJulian Elischer static void
334130400f03SJulian Elischer ng_dumpnodes(void)
334230400f03SJulian Elischer {
334330400f03SJulian Elischer 	node_p node;
334430400f03SJulian Elischer 	int i = 1;
334553f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
334630400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
334730400f03SJulian Elischer 		printf("[%d] ", i++);
334830400f03SJulian Elischer 		dumpnode(node, NULL, 0);
334930400f03SJulian Elischer 	}
335053f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
335130400f03SJulian Elischer }
335230400f03SJulian Elischer 
335330400f03SJulian Elischer static void
335430400f03SJulian Elischer ng_dumphooks(void)
335530400f03SJulian Elischer {
335630400f03SJulian Elischer 	hook_p hook;
335730400f03SJulian Elischer 	int i = 1;
335853f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
335930400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
336030400f03SJulian Elischer 		printf("[%d] ", i++);
336130400f03SJulian Elischer 		dumphook(hook, NULL, 0);
336230400f03SJulian Elischer 	}
336353f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
336430400f03SJulian Elischer }
3365069154d5SJulian Elischer 
3366069154d5SJulian Elischer static int
3367069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3368069154d5SJulian Elischer {
3369069154d5SJulian Elischer 	int error;
3370069154d5SJulian Elischer 	int val;
3371069154d5SJulian Elischer 	int i;
3372069154d5SJulian Elischer 
3373069154d5SJulian Elischer 	val = allocated;
3374069154d5SJulian Elischer 	i = 1;
3375041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
33766b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
33776b795970SJulian Elischer 		return (error);
33786b795970SJulian Elischer 	if (val == 42) {
337930400f03SJulian Elischer 		ng_dumpitems();
338030400f03SJulian Elischer 		ng_dumpnodes();
338130400f03SJulian Elischer 		ng_dumphooks();
3382069154d5SJulian Elischer 	}
33836b795970SJulian Elischer 	return (0);
3384069154d5SJulian Elischer }
3385069154d5SJulian Elischer 
33866b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
33876b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
338830400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3389069154d5SJulian Elischer 
3390069154d5SJulian Elischer /***********************************************************************
3391069154d5SJulian Elischer * Worklist routines
3392069154d5SJulian Elischer **********************************************************************/
3393069154d5SJulian Elischer /*
3394069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3395f2fbb838SAlexander Motin  * try get an item to process off it. Remove the node from the list.
3396069154d5SJulian Elischer  */
3397069154d5SJulian Elischer static void
3398f2fbb838SAlexander Motin ngthread(void *arg)
3399069154d5SJulian Elischer {
3400069154d5SJulian Elischer 	for (;;) {
3401394cb30aSAlexander Motin 		node_p  node;
3402394cb30aSAlexander Motin 
3403394cb30aSAlexander Motin 		/* Get node from the worklist. */
34042c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
3405f2fbb838SAlexander Motin 		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3406f2fbb838SAlexander Motin 			NG_WORKLIST_SLEEP();
34079852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
34082c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3409bc29160dSMarko Zec 		CURVNET_SET(node->nd_vnet);
34102955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
34112955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3412069154d5SJulian Elischer 		/*
3413069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3414069154d5SJulian Elischer 		 * that the list had on it.
3415069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3416069154d5SJulian Elischer 		 * let you have another item off the queue.
3417069154d5SJulian Elischer 		 * All this time, keep the reference
3418069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3419069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3420069154d5SJulian Elischer 		 */
3421069154d5SJulian Elischer 		for (;;) {
3422394cb30aSAlexander Motin 			item_p item;
3423714fb865SGleb Smirnoff 			int rw;
3424714fb865SGleb Smirnoff 
34252c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
34269852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3427069154d5SJulian Elischer 			if (item == NULL) {
34289852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
34292c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3430069154d5SJulian Elischer 				break; /* go look for another node */
3431069154d5SJulian Elischer 			} else {
34322c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
34335951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3434714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
34355951069aSJulian Elischer 				NG_NODE_UNREF(node);
3436069154d5SJulian Elischer 			}
3437069154d5SJulian Elischer 		}
3438a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3439bc29160dSMarko Zec 		CURVNET_RESTORE();
3440069154d5SJulian Elischer 	}
3441069154d5SJulian Elischer }
3442069154d5SJulian Elischer 
344333338e73SJulian Elischer /*
344433338e73SJulian Elischer  * XXX
344533338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
344633338e73SJulian Elischer  * to be outside the mutex zone
344733338e73SJulian Elischer  */
3448069154d5SJulian Elischer static void
3449394cb30aSAlexander Motin ng_worklist_add(node_p node)
3450069154d5SJulian Elischer {
3451f912c0f7SGleb Smirnoff 
34525bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3453f912c0f7SGleb Smirnoff 
34549852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3455069154d5SJulian Elischer 		/*
3456069154d5SJulian Elischer 		 * If we are not already on the work queue,
3457069154d5SJulian Elischer 		 * then put us on.
3458069154d5SJulian Elischer 		 */
34599852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
34604bd1b557SGleb Smirnoff 		NG_NODE_REF(node); /* XXX safe in mutex? */
34612c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
34629852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
34632c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
34642955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
34652955ee18SGleb Smirnoff 		    node->nd_ID, node);
3466f2fbb838SAlexander Motin 		NG_WORKLIST_WAKEUP();
3467394cb30aSAlexander Motin 	} else {
34682955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
34692955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3470394cb30aSAlexander Motin 	}
3471069154d5SJulian Elischer }
3472069154d5SJulian Elischer 
3473069154d5SJulian Elischer /***********************************************************************
3474069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3475069154d5SJulian Elischer ***********************************************************************/
3476069154d5SJulian Elischer 
347730400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
347830400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
34794cf49a43SJulian Elischer 	do {								\
34801acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3481069154d5SJulian Elischer 			printf("item already has node");		\
34823de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
34831acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3484069154d5SJulian Elischer 		}							\
34851acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3486069154d5SJulian Elischer 			printf("item already has hook");		\
34873de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
34881acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3489069154d5SJulian Elischer 		}							\
3490069154d5SJulian Elischer 	} while (0)
3491069154d5SJulian Elischer #else
349230400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3493069154d5SJulian Elischer #endif
3494069154d5SJulian Elischer 
3495069154d5SJulian Elischer /*
34968ed370fdSJulian Elischer  * Put mbuf into the item.
3497069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3498069154d5SJulian Elischer  * (or equivalent)
3499069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3500069154d5SJulian Elischer  * remote node might go away in this timescale.
3501069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3502069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
35034be59335SGleb Smirnoff  * here to be able to do this.
3504069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3505069154d5SJulian Elischer  *
3506069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3507069154d5SJulian Elischer  */
3508069154d5SJulian Elischer item_p
350942282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3510069154d5SJulian Elischer {
3511069154d5SJulian Elischer 	item_p item;
3512069154d5SJulian Elischer 
35136aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3514069154d5SJulian Elischer 		NG_FREE_M(m);
3515069154d5SJulian Elischer 		return (NULL);
3516069154d5SJulian Elischer 	}
351730400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
35186aa6d011SAlexander Motin 	item->el_flags |= NGQF_READER;
3519069154d5SJulian Elischer 	NGI_M(item) = m;
3520069154d5SJulian Elischer 	return (item);
3521069154d5SJulian Elischer }
3522069154d5SJulian Elischer 
3523069154d5SJulian Elischer /*
3524069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3525069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3526069154d5SJulian Elischer  * to work out what some of the fields should be.
3527069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3528069154d5SJulian Elischer  * (or equivalent)
3529069154d5SJulian Elischer  */
3530069154d5SJulian Elischer item_p
353142282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3532069154d5SJulian Elischer {
3533069154d5SJulian Elischer 	item_p item;
3534069154d5SJulian Elischer 
35356aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3536069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3537069154d5SJulian Elischer 		return (NULL);
3538069154d5SJulian Elischer 	}
353930400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
35406f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
35416f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
35426aa6d011SAlexander Motin 		item->el_flags |= NGQF_READER;
35436f683eeeSGleb Smirnoff 	else
35446aa6d011SAlexander Motin 		item->el_flags |= NGQF_WRITER;
3545069154d5SJulian Elischer 	/*
3546069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3547069154d5SJulian Elischer 	 */
3548069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3549facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3550069154d5SJulian Elischer 	return (item);
3551069154d5SJulian Elischer }
3552069154d5SJulian Elischer 
35531acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
35546b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
35556b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3556069154d5SJulian Elischer 			if (retaddr) {					\
3557069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
35584cf49a43SJulian Elischer 			} else {					\
3559069154d5SJulian Elischer 				/*					\
3560069154d5SJulian Elischer 				 * The old return address should be ok.	\
3561069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3562069154d5SJulian Elischer 				 * here.				\
3563069154d5SJulian Elischer 				 */					\
3564069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3565069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3566069154d5SJulian Elischer 						= ng_node2ID(here);	\
3567069154d5SJulian Elischer 				}					\
3568069154d5SJulian Elischer 			}						\
35694cf49a43SJulian Elischer 		}							\
35704cf49a43SJulian Elischer 	} while (0)
35714cf49a43SJulian Elischer 
35724cf49a43SJulian Elischer int
3573069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
35744cf49a43SJulian Elischer {
35751acb27c6SJulian Elischer 	hook_p peer;
35761acb27c6SJulian Elischer 	node_p peernode;
357730400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3578069154d5SJulian Elischer 	/*
3579069154d5SJulian Elischer 	 * Quick sanity check..
358028323addSBryan Drewery 	 * Since a hook holds a reference on its node, once we know
358130400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
358230400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3583069154d5SJulian Elischer 	 */
3584d2fd0788SAlexander V. Chernikov 	TOPOLOGY_RLOCK();
35854bd1b557SGleb Smirnoff 	if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
3586a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3587a04e9846SAlexander Motin 	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3588069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35896b795970SJulian Elischer 		TRAP_ERROR();
3590d2fd0788SAlexander V. Chernikov 		TOPOLOGY_RUNLOCK();
3591e08d3e3cSJulian Elischer 		return (ENETDOWN);
35924cf49a43SJulian Elischer 	}
35934cf49a43SJulian Elischer 
35944cf49a43SJulian Elischer 	/*
3595069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
35964cf49a43SJulian Elischer 	 */
35971acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
35981acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
3599a04e9846SAlexander Motin 	NGI_SET_HOOK(item, peer);
36001acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
36018b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3602a7da736aSGleb Smirnoff 
3603d2fd0788SAlexander V. Chernikov 	TOPOLOGY_RUNLOCK();
3604a7da736aSGleb Smirnoff 
3605069154d5SJulian Elischer 	return (0);
3606069154d5SJulian Elischer }
3607069154d5SJulian Elischer 
36084cf49a43SJulian Elischer int
3609707d2058SMax Khon ng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
36104cf49a43SJulian Elischer {
36114cf49a43SJulian Elischer 	node_p	dest = NULL;
3612069154d5SJulian Elischer 	hook_p	hook = NULL;
36134cf49a43SJulian Elischer 	int	error;
3614069154d5SJulian Elischer 
361530400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3616069154d5SJulian Elischer 	/*
3617069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3618069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3619069154d5SJulian Elischer 	 */
3620069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3621069154d5SJulian Elischer 	if (error) {
3622069154d5SJulian Elischer 		NG_FREE_ITEM(item);
362330400f03SJulian Elischer 		return (error);
3624069154d5SJulian Elischer 	}
36251acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
3626a7da736aSGleb Smirnoff 	if (hook)
36271acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
3628a7da736aSGleb Smirnoff 
36291acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3630069154d5SJulian Elischer 	return (0);
3631069154d5SJulian Elischer }
3632069154d5SJulian Elischer 
3633069154d5SJulian Elischer int
3634069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3635069154d5SJulian Elischer {
3636069154d5SJulian Elischer 	node_p dest;
3637069154d5SJulian Elischer 
363830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3639069154d5SJulian Elischer 	/*
3640069154d5SJulian Elischer 	 * Find the target node.
3641069154d5SJulian Elischer 	 */
3642069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3643069154d5SJulian Elischer 	if (dest == NULL) {
3644069154d5SJulian Elischer 		NG_FREE_ITEM(item);
36456b795970SJulian Elischer 		TRAP_ERROR();
3646069154d5SJulian Elischer 		return(EINVAL);
3647069154d5SJulian Elischer 	}
3648069154d5SJulian Elischer 	/* Fill out the contents */
36491acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
36501acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
36511acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3652069154d5SJulian Elischer 	return (0);
3653069154d5SJulian Elischer }
3654069154d5SJulian Elischer 
3655069154d5SJulian Elischer /*
3656069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3657069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3658069154d5SJulian Elischer  * Useful for removing that hook :-)
3659069154d5SJulian Elischer  */
3660069154d5SJulian Elischer item_p
3661069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3662069154d5SJulian Elischer {
3663069154d5SJulian Elischer 	item_p item;
36644cf49a43SJulian Elischer 
3665859a4d16SJulian Elischer 	/*
3666859a4d16SJulian Elischer 	 * Find the target node.
3667859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3668859a4d16SJulian Elischer 	 * to the address.
3669859a4d16SJulian Elischer 	 */
36706aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3671069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3672069154d5SJulian Elischer 		return (NULL);
36734cf49a43SJulian Elischer 	}
36744cf49a43SJulian Elischer 
36754cf49a43SJulian Elischer 	/* Fill out the contents */
36766aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
367730400f03SJulian Elischer 	NG_NODE_REF(here);
36781acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
36791acb27c6SJulian Elischer 	if (hook) {
368030400f03SJulian Elischer 		NG_HOOK_REF(hook);
36811acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36821acb27c6SJulian Elischer 	}
3683069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3684069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3685069154d5SJulian Elischer 	return (item);
36864cf49a43SJulian Elischer }
36874cf49a43SJulian Elischer 
3688e088dd4cSAlexander Motin /*
3689e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3690e088dd4cSAlexander Motin  */
3691e088dd4cSAlexander Motin 
369242282202SGleb Smirnoff int
3693b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3694b332b91fSGleb Smirnoff {
3695b332b91fSGleb Smirnoff 
3696b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3697b332b91fSGleb Smirnoff }
3698b332b91fSGleb Smirnoff 
3699b332b91fSGleb Smirnoff int
3700aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
370142282202SGleb Smirnoff 	int flags)
37026b795970SJulian Elischer {
37036b795970SJulian Elischer 	item_p item;
37046b795970SJulian Elischer 
37056aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
37066b795970SJulian Elischer 		return (ENOMEM);
37076b795970SJulian Elischer 	}
37086aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3709a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
37101acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
37111acb27c6SJulian Elischer 	if (hook) {
37126b795970SJulian Elischer 		NG_HOOK_REF(hook);
37131acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
37146b795970SJulian Elischer 	}
37156b795970SJulian Elischer 	NGI_FN(item) = fn;
37166b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
37176b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
371842282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
37196b795970SJulian Elischer }
37206b795970SJulian Elischer 
37214cf49a43SJulian Elischer /*
3722b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3723b332b91fSGleb Smirnoff  *
3724b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3725b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3726b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3727b332b91fSGleb Smirnoff  * be used.
3728e088dd4cSAlexander Motin  */
3729e088dd4cSAlexander Motin int
3730b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3731b332b91fSGleb Smirnoff 	int arg2, int flags)
3732e088dd4cSAlexander Motin {
3733e088dd4cSAlexander Motin 	item_p item;
3734e088dd4cSAlexander Motin 
3735b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3736b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3737e088dd4cSAlexander Motin 
3738e088dd4cSAlexander Motin 	/*
3739b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3740b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3741e088dd4cSAlexander Motin 	 */
3742b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
37436aa6d011SAlexander Motin 		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3744e088dd4cSAlexander Motin 			return (ENOMEM);
37456aa6d011SAlexander Motin 		if (pitem != NULL)
37466aa6d011SAlexander Motin 			item->apply = pitem->apply;
3747ed75521fSAlexander Motin 	} else {
37486aa6d011SAlexander Motin 		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
37496aa6d011SAlexander Motin 			return (ENOMEM);
3750ed75521fSAlexander Motin 	}
3751b332b91fSGleb Smirnoff 
37526aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3753e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3754e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3755e088dd4cSAlexander Motin 	if (hook) {
3756e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3757e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3758e088dd4cSAlexander Motin 	}
3759e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3760e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3761e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3762e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3763e088dd4cSAlexander Motin }
3764e088dd4cSAlexander Motin 
3765e088dd4cSAlexander Motin /*
3766d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3767d2ca21a9SJulian Elischer  */
3768d2ca21a9SJulian Elischer static void
37691fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3770d2ca21a9SJulian Elischer {
3771d2ca21a9SJulian Elischer 	item_p item = arg;
3772d2ca21a9SJulian Elischer 
3773bc29160dSMarko Zec 	CURVNET_SET(NGI_NODE(item)->nd_vnet);
3774d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3775bc29160dSMarko Zec 	CURVNET_RESTORE();
3776d2ca21a9SJulian Elischer }
3777d2ca21a9SJulian Elischer 
377830bef41bSGleb Smirnoff int
3779f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3780d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3781d2ca21a9SJulian Elischer {
37821bf8e0faSGleb Smirnoff 	item_p item, oitem;
3783d2ca21a9SJulian Elischer 
37846aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
378530bef41bSGleb Smirnoff 		return (ENOMEM);
378630bef41bSGleb Smirnoff 
37876aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3788d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3789d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3790d2ca21a9SJulian Elischer 	if (hook) {
3791d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3792d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3793d2ca21a9SJulian Elischer 	}
3794d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3795d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3796d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
37971bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
37981bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
37991bf8e0faSGleb Smirnoff 	    oitem != NULL)
38001bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
380130bef41bSGleb Smirnoff 	return (0);
3802d2ca21a9SJulian Elischer }
3803d2ca21a9SJulian Elischer 
3804d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3805d2ca21a9SJulian Elischer int
3806f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3807d2ca21a9SJulian Elischer {
3808d2ca21a9SJulian Elischer 	item_p item;
380930bef41bSGleb Smirnoff 	int rval;
3810d2ca21a9SJulian Elischer 
381103b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
381203b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
381303b25f5dSGleb Smirnoff 
38143eadb26dSGleb Smirnoff 	rval = callout_stop(c);
381530bef41bSGleb Smirnoff 	item = c->c_arg;
381630bef41bSGleb Smirnoff 	/* Do an extra check */
38171fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
38182f632dbbSSean Bruno 	    (item != NULL) && (NGI_NODE(item) == node)) {
3819d2ca21a9SJulian Elischer 		/*
3820d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3821d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3822d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3823d2ca21a9SJulian Elischer 		 */
3824d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3825d2ca21a9SJulian Elischer 	}
38261bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
382730bef41bSGleb Smirnoff 
38280e493ed3SHans Petter Selasky 	/*
38290e493ed3SHans Petter Selasky 	 * Callers only want to know if the callout was cancelled and
38300e493ed3SHans Petter Selasky 	 * not draining or stopped.
38310e493ed3SHans Petter Selasky 	 */
38320e493ed3SHans Petter Selasky 	return (rval > 0);
3833d2ca21a9SJulian Elischer }
3834d2ca21a9SJulian Elischer 
3835d2ca21a9SJulian Elischer /*
3836069154d5SJulian Elischer  * Set the address, if none given, give the node here.
38374cf49a43SJulian Elischer  */
3838069154d5SJulian Elischer void
3839069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
38404cf49a43SJulian Elischer {
3841069154d5SJulian Elischer 	if (retaddr) {
3842069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3843069154d5SJulian Elischer 	} else {
3844069154d5SJulian Elischer 		/*
3845069154d5SJulian Elischer 		 * The old return address should be ok.
3846069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3847069154d5SJulian Elischer 		 */
3848069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3849069154d5SJulian Elischer 	}
3850069154d5SJulian Elischer }
3851