xref: /freebsd/sys/netgraph/ng_base.c (revision 74c9119d4a985d841285abd262c5f3d06e7c5e18)
14cf49a43SJulian Elischer /*
24cf49a43SJulian Elischer  * ng_base.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
64cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
74cf49a43SJulian Elischer  * All rights reserved.
84cf49a43SJulian Elischer  *
94cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
104cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
114cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
124cf49a43SJulian Elischer  * provided, however, that:
134cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
144cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
154cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
164cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
174cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
184cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
194cf49a43SJulian Elischer  *
204cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
214cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
224cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
234cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
244cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
254cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
264cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
274cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
284cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
294cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
304cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
314cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
324cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
334cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
344cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
354cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
364cf49a43SJulian Elischer  * OF SUCH DAMAGE.
374cf49a43SJulian Elischer  *
38cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
39cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
404cf49a43SJulian Elischer  *
414cf49a43SJulian Elischer  * $FreeBSD$
424cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
434cf49a43SJulian Elischer  */
444cf49a43SJulian Elischer 
454cf49a43SJulian Elischer /*
464cf49a43SJulian Elischer  * This file implements the base netgraph code.
474cf49a43SJulian Elischer  */
484cf49a43SJulian Elischer 
494cf49a43SJulian Elischer #include <sys/param.h>
508e853a2cSGleb Smirnoff #include <sys/systm.h>
5177a58296SGleb Smirnoff #include <sys/ctype.h>
524cf49a43SJulian Elischer #include <sys/errno.h>
53f33ca0c9SMarcel Moolenaar #include <sys/kdb.h>
544cf49a43SJulian Elischer #include <sys/kernel.h>
553b33fbe7SGleb Smirnoff #include <sys/ktr.h>
56104a9b7eSAlexander Kabaev #include <sys/limits.h>
574cf49a43SJulian Elischer #include <sys/malloc.h>
584cf49a43SJulian Elischer #include <sys/mbuf.h>
5977a58296SGleb Smirnoff #include <sys/queue.h>
60bfa7e882SJulian Elischer #include <sys/sysctl.h>
6177a58296SGleb Smirnoff #include <sys/syslog.h>
62e088dd4cSAlexander Motin #include <sys/refcount.h>
6381a253a4SAlexander Motin #include <sys/proc.h>
64603724d3SBjoern A. Zeeb #include <sys/vimage.h>
65394cb30aSAlexander Motin #include <machine/cpu.h>
664cf49a43SJulian Elischer 
674cf49a43SJulian Elischer #include <net/netisr.h>
684cf49a43SJulian Elischer 
694cf49a43SJulian Elischer #include <netgraph/ng_message.h>
704cf49a43SJulian Elischer #include <netgraph/netgraph.h>
71f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
724cf49a43SJulian Elischer 
739d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION);
7499ff8176SPeter Wemm 
75ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */
76ac5dd141SGleb Smirnoff static struct mtx	ng_topo_mtx;
77ac5dd141SGleb Smirnoff 
7830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
79cfea3f85SAlexander Motin static struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
801489164fSGleb Smirnoff static struct mtx	ngq_mtx;	/* protects the queue item list */
8130400f03SJulian Elischer 
8230400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
8330400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8430400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
8530400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8630400f03SJulian Elischer 
8730400f03SJulian Elischer static void ng_dumpitems(void);
8830400f03SJulian Elischer static void ng_dumpnodes(void);
8930400f03SJulian Elischer static void ng_dumphooks(void);
9030400f03SJulian Elischer 
9130400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
92954c4772SJulian Elischer /*
93954c4772SJulian Elischer  * DEAD versions of the structures.
94954c4772SJulian Elischer  * In order to avoid races, it is sometimes neccesary to point
95954c4772SJulian Elischer  * at SOMETHING even though theoretically, the current entity is
96954c4772SJulian Elischer  * INVALID. Use these to avoid these races.
97954c4772SJulian Elischer  */
98954c4772SJulian Elischer struct ng_type ng_deadtype = {
99954c4772SJulian Elischer 	NG_ABI_VERSION,
100954c4772SJulian Elischer 	"dead",
101954c4772SJulian Elischer 	NULL,	/* modevent */
102954c4772SJulian Elischer 	NULL,	/* constructor */
103954c4772SJulian Elischer 	NULL,	/* rcvmsg */
104954c4772SJulian Elischer 	NULL,	/* shutdown */
105954c4772SJulian Elischer 	NULL,	/* newhook */
106954c4772SJulian Elischer 	NULL,	/* findhook */
107954c4772SJulian Elischer 	NULL,	/* connect */
108954c4772SJulian Elischer 	NULL,	/* rcvdata */
109954c4772SJulian Elischer 	NULL,	/* disconnect */
110954c4772SJulian Elischer 	NULL, 	/* cmdlist */
111954c4772SJulian Elischer };
11230400f03SJulian Elischer 
113954c4772SJulian Elischer struct ng_node ng_deadnode = {
114954c4772SJulian Elischer 	"dead",
115954c4772SJulian Elischer 	&ng_deadtype,
116be4252b3SJulian Elischer 	NGF_INVALID,
117954c4772SJulian Elischer 	0,	/* numhooks */
118954c4772SJulian Elischer 	NULL,	/* private */
119954c4772SJulian Elischer 	0,	/* ID */
120954c4772SJulian Elischer 	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
121954c4772SJulian Elischer 	{},	/* all_nodes list entry */
122954c4772SJulian Elischer 	{},	/* id hashtable list entry */
123954c4772SJulian Elischer 	{	0,
1249852972bSAlexander Motin 		0,
125954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
1269852972bSAlexander Motin 		{}, /* workqueue entry */
1279852972bSAlexander Motin 		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
128954c4772SJulian Elischer 	},
1299852972bSAlexander Motin 	1,	/* refs */
130954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
131954c4772SJulian Elischer 	ND_MAGIC,
132954c4772SJulian Elischer 	__FILE__,
133954c4772SJulian Elischer 	__LINE__,
134954c4772SJulian Elischer 	{NULL}
135954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
136954c4772SJulian Elischer };
137954c4772SJulian Elischer 
138954c4772SJulian Elischer struct ng_hook ng_deadhook = {
139954c4772SJulian Elischer 	"dead",
140954c4772SJulian Elischer 	NULL,		/* private */
141954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
142e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
143954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
144954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
145954c4772SJulian Elischer 	{},		/* hooks list */
146c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
147c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1489852972bSAlexander Motin 	1,		/* refs always >= 1 */
149954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
150954c4772SJulian Elischer 	HK_MAGIC,
151954c4772SJulian Elischer 	__FILE__,
152954c4772SJulian Elischer 	__LINE__,
153954c4772SJulian Elischer 	{NULL}
154954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
155954c4772SJulian Elischer };
156954c4772SJulian Elischer 
157954c4772SJulian Elischer /*
158954c4772SJulian Elischer  * END DEAD STRUCTURES
159954c4772SJulian Elischer  */
160069154d5SJulian Elischer /* List nodes with unallocated work */
1619852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
162b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1634cf49a43SJulian Elischer 
1644cf49a43SJulian Elischer /* List of installed types */
165069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
166069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
1674cf49a43SJulian Elischer 
168069154d5SJulian Elischer /* Hash related definitions */
1690f150d04SJulian Elischer /* XXX Don't need to initialise them because it's a LIST */
170cfea3f85SAlexander Motin #define NG_ID_HASH_SIZE 128 /* most systems wont need even this many */
1710f150d04SJulian Elischer static LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
172069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
1730f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
1740f150d04SJulian Elischer #define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
1750f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
1760f150d04SJulian Elischer 	do { 								\
17753f9c5e9SRobert Watson 		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
178603724d3SBjoern A. Zeeb 		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
1790f150d04SJulian Elischer 						nd_idnodes) {		\
1800f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
1810f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
1820f150d04SJulian Elischer 				break;					\
1830f150d04SJulian Elischer 			}						\
1840f150d04SJulian Elischer 		}							\
1850f150d04SJulian Elischer 	} while (0)
186069154d5SJulian Elischer 
187cfea3f85SAlexander Motin #define NG_NAME_HASH_SIZE 128 /* most systems wont need even this many */
188cfea3f85SAlexander Motin static LIST_HEAD(, ng_node) ng_name_hash[NG_NAME_HASH_SIZE];
189cfea3f85SAlexander Motin static struct mtx	ng_namehash_mtx;
190cfea3f85SAlexander Motin #define NG_NAMEHASH(NAME, HASH)				\
191cfea3f85SAlexander Motin 	do {						\
192cfea3f85SAlexander Motin 		u_char	h = 0;				\
193cfea3f85SAlexander Motin 		const u_char	*c;			\
194cfea3f85SAlexander Motin 		for (c = (const u_char*)(NAME); *c; c++)\
195cfea3f85SAlexander Motin 			h += *c;			\
196cfea3f85SAlexander Motin 		(HASH) = h % (NG_NAME_HASH_SIZE);	\
197cfea3f85SAlexander Motin 	} while (0)
198cfea3f85SAlexander Motin 
199dc90cad9SJulian Elischer 
2004cf49a43SJulian Elischer /* Internal functions */
2014cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
202069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
203dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2044cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
205394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
2064cf49a43SJulian Elischer static void	ngintr(void);
20727757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2089852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
209069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
210e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
211e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
212e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
213e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
2146b795970SJulian Elischer static int	ng_mkpeer(node_p node, const char *name,
2156b795970SJulian Elischer 						const char *name2, char *type);
216069154d5SJulian Elischer 
2174c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */
218069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
219069154d5SJulian Elischer node_p	ng_name2noderef(node_p node, const char *name);
220069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
221069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
222069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
223069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
2241acb27c6SJulian Elischer void	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
22530400f03SJulian Elischer void	ng_unname(node_p node);
226069154d5SJulian Elischer 
2274cf49a43SJulian Elischer 
2284cf49a43SJulian Elischer /* Our own netgraph malloc type */
2294cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
230069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
231069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
232069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
233069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
234069154d5SJulian Elischer 
235069154d5SJulian Elischer /* Should not be visible outside this file */
23630400f03SJulian Elischer 
23730400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
23830400f03SJulian Elischer 	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
23930400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
24030400f03SJulian Elischer 	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
24130400f03SJulian Elischer 
2422c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2434abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2442c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2454abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2462c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2474abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2482c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2494abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2502c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2514abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2522c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2534abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
2542c8dda8dSWojciech A. Koszek 
25530400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
25630400f03SJulian Elischer /*
25730400f03SJulian Elischer  * In debug mode:
25830400f03SJulian Elischer  * In an attempt to help track reference count screwups
25930400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
26030400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
26130400f03SJulian Elischer  * after they have been freed.
26230400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
26330400f03SJulian Elischer  */
26430400f03SJulian Elischer static __inline hook_p
26530400f03SJulian Elischer ng_alloc_hook(void)
26630400f03SJulian Elischer {
26730400f03SJulian Elischer 	hook_p hook;
26830400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2699ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
27030400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
27130400f03SJulian Elischer 	if (hook) {
27230400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
27330400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
27430400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
27530400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
2769ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
27730400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
27830400f03SJulian Elischer 	} else {
2799ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
28030400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
28130400f03SJulian Elischer 		if (hook) {
28230400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
2839ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
28430400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
2859ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
28630400f03SJulian Elischer 		}
28730400f03SJulian Elischer 	}
28830400f03SJulian Elischer 	return (hook);
28930400f03SJulian Elischer }
29030400f03SJulian Elischer 
29130400f03SJulian Elischer static __inline node_p
29230400f03SJulian Elischer ng_alloc_node(void)
29330400f03SJulian Elischer {
29430400f03SJulian Elischer 	node_p node;
29530400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
2969ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
29730400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
29830400f03SJulian Elischer 	if (node) {
29930400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
30030400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
30130400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
30230400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3039ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30430400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
30530400f03SJulian Elischer 	} else {
3069ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30730400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
30830400f03SJulian Elischer 		if (node) {
30930400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3109ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
31130400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3129ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
31330400f03SJulian Elischer 		}
31430400f03SJulian Elischer 	}
31530400f03SJulian Elischer 	return (node);
31630400f03SJulian Elischer }
31730400f03SJulian Elischer 
31830400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
31930400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
32030400f03SJulian Elischer 
32130400f03SJulian Elischer 
32230400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
32330400f03SJulian Elischer 	do {								\
3249ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
32530400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
32630400f03SJulian Elischer 		hook->hk_magic = 0;					\
3279ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
32830400f03SJulian Elischer 	} while (0)
32930400f03SJulian Elischer 
33030400f03SJulian Elischer #define NG_FREE_NODE(node)						\
33130400f03SJulian Elischer 	do {								\
3329ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
33330400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
33430400f03SJulian Elischer 		node->nd_magic = 0;					\
3359ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
33630400f03SJulian Elischer 	} while (0)
33730400f03SJulian Elischer 
33830400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
33930400f03SJulian Elischer 
34030400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
34130400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
34230400f03SJulian Elischer 
343069154d5SJulian Elischer #define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
344069154d5SJulian Elischer #define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
34530400f03SJulian Elischer 
34630400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
34730400f03SJulian Elischer 
348f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3494cf49a43SJulian Elischer #ifndef TRAP_ERROR
3506b795970SJulian Elischer #define TRAP_ERROR()
3514cf49a43SJulian Elischer #endif
3524cf49a43SJulian Elischer 
353dc90cad9SJulian Elischer static	ng_ID_t nextID = 1;
354dc90cad9SJulian Elischer 
355b2da83c2SArchie Cobbs #ifdef INVARIANTS
356b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
357b2da83c2SArchie Cobbs 		struct mbuf *n;						\
358b2da83c2SArchie Cobbs 		int total;						\
359b2da83c2SArchie Cobbs 									\
360fe584538SDag-Erling Smørgrav 		M_ASSERTPKTHDR(m);					\
361b32cfb32SGleb Smirnoff 		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
362b2da83c2SArchie Cobbs 			total += n->m_len;				\
363b32cfb32SGleb Smirnoff 			if (n->m_nextpkt != NULL)			\
364b32cfb32SGleb Smirnoff 				panic("%s: m_nextpkt", __func__);	\
365b32cfb32SGleb Smirnoff 		}							\
366ba5b359aSGleb Smirnoff 									\
367b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
368b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
3696e551fb6SDavid E. O'Brien 			    __func__, (m)->m_pkthdr.len, total);	\
370b2da83c2SArchie Cobbs 		}							\
371b2da83c2SArchie Cobbs 	} while (0)
372b2da83c2SArchie Cobbs #else
373b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
374b2da83c2SArchie Cobbs #endif
375b2da83c2SArchie Cobbs 
376e088dd4cSAlexander Motin #define ERROUT(x)	do { error = (x); goto done; } while (0)
377dc90cad9SJulian Elischer 
3784cf49a43SJulian Elischer /************************************************************************
379f8307e12SArchie Cobbs 	Parse type definitions for generic messages
380f8307e12SArchie Cobbs ************************************************************************/
381f8307e12SArchie Cobbs 
382f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
383f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
384f0184ff8SArchie Cobbs static const struct ng_parse_struct_field				\
385f0184ff8SArchie Cobbs 	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
386f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
387f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
388f0184ff8SArchie Cobbs 	&ng_ ## lo ## _type_fields					\
389f8307e12SArchie Cobbs }
390f8307e12SArchie Cobbs 
391f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
392f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
393f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
394f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
395f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
396f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
397f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
398f8307e12SArchie Cobbs 
399f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
400d7d97eb0SJeroen Ruigrok van der Werven    value immediately preceding the array -- as with struct namelist
401f8307e12SArchie Cobbs    and struct typelist. */
402f8307e12SArchie Cobbs static int
403f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
404f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
405f8307e12SArchie Cobbs {
406f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
407f8307e12SArchie Cobbs }
408f8307e12SArchie Cobbs 
409f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
410f8307e12SArchie Cobbs static int
411f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
412f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
413f8307e12SArchie Cobbs {
414f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
415f8307e12SArchie Cobbs 
416f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
417f8307e12SArchie Cobbs }
418f8307e12SArchie Cobbs 
419f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
420f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
421f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
422f8307e12SArchie Cobbs 	&ng_generic_list_getLength
423f8307e12SArchie Cobbs };
424f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
425f8307e12SArchie Cobbs 	&ng_parse_array_type,
426f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
427f8307e12SArchie Cobbs };
428f8307e12SArchie Cobbs 
429f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
430f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
431f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
432f8307e12SArchie Cobbs 	&ng_generic_list_getLength
433f8307e12SArchie Cobbs };
434f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
435f8307e12SArchie Cobbs 	&ng_parse_array_type,
436f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
437f8307e12SArchie Cobbs };
438f8307e12SArchie Cobbs 
439f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
440f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
441f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
442f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
443f8307e12SArchie Cobbs };
444f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
445f8307e12SArchie Cobbs 	&ng_parse_array_type,
446f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
447f8307e12SArchie Cobbs };
448f8307e12SArchie Cobbs 
449f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
450f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
451f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
452f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
453f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
454f8307e12SArchie Cobbs 
455f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
456f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
457f8307e12SArchie Cobbs 	{
458f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
459f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
460f8307e12SArchie Cobbs 	  "shutdown",
461f8307e12SArchie Cobbs 	  NULL,
462f8307e12SArchie Cobbs 	  NULL
463f8307e12SArchie Cobbs 	},
464f8307e12SArchie Cobbs 	{
465f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
466f8307e12SArchie Cobbs 	  NGM_MKPEER,
467f8307e12SArchie Cobbs 	  "mkpeer",
468f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
469f8307e12SArchie Cobbs 	  NULL
470f8307e12SArchie Cobbs 	},
471f8307e12SArchie Cobbs 	{
472f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
473f8307e12SArchie Cobbs 	  NGM_CONNECT,
474f8307e12SArchie Cobbs 	  "connect",
475f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
476f8307e12SArchie Cobbs 	  NULL
477f8307e12SArchie Cobbs 	},
478f8307e12SArchie Cobbs 	{
479f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
480f8307e12SArchie Cobbs 	  NGM_NAME,
481f8307e12SArchie Cobbs 	  "name",
482f8307e12SArchie Cobbs 	  &ng_generic_name_type,
483f8307e12SArchie Cobbs 	  NULL
484f8307e12SArchie Cobbs 	},
485f8307e12SArchie Cobbs 	{
486f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
487f8307e12SArchie Cobbs 	  NGM_RMHOOK,
488f8307e12SArchie Cobbs 	  "rmhook",
489f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
490f8307e12SArchie Cobbs 	  NULL
491f8307e12SArchie Cobbs 	},
492f8307e12SArchie Cobbs 	{
493f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
494f8307e12SArchie Cobbs 	  NGM_NODEINFO,
495f8307e12SArchie Cobbs 	  "nodeinfo",
496f8307e12SArchie Cobbs 	  NULL,
497f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
498f8307e12SArchie Cobbs 	},
499f8307e12SArchie Cobbs 	{
500f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
501f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
502f8307e12SArchie Cobbs 	  "listhooks",
503f8307e12SArchie Cobbs 	  NULL,
504f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
505f8307e12SArchie Cobbs 	},
506f8307e12SArchie Cobbs 	{
507f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
508f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
509f8307e12SArchie Cobbs 	  "listnames",
510f8307e12SArchie Cobbs 	  NULL,
511f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
512f8307e12SArchie Cobbs 	},
513f8307e12SArchie Cobbs 	{
514f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
515f8307e12SArchie Cobbs 	  NGM_LISTNODES,
516f8307e12SArchie Cobbs 	  "listnodes",
517f8307e12SArchie Cobbs 	  NULL,
518f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
519f8307e12SArchie Cobbs 	},
520f8307e12SArchie Cobbs 	{
521f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
522f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
523f8307e12SArchie Cobbs 	  "listtypes",
524f8307e12SArchie Cobbs 	  NULL,
525f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
526f8307e12SArchie Cobbs 	},
527f8307e12SArchie Cobbs 	{
528f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
5297095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
5307095e097SPoul-Henning Kamp 	  "textconfig",
5317095e097SPoul-Henning Kamp 	  NULL,
5327095e097SPoul-Henning Kamp 	  &ng_parse_string_type
5337095e097SPoul-Henning Kamp 	},
5347095e097SPoul-Henning Kamp 	{
5357095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
536f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
537f8307e12SArchie Cobbs 	  "textstatus",
538f8307e12SArchie Cobbs 	  NULL,
539f8307e12SArchie Cobbs 	  &ng_parse_string_type
540f8307e12SArchie Cobbs 	},
541f8307e12SArchie Cobbs 	{
542f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
543f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
544f8307e12SArchie Cobbs 	  "ascii2binary",
545f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
546f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
547f8307e12SArchie Cobbs 	},
548f8307e12SArchie Cobbs 	{
549f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
550f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
551f8307e12SArchie Cobbs 	  "binary2ascii",
552f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
553f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
554f8307e12SArchie Cobbs 	},
555f8307e12SArchie Cobbs 	{ 0 }
556f8307e12SArchie Cobbs };
557f8307e12SArchie Cobbs 
558f8307e12SArchie Cobbs /************************************************************************
5594cf49a43SJulian Elischer 			Node routines
5604cf49a43SJulian Elischer ************************************************************************/
5614cf49a43SJulian Elischer 
5624cf49a43SJulian Elischer /*
5634cf49a43SJulian Elischer  * Instantiate a node of the requested type
5644cf49a43SJulian Elischer  */
5654cf49a43SJulian Elischer int
5664cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
5674cf49a43SJulian Elischer {
5684cf49a43SJulian Elischer 	struct ng_type *type;
569069154d5SJulian Elischer 	int	error;
5704cf49a43SJulian Elischer 
5714cf49a43SJulian Elischer 	/* Check that the type makes sense */
5724cf49a43SJulian Elischer 	if (typename == NULL) {
5736b795970SJulian Elischer 		TRAP_ERROR();
5744cf49a43SJulian Elischer 		return (EINVAL);
5754cf49a43SJulian Elischer 	}
5764cf49a43SJulian Elischer 
5777610f574SGleb Smirnoff 	/* Locate the node type. If we fail we return. Do not try to load
5787610f574SGleb Smirnoff 	 * module.
5797610f574SGleb Smirnoff 	 */
5804cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL)
5814cf49a43SJulian Elischer 		return (ENXIO);
5824cf49a43SJulian Elischer 
583069154d5SJulian Elischer 	/*
584069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
585069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
586069154d5SJulian Elischer 	 */
587069154d5SJulian Elischer 	if (type->constructor != NULL) {
588069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
589069154d5SJulian Elischer 			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
59030400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
591069154d5SJulian Elischer 			}
592069154d5SJulian Elischer 		}
593069154d5SJulian Elischer 	} else {
594069154d5SJulian Elischer 		/*
595069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
59664efc707SRobert Watson 		 * to be made. It must be brought into existence by
597954c4772SJulian Elischer 		 * some external agency. The external agency should
598069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
599069154d5SJulian Elischer 		 * netgraph part initialised.
600069154d5SJulian Elischer 		 */
6016b795970SJulian Elischer 		TRAP_ERROR();
602069154d5SJulian Elischer 		error = EINVAL;
603069154d5SJulian Elischer 	}
604069154d5SJulian Elischer 	return (error);
6054cf49a43SJulian Elischer }
6064cf49a43SJulian Elischer 
6074cf49a43SJulian Elischer /*
608069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
609069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
6104cf49a43SJulian Elischer  * The returned node has a reference count of 1.
6114cf49a43SJulian Elischer  */
6124cf49a43SJulian Elischer int
6134cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
6144cf49a43SJulian Elischer {
6154cf49a43SJulian Elischer 	node_p node;
6164cf49a43SJulian Elischer 
6174cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6184cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6196b795970SJulian Elischer 		TRAP_ERROR();
6204cf49a43SJulian Elischer 		return (EINVAL);
6214cf49a43SJulian Elischer 	}
6224cf49a43SJulian Elischer 
6234cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
62430400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6254cf49a43SJulian Elischer 	if (node == NULL) {
6266b795970SJulian Elischer 		TRAP_ERROR();
6274cf49a43SJulian Elischer 		return (ENOMEM);
6284cf49a43SJulian Elischer 	}
62930400f03SJulian Elischer 	node->nd_type = type;
63030400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6314cf49a43SJulian Elischer 	type->refs++;
6324cf49a43SJulian Elischer 
6332c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6349852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
63530400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6364cf49a43SJulian Elischer 
6374cf49a43SJulian Elischer 	/* Initialize hook list for new node */
63830400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6394cf49a43SJulian Elischer 
640cfea3f85SAlexander Motin 	/* Link us into the name hash. */
641cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
642603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[0], node, nd_nodes);
643cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
644069154d5SJulian Elischer 
645dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
6469ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
64730400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
648069154d5SJulian Elischer 		node_p node2 = NULL;
649ac957cd2SJulian Elischer 		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6500f150d04SJulian Elischer 
65130400f03SJulian Elischer 		/* Is there a problem with the new number? */
6520f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6530f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
65430400f03SJulian Elischer 			break;
655069154d5SJulian Elischer 		}
65630400f03SJulian Elischer 	}
657603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
65830400f03SJulian Elischer 							node, nd_idnodes);
6599ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
660dc90cad9SJulian Elischer 
6614cf49a43SJulian Elischer 	/* Done */
6624cf49a43SJulian Elischer 	*nodepp = node;
6634cf49a43SJulian Elischer 	return (0);
6644cf49a43SJulian Elischer }
6654cf49a43SJulian Elischer 
6664cf49a43SJulian Elischer /*
6674cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
66864efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6694cf49a43SJulian Elischer  * no type-specific method.
6704cf49a43SJulian Elischer  *
67164efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6723e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6733e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
674069154d5SJulian Elischer  *
675069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
67664efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
6773e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
6783e4084c8SJulian Elischer  * in which case we allow the node to survive.
6793e4084c8SJulian Elischer  *
68064efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
6813e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
6823e4084c8SJulian Elischer  * are rebooting.... etc.
6834cf49a43SJulian Elischer  */
6844cf49a43SJulian Elischer void
6851acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
6864cf49a43SJulian Elischer {
6873e4084c8SJulian Elischer 	hook_p hook;
6883e4084c8SJulian Elischer 
6894cf49a43SJulian Elischer 	/* Check if it's already shutting down */
690be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
6914cf49a43SJulian Elischer 		return;
6924cf49a43SJulian Elischer 
6931acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
6941acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
6951acb27c6SJulian Elischer 		return;
6961acb27c6SJulian Elischer 	}
6971acb27c6SJulian Elischer 
6984cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
69930400f03SJulian Elischer 	NG_NODE_REF(node);
7004cf49a43SJulian Elischer 
70130400f03SJulian Elischer 	/*
70230400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
70330400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
704be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
70530400f03SJulian Elischer 	 * creation
70630400f03SJulian Elischer 	 */
707be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7084cf49a43SJulian Elischer 
709991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
710991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
711991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
712991fc65aSJulian Elischer 
7133e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7143e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7153e4084c8SJulian Elischer 		ng_destroy_hook(hook);
71630400f03SJulian Elischer 
717069154d5SJulian Elischer 	/*
718069154d5SJulian Elischer 	 * Drain the input queue forceably.
71930400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
72030400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
72130400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
722b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
723069154d5SJulian Elischer 	 */
7249852972bSAlexander Motin 	ng_flush_input_queue(node);
725069154d5SJulian Elischer 
726069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
72730400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
72830400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
72930400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
73030400f03SJulian Elischer 			/*
73130400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
73230400f03SJulian Elischer 			 * that it doesn't want to die.
73330400f03SJulian Elischer 			 * Presumably it is a persistant node.
7341acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7351acb27c6SJulian Elischer 			 *  e.g. hardware going away,
736be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
73730400f03SJulian Elischer 			 */
738be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7391acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
74030400f03SJulian Elischer 			return;
7414cf49a43SJulian Elischer 		}
7421acb27c6SJulian Elischer 	} else {				/* do the default thing */
7431acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7441acb27c6SJulian Elischer 	}
7454cf49a43SJulian Elischer 
74630400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
74730400f03SJulian Elischer 
74830400f03SJulian Elischer 	/*
74930400f03SJulian Elischer 	 * Remove extra reference, possibly the last
75030400f03SJulian Elischer 	 * Possible other holders of references may include
75130400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
75230400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
75330400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
75430400f03SJulian Elischer 	 */
75530400f03SJulian Elischer 	NG_NODE_UNREF(node);
7564cf49a43SJulian Elischer }
7574cf49a43SJulian Elischer 
7585951069aSJulian Elischer /*
7595951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7605951069aSJulian Elischer  * deadnode always acts as it it were the last.
7615951069aSJulian Elischer  */
7625951069aSJulian Elischer int
76330400f03SJulian Elischer ng_unref_node(node_p node)
7644cf49a43SJulian Elischer {
76530400f03SJulian Elischer 	int v;
7666b795970SJulian Elischer 
7676b795970SJulian Elischer 	if (node == &ng_deadnode) {
7685951069aSJulian Elischer 		return (0);
7696b795970SJulian Elischer 	}
7706b795970SJulian Elischer 
771018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&node->nd_refs, -1);
772e8a49db2SJulian Elischer 
773018fe3d1SAlexander Motin 	if (v == 1) { /* we were the last */
774069154d5SJulian Elischer 
775cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
77630400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
77730400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
778cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
779069154d5SJulian Elischer 
7809ed346baSBosko Milekic 		mtx_lock(&ng_idhash_mtx);
78130400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
7829ed346baSBosko Milekic 		mtx_unlock(&ng_idhash_mtx);
783069154d5SJulian Elischer 
78412574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
785069154d5SJulian Elischer 		NG_FREE_NODE(node);
7864cf49a43SJulian Elischer 	}
787018fe3d1SAlexander Motin 	return (v - 1);
7884cf49a43SJulian Elischer }
7894cf49a43SJulian Elischer 
7904cf49a43SJulian Elischer /************************************************************************
791dc90cad9SJulian Elischer 			Node ID handling
792dc90cad9SJulian Elischer ************************************************************************/
793dc90cad9SJulian Elischer static node_p
794069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
795dc90cad9SJulian Elischer {
79630400f03SJulian Elischer 	node_p node;
7979ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
7980f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
79930400f03SJulian Elischer 	if(node)
80030400f03SJulian Elischer 		NG_NODE_REF(node);
8019ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
80230400f03SJulian Elischer 	return(node);
803dc90cad9SJulian Elischer }
804dc90cad9SJulian Elischer 
805dc90cad9SJulian Elischer ng_ID_t
806dc90cad9SJulian Elischer ng_node2ID(node_p node)
807dc90cad9SJulian Elischer {
80870de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
809dc90cad9SJulian Elischer }
810dc90cad9SJulian Elischer 
811dc90cad9SJulian Elischer /************************************************************************
8124cf49a43SJulian Elischer 			Node name handling
8134cf49a43SJulian Elischer ************************************************************************/
8144cf49a43SJulian Elischer 
8154cf49a43SJulian Elischer /*
8164cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
8174cf49a43SJulian Elischer  */
8184cf49a43SJulian Elischer int
8194cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8204cf49a43SJulian Elischer {
821cfea3f85SAlexander Motin 	int i, hash;
822069154d5SJulian Elischer 	node_p node2;
8234cf49a43SJulian Elischer 
8244cf49a43SJulian Elischer 	/* Check the name is valid */
82587e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8264cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8274cf49a43SJulian Elischer 			break;
8284cf49a43SJulian Elischer 	}
8294cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8306b795970SJulian Elischer 		TRAP_ERROR();
8314cf49a43SJulian Elischer 		return (EINVAL);
8324cf49a43SJulian Elischer 	}
833dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8346b795970SJulian Elischer 		TRAP_ERROR();
8354cf49a43SJulian Elischer 		return (EINVAL);
8364cf49a43SJulian Elischer 	}
8374cf49a43SJulian Elischer 
8384cf49a43SJulian Elischer 	/* Check the name isn't already being used */
839069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
84030400f03SJulian Elischer 		NG_NODE_UNREF(node2);
8416b795970SJulian Elischer 		TRAP_ERROR();
8424cf49a43SJulian Elischer 		return (EADDRINUSE);
8434cf49a43SJulian Elischer 	}
8444cf49a43SJulian Elischer 
845069154d5SJulian Elischer 	/* copy it */
84687e2c66aSHartmut Brandt 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8474cf49a43SJulian Elischer 
848cfea3f85SAlexander Motin 	/* Update name hash. */
849cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
850cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
851cfea3f85SAlexander Motin 	LIST_REMOVE(node, nd_nodes);
852603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
853cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
854cfea3f85SAlexander Motin 
8554cf49a43SJulian Elischer 	return (0);
8564cf49a43SJulian Elischer }
8574cf49a43SJulian Elischer 
8584cf49a43SJulian Elischer /*
8594cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8604cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8614cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8624cf49a43SJulian Elischer  *
8634cf49a43SJulian Elischer  * Returns the node if found, else NULL.
864069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
865e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
866e1e8f51bSRobert Watson  * there.
8674cf49a43SJulian Elischer  */
8684cf49a43SJulian Elischer node_p
869069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
8704cf49a43SJulian Elischer {
871dc90cad9SJulian Elischer 	node_p node;
872dc90cad9SJulian Elischer 	ng_ID_t temp;
873cfea3f85SAlexander Motin 	int	hash;
8744cf49a43SJulian Elischer 
8754cf49a43SJulian Elischer 	/* "." means "this node" */
876069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
87730400f03SJulian Elischer 		NG_NODE_REF(here);
878069154d5SJulian Elischer 		return(here);
879069154d5SJulian Elischer 	}
8804cf49a43SJulian Elischer 
8814cf49a43SJulian Elischer 	/* Check for name-by-ID */
882dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
883069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
8844cf49a43SJulian Elischer 	}
8854cf49a43SJulian Elischer 
8864cf49a43SJulian Elischer 	/* Find node by name */
887cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
888cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
889603724d3SBjoern A. Zeeb 	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) {
890cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
891cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
8924cf49a43SJulian Elischer 			break;
8934cf49a43SJulian Elischer 		}
89470de87f2SJulian Elischer 	}
895069154d5SJulian Elischer 	if (node)
89630400f03SJulian Elischer 		NG_NODE_REF(node);
897cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
8984cf49a43SJulian Elischer 	return (node);
8994cf49a43SJulian Elischer }
9004cf49a43SJulian Elischer 
9014cf49a43SJulian Elischer /*
9029d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
903dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9044cf49a43SJulian Elischer  */
905dc90cad9SJulian Elischer static ng_ID_t
9064cf49a43SJulian Elischer ng_decodeidname(const char *name)
9074cf49a43SJulian Elischer {
9082b70adcbSArchie Cobbs 	const int len = strlen(name);
90925792ef3SArchie Cobbs 	char *eptr;
9102b70adcbSArchie Cobbs 	u_long val;
9114cf49a43SJulian Elischer 
9122b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
91370de87f2SJulian Elischer 	if ((len < 3)
91470de87f2SJulian Elischer 	|| (name[0] != '[')
91570de87f2SJulian Elischer 	|| (name[len - 1] != ']')
91670de87f2SJulian Elischer 	|| (!isxdigit(name[1]))) {
91770de87f2SJulian Elischer 		return ((ng_ID_t)0);
91870de87f2SJulian Elischer 	}
9194cf49a43SJulian Elischer 
9202b70adcbSArchie Cobbs 	/* Decode number */
9212b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
92270de87f2SJulian Elischer 	if ((eptr - name != len - 1)
92370de87f2SJulian Elischer 	|| (val == ULONG_MAX)
92470de87f2SJulian Elischer 	|| (val == 0)) {
92512f035e0SJulian Elischer 		return ((ng_ID_t)0);
92670de87f2SJulian Elischer 	}
9272b70adcbSArchie Cobbs 	return (ng_ID_t)val;
9284cf49a43SJulian Elischer }
9294cf49a43SJulian Elischer 
9304cf49a43SJulian Elischer /*
9314cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9324cf49a43SJulian Elischer  * when shutting down and removing the node.
93364efc707SRobert Watson  * IF we allow name changing this may be more resurrected.
9344cf49a43SJulian Elischer  */
9354cf49a43SJulian Elischer void
9364cf49a43SJulian Elischer ng_unname(node_p node)
9374cf49a43SJulian Elischer {
9384cf49a43SJulian Elischer }
9394cf49a43SJulian Elischer 
9404cf49a43SJulian Elischer /************************************************************************
9414cf49a43SJulian Elischer 			Hook routines
9424cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
9433e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
9443e4084c8SJulian Elischer  they are connected to the 'dead' hook.
9454cf49a43SJulian Elischer ************************************************************************/
9464cf49a43SJulian Elischer 
9474cf49a43SJulian Elischer /*
9484cf49a43SJulian Elischer  * Remove a hook reference
9494cf49a43SJulian Elischer  */
95030400f03SJulian Elischer void
9514cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
9524cf49a43SJulian Elischer {
95330400f03SJulian Elischer 	int v;
9546b795970SJulian Elischer 
9556b795970SJulian Elischer 	if (hook == &ng_deadhook) {
9566b795970SJulian Elischer 		return;
9576b795970SJulian Elischer 	}
958018fe3d1SAlexander Motin 
959018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&hook->hk_refs, -1);
960e8a49db2SJulian Elischer 
96130400f03SJulian Elischer 	if (v == 1) { /* we were the last */
962f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
9636b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
964069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
965069154d5SJulian Elischer 	}
9664cf49a43SJulian Elischer }
9674cf49a43SJulian Elischer 
9684cf49a43SJulian Elischer /*
9694cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
9703e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
9714cf49a43SJulian Elischer  */
9724cf49a43SJulian Elischer static int
9734cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
9744cf49a43SJulian Elischer {
9754cf49a43SJulian Elischer 	hook_p hook;
9764cf49a43SJulian Elischer 	int error = 0;
9774cf49a43SJulian Elischer 
9784cf49a43SJulian Elischer 	/* Check that the given name is good */
9794cf49a43SJulian Elischer 	if (name == NULL) {
9806b795970SJulian Elischer 		TRAP_ERROR();
9814cf49a43SJulian Elischer 		return (EINVAL);
9824cf49a43SJulian Elischer 	}
983899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
9846b795970SJulian Elischer 		TRAP_ERROR();
9854cf49a43SJulian Elischer 		return (EEXIST);
9864cf49a43SJulian Elischer 	}
9874cf49a43SJulian Elischer 
9884cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
98930400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
9904cf49a43SJulian Elischer 	if (hook == NULL) {
9916b795970SJulian Elischer 		TRAP_ERROR();
9924cf49a43SJulian Elischer 		return (ENOMEM);
9934cf49a43SJulian Elischer 	}
9943e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
99530400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
9963e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
99730400f03SJulian Elischer 	hook->hk_node = node;
99830400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
9994cf49a43SJulian Elischer 
10003e4084c8SJulian Elischer 	/* Set hook name */
100187e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10023e4084c8SJulian Elischer 
10033e4084c8SJulian Elischer 	/*
10043e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10053e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10063e4084c8SJulian Elischer 	 */
1007954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1008954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
100930400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1010069154d5SJulian Elischer 			return (error);
1011069154d5SJulian Elischer 		}
1012954c4772SJulian Elischer 	}
10134cf49a43SJulian Elischer 	/*
10144cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10154cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10164cf49a43SJulian Elischer 	 */
101730400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
101830400f03SJulian Elischer 	node->nd_numhooks++;
10193e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
10204cf49a43SJulian Elischer 
10214cf49a43SJulian Elischer 	if (hookp)
10224cf49a43SJulian Elischer 		*hookp = hook;
10233e4084c8SJulian Elischer 	return (0);
10244cf49a43SJulian Elischer }
10254cf49a43SJulian Elischer 
10264cf49a43SJulian Elischer /*
1027899e9c4eSArchie Cobbs  * Find a hook
1028899e9c4eSArchie Cobbs  *
1029899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1030899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
10313e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1032899e9c4eSArchie Cobbs  */
1033899e9c4eSArchie Cobbs hook_p
1034899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1035899e9c4eSArchie Cobbs {
1036899e9c4eSArchie Cobbs 	hook_p hook;
1037899e9c4eSArchie Cobbs 
103830400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
103930400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
104030400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
104170de87f2SJulian Elischer 		if (NG_HOOK_IS_VALID(hook)
1042ce5e5f99SArchie Cobbs 		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
1043899e9c4eSArchie Cobbs 			return (hook);
1044899e9c4eSArchie Cobbs 	}
1045899e9c4eSArchie Cobbs 	return (NULL);
1046899e9c4eSArchie Cobbs }
1047899e9c4eSArchie Cobbs 
1048899e9c4eSArchie Cobbs /*
10494cf49a43SJulian Elischer  * Destroy a hook
10504cf49a43SJulian Elischer  *
10514cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
10524cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
10533e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
10543e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
10553e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
10563e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
10573e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
10583e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
10593e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
10603e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
10613e4084c8SJulian Elischer  * attached to the 'dead' hook.
10626b795970SJulian Elischer  *
10636b795970SJulian Elischer  * This routine is called at all stages of hook creation
10646b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
10654cf49a43SJulian Elischer  */
10664cf49a43SJulian Elischer void
10674cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
10684cf49a43SJulian Elischer {
1069ac5dd141SGleb Smirnoff 	hook_p peer;
1070ac5dd141SGleb Smirnoff 	node_p node;
10714cf49a43SJulian Elischer 
10726b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
10736b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
10746b795970SJulian Elischer 		return;
10756b795970SJulian Elischer 	}
1076ac5dd141SGleb Smirnoff 
1077ac5dd141SGleb Smirnoff 	/*
1078ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1079ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1080ac5dd141SGleb Smirnoff 	 */
1081ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1082ac5dd141SGleb Smirnoff 
1083ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1084ac5dd141SGleb Smirnoff 
1085ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1086ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1087ac5dd141SGleb Smirnoff 
10883e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
10893e4084c8SJulian Elischer 		/*
10903e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
10913e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
10923e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
10933e4084c8SJulian Elischer 		 */
10943e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
10953e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
10966b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
10976b795970SJulian Elischer 			/*
10986b795970SJulian Elischer 			 * If it's already divorced from a node,
10996b795970SJulian Elischer 			 * just free it.
11006b795970SJulian Elischer 			 */
1101ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11026b795970SJulian Elischer 		} else {
1103ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11046b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11056b795970SJulian Elischer 		}
110652fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
110752fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1108ac5dd141SGleb Smirnoff 	} else
1109ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1110ac5dd141SGleb Smirnoff 
1111ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11124cf49a43SJulian Elischer 
11134cf49a43SJulian Elischer 	/*
11144cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11154cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11164cf49a43SJulian Elischer 	 */
11176b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11186b795970SJulian Elischer 		return;
11196b795970SJulian Elischer 	}
112030400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
112130400f03SJulian Elischer 	node->nd_numhooks--;
112230400f03SJulian Elischer 	if (node->nd_type->disconnect) {
11234cf49a43SJulian Elischer 		/*
11246b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
112564efc707SRobert Watson 		 * trust its existence after this point. (except
11266b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
11276b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
11284cf49a43SJulian Elischer 		 */
112930400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
11304cf49a43SJulian Elischer 	}
11316b795970SJulian Elischer 
11326b795970SJulian Elischer 	/*
11336b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
11346b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
11356b795970SJulian Elischer 	 */
11366b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
11376b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
11386b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
11394cf49a43SJulian Elischer }
11404cf49a43SJulian Elischer 
11414cf49a43SJulian Elischer /*
11424cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
11434cf49a43SJulian Elischer  * is effectively bypassed.
11444cf49a43SJulian Elischer  */
11454cf49a43SJulian Elischer int
11464cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
11474cf49a43SJulian Elischer {
114830400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
11496b795970SJulian Elischer 		TRAP_ERROR();
11504cf49a43SJulian Elischer 		return (EINVAL);
115130400f03SJulian Elischer 	}
115230400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
115330400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
11544cf49a43SJulian Elischer 
11553e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
11563e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
11573e4084c8SJulian Elischer 
1158cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1159cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1160cf3254aaSGleb Smirnoff 
11614cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
11624cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
11634cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
11644cf49a43SJulian Elischer 	return (0);
11654cf49a43SJulian Elischer }
11664cf49a43SJulian Elischer 
11674cf49a43SJulian Elischer /*
11684cf49a43SJulian Elischer  * Install a new netgraph type
11694cf49a43SJulian Elischer  */
11704cf49a43SJulian Elischer int
11714cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
11724cf49a43SJulian Elischer {
11734cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
11744cf49a43SJulian Elischer 
11754cf49a43SJulian Elischer 	/* Check version and type name fields */
1176589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
1177589f6ed8SJulian Elischer 	|| (namelen == 0)
117887e2c66aSHartmut Brandt 	|| (namelen >= NG_TYPESIZ)) {
11796b795970SJulian Elischer 		TRAP_ERROR();
11808ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
11818ed370fdSJulian Elischer 			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
11828ed370fdSJulian Elischer 		}
11834cf49a43SJulian Elischer 		return (EINVAL);
11844cf49a43SJulian Elischer 	}
11854cf49a43SJulian Elischer 
11864cf49a43SJulian Elischer 	/* Check for name collision */
11874cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
11886b795970SJulian Elischer 		TRAP_ERROR();
11894cf49a43SJulian Elischer 		return (EEXIST);
11904cf49a43SJulian Elischer 	}
11914cf49a43SJulian Elischer 
1192069154d5SJulian Elischer 
1193069154d5SJulian Elischer 	/* Link in new type */
11949ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1195069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1196c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
11979ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
11984cf49a43SJulian Elischer 	return (0);
11994cf49a43SJulian Elischer }
12004cf49a43SJulian Elischer 
12014cf49a43SJulian Elischer /*
1202c31b4a53SJulian Elischer  * unlink a netgraph type
1203c31b4a53SJulian Elischer  * If no examples exist
1204c31b4a53SJulian Elischer  */
1205c31b4a53SJulian Elischer int
1206c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1207c31b4a53SJulian Elischer {
1208c31b4a53SJulian Elischer 	/* Check for name collision */
1209c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1210c31b4a53SJulian Elischer 		TRAP_ERROR();
1211c31b4a53SJulian Elischer 		return (EBUSY);
1212c31b4a53SJulian Elischer 	}
1213c31b4a53SJulian Elischer 
1214c31b4a53SJulian Elischer 	/* Unlink type */
1215c31b4a53SJulian Elischer 	mtx_lock(&ng_typelist_mtx);
1216c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1217c31b4a53SJulian Elischer 	mtx_unlock(&ng_typelist_mtx);
1218c31b4a53SJulian Elischer 	return (0);
1219c31b4a53SJulian Elischer }
1220c31b4a53SJulian Elischer 
1221c31b4a53SJulian Elischer /*
12224cf49a43SJulian Elischer  * Look for a type of the name given
12234cf49a43SJulian Elischer  */
12244cf49a43SJulian Elischer struct ng_type *
12254cf49a43SJulian Elischer ng_findtype(const char *typename)
12264cf49a43SJulian Elischer {
12274cf49a43SJulian Elischer 	struct ng_type *type;
12284cf49a43SJulian Elischer 
12299ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1230069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
12314cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
12324cf49a43SJulian Elischer 			break;
12334cf49a43SJulian Elischer 	}
12349ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12354cf49a43SJulian Elischer 	return (type);
12364cf49a43SJulian Elischer }
12374cf49a43SJulian Elischer 
12384cf49a43SJulian Elischer /************************************************************************
12394cf49a43SJulian Elischer 			Composite routines
12404cf49a43SJulian Elischer ************************************************************************/
12414cf49a43SJulian Elischer /*
12426b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
12434cf49a43SJulian Elischer  */
1244e088dd4cSAlexander Motin static int
1245e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
12464cf49a43SJulian Elischer {
1247e088dd4cSAlexander Motin 	int	error = 0;
12484cf49a43SJulian Elischer 
12496b795970SJulian Elischer 	/*
12506b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12516b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12526b795970SJulian Elischer 	 * Our caller has a reference on the node.
12536b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12546b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12551acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
12561acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
12576b795970SJulian Elischer 	 */
12586b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
12596b795970SJulian Elischer 		/*
12606b795970SJulian Elischer 		 * The node must have been freed again since we last visited
12616b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
12626b795970SJulian Elischer 		 * We should just release our references and
12636b795970SJulian Elischer 		 * free anything we can think of.
12646b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
12656b795970SJulian Elischer 		 * that holds the references, just return.
12666b795970SJulian Elischer 		 */
1267e088dd4cSAlexander Motin 		ERROUT(ENOENT);
12686b795970SJulian Elischer 	}
12696b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1270e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
12716b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
12721acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1273e088dd4cSAlexander Motin 			ERROUT(error);
12744cf49a43SJulian Elischer 		}
12756b795970SJulian Elischer 	}
12766b795970SJulian Elischer 	/*
12776b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
12786b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
12796b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
12806b795970SJulian Elischer 	 */
12816b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1282e088dd4cSAlexander Motin done:
1283e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1284e088dd4cSAlexander Motin 	return (error);
12856b795970SJulian Elischer }
12866b795970SJulian Elischer 
1287e088dd4cSAlexander Motin static int
1288e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
12896b795970SJulian Elischer {
1290ac5dd141SGleb Smirnoff 	hook_p	peer;
1291e088dd4cSAlexander Motin 	int	error = 0;
12926b795970SJulian Elischer 
12936b795970SJulian Elischer 	/*
12946b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12956b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12966b795970SJulian Elischer 	 * Our caller has a reference on the node.
12976b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12986b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12996b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
13006b795970SJulian Elischer 	 * First check the hook name is unique.
13011acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13026b795970SJulian Elischer 	 */
13036b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13046b795970SJulian Elischer 		TRAP_ERROR();
13056b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
13061acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1307e088dd4cSAlexander Motin 		ERROUT(EEXIST);
13086b795970SJulian Elischer 	}
13096b795970SJulian Elischer 	/*
13106b795970SJulian Elischer 	 * Check if the node type code has something to say about it
13116b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
13126b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
13136b795970SJulian Elischer 	 * The peer hook will also be destroyed.
13146b795970SJulian Elischer 	 */
13156b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1316e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1317e088dd4cSAlexander Motin 		    hook->hk_name))) {
13186b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
13191acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1320e088dd4cSAlexander Motin 			ERROUT(error);
13216b795970SJulian Elischer 		}
13226b795970SJulian Elischer 	}
13236b795970SJulian Elischer 
13246b795970SJulian Elischer 	/*
13256b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
13266b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
13276b795970SJulian Elischer 	 */
13286b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
13296b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
13306b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
13316b795970SJulian Elischer 	node->nd_numhooks++;
13326b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
13336b795970SJulian Elischer 
13346b795970SJulian Elischer 	/*
133564efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
13365951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
13376b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
13386b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
13396b795970SJulian Elischer 	 * yet.
134064efc707SRobert Watson 	 * We can call the local one immediately as we have the
13416b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
13426b795970SJulian Elischer 	 */
13436b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1344e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13456b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13461acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1347e088dd4cSAlexander Motin 			ERROUT(error);
13486b795970SJulian Elischer 		}
13496b795970SJulian Elischer 	}
1350ac5dd141SGleb Smirnoff 
1351ac5dd141SGleb Smirnoff 	/*
1352ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1353ac5dd141SGleb Smirnoff 	 */
1354ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1355ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1356ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1357ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1358ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1359ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1360e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1361ac5dd141SGleb Smirnoff 	}
1362ac5dd141SGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
1363ac5dd141SGleb Smirnoff 
1364b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1365b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1366ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
13671acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1368e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
13691acb27c6SJulian Elischer 	}
13706b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1371e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1372e088dd4cSAlexander Motin done:
1373e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1374e088dd4cSAlexander Motin 	return (error);
13754cf49a43SJulian Elischer }
13764cf49a43SJulian Elischer 
13774cf49a43SJulian Elischer /*
13786b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
13796b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
13804cf49a43SJulian Elischer  */
13816b795970SJulian Elischer static int
1382e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1383e088dd4cSAlexander Motin     node_p node2, const char *name2)
13844cf49a43SJulian Elischer {
13854cf49a43SJulian Elischer 	int	error;
13864cf49a43SJulian Elischer 	hook_p	hook;
13874cf49a43SJulian Elischer 	hook_p	hook2;
13884cf49a43SJulian Elischer 
13891acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
13901acb27c6SJulian Elischer 		return(EEXIST);
13911acb27c6SJulian Elischer 	}
13923e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
13934cf49a43SJulian Elischer 		return (error);
13946b795970SJulian Elischer 	/* Allocate the other hook and link it up */
13956b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
139619724144SGleb Smirnoff 	if (hook2 == NULL) {
13976b795970SJulian Elischer 		TRAP_ERROR();
13986b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
13996b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
14006b795970SJulian Elischer 		return (ENOMEM);
14016b795970SJulian Elischer 	}
14026b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14036b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14046b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
14056b795970SJulian Elischer 	hook->hk_peer = hook2;
14066b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
14076b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14086b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
140987e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
14106b795970SJulian Elischer 
14116b795970SJulian Elischer 	/*
14126b795970SJulian Elischer 	 * Queue the function above.
14136b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
14146b795970SJulian Elischer 	 * the other node.
14156b795970SJulian Elischer 	 */
1416b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1417b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
14183fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
14193fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
14203fb87c24SAlexander Motin 	}
14216b795970SJulian Elischer 
14226b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
14236b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
14243fb87c24SAlexander Motin 	return (error);
14254cf49a43SJulian Elischer }
14266b795970SJulian Elischer 
14276b795970SJulian Elischer /*
14286b795970SJulian Elischer  * Make a peer and connect.
14296b795970SJulian Elischer  * We assume that the local node is locked.
14306b795970SJulian Elischer  * The new node probably doesn't need a lock until
14316b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
14326b795970SJulian Elischer  * but we should think about it a bit more.
14336b795970SJulian Elischer  *
14346b795970SJulian Elischer  * The problem may come if the other node also fires up
14356b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
14366b795970SJulian Elischer  * also it may already get a command msg via it's ID.
14376b795970SJulian Elischer  *
14386b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
14396b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
14406b795970SJulian Elischer  * make arg1 point to the node to remove).
14416b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
14426b795970SJulian Elischer  * an unconnected node?
14436b795970SJulian Elischer  */
14446b795970SJulian Elischer static int
14456b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
14466b795970SJulian Elischer {
14476b795970SJulian Elischer 	node_p	node2;
14484c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
14496b795970SJulian Elischer 	int	error;
14506b795970SJulian Elischer 
14516b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
14526b795970SJulian Elischer 		return (error);
14536b795970SJulian Elischer 	}
14546b795970SJulian Elischer 
14556b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
14561acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14576b795970SJulian Elischer 		return (error);
14586b795970SJulian Elischer 	}
14596b795970SJulian Elischer 
14606b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
14611acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14626b795970SJulian Elischer 		ng_destroy_hook(hook1);
14636b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
14646b795970SJulian Elischer 		return (error);
14656b795970SJulian Elischer 	}
14666b795970SJulian Elischer 
14676b795970SJulian Elischer 	/*
14686b795970SJulian Elischer 	 * Actually link the two hooks together.
14696b795970SJulian Elischer 	 */
14706b795970SJulian Elischer 	hook1->hk_peer = hook2;
14716b795970SJulian Elischer 	hook2->hk_peer = hook1;
14726b795970SJulian Elischer 
14736b795970SJulian Elischer 	/* Each hook is referenced by the other */
14746b795970SJulian Elischer 	NG_HOOK_REF(hook1);
14756b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14766b795970SJulian Elischer 
14776b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
14786b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
14796b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
14806b795970SJulian Elischer 	}
14816b795970SJulian Elischer 
14826b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
14836b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
14846b795970SJulian Elischer 
14856b795970SJulian Elischer 	}
14863e4084c8SJulian Elischer 
14873e4084c8SJulian Elischer 	/*
14883e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
14893e4084c8SJulian Elischer 	 */
14906b795970SJulian Elischer 	if (error) {
14916b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
14921acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14936b795970SJulian Elischer 	} else {
14946b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
14956b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
14966b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
14976b795970SJulian Elischer 	}
14986b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
14993e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
15003e4084c8SJulian Elischer 	return (error);
15014cf49a43SJulian Elischer }
15026b795970SJulian Elischer 
1503069154d5SJulian Elischer /************************************************************************
1504069154d5SJulian Elischer 		Utility routines to send self messages
1505069154d5SJulian Elischer ************************************************************************/
1506069154d5SJulian Elischer 
15071acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
150864efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1509069154d5SJulian Elischer int
15101acb27c6SJulian Elischer ng_rmnode_self(node_p node)
1511069154d5SJulian Elischer {
15121acb27c6SJulian Elischer 	int		error;
15134cf49a43SJulian Elischer 
15141acb27c6SJulian Elischer 	if (node == &ng_deadnode)
15151acb27c6SJulian Elischer 		return (0);
1516be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1517be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
15181acb27c6SJulian Elischer 		return (0);
1519069154d5SJulian Elischer 
15201acb27c6SJulian Elischer 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
15211acb27c6SJulian Elischer 	return (error);
1522069154d5SJulian Elischer }
1523069154d5SJulian Elischer 
15241acb27c6SJulian Elischer static void
15256b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
15266b795970SJulian Elischer {
15276b795970SJulian Elischer 	ng_destroy_hook(hook);
15281acb27c6SJulian Elischer 	return ;
15296b795970SJulian Elischer }
15306b795970SJulian Elischer 
1531954c4772SJulian Elischer int
1532954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1533954c4772SJulian Elischer {
15346b795970SJulian Elischer 	int		error;
1535954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1536954c4772SJulian Elischer 
15376b795970SJulian Elischer 	if (node == &ng_deadnode)
15386b795970SJulian Elischer 		return (0);
15396b795970SJulian Elischer 
15406b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
15416b795970SJulian Elischer 	return (error);
1542954c4772SJulian Elischer }
1543954c4772SJulian Elischer 
1544069154d5SJulian Elischer /***********************************************************************
15454cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
15464cf49a43SJulian Elischer  *
15474cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
15484cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
15494cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
15504cf49a43SJulian Elischer  *
15514cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
15524cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
15534cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
15544cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
15554cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
15564cf49a43SJulian Elischer  *
15574cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1558069154d5SJulian Elischer  ***********************************************************************/
15594cf49a43SJulian Elischer int
15604cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
15614cf49a43SJulian Elischer {
15624cf49a43SJulian Elischer 	char	*node, *path, *hook;
15634cf49a43SJulian Elischer 	int	k;
15644cf49a43SJulian Elischer 
15654cf49a43SJulian Elischer 	/*
15664cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
15674cf49a43SJulian Elischer 	 */
15684cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
15694cf49a43SJulian Elischer 	if (*path) {
15704cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
15714cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
15724cf49a43SJulian Elischer 
15734cf49a43SJulian Elischer 		/* Node name must not be empty */
15744cf49a43SJulian Elischer 		if (!*node)
15754cf49a43SJulian Elischer 			return -1;
15764cf49a43SJulian Elischer 
15774cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
15784cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
15794cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
15804cf49a43SJulian Elischer 				if (node[k] == '.')
15814cf49a43SJulian Elischer 					return -1;
15824cf49a43SJulian Elischer 		}
15834cf49a43SJulian Elischer 	} else {
15844cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
15854cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
15864cf49a43SJulian Elischer 	}
15874cf49a43SJulian Elischer 
15884cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
15894cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
15904cf49a43SJulian Elischer 		if (path[k] == ':')
15914cf49a43SJulian Elischer 			return -1;
15924cf49a43SJulian Elischer 
15934cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
15944cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
15954cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
15964cf49a43SJulian Elischer 			return -1;
15974cf49a43SJulian Elischer 
15984cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
15994cf49a43SJulian Elischer 	if (path[0] == '.')
16004cf49a43SJulian Elischer 		path++;
16014cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16024cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16034cf49a43SJulian Elischer 
16044cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
16054cf49a43SJulian Elischer 	if (*path) {
16064cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
16074cf49a43SJulian Elischer 			if (path[k] == '.') {
16084cf49a43SJulian Elischer 				hook = NULL;
16094cf49a43SJulian Elischer 				break;
16104cf49a43SJulian Elischer 			}
16114cf49a43SJulian Elischer 	} else
16124cf49a43SJulian Elischer 		path = hook = NULL;
16134cf49a43SJulian Elischer 
16144cf49a43SJulian Elischer 	/* Done */
16154cf49a43SJulian Elischer 	if (nodep)
16164cf49a43SJulian Elischer 		*nodep = node;
16174cf49a43SJulian Elischer 	if (pathp)
16184cf49a43SJulian Elischer 		*pathp = path;
16194cf49a43SJulian Elischer 	if (hookp)
16204cf49a43SJulian Elischer 		*hookp = hook;
16214cf49a43SJulian Elischer 	return (0);
16224cf49a43SJulian Elischer }
16234cf49a43SJulian Elischer 
16244cf49a43SJulian Elischer /*
16254cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1626069154d5SJulian Elischer  * return the destination node.
16274cf49a43SJulian Elischer  */
16284cf49a43SJulian Elischer int
1629069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1630069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
16314cf49a43SJulian Elischer {
163287e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
16334cf49a43SJulian Elischer 	char   *nodename, *path, pbuf[2];
1634069154d5SJulian Elischer 	node_p  node, oldnode;
16354cf49a43SJulian Elischer 	char   *cp;
1636a4ec03cfSJulian Elischer 	hook_p hook = NULL;
16374cf49a43SJulian Elischer 
16384cf49a43SJulian Elischer 	/* Initialize */
163930400f03SJulian Elischer 	if (destp == NULL) {
16406b795970SJulian Elischer 		TRAP_ERROR();
16414cf49a43SJulian Elischer 		return EINVAL;
164230400f03SJulian Elischer 	}
16434cf49a43SJulian Elischer 	*destp = NULL;
16444cf49a43SJulian Elischer 
16454cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
16464cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
16474cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
16484cf49a43SJulian Elischer 
16494cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
16504cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
16516b795970SJulian Elischer 		TRAP_ERROR();
16524cf49a43SJulian Elischer 		return EINVAL;
16534cf49a43SJulian Elischer 	}
16544cf49a43SJulian Elischer 	if (path == NULL) {
16554cf49a43SJulian Elischer 		pbuf[0] = '.';	/* Needs to be writable */
16564cf49a43SJulian Elischer 		pbuf[1] = '\0';
16574cf49a43SJulian Elischer 		path = pbuf;
16584cf49a43SJulian Elischer 	}
16594cf49a43SJulian Elischer 
1660069154d5SJulian Elischer 	/*
1661069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1662069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1663069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1664069154d5SJulian Elischer 	 */
16654cf49a43SJulian Elischer 	if (nodename) {
1666069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
16674cf49a43SJulian Elischer 		if (node == NULL) {
16686b795970SJulian Elischer 			TRAP_ERROR();
16694cf49a43SJulian Elischer 			return (ENOENT);
16704cf49a43SJulian Elischer 		}
1671069154d5SJulian Elischer 	} else {
1672069154d5SJulian Elischer 		if (here == NULL) {
16736b795970SJulian Elischer 			TRAP_ERROR();
1674069154d5SJulian Elischer 			return (EINVAL);
1675069154d5SJulian Elischer 		}
16764cf49a43SJulian Elischer 		node = here;
167730400f03SJulian Elischer 		NG_NODE_REF(node);
1678069154d5SJulian Elischer 	}
16794cf49a43SJulian Elischer 
1680069154d5SJulian Elischer 	/*
1681069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1682069154d5SJulian Elischer 	 * XXX
1683069154d5SJulian Elischer 	 * We actually cannot guarantee that the sequence
1684069154d5SJulian Elischer 	 * is not being demolished as we crawl along it
1685069154d5SJulian Elischer 	 * without extra-ordinary locking etc.
1686069154d5SJulian Elischer 	 * So this is a bit dodgy to say the least.
1687069154d5SJulian Elischer 	 * We can probably hold up some things by holding
1688069154d5SJulian Elischer 	 * the nodelist mutex for the time of this
1689069154d5SJulian Elischer 	 * crawl if we wanted.. At least that way we wouldn't have to
169064efc707SRobert Watson 	 * worry about the nodes disappearing, but the hooks would still
1691069154d5SJulian Elischer 	 * be a problem.
1692069154d5SJulian Elischer 	 */
16934cf49a43SJulian Elischer 	for (cp = path; node != NULL && *cp != '\0'; ) {
16944cf49a43SJulian Elischer 		char *segment;
16954cf49a43SJulian Elischer 
16964cf49a43SJulian Elischer 		/*
16974cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
16984cf49a43SJulian Elischer 		 * found with a NUL; "cp" points to the next segment (or the
16994cf49a43SJulian Elischer 		 * NUL at the end).
17004cf49a43SJulian Elischer 		 */
17014cf49a43SJulian Elischer 		for (segment = cp; *cp != '\0'; cp++) {
17024cf49a43SJulian Elischer 			if (*cp == '.') {
17034cf49a43SJulian Elischer 				*cp++ = '\0';
17044cf49a43SJulian Elischer 				break;
17054cf49a43SJulian Elischer 			}
17064cf49a43SJulian Elischer 		}
17074cf49a43SJulian Elischer 
17084cf49a43SJulian Elischer 		/* Empty segment */
17094cf49a43SJulian Elischer 		if (*segment == '\0')
17104cf49a43SJulian Elischer 			continue;
17114cf49a43SJulian Elischer 
17124cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1713899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17144cf49a43SJulian Elischer 
17154cf49a43SJulian Elischer 		/* Can't get there from here... */
17164cf49a43SJulian Elischer 		if (hook == NULL
171730400f03SJulian Elischer 		    || NG_HOOK_PEER(hook) == NULL
171830400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(hook)
171930400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17206b795970SJulian Elischer 			TRAP_ERROR();
172130400f03SJulian Elischer 			NG_NODE_UNREF(node);
172230400f03SJulian Elischer #if 0
172330400f03SJulian Elischer 			printf("hooknotvalid %s %s %d %d %d %d ",
172430400f03SJulian Elischer 					path,
172530400f03SJulian Elischer 					segment,
172630400f03SJulian Elischer 					hook == NULL,
172730400f03SJulian Elischer 					NG_HOOK_PEER(hook) == NULL,
172830400f03SJulian Elischer 					NG_HOOK_NOT_VALID(hook),
172930400f03SJulian Elischer 					NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
173030400f03SJulian Elischer #endif
17314cf49a43SJulian Elischer 			return (ENOENT);
17324cf49a43SJulian Elischer 		}
17334cf49a43SJulian Elischer 
1734069154d5SJulian Elischer 		/*
1735069154d5SJulian Elischer 		 * Hop on over to the next node
1736069154d5SJulian Elischer 		 * XXX
1737069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1738069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1739069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1740069154d5SJulian Elischer 		 */
1741069154d5SJulian Elischer 		oldnode = node;
174230400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
174330400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
174430400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
174530400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
174630400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1747069154d5SJulian Elischer 			node = NULL;
1748069154d5SJulian Elischer 		}
17494cf49a43SJulian Elischer 	}
17504cf49a43SJulian Elischer 
17514cf49a43SJulian Elischer 	/* If node somehow missing, fail here (probably this is not needed) */
17524cf49a43SJulian Elischer 	if (node == NULL) {
17536b795970SJulian Elischer 		TRAP_ERROR();
17544cf49a43SJulian Elischer 		return (ENXIO);
17554cf49a43SJulian Elischer 	}
17564cf49a43SJulian Elischer 
17574cf49a43SJulian Elischer 	/* Done */
17584cf49a43SJulian Elischer 	*destp = node;
17591bdebe4dSArchie Cobbs 	if (lasthook != NULL)
176030400f03SJulian Elischer 		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
1761069154d5SJulian Elischer 	return (0);
1762069154d5SJulian Elischer }
1763069154d5SJulian Elischer 
1764069154d5SJulian Elischer /***************************************************************\
1765069154d5SJulian Elischer * Input queue handling.
1766069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1767069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
176864efc707SRobert Watson * Items which cannot be handled immediately are queued.
1769069154d5SJulian Elischer *
1770069154d5SJulian Elischer * read-write queue locking inline functions			*
1771069154d5SJulian Elischer \***************************************************************/
1772069154d5SJulian Elischer 
17739852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
17749852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
17759852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
17769852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
17779852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
17789852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1779069154d5SJulian Elischer 
1780069154d5SJulian Elischer /*
1781069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1782069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1783069154d5SJulian Elischer  * with them.
1784069154d5SJulian Elischer  *
1785b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1786069154d5SJulian Elischer  */
1787069154d5SJulian Elischer /*-
1788069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1789069154d5SJulian Elischer                        |
1790069154d5SJulian Elischer                        V
1791069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17924be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
17934be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
17944be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1795069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17964be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
17974be59335SGleb Smirnoff                             V                                | |
17984be59335SGleb Smirnoff                   [active reader count]                      | |
1799069154d5SJulian Elischer                                                              | |
18004be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1801069154d5SJulian Elischer                                                                |
18024be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1803b57a7965SJulian Elischer 
18048f9ac44aSAlexander Motin Node queue has such semantics:
18058f9ac44aSAlexander Motin - All flags modifications are atomic.
18068f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18078f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18088f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18098f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18108f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18118f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18128f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18138f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18148f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1815069154d5SJulian Elischer */
18168f9ac44aSAlexander Motin 
18174be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18184be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18194be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18204be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18214be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1822b57a7965SJulian Elischer 
1823b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18244be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18254be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1826b57a7965SJulian Elischer 
18274be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1828b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1829b57a7965SJulian Elischer 
18304be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
18314be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
18324be59335SGleb Smirnoff 
18334be59335SGleb Smirnoff /* How to decide what the next queued item is. */
18349852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
18359852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
18364be59335SGleb Smirnoff 
18374be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
18384be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
18394be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
18404be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
18414be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1842b57a7965SJulian Elischer 
1843b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
18444be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
18454be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1846394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
18474be59335SGleb Smirnoff 
1848714fb865SGleb Smirnoff #define NGQRW_R 0
1849714fb865SGleb Smirnoff #define NGQRW_W 1
1850714fb865SGleb Smirnoff 
18519852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
18529852972bSAlexander Motin 
1853069154d5SJulian Elischer /*
1854069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1855069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1856069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1857069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1858069154d5SJulian Elischer  * on the queue.
1859069154d5SJulian Elischer  */
1860069154d5SJulian Elischer static __inline item_p
18619852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1862069154d5SJulian Elischer {
1863069154d5SJulian Elischer 	item_p item;
18649852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1865b57a7965SJulian Elischer 
18668f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1867d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
18688f9ac44aSAlexander Motin 
18698f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
18704be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
18712955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
18722955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
18739852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
18744be59335SGleb Smirnoff 		return (NULL);
18754be59335SGleb Smirnoff 	}
1876d58e678fSGleb Smirnoff 
18774be59335SGleb Smirnoff 	/*
18784be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
18794be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
18804be59335SGleb Smirnoff 	 * the current state of the node.
18814be59335SGleb Smirnoff 	 */
18824be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1883394cb30aSAlexander Motin 		while (1) {
1884394cb30aSAlexander Motin 			long t = ngq->q_flags;
1885394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
18868f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
18872955ee18SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
18882955ee18SGleb Smirnoff 				    "can't proceed; queue flags 0x%lx", __func__,
18899852972bSAlexander Motin 				    node->nd_ID, node, t);
18904be59335SGleb Smirnoff 				return (NULL);
18914be59335SGleb Smirnoff 			}
18929852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1893394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1894394cb30aSAlexander Motin 				break;
1895394cb30aSAlexander Motin 			cpu_spinwait();
1896394cb30aSAlexander Motin 		}
18978f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1898714fb865SGleb Smirnoff 		*rw = NGQRW_R;
18999852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1900394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19018f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1902714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1903069154d5SJulian Elischer 	} else {
19048f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
1905394cb30aSAlexander Motin 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer "
1906394cb30aSAlexander Motin 		    "can't proceed; queue flags 0x%lx", __func__,
19079852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19084be59335SGleb Smirnoff 		return (NULL);
19094cf49a43SJulian Elischer 	}
19104cf49a43SJulian Elischer 
19114cf49a43SJulian Elischer 	/*
1912069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1913069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19144cf49a43SJulian Elischer 	 */
19159852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19169852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
19179852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
19189852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
19192955ee18SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
19202955ee18SGleb Smirnoff 	    "queue flags 0x%lx", __func__,
19219852972bSAlexander Motin 	    node->nd_ID, node, item, *rw ? "WRITER" : "READER" ,
19223b33fbe7SGleb Smirnoff 	    ngq->q_flags);
1923069154d5SJulian Elischer 	return (item);
1924069154d5SJulian Elischer }
1925859a4d16SJulian Elischer 
1926859a4d16SJulian Elischer /*
19278f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
19288f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
1929859a4d16SJulian Elischer  */
1930069154d5SJulian Elischer static __inline void
19319852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
1932069154d5SJulian Elischer {
19339852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
19344be59335SGleb Smirnoff 	if (rw == NGQRW_W)
19354be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
19364be59335SGleb Smirnoff 	else
19374be59335SGleb Smirnoff 		NGI_SET_READER(item);
1938394cb30aSAlexander Motin 
1939394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
1940394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
19419852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
19429852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
1943394cb30aSAlexander Motin 
19442955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
19459852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
19464be59335SGleb Smirnoff 
19474be59335SGleb Smirnoff 	/*
19484be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
19494be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
19504be59335SGleb Smirnoff 	 */
19514be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
19529852972bSAlexander Motin 		ng_worklist_add(node);
1953394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
1954069154d5SJulian Elischer }
1955069154d5SJulian Elischer 
19568f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
1957069154d5SJulian Elischer static __inline item_p
19589852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
1959069154d5SJulian Elischer {
19609852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1961ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1962069154d5SJulian Elischer 
1963394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
1964394cb30aSAlexander Motin 	while (1) {
19659852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
1966394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
1967394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
19689852972bSAlexander Motin 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19699852972bSAlexander Motin 		    t, t + READER_INCREMENT)) {
1970069154d5SJulian Elischer 	    		/* Successfully grabbed node */
1971394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19729852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
1973069154d5SJulian Elischer 			return (item);
1974069154d5SJulian Elischer 		}
1975394cb30aSAlexander Motin 		cpu_spinwait();
1976394cb30aSAlexander Motin 	};
1977069154d5SJulian Elischer 
1978394cb30aSAlexander Motin 	/* Queue the request for later. */
19799852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
1980f912c0f7SGleb Smirnoff 
1981f912c0f7SGleb Smirnoff 	return (NULL);
1982069154d5SJulian Elischer }
1983069154d5SJulian Elischer 
19848f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
1985069154d5SJulian Elischer static __inline item_p
19869852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
1987069154d5SJulian Elischer {
19889852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1989ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1990ac5dd141SGleb Smirnoff 
1991394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
19929852972bSAlexander Motin 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19939852972bSAlexander Motin 	    0, WRITER_ACTIVE)) {
1994394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
19952955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19969852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
1997069154d5SJulian Elischer 		return (item);
1998069154d5SJulian Elischer 	}
1999069154d5SJulian Elischer 
2000394cb30aSAlexander Motin 	/* Queue the request for later. */
20019852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
2002f912c0f7SGleb Smirnoff 
2003f912c0f7SGleb Smirnoff 	return (NULL);
2004069154d5SJulian Elischer }
2005069154d5SJulian Elischer 
2006262dfd7fSJulian Elischer #if 0
2007262dfd7fSJulian Elischer static __inline item_p
20089852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
2009262dfd7fSJulian Elischer {
20109852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20119852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2012262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2013262dfd7fSJulian Elischer 
2014262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2015262dfd7fSJulian Elischer 
20169852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2017262dfd7fSJulian Elischer 
2018262dfd7fSJulian Elischer 	/*
2019262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2020262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2021262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2022262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2023262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2024262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2025262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2026262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2027262dfd7fSJulian Elischer 	 */
20289852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
20299852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
20309852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2031262dfd7fSJulian Elischer 
2032262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2033262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2034262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2035262dfd7fSJulian Elischer 
2036262dfd7fSJulian Elischer 		/*
2037262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
2038262dfd7fSJulian Elischer 		 * down grade back to READER and finish up
2039262dfd7fSJulian Elischer 	 	 */
20409852972bSAlexander Motin 		atomic_add_int(&ngq->q_flags,
2041262dfd7fSJulian Elischer 		    READER_INCREMENT - WRITER_ACTIVE);
2042262dfd7fSJulian Elischer 
2043262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2044262dfd7fSJulian Elischer 		return;
2045262dfd7fSJulian Elischer 	}
2046262dfd7fSJulian Elischer 	/*
2047262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2048262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2049262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2050262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2051262dfd7fSJulian Elischer 	 * set the correct flags.
2052262dfd7fSJulian Elischer 	 */
20539852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2054262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
20559852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2056262dfd7fSJulian Elischer 
2057262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
20589852972bSAlexander Motin 		    node->nd_ID, node);
2059262dfd7fSJulian Elischer 	};
20609852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
20619852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
20629852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2063262dfd7fSJulian Elischer 
2064262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
20659852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2066394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20679852972bSAlexander Motin 		ng_worklist_add(node);
20689852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2069262dfd7fSJulian Elischer 
2070262dfd7fSJulian Elischer 	return;
2071262dfd7fSJulian Elischer }
2072262dfd7fSJulian Elischer #endif
2073262dfd7fSJulian Elischer 
20748f9ac44aSAlexander Motin /* Release reader lock. */
2075069154d5SJulian Elischer static __inline void
20769852972bSAlexander Motin ng_leave_read(node_p node)
2077069154d5SJulian Elischer {
20789852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2079069154d5SJulian Elischer }
2080069154d5SJulian Elischer 
20818f9ac44aSAlexander Motin /* Release writer lock. */
2082069154d5SJulian Elischer static __inline void
20839852972bSAlexander Motin ng_leave_write(node_p node)
2084069154d5SJulian Elischer {
20859852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2086069154d5SJulian Elischer }
2087069154d5SJulian Elischer 
20888f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2089069154d5SJulian Elischer static void
20909852972bSAlexander Motin ng_flush_input_queue(node_p node)
2091069154d5SJulian Elischer {
20929852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2093069154d5SJulian Elischer 	item_p item;
2094069154d5SJulian Elischer 
20952c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
20969852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
20979852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
20989852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
20999852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
21002c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21014be59335SGleb Smirnoff 
21024be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
210310e87318SAlexander Motin 		if (item->apply != NULL) {
210410e87318SAlexander Motin 			if (item->depth == 1)
210510e87318SAlexander Motin 				item->apply->error = ENOENT;
210610e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
210710e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
210810e87318SAlexander Motin 				    item->apply->error);
210910e87318SAlexander Motin 			}
2110eb2405ddSGleb Smirnoff 		}
2111505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21122c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2113069154d5SJulian Elischer 	}
21142c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2115069154d5SJulian Elischer }
2116069154d5SJulian Elischer 
2117069154d5SJulian Elischer /***********************************************************************
2118069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2119069154d5SJulian Elischer ***********************************************************************/
2120069154d5SJulian Elischer 
2121069154d5SJulian Elischer /*
21221acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2123069154d5SJulian Elischer  * Common:
2124069154d5SJulian Elischer  *    reference to destination node.
2125069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2126e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2127069154d5SJulian Elischer  * Data:
2128069154d5SJulian Elischer  *    pointer to mbuf
2129069154d5SJulian Elischer  * Control_Message:
2130069154d5SJulian Elischer  *    pointer to msg.
2131069154d5SJulian Elischer  *    ID of original sender node. (return address)
21321acb27c6SJulian Elischer  * Function:
21331acb27c6SJulian Elischer  *    Function pointer
21341acb27c6SJulian Elischer  *    void * argument
21351acb27c6SJulian Elischer  *    integer argument
2136069154d5SJulian Elischer  *
2137069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2138069154d5SJulian Elischer  */
2139069154d5SJulian Elischer 
2140069154d5SJulian Elischer int
214142282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2142069154d5SJulian Elischer {
2143e088dd4cSAlexander Motin 	hook_p hook;
2144e088dd4cSAlexander Motin 	node_p node;
214542282202SGleb Smirnoff 	int queue, rw;
2146e088dd4cSAlexander Motin 	struct ng_queue *ngq;
214732b33288SGleb Smirnoff 	int error = 0;
2148069154d5SJulian Elischer 
2149f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2150f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2151e088dd4cSAlexander Motin 
2152e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2153e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2154e088dd4cSAlexander Motin #endif
2155e088dd4cSAlexander Motin 
2156f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2157e088dd4cSAlexander Motin 	if (item->apply)
2158e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2159e088dd4cSAlexander Motin 
2160e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2161f50597f5SAlexander Motin 	/* Node is never optional. */
2162f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2163e088dd4cSAlexander Motin 
2164e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2165f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2166f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2167f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
216810204449SJulian Elischer 		if (NGI_M(item) == NULL)
2169e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2170069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
21716f683eeeSGleb Smirnoff 	}
21726f683eeeSGleb Smirnoff 
2173069154d5SJulian Elischer 	/*
2174f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2175f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2176f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2177069154d5SJulian Elischer 	 */
2178db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2179f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2180f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2181069154d5SJulian Elischer 		rw = NGQRW_W;
2182f50597f5SAlexander Motin 	} else {
2183f50597f5SAlexander Motin 		rw = NGQRW_R;
2184f50597f5SAlexander Motin 	}
21856f683eeeSGleb Smirnoff 
218681a253a4SAlexander Motin 	/*
218781a253a4SAlexander Motin 	 * If sender or receiver requests queued delivery or stack usage
218881a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
218981a253a4SAlexander Motin 	 */
219081a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
219181a253a4SAlexander Motin 		queue = 1;
2192f50597f5SAlexander Motin 	} else {
2193f50597f5SAlexander Motin 		queue = 0;
219481a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2195d4529f98SAlexander Motin 		/*
2196942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2197f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2198d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2199f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2200f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2201d4529f98SAlexander Motin 		 */
2202f50597f5SAlexander Motin 		size_t	st, su, sl;
220381a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2204f50597f5SAlexander Motin 		sl = st - su;
2205f50597f5SAlexander Motin 		if ((sl * 4 < st) ||
2206f50597f5SAlexander Motin 		    ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) ||
220781a253a4SAlexander Motin 		      (hook && (hook->hk_flags & HK_HI_STACK))))) {
220881a253a4SAlexander Motin 			queue = 1;
220981a253a4SAlexander Motin 		}
221081a253a4SAlexander Motin #endif
2211f50597f5SAlexander Motin 	}
221281a253a4SAlexander Motin 
2213069154d5SJulian Elischer 	if (queue) {
221410e87318SAlexander Motin 		item->depth = 1;
2215394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
22169852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2217f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2218069154d5SJulian Elischer 	}
2219069154d5SJulian Elischer 
2220069154d5SJulian Elischer 	/*
2221069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2222069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2223069154d5SJulian Elischer 	 */
222432b33288SGleb Smirnoff  	if (rw == NGQRW_R)
22259852972bSAlexander Motin 		item = ng_acquire_read(node, item);
222632b33288SGleb Smirnoff 	else
22279852972bSAlexander Motin 		item = ng_acquire_write(node, item);
22284cf49a43SJulian Elischer 
2229f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2230f50597f5SAlexander Motin 	if (item == NULL)
2231f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2232069154d5SJulian Elischer 
22335951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
22345951069aSJulian Elischer 
223510e87318SAlexander Motin 	item->depth++;
223627757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2237069154d5SJulian Elischer 
2238394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
22399852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2240394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
22412c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2242394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
22439852972bSAlexander Motin 			ng_worklist_add(node);
22442c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2245394cb30aSAlexander Motin 	}
2246394cb30aSAlexander Motin 
2247394cb30aSAlexander Motin 	/*
2248394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2249394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2250394cb30aSAlexander Motin 	 */
2251394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2252069154d5SJulian Elischer 
22531acb27c6SJulian Elischer 	return (error);
2254e088dd4cSAlexander Motin 
2255e088dd4cSAlexander Motin done:
2256f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
225710e87318SAlexander Motin 	if (item->apply != NULL) {
225810e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
225910e87318SAlexander Motin 			item->apply->error = error;
226010e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
226110e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
226210e87318SAlexander Motin 			    item->apply->error);
226310e87318SAlexander Motin 		}
226410e87318SAlexander Motin 	}
2265e72a98f4SAlexander Motin 
2266e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2267e088dd4cSAlexander Motin 	return (error);
2268069154d5SJulian Elischer }
2269069154d5SJulian Elischer 
2270069154d5SJulian Elischer /*
2271069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2272069154d5SJulian Elischer  * It should contain all the information needed
2273069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2274e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
22754cf49a43SJulian Elischer  */
227627757487SGleb Smirnoff static int
2277714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2278069154d5SJulian Elischer {
2279069154d5SJulian Elischer 	hook_p  hook;
2280069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2281c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2282e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
228310e87318SAlexander Motin 	int	error = 0, depth;
2284069154d5SJulian Elischer 
2285f50597f5SAlexander Motin 	/* Node and item are never optional. */
2286f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2287f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2288f50597f5SAlexander Motin 
22891acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
229030400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2291069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2292069154d5SJulian Elischer #endif
22938afe16d5SGleb Smirnoff 
22948afe16d5SGleb Smirnoff 	apply = item->apply;
229510e87318SAlexander Motin 	depth = item->depth;
22968afe16d5SGleb Smirnoff 
22976b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2298069154d5SJulian Elischer 	case NGQF_DATA:
2299069154d5SJulian Elischer 		/*
2300069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2301069154d5SJulian Elischer 		 */
2302f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2303f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2304f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2305a54a69d7SJulian Elischer 			error = EIO;
2306069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2307c4b5eea4SJulian Elischer 			break;
2308069154d5SJulian Elischer 		}
2309c4b5eea4SJulian Elischer 		/*
2310c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
2311c4b5eea4SJulian Elischer 		 * Give preference to the hook over-ride method
2312c4b5eea4SJulian Elischer 		 */
2313c4b5eea4SJulian Elischer 		if ((!(rcvdata = hook->hk_rcvdata))
2314c4b5eea4SJulian Elischer 		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2315c4b5eea4SJulian Elischer 			error = 0;
2316c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2317c4b5eea4SJulian Elischer 			break;
2318c4b5eea4SJulian Elischer 		}
2319a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2320069154d5SJulian Elischer 		break;
2321069154d5SJulian Elischer 	case NGQF_MESG:
2322e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2323069154d5SJulian Elischer 			/*
2324e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2325e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2326069154d5SJulian Elischer 			 * The message may not need it.
2327069154d5SJulian Elischer 			 */
232830400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2329069154d5SJulian Elischer 			hook = NULL;
2330069154d5SJulian Elischer 		}
2331069154d5SJulian Elischer 		/*
2332069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2333069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2334069154d5SJulian Elischer 		 */
233530400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
23366b795970SJulian Elischer 			TRAP_ERROR();
2337a54a69d7SJulian Elischer 			error = EINVAL;
2338069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2339e72a98f4SAlexander Motin 			break;
2340e72a98f4SAlexander Motin 		}
2341069154d5SJulian Elischer 		/*
2342069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2343069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2344069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2345e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2346069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2347069154d5SJulian Elischer 		 * reference a node or hook that has just been
2348069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2349069154d5SJulian Elischer 		 * is holding a reference, but..
2350069154d5SJulian Elischer 		 */
2351e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2352e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2353a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2354c4b5eea4SJulian Elischer 			break;
2355c4b5eea4SJulian Elischer 		}
2356e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2357e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
23586b795970SJulian Elischer 			TRAP_ERROR();
2359a54a69d7SJulian Elischer 			error = 0;
2360069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2361c4b5eea4SJulian Elischer 			break;
2362069154d5SJulian Elischer 		}
2363a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2364069154d5SJulian Elischer 		break;
23656b795970SJulian Elischer 	case NGQF_FN:
2366e088dd4cSAlexander Motin 	case NGQF_FN2:
2367e088dd4cSAlexander Motin 		/*
236874c9119dSAlexander Motin 		 * In the case of the shutdown message we allow it to hit
2369e088dd4cSAlexander Motin 		 * even if the node is invalid.
2370e088dd4cSAlexander Motin 		 */
237174c9119dSAlexander Motin 		if (NG_NODE_NOT_VALID(node) &&
237274c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmnode) {
2373e088dd4cSAlexander Motin 			TRAP_ERROR();
2374e088dd4cSAlexander Motin 			error = EINVAL;
2375e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2376e088dd4cSAlexander Motin 			break;
2377e088dd4cSAlexander Motin 		}
237874c9119dSAlexander Motin 		/* Same is about some internal functions and invalid hook. */
237974c9119dSAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook) &&
238074c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part2 &&
238174c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part3 &&
238274c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmhook_part2) {
238374c9119dSAlexander Motin 			TRAP_ERROR();
238474c9119dSAlexander Motin 			error = EINVAL;
238574c9119dSAlexander Motin 			NG_FREE_ITEM(item);
238674c9119dSAlexander Motin 			break;
238774c9119dSAlexander Motin 		}
238874c9119dSAlexander Motin 
2389b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2390b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2391b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2392b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2393b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2394e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2395e088dd4cSAlexander Motin 		break;
2396069154d5SJulian Elischer 	}
2397069154d5SJulian Elischer 	/*
2398069154d5SJulian Elischer 	 * We held references on some of the resources
2399069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2400069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2401069154d5SJulian Elischer 	 */
2402e72a98f4SAlexander Motin 	if (hook)
240330400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2404069154d5SJulian Elischer 
2405f50597f5SAlexander Motin  	if (rw == NGQRW_R)
24069852972bSAlexander Motin 		ng_leave_read(node);
2407f50597f5SAlexander Motin 	else
24089852972bSAlexander Motin 		ng_leave_write(node);
24098afe16d5SGleb Smirnoff 
24108afe16d5SGleb Smirnoff 	/* Apply callback. */
241110e87318SAlexander Motin 	if (apply != NULL) {
241210e87318SAlexander Motin 		if (depth == 1 && error != 0)
241310e87318SAlexander Motin 			apply->error = error;
241410e87318SAlexander Motin 		if (refcount_release(&apply->refs))
241510e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
241610e87318SAlexander Motin 	}
24178afe16d5SGleb Smirnoff 
241827757487SGleb Smirnoff 	return (error);
2419069154d5SJulian Elischer }
2420069154d5SJulian Elischer 
2421069154d5SJulian Elischer /***********************************************************************
2422069154d5SJulian Elischer  * Implement the 'generic' control messages
2423069154d5SJulian Elischer  ***********************************************************************/
2424069154d5SJulian Elischer static int
2425069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
24264cf49a43SJulian Elischer {
24274cf49a43SJulian Elischer 	int error = 0;
2428069154d5SJulian Elischer 	struct ng_mesg *msg;
2429069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
24304cf49a43SJulian Elischer 
2431069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
24324cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
24336b795970SJulian Elischer 		TRAP_ERROR();
2434069154d5SJulian Elischer 		error = EINVAL;
2435069154d5SJulian Elischer 		goto out;
24364cf49a43SJulian Elischer 	}
24374cf49a43SJulian Elischer 	switch (msg->header.cmd) {
24384cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
24391acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
24404cf49a43SJulian Elischer 		break;
24414cf49a43SJulian Elischer 	case NGM_MKPEER:
24424cf49a43SJulian Elischer 	    {
24434cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
24444cf49a43SJulian Elischer 
24454cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
24466b795970SJulian Elischer 			TRAP_ERROR();
2447069154d5SJulian Elischer 			error = EINVAL;
2448069154d5SJulian Elischer 			break;
24494cf49a43SJulian Elischer 		}
24504cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
24514cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
24524cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
24534cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
24544cf49a43SJulian Elischer 		break;
24554cf49a43SJulian Elischer 	    }
24564cf49a43SJulian Elischer 	case NGM_CONNECT:
24574cf49a43SJulian Elischer 	    {
24584cf49a43SJulian Elischer 		struct ngm_connect *const con =
24594cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
24604cf49a43SJulian Elischer 		node_p node2;
24614cf49a43SJulian Elischer 
24624cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
24636b795970SJulian Elischer 			TRAP_ERROR();
2464069154d5SJulian Elischer 			error = EINVAL;
2465069154d5SJulian Elischer 			break;
24664cf49a43SJulian Elischer 		}
24674cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
24684cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
24694cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2470069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2471069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
24724cf49a43SJulian Elischer 		if (error)
24734cf49a43SJulian Elischer 			break;
2474e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2475e088dd4cSAlexander Motin 		    node2, con->peerhook);
247630400f03SJulian Elischer 		NG_NODE_UNREF(node2);
24774cf49a43SJulian Elischer 		break;
24784cf49a43SJulian Elischer 	    }
24794cf49a43SJulian Elischer 	case NGM_NAME:
24804cf49a43SJulian Elischer 	    {
24814cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
24824cf49a43SJulian Elischer 
24834cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
24846b795970SJulian Elischer 			TRAP_ERROR();
2485069154d5SJulian Elischer 			error = EINVAL;
2486069154d5SJulian Elischer 			break;
24874cf49a43SJulian Elischer 		}
24884cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
24894cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
24904cf49a43SJulian Elischer 		break;
24914cf49a43SJulian Elischer 	    }
24924cf49a43SJulian Elischer 	case NGM_RMHOOK:
24934cf49a43SJulian Elischer 	    {
24944cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
24954cf49a43SJulian Elischer 		hook_p hook;
24964cf49a43SJulian Elischer 
24974cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
24986b795970SJulian Elischer 			TRAP_ERROR();
2499069154d5SJulian Elischer 			error = EINVAL;
2500069154d5SJulian Elischer 			break;
25014cf49a43SJulian Elischer 		}
25024cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2503899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
25044cf49a43SJulian Elischer 			ng_destroy_hook(hook);
25054cf49a43SJulian Elischer 		break;
25064cf49a43SJulian Elischer 	    }
25074cf49a43SJulian Elischer 	case NGM_NODEINFO:
25084cf49a43SJulian Elischer 	    {
25094cf49a43SJulian Elischer 		struct nodeinfo *ni;
25104cf49a43SJulian Elischer 
2511069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25124cf49a43SJulian Elischer 		if (resp == NULL) {
25134cf49a43SJulian Elischer 			error = ENOMEM;
25144cf49a43SJulian Elischer 			break;
25154cf49a43SJulian Elischer 		}
25164cf49a43SJulian Elischer 
25174cf49a43SJulian Elischer 		/* Fill in node info */
2518069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
251930400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
252087e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
252187e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2522dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
252330400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25244cf49a43SJulian Elischer 		break;
25254cf49a43SJulian Elischer 	    }
25264cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
25274cf49a43SJulian Elischer 	    {
252830400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
25294cf49a43SJulian Elischer 		struct hooklist *hl;
25304cf49a43SJulian Elischer 		struct nodeinfo *ni;
25314cf49a43SJulian Elischer 		hook_p hook;
25324cf49a43SJulian Elischer 
25334cf49a43SJulian Elischer 		/* Get response struct */
2534069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
25354cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2536069154d5SJulian Elischer 		if (resp == NULL) {
25374cf49a43SJulian Elischer 			error = ENOMEM;
25384cf49a43SJulian Elischer 			break;
25394cf49a43SJulian Elischer 		}
2540069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
25414cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
25424cf49a43SJulian Elischer 
25434cf49a43SJulian Elischer 		/* Fill in node info */
254430400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
254587e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
254687e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2547dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
25484cf49a43SJulian Elischer 
25494cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
25504cf49a43SJulian Elischer 		ni->hooks = 0;
255130400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
25524cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
25534cf49a43SJulian Elischer 
25544cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
25554cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
25566e551fb6SDavid E. O'Brien 				    __func__, "hooks");
25574cf49a43SJulian Elischer 				break;
25584cf49a43SJulian Elischer 			}
255930400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
25604cf49a43SJulian Elischer 				continue;
256187e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
256287e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
256330400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
256487e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
256587e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
256687e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
256787e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
256830400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
256930400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
25704cf49a43SJulian Elischer 			ni->hooks++;
25714cf49a43SJulian Elischer 		}
25724cf49a43SJulian Elischer 		break;
25734cf49a43SJulian Elischer 	    }
25744cf49a43SJulian Elischer 
25754cf49a43SJulian Elischer 	case NGM_LISTNAMES:
25764cf49a43SJulian Elischer 	case NGM_LISTNODES:
25774cf49a43SJulian Elischer 	    {
25784cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
25794cf49a43SJulian Elischer 		struct namelist *nl;
25804cf49a43SJulian Elischer 		node_p node;
2581cfea3f85SAlexander Motin 		int num = 0, i;
25824cf49a43SJulian Elischer 
2583cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
25844cf49a43SJulian Elischer 		/* Count number of nodes */
2585cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2586603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2587cfea3f85SAlexander Motin 				if (NG_NODE_IS_VALID(node) &&
2588cfea3f85SAlexander Motin 				    (unnamed || NG_NODE_HAS_NAME(node))) {
25894cf49a43SJulian Elischer 					num++;
25904cf49a43SJulian Elischer 				}
259170de87f2SJulian Elischer 			}
2592cfea3f85SAlexander Motin 		}
2593cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
25944cf49a43SJulian Elischer 
25954cf49a43SJulian Elischer 		/* Get response struct */
2596069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
25974cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2598069154d5SJulian Elischer 		if (resp == NULL) {
25994cf49a43SJulian Elischer 			error = ENOMEM;
26004cf49a43SJulian Elischer 			break;
26014cf49a43SJulian Elischer 		}
2602069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
26034cf49a43SJulian Elischer 
26044cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
26054cf49a43SJulian Elischer 		nl->numnames = 0;
2606cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
2607cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2608603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2609cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2610cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
26114cf49a43SJulian Elischer 
2612b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2613b96baf0aSGleb Smirnoff 					continue;
2614b96baf0aSGleb Smirnoff 				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2615b96baf0aSGleb Smirnoff 					continue;
26164cf49a43SJulian Elischer 				if (nl->numnames >= num) {
2617cfea3f85SAlexander Motin 					log(LOG_ERR, "%s: number of nodes changed\n",
2618cfea3f85SAlexander Motin 					    __func__);
26194cf49a43SJulian Elischer 					break;
26204cf49a43SJulian Elischer 				}
262130400f03SJulian Elischer 				if (NG_NODE_HAS_NAME(node))
262287e2c66aSHartmut Brandt 					strcpy(np->name, NG_NODE_NAME(node));
262387e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2624dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
262530400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
26264cf49a43SJulian Elischer 				nl->numnames++;
26274cf49a43SJulian Elischer 			}
2628cfea3f85SAlexander Motin 		}
2629cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26304cf49a43SJulian Elischer 		break;
26314cf49a43SJulian Elischer 	    }
26324cf49a43SJulian Elischer 
26334cf49a43SJulian Elischer 	case NGM_LISTTYPES:
26344cf49a43SJulian Elischer 	    {
26354cf49a43SJulian Elischer 		struct typelist *tl;
26364cf49a43SJulian Elischer 		struct ng_type *type;
26374cf49a43SJulian Elischer 		int num = 0;
26384cf49a43SJulian Elischer 
26399ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
26404cf49a43SJulian Elischer 		/* Count number of types */
264170de87f2SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26424cf49a43SJulian Elischer 			num++;
264370de87f2SJulian Elischer 		}
26449ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26454cf49a43SJulian Elischer 
26464cf49a43SJulian Elischer 		/* Get response struct */
2647069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
26484cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2649069154d5SJulian Elischer 		if (resp == NULL) {
26504cf49a43SJulian Elischer 			error = ENOMEM;
26514cf49a43SJulian Elischer 			break;
26524cf49a43SJulian Elischer 		}
2653069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
26544cf49a43SJulian Elischer 
26554cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
26564cf49a43SJulian Elischer 		tl->numtypes = 0;
26579ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
2658069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26594cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
26604cf49a43SJulian Elischer 
26614cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
26624cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26636e551fb6SDavid E. O'Brien 				    __func__, "types");
26644cf49a43SJulian Elischer 				break;
26654cf49a43SJulian Elischer 			}
266687e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2667c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
26684cf49a43SJulian Elischer 			tl->numtypes++;
26694cf49a43SJulian Elischer 		}
26709ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26714cf49a43SJulian Elischer 		break;
26724cf49a43SJulian Elischer 	    }
26734cf49a43SJulian Elischer 
2674f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2675f8307e12SArchie Cobbs 	    {
26767133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2677f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2678f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2679069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2680f8307e12SArchie Cobbs 
2681f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2682f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
26834c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
26844c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
26854c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
26866b795970SJulian Elischer 			TRAP_ERROR();
2687f8307e12SArchie Cobbs 			error = EINVAL;
2688f8307e12SArchie Cobbs 			break;
2689f8307e12SArchie Cobbs 		}
2690f8307e12SArchie Cobbs 
2691f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2692069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2693069154d5SJulian Elischer 		if (resp == NULL) {
2694f8307e12SArchie Cobbs 			error = ENOMEM;
2695f8307e12SArchie Cobbs 			break;
2696f8307e12SArchie Cobbs 		}
2697069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2698f8307e12SArchie Cobbs 
2699f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2700f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2701f8307e12SArchie Cobbs 
2702f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
270330400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2704f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2705f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2706f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2707f8307e12SArchie Cobbs 				break;
2708f8307e12SArchie Cobbs 		}
2709f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2710f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2711f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2712f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2713f8307e12SArchie Cobbs 					break;
2714f8307e12SArchie Cobbs 			}
2715f8307e12SArchie Cobbs 			if (c->name == NULL) {
2716069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2717f8307e12SArchie Cobbs 				error = ENOSYS;
2718f8307e12SArchie Cobbs 				break;
2719f8307e12SArchie Cobbs 			}
2720f8307e12SArchie Cobbs 		}
2721f8307e12SArchie Cobbs 
2722f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2723f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2724f8307e12SArchie Cobbs 		    "%s", c->name);
2725f8307e12SArchie Cobbs 
2726f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2727f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2728f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
272970de87f2SJulian Elischer 		if (argstype == NULL) {
2730f8307e12SArchie Cobbs 			*ascii->data = '\0';
273170de87f2SJulian Elischer 		} else {
2732f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2733f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2734f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2735069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2736f8307e12SArchie Cobbs 				break;
2737f8307e12SArchie Cobbs 			}
2738f8307e12SArchie Cobbs 		}
2739f8307e12SArchie Cobbs 
2740f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2741f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2742f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2743069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2744f8307e12SArchie Cobbs 		break;
2745f8307e12SArchie Cobbs 	    }
2746f8307e12SArchie Cobbs 
2747f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2748f8307e12SArchie Cobbs 	    {
2749f8307e12SArchie Cobbs 		int bufSize = 2000;	/* XXX hard coded constant */
2750f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2751f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2752069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
275352ec4a03SArchie Cobbs 		int off = 0;
2754f8307e12SArchie Cobbs 
2755f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2756f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
27574c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
27584c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
27594c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
27604c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
27616b795970SJulian Elischer 			TRAP_ERROR();
2762f8307e12SArchie Cobbs 			error = EINVAL;
2763f8307e12SArchie Cobbs 			break;
2764f8307e12SArchie Cobbs 		}
2765f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2766f8307e12SArchie Cobbs 
2767f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2768069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2769069154d5SJulian Elischer 		if (resp == NULL) {
2770f8307e12SArchie Cobbs 			error = ENOMEM;
2771f8307e12SArchie Cobbs 			break;
2772f8307e12SArchie Cobbs 		}
2773069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2774f8307e12SArchie Cobbs 
2775f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2776f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2777f8307e12SArchie Cobbs 
2778f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
277930400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2780f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2781f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2782f8307e12SArchie Cobbs 				break;
2783f8307e12SArchie Cobbs 		}
2784f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2785f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2786f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2787f8307e12SArchie Cobbs 					break;
2788f8307e12SArchie Cobbs 			}
2789f8307e12SArchie Cobbs 			if (c->name == NULL) {
2790069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2791f8307e12SArchie Cobbs 				error = ENOSYS;
2792f8307e12SArchie Cobbs 				break;
2793f8307e12SArchie Cobbs 			}
2794f8307e12SArchie Cobbs 		}
2795f8307e12SArchie Cobbs 
2796f8307e12SArchie Cobbs 		/* Convert command name to binary */
2797f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2798f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2799f8307e12SArchie Cobbs 
2800f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2801f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2802f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
280370de87f2SJulian Elischer 		if (argstype == NULL) {
2804f8307e12SArchie Cobbs 			bufSize = 0;
280570de87f2SJulian Elischer 		} else {
2806f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2807f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2808069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2809f8307e12SArchie Cobbs 				break;
2810f8307e12SArchie Cobbs 			}
2811f8307e12SArchie Cobbs 		}
2812f8307e12SArchie Cobbs 
2813f8307e12SArchie Cobbs 		/* Return the result */
2814f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2815069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2816f8307e12SArchie Cobbs 		break;
2817f8307e12SArchie Cobbs 	    }
2818f8307e12SArchie Cobbs 
28197095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
28204cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
28214cf49a43SJulian Elischer 		/*
28224cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
28234cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2824069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
28254cf49a43SJulian Elischer 		 * when control passes back to us.
28264cf49a43SJulian Elischer 		 */
282730400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2828069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
282930400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
28304cf49a43SJulian Elischer 		}
28314cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
28324cf49a43SJulian Elischer 	default:
28336b795970SJulian Elischer 		TRAP_ERROR();
28344cf49a43SJulian Elischer 		error = EINVAL;
28354cf49a43SJulian Elischer 	}
2836069154d5SJulian Elischer 	/*
2837069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2838069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2839069154d5SJulian Elischer 	 * Don't free it if it is so.
2840069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2841069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2842069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2843069154d5SJulian Elischer 	 */
2844069154d5SJulian Elischer out:
2845069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
28461acb27c6SJulian Elischer 	if (msg)
2847069154d5SJulian Elischer 		NG_FREE_MSG(msg);
28484cf49a43SJulian Elischer 	return (error);
28494cf49a43SJulian Elischer }
28504cf49a43SJulian Elischer 
28514cf49a43SJulian Elischer /************************************************************************
28528253c060SGleb Smirnoff 			Queue element get/free routines
28538253c060SGleb Smirnoff ************************************************************************/
28548253c060SGleb Smirnoff 
28558253c060SGleb Smirnoff uma_zone_t			ng_qzone;
28566aa6d011SAlexander Motin uma_zone_t			ng_qdzone;
2857ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2858ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
28598253c060SGleb Smirnoff 
28608253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
28618253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
28626aa6d011SAlexander Motin     0, "Maximum number of non-data queue items to allocate");
2863ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
28646aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
28656aa6d011SAlexander Motin     0, "Maximum number of data queue items to allocate");
28668253c060SGleb Smirnoff 
28678253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
28688253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
28698253c060SGleb Smirnoff static int			allocated;	/* number of items malloc'd */
28708253c060SGleb Smirnoff #endif
28718253c060SGleb Smirnoff 
28728253c060SGleb Smirnoff /*
28738253c060SGleb Smirnoff  * Get a queue entry.
28748253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
28758253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
28768253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
28778253c060SGleb Smirnoff  * an interrupt.
28788253c060SGleb Smirnoff  */
28798253c060SGleb Smirnoff static __inline item_p
28806aa6d011SAlexander Motin ng_alloc_item(int type, int flags)
28818253c060SGleb Smirnoff {
28826aa6d011SAlexander Motin 	item_p item;
28838253c060SGleb Smirnoff 
28846aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
28856aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
288642282202SGleb Smirnoff 
28876aa6d011SAlexander Motin 	item = uma_zalloc((type == NGQF_DATA)?ng_qdzone:ng_qzone,
28886aa6d011SAlexander Motin 	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
28898253c060SGleb Smirnoff 
28908253c060SGleb Smirnoff 	if (item) {
28916aa6d011SAlexander Motin 		item->el_flags = type;
28926aa6d011SAlexander Motin #ifdef	NETGRAPH_DEBUG
28938253c060SGleb Smirnoff 		mtx_lock(&ngq_mtx);
28948253c060SGleb Smirnoff 		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
28958253c060SGleb Smirnoff 		allocated++;
28968253c060SGleb Smirnoff 		mtx_unlock(&ngq_mtx);
28978253c060SGleb Smirnoff #endif
28986aa6d011SAlexander Motin 	}
28998253c060SGleb Smirnoff 
29008253c060SGleb Smirnoff 	return (item);
29018253c060SGleb Smirnoff }
29028253c060SGleb Smirnoff 
29038253c060SGleb Smirnoff /*
29048253c060SGleb Smirnoff  * Release a queue entry
29058253c060SGleb Smirnoff  */
29068253c060SGleb Smirnoff void
29078253c060SGleb Smirnoff ng_free_item(item_p item)
29088253c060SGleb Smirnoff {
29098253c060SGleb Smirnoff 	/*
29108253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
29118253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
29128253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
29138253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
29148253c060SGleb Smirnoff 	 * or we release them again here.
29158253c060SGleb Smirnoff 	 */
29168253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
29178253c060SGleb Smirnoff 	case NGQF_DATA:
29188253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
29198253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
29208253c060SGleb Smirnoff 		break;
29218253c060SGleb Smirnoff 	case NGQF_MESG:
29228253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
29238253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
29248253c060SGleb Smirnoff 		break;
29258253c060SGleb Smirnoff 	case NGQF_FN:
2926e088dd4cSAlexander Motin 	case NGQF_FN2:
29278253c060SGleb Smirnoff 		/* nothing to free really, */
29288253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
29298253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
29308253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
29318253c060SGleb Smirnoff 		break;
29328253c060SGleb Smirnoff 	}
29338253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
29348253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
29358253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
29368253c060SGleb Smirnoff 
29378253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29388253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
29398253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
29408253c060SGleb Smirnoff 	allocated--;
29418253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
29428253c060SGleb Smirnoff #endif
29436aa6d011SAlexander Motin 	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA)?
29446aa6d011SAlexander Motin 	    ng_qdzone:ng_qzone, item);
29456aa6d011SAlexander Motin }
29466aa6d011SAlexander Motin 
29476aa6d011SAlexander Motin /*
29486aa6d011SAlexander Motin  * Change type of the queue entry.
29496aa6d011SAlexander Motin  * Possibly reallocates it from another UMA zone.
29506aa6d011SAlexander Motin  */
29516aa6d011SAlexander Motin static __inline item_p
29526aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags)
29536aa6d011SAlexander Motin {
29546aa6d011SAlexander Motin 	item_p item;
29556aa6d011SAlexander Motin 	int from, to;
29566aa6d011SAlexander Motin 
29576aa6d011SAlexander Motin 	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
29586aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29596aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
29606aa6d011SAlexander Motin 
29616aa6d011SAlexander Motin 	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
29626aa6d011SAlexander Motin 	to = (type == NGQF_DATA);
29636aa6d011SAlexander Motin 	if (from != to) {
29646aa6d011SAlexander Motin 		/* If reallocation is required do it and copy item. */
29656aa6d011SAlexander Motin 		if ((item = ng_alloc_item(type, flags)) == NULL) {
29666aa6d011SAlexander Motin 			ng_free_item(pitem);
29676aa6d011SAlexander Motin 			return (NULL);
29686aa6d011SAlexander Motin 		}
29696aa6d011SAlexander Motin 		*item = *pitem;
29706aa6d011SAlexander Motin 		ng_free_item(pitem);
29716aa6d011SAlexander Motin 	} else
29726aa6d011SAlexander Motin 		item = pitem;
29736aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
29746aa6d011SAlexander Motin 
29756aa6d011SAlexander Motin 	return (item);
29768253c060SGleb Smirnoff }
29778253c060SGleb Smirnoff 
29788253c060SGleb Smirnoff /************************************************************************
29794cf49a43SJulian Elischer 			Module routines
29804cf49a43SJulian Elischer ************************************************************************/
29814cf49a43SJulian Elischer 
29824cf49a43SJulian Elischer /*
29834cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
29844cf49a43SJulian Elischer  */
29854cf49a43SJulian Elischer int
29864cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
29874cf49a43SJulian Elischer {
29884cf49a43SJulian Elischer 	struct ng_type *const type = data;
29894cf49a43SJulian Elischer 	int s, error = 0;
29904cf49a43SJulian Elischer 
29914cf49a43SJulian Elischer 	switch (event) {
29924cf49a43SJulian Elischer 	case MOD_LOAD:
29934cf49a43SJulian Elischer 
29944cf49a43SJulian Elischer 		/* Register new netgraph node type */
29954cf49a43SJulian Elischer 		s = splnet();
29964cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
29974cf49a43SJulian Elischer 			splx(s);
29984cf49a43SJulian Elischer 			break;
29994cf49a43SJulian Elischer 		}
30004cf49a43SJulian Elischer 
30014cf49a43SJulian Elischer 		/* Call type specific code */
30024cf49a43SJulian Elischer 		if (type->mod_event != NULL)
3003069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
30049ed346baSBosko Milekic 				mtx_lock(&ng_typelist_mtx);
3005c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
30064cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
30079ed346baSBosko Milekic 				mtx_unlock(&ng_typelist_mtx);
3008069154d5SJulian Elischer 			}
30094cf49a43SJulian Elischer 		splx(s);
30104cf49a43SJulian Elischer 		break;
30114cf49a43SJulian Elischer 
30124cf49a43SJulian Elischer 	case MOD_UNLOAD:
30134cf49a43SJulian Elischer 		s = splnet();
3014c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
30154cf49a43SJulian Elischer 			error = EBUSY;
3016c73b94a2SJulian Elischer 		} else {
3017c73b94a2SJulian Elischer 			if (type->refs == 0) {
3018c73b94a2SJulian Elischer 				/* failed load, nothing to undo */
3019c73b94a2SJulian Elischer 				splx(s);
3020c73b94a2SJulian Elischer 				break;
3021c73b94a2SJulian Elischer 			}
30224cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
30234cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
30244cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
30254cf49a43SJulian Elischer 					splx(s);
30264cf49a43SJulian Elischer 					break;
30274cf49a43SJulian Elischer 				}
30284cf49a43SJulian Elischer 			}
30299ed346baSBosko Milekic 			mtx_lock(&ng_typelist_mtx);
30304cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
30319ed346baSBosko Milekic 			mtx_unlock(&ng_typelist_mtx);
30324cf49a43SJulian Elischer 		}
30334cf49a43SJulian Elischer 		splx(s);
30344cf49a43SJulian Elischer 		break;
30354cf49a43SJulian Elischer 
30364cf49a43SJulian Elischer 	default:
30374cf49a43SJulian Elischer 		if (type->mod_event != NULL)
30384cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
30394cf49a43SJulian Elischer 		else
30403e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
30414cf49a43SJulian Elischer 		break;
30424cf49a43SJulian Elischer 	}
30434cf49a43SJulian Elischer 	return (error);
30444cf49a43SJulian Elischer }
30454cf49a43SJulian Elischer 
30464cf49a43SJulian Elischer /*
30474cf49a43SJulian Elischer  * Handle loading and unloading for this code.
30484cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
30494cf49a43SJulian Elischer  */
30504cf49a43SJulian Elischer static int
30514cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
30524cf49a43SJulian Elischer {
30531489164fSGleb Smirnoff 	int error = 0;
30544cf49a43SJulian Elischer 
30554cf49a43SJulian Elischer 	switch (event) {
30564cf49a43SJulian Elischer 	case MOD_LOAD:
30571489164fSGleb Smirnoff 		/* Initialize everything. */
30582c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3059efd8e7c9SDon Lewis 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3060efd8e7c9SDon Lewis 		    MTX_DEF);
3061efd8e7c9SDon Lewis 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3062efd8e7c9SDon Lewis 		    MTX_DEF);
3063cfea3f85SAlexander Motin 		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3064cfea3f85SAlexander Motin 		    MTX_DEF);
3065ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3066ac5dd141SGleb Smirnoff 		    MTX_DEF);
30671489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3068cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3069cfea3f85SAlexander Motin 		    MTX_DEF);
30701489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3071efd8e7c9SDon Lewis 		    MTX_DEF);
30721489164fSGleb Smirnoff #endif
30731489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
30741489164fSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
30751489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
30766aa6d011SAlexander Motin 		ng_qdzone = uma_zcreate("NetGraph data items", sizeof(struct ng_item),
30776aa6d011SAlexander Motin 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
30786aa6d011SAlexander Motin 		uma_zone_set_max(ng_qdzone, maxdata);
307959dd72d0SRobert Watson 		netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL, 0);
30804cf49a43SJulian Elischer 		break;
30814cf49a43SJulian Elischer 	case MOD_UNLOAD:
308264efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
30834cf49a43SJulian Elischer 		error = EBUSY;
30844cf49a43SJulian Elischer 		break;
30854cf49a43SJulian Elischer 	default:
30864cf49a43SJulian Elischer 		error = EOPNOTSUPP;
30874cf49a43SJulian Elischer 		break;
30884cf49a43SJulian Elischer 	}
30894cf49a43SJulian Elischer 	return (error);
30904cf49a43SJulian Elischer }
30914cf49a43SJulian Elischer 
30924cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
30934cf49a43SJulian Elischer 	"netgraph",
30944cf49a43SJulian Elischer 	ngb_mod_event,
30954cf49a43SJulian Elischer 	(NULL)
30964cf49a43SJulian Elischer };
3097aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
3098bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3099bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3100bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
31014cf49a43SJulian Elischer 
310230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
310330400f03SJulian Elischer void
310430400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
310530400f03SJulian Elischer {
310630400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
310730400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
310830400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
310930400f03SJulian Elischer 		hook->lastfile, hook->lastline);
311030400f03SJulian Elischer 	if (line) {
311130400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
311230400f03SJulian Elischer 	}
311330400f03SJulian Elischer }
311430400f03SJulian Elischer 
311530400f03SJulian Elischer void
311630400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
311730400f03SJulian Elischer {
311830400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
31196b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
312030400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
312130400f03SJulian Elischer 		node->nd_refs, node->nd_name);
312230400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
312330400f03SJulian Elischer 		node->lastfile, node->lastline);
312430400f03SJulian Elischer 	if (line) {
312530400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
312630400f03SJulian Elischer 	}
312730400f03SJulian Elischer }
312830400f03SJulian Elischer 
3129069154d5SJulian Elischer void
3130069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3131069154d5SJulian Elischer {
3132069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3133069154d5SJulian Elischer 		item->lastfile, item->lastline);
31346b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
31356b795970SJulian Elischer 	case NGQF_DATA:
3136069154d5SJulian Elischer 		printf(" - [data]\n");
31376b795970SJulian Elischer 		break;
31386b795970SJulian Elischer 	case NGQF_MESG:
31396b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
31406b795970SJulian Elischer 		break;
31416b795970SJulian Elischer 	case NGQF_FN:
3142857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3143857304e6SRuslan Ermilov 			_NGI_FN(item),
3144857304e6SRuslan Ermilov 			_NGI_NODE(item),
3145857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3146857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3147857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3148857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3149857304e6SRuslan Ermilov 		break;
3150e088dd4cSAlexander Motin 	case NGQF_FN2:
3151eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3152857304e6SRuslan Ermilov 			_NGI_FN2(item),
31536064e568SGleb Smirnoff 			_NGI_NODE(item),
31546064e568SGleb Smirnoff 			_NGI_HOOK(item),
31556b795970SJulian Elischer 			item->body.fn.fn_arg1,
31566b795970SJulian Elischer 			item->body.fn.fn_arg2,
31576b795970SJulian Elischer 			item->body.fn.fn_arg2);
31586b795970SJulian Elischer 		break;
3159069154d5SJulian Elischer 	}
316030400f03SJulian Elischer 	if (line) {
3161069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
31626064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
316330400f03SJulian Elischer 			printf("node %p ([%x])\n",
31646064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3165069154d5SJulian Elischer 		}
316630400f03SJulian Elischer 	}
316730400f03SJulian Elischer }
316830400f03SJulian Elischer 
316930400f03SJulian Elischer static void
317030400f03SJulian Elischer ng_dumpitems(void)
317130400f03SJulian Elischer {
317230400f03SJulian Elischer 	item_p item;
317330400f03SJulian Elischer 	int i = 1;
317430400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
317530400f03SJulian Elischer 		printf("[%d] ", i++);
317630400f03SJulian Elischer 		dumpitem(item, NULL, 0);
317730400f03SJulian Elischer 	}
317830400f03SJulian Elischer }
317930400f03SJulian Elischer 
318030400f03SJulian Elischer static void
318130400f03SJulian Elischer ng_dumpnodes(void)
318230400f03SJulian Elischer {
318330400f03SJulian Elischer 	node_p node;
318430400f03SJulian Elischer 	int i = 1;
318553f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
318630400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
318730400f03SJulian Elischer 		printf("[%d] ", i++);
318830400f03SJulian Elischer 		dumpnode(node, NULL, 0);
318930400f03SJulian Elischer 	}
319053f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
319130400f03SJulian Elischer }
319230400f03SJulian Elischer 
319330400f03SJulian Elischer static void
319430400f03SJulian Elischer ng_dumphooks(void)
319530400f03SJulian Elischer {
319630400f03SJulian Elischer 	hook_p hook;
319730400f03SJulian Elischer 	int i = 1;
319853f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
319930400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
320030400f03SJulian Elischer 		printf("[%d] ", i++);
320130400f03SJulian Elischer 		dumphook(hook, NULL, 0);
320230400f03SJulian Elischer 	}
320353f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
320430400f03SJulian Elischer }
3205069154d5SJulian Elischer 
3206069154d5SJulian Elischer static int
3207069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3208069154d5SJulian Elischer {
3209069154d5SJulian Elischer 	int error;
3210069154d5SJulian Elischer 	int val;
3211069154d5SJulian Elischer 	int i;
3212069154d5SJulian Elischer 
3213069154d5SJulian Elischer 	val = allocated;
3214069154d5SJulian Elischer 	i = 1;
3215041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
32166b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
32176b795970SJulian Elischer 		return (error);
32186b795970SJulian Elischer 	if (val == 42) {
321930400f03SJulian Elischer 		ng_dumpitems();
322030400f03SJulian Elischer 		ng_dumpnodes();
322130400f03SJulian Elischer 		ng_dumphooks();
3222069154d5SJulian Elischer 	}
32236b795970SJulian Elischer 	return (0);
3224069154d5SJulian Elischer }
3225069154d5SJulian Elischer 
32266b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
32276b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
322830400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3229069154d5SJulian Elischer 
3230069154d5SJulian Elischer 
3231069154d5SJulian Elischer /***********************************************************************
3232069154d5SJulian Elischer * Worklist routines
3233069154d5SJulian Elischer **********************************************************************/
3234069154d5SJulian Elischer /* NETISR thread enters here */
3235069154d5SJulian Elischer /*
3236069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3237069154d5SJulian Elischer  * try get an item to process off it.
3238069154d5SJulian Elischer  * If there are no more, remove the node from the list.
3239069154d5SJulian Elischer  */
3240069154d5SJulian Elischer static void
3241069154d5SJulian Elischer ngintr(void)
3242069154d5SJulian Elischer {
3243069154d5SJulian Elischer 	for (;;) {
3244394cb30aSAlexander Motin 		node_p  node;
3245394cb30aSAlexander Motin 
3246394cb30aSAlexander Motin 		/* Get node from the worklist. */
32472c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
32489852972bSAlexander Motin 		node = STAILQ_FIRST(&ng_worklist);
3249069154d5SJulian Elischer 		if (!node) {
32502c8dda8dSWojciech A. Koszek 			NG_WORKLIST_UNLOCK();
3251069154d5SJulian Elischer 			break;
3252069154d5SJulian Elischer 		}
32539852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
32542c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
32552955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
32562955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3257069154d5SJulian Elischer 		/*
3258069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3259069154d5SJulian Elischer 		 * that the list had on it.
3260069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3261069154d5SJulian Elischer 		 * let you have another item off the queue.
3262069154d5SJulian Elischer 		 * All this time, keep the reference
3263069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3264069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3265069154d5SJulian Elischer 		 */
3266069154d5SJulian Elischer 		for (;;) {
3267394cb30aSAlexander Motin 			item_p item;
3268714fb865SGleb Smirnoff 			int rw;
3269714fb865SGleb Smirnoff 
32702c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
32719852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3272069154d5SJulian Elischer 			if (item == NULL) {
32739852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
32742c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3275069154d5SJulian Elischer 				break; /* go look for another node */
3276069154d5SJulian Elischer 			} else {
32772c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
32785951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3279714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
32805951069aSJulian Elischer 				NG_NODE_UNREF(node);
3281069154d5SJulian Elischer 			}
3282069154d5SJulian Elischer 		}
3283a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3284069154d5SJulian Elischer 	}
3285069154d5SJulian Elischer }
3286069154d5SJulian Elischer 
328733338e73SJulian Elischer /*
328833338e73SJulian Elischer  * XXX
328933338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
329033338e73SJulian Elischer  * to be outside the mutex zone
329133338e73SJulian Elischer  */
3292069154d5SJulian Elischer static void
3293394cb30aSAlexander Motin ng_worklist_add(node_p node)
3294069154d5SJulian Elischer {
3295f912c0f7SGleb Smirnoff 
32965bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3297f912c0f7SGleb Smirnoff 
32989852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3299069154d5SJulian Elischer 		/*
3300069154d5SJulian Elischer 		 * If we are not already on the work queue,
3301069154d5SJulian Elischer 		 * then put us on.
3302069154d5SJulian Elischer 		 */
33039852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3304394cb30aSAlexander Motin 		NG_NODE_REF(node); /* XXX fafe in mutex? */
33052c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
33069852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
33072c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3308394cb30aSAlexander Motin 		schednetisr(NETISR_NETGRAPH);
33092955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
33102955ee18SGleb Smirnoff 		    node->nd_ID, node);
3311394cb30aSAlexander Motin 	} else {
33122955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
33132955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3314394cb30aSAlexander Motin 	}
3315069154d5SJulian Elischer }
3316069154d5SJulian Elischer 
3317069154d5SJulian Elischer 
3318069154d5SJulian Elischer /***********************************************************************
3319069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3320069154d5SJulian Elischer ***********************************************************************/
3321069154d5SJulian Elischer 
332230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
332330400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
33244cf49a43SJulian Elischer 	do {								\
33251acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3326069154d5SJulian Elischer 			printf("item already has node");		\
33273de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
33281acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3329069154d5SJulian Elischer 		}							\
33301acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3331069154d5SJulian Elischer 			printf("item already has hook");		\
33323de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
33331acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3334069154d5SJulian Elischer 		}							\
3335069154d5SJulian Elischer 	} while (0)
3336069154d5SJulian Elischer #else
333730400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3338069154d5SJulian Elischer #endif
3339069154d5SJulian Elischer 
3340069154d5SJulian Elischer /*
33418ed370fdSJulian Elischer  * Put mbuf into the item.
3342069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3343069154d5SJulian Elischer  * (or equivalent)
3344069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3345069154d5SJulian Elischer  * remote node might go away in this timescale.
3346069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3347069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
33484be59335SGleb Smirnoff  * here to be able to do this.
3349069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3350069154d5SJulian Elischer  *
3351069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3352069154d5SJulian Elischer  */
3353069154d5SJulian Elischer item_p
335442282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3355069154d5SJulian Elischer {
3356069154d5SJulian Elischer 	item_p item;
3357069154d5SJulian Elischer 
33586aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3359069154d5SJulian Elischer 		NG_FREE_M(m);
3360069154d5SJulian Elischer 		return (NULL);
3361069154d5SJulian Elischer 	}
336230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
33636aa6d011SAlexander Motin 	item->el_flags |= NGQF_READER;
3364069154d5SJulian Elischer 	NGI_M(item) = m;
3365069154d5SJulian Elischer 	return (item);
3366069154d5SJulian Elischer }
3367069154d5SJulian Elischer 
3368069154d5SJulian Elischer /*
3369069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3370069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3371069154d5SJulian Elischer  * to work out what some of the fields should be.
3372069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3373069154d5SJulian Elischer  * (or equivalent)
3374069154d5SJulian Elischer  */
3375069154d5SJulian Elischer item_p
337642282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3377069154d5SJulian Elischer {
3378069154d5SJulian Elischer 	item_p item;
3379069154d5SJulian Elischer 
33806aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3381069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3382069154d5SJulian Elischer 		return (NULL);
3383069154d5SJulian Elischer 	}
338430400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
33856f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
33866f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
33876aa6d011SAlexander Motin 		item->el_flags |= NGQF_READER;
33886f683eeeSGleb Smirnoff 	else
33896aa6d011SAlexander Motin 		item->el_flags |= NGQF_WRITER;
3390069154d5SJulian Elischer 	/*
3391069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3392069154d5SJulian Elischer 	 */
3393069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3394facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3395069154d5SJulian Elischer 	return (item);
3396069154d5SJulian Elischer }
3397069154d5SJulian Elischer 
3398069154d5SJulian Elischer 
3399069154d5SJulian Elischer 
34001acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
34016b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
34026b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3403069154d5SJulian Elischer 			if (retaddr) {					\
3404069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
34054cf49a43SJulian Elischer 			} else {					\
3406069154d5SJulian Elischer 				/*					\
3407069154d5SJulian Elischer 				 * The old return address should be ok.	\
3408069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3409069154d5SJulian Elischer 				 * here.				\
3410069154d5SJulian Elischer 				 */					\
3411069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3412069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3413069154d5SJulian Elischer 						= ng_node2ID(here);	\
3414069154d5SJulian Elischer 				}					\
3415069154d5SJulian Elischer 			}						\
34164cf49a43SJulian Elischer 		}							\
34174cf49a43SJulian Elischer 	} while (0)
34184cf49a43SJulian Elischer 
34194cf49a43SJulian Elischer int
3420069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
34214cf49a43SJulian Elischer {
34221acb27c6SJulian Elischer 	hook_p peer;
34231acb27c6SJulian Elischer 	node_p peernode;
342430400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3425069154d5SJulian Elischer 	/*
3426069154d5SJulian Elischer 	 * Quick sanity check..
342730400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
342830400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
342930400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3430069154d5SJulian Elischer 	 */
3431a04e9846SAlexander Motin 	if ((hook == NULL) ||
3432a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(hook) ||
3433a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3434a04e9846SAlexander Motin 	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3435069154d5SJulian Elischer 		NG_FREE_ITEM(item);
34366b795970SJulian Elischer 		TRAP_ERROR();
3437e08d3e3cSJulian Elischer 		return (ENETDOWN);
34384cf49a43SJulian Elischer 	}
34394cf49a43SJulian Elischer 
34404cf49a43SJulian Elischer 	/*
3441069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
34424cf49a43SJulian Elischer 	 */
34431acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
34441acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
3445a04e9846SAlexander Motin 	NGI_SET_HOOK(item, peer);
34461acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
34478b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3448069154d5SJulian Elischer 	return (0);
3449069154d5SJulian Elischer }
3450069154d5SJulian Elischer 
34514cf49a43SJulian Elischer int
3452069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
34534cf49a43SJulian Elischer {
34544cf49a43SJulian Elischer 	node_p	dest = NULL;
3455069154d5SJulian Elischer 	hook_p	hook = NULL;
34564cf49a43SJulian Elischer 	int	error;
3457069154d5SJulian Elischer 
345830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3459069154d5SJulian Elischer 	/*
3460069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3461069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3462069154d5SJulian Elischer 	 */
3463069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3464069154d5SJulian Elischer 	if (error) {
3465069154d5SJulian Elischer 		NG_FREE_ITEM(item);
346630400f03SJulian Elischer 		return (error);
3467069154d5SJulian Elischer 	}
34681acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
34691acb27c6SJulian Elischer 	if ( hook) {
347030400f03SJulian Elischer 		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
34711acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
34721acb27c6SJulian Elischer 	}
34731acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3474069154d5SJulian Elischer 	return (0);
3475069154d5SJulian Elischer }
3476069154d5SJulian Elischer 
3477069154d5SJulian Elischer int
3478069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3479069154d5SJulian Elischer {
3480069154d5SJulian Elischer 	node_p dest;
3481069154d5SJulian Elischer 
348230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3483069154d5SJulian Elischer 	/*
3484069154d5SJulian Elischer 	 * Find the target node.
3485069154d5SJulian Elischer 	 */
3486069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3487069154d5SJulian Elischer 	if (dest == NULL) {
3488069154d5SJulian Elischer 		NG_FREE_ITEM(item);
34896b795970SJulian Elischer 		TRAP_ERROR();
3490069154d5SJulian Elischer 		return(EINVAL);
3491069154d5SJulian Elischer 	}
3492069154d5SJulian Elischer 	/* Fill out the contents */
34931acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
34941acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
34951acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3496069154d5SJulian Elischer 	return (0);
3497069154d5SJulian Elischer }
3498069154d5SJulian Elischer 
3499069154d5SJulian Elischer /*
3500069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3501069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3502069154d5SJulian Elischer  * Useful for removing that hook :-)
3503069154d5SJulian Elischer  */
3504069154d5SJulian Elischer item_p
3505069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3506069154d5SJulian Elischer {
3507069154d5SJulian Elischer 	item_p item;
35084cf49a43SJulian Elischer 
3509859a4d16SJulian Elischer 	/*
3510859a4d16SJulian Elischer 	 * Find the target node.
3511859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3512859a4d16SJulian Elischer 	 * to the address.
3513859a4d16SJulian Elischer 	 */
35146aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3515069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3516069154d5SJulian Elischer 		return (NULL);
35174cf49a43SJulian Elischer 	}
35184cf49a43SJulian Elischer 
35194cf49a43SJulian Elischer 	/* Fill out the contents */
35206aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
352130400f03SJulian Elischer 	NG_NODE_REF(here);
35221acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
35231acb27c6SJulian Elischer 	if (hook) {
352430400f03SJulian Elischer 		NG_HOOK_REF(hook);
35251acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35261acb27c6SJulian Elischer 	}
3527069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3528069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3529069154d5SJulian Elischer 	return (item);
35304cf49a43SJulian Elischer }
35314cf49a43SJulian Elischer 
3532e088dd4cSAlexander Motin /*
3533e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3534e088dd4cSAlexander Motin  */
3535e088dd4cSAlexander Motin 
353642282202SGleb Smirnoff int
3537b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3538b332b91fSGleb Smirnoff {
3539b332b91fSGleb Smirnoff 
3540b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3541b332b91fSGleb Smirnoff }
3542b332b91fSGleb Smirnoff 
3543b332b91fSGleb Smirnoff int
3544aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
354542282202SGleb Smirnoff 	int flags)
35466b795970SJulian Elischer {
35476b795970SJulian Elischer 	item_p item;
35486b795970SJulian Elischer 
35496aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
35506b795970SJulian Elischer 		return (ENOMEM);
35516b795970SJulian Elischer 	}
35526aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3553a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
35541acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
35551acb27c6SJulian Elischer 	if (hook) {
35566b795970SJulian Elischer 		NG_HOOK_REF(hook);
35571acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35586b795970SJulian Elischer 	}
35596b795970SJulian Elischer 	NGI_FN(item) = fn;
35606b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
35616b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
356242282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
35636b795970SJulian Elischer }
35646b795970SJulian Elischer 
35654cf49a43SJulian Elischer /*
3566b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3567b332b91fSGleb Smirnoff  *
3568b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3569b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3570b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3571b332b91fSGleb Smirnoff  * be used.
3572e088dd4cSAlexander Motin  */
3573e088dd4cSAlexander Motin int
3574b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3575b332b91fSGleb Smirnoff 	int arg2, int flags)
3576e088dd4cSAlexander Motin {
3577e088dd4cSAlexander Motin 	item_p item;
3578e088dd4cSAlexander Motin 
3579b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3580b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3581e088dd4cSAlexander Motin 
3582e088dd4cSAlexander Motin 	/*
3583b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3584b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3585e088dd4cSAlexander Motin 	 */
3586b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
35876aa6d011SAlexander Motin 		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3588e088dd4cSAlexander Motin 			return (ENOMEM);
35896aa6d011SAlexander Motin 		if (pitem != NULL)
35906aa6d011SAlexander Motin 			item->apply = pitem->apply;
3591ed75521fSAlexander Motin 	} else {
35926aa6d011SAlexander Motin 		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
35936aa6d011SAlexander Motin 			return (ENOMEM);
3594ed75521fSAlexander Motin 	}
3595b332b91fSGleb Smirnoff 
35966aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3597e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3598e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3599e088dd4cSAlexander Motin 	if (hook) {
3600e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3601e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3602e088dd4cSAlexander Motin 	}
3603e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3604e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3605e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3606e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3607e088dd4cSAlexander Motin }
3608e088dd4cSAlexander Motin 
3609e088dd4cSAlexander Motin /*
3610d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3611d2ca21a9SJulian Elischer  */
3612d2ca21a9SJulian Elischer static void
36131fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3614d2ca21a9SJulian Elischer {
3615d2ca21a9SJulian Elischer 	item_p item = arg;
3616d2ca21a9SJulian Elischer 
3617d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3618d2ca21a9SJulian Elischer }
3619d2ca21a9SJulian Elischer 
3620d2ca21a9SJulian Elischer 
362130bef41bSGleb Smirnoff int
3622f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3623d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3624d2ca21a9SJulian Elischer {
36251bf8e0faSGleb Smirnoff 	item_p item, oitem;
3626d2ca21a9SJulian Elischer 
36276aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
362830bef41bSGleb Smirnoff 		return (ENOMEM);
362930bef41bSGleb Smirnoff 
36306aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3631d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3632d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3633d2ca21a9SJulian Elischer 	if (hook) {
3634d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3635d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3636d2ca21a9SJulian Elischer 	}
3637d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3638d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3639d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
36401bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
36411bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
36421bf8e0faSGleb Smirnoff 	    oitem != NULL)
36431bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
364430bef41bSGleb Smirnoff 	return (0);
3645d2ca21a9SJulian Elischer }
3646d2ca21a9SJulian Elischer 
3647d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3648d2ca21a9SJulian Elischer int
3649f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3650d2ca21a9SJulian Elischer {
3651d2ca21a9SJulian Elischer 	item_p item;
365230bef41bSGleb Smirnoff 	int rval;
3653d2ca21a9SJulian Elischer 
365403b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
365503b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
365603b25f5dSGleb Smirnoff 
36573eadb26dSGleb Smirnoff 	rval = callout_stop(c);
365830bef41bSGleb Smirnoff 	item = c->c_arg;
365930bef41bSGleb Smirnoff 	/* Do an extra check */
36601fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
366130bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3662d2ca21a9SJulian Elischer 		/*
3663d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3664d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3665d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3666d2ca21a9SJulian Elischer 		 */
3667d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3668d2ca21a9SJulian Elischer 	}
36691bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
367030bef41bSGleb Smirnoff 
367130bef41bSGleb Smirnoff 	return (rval);
3672d2ca21a9SJulian Elischer }
3673d2ca21a9SJulian Elischer 
3674d2ca21a9SJulian Elischer /*
3675069154d5SJulian Elischer  * Set the address, if none given, give the node here.
36764cf49a43SJulian Elischer  */
3677069154d5SJulian Elischer void
3678069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
36794cf49a43SJulian Elischer {
3680069154d5SJulian Elischer 	if (retaddr) {
3681069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3682069154d5SJulian Elischer 	} else {
3683069154d5SJulian Elischer 		/*
3684069154d5SJulian Elischer 		 * The old return address should be ok.
3685069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3686069154d5SJulian Elischer 		 */
3687069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3688069154d5SJulian Elischer 	}
3689069154d5SJulian Elischer }
3690069154d5SJulian Elischer 
3691069154d5SJulian Elischer #define TESTING
3692069154d5SJulian Elischer #ifdef TESTING
3693069154d5SJulian Elischer /* just test all the macros */
3694069154d5SJulian Elischer void
3695069154d5SJulian Elischer ng_macro_test(item_p item);
3696069154d5SJulian Elischer void
3697069154d5SJulian Elischer ng_macro_test(item_p item)
3698069154d5SJulian Elischer {
3699069154d5SJulian Elischer 	node_p node = NULL;
3700069154d5SJulian Elischer 	hook_p hook = NULL;
37014cf49a43SJulian Elischer 	struct mbuf *m;
37024cf49a43SJulian Elischer 	struct ng_mesg *msg;
3703069154d5SJulian Elischer 	ng_ID_t retaddr;
3704069154d5SJulian Elischer 	int	error;
37054cf49a43SJulian Elischer 
3706069154d5SJulian Elischer 	NGI_GET_M(item, m);
3707069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3708069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
37098ed370fdSJulian Elischer 	NG_SEND_DATA(error, hook, m, NULL);
3710069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3711069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
371230400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3713069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3714069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3715069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3716069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
37174cf49a43SJulian Elischer }
3718069154d5SJulian Elischer #endif /* TESTING */
37194cf49a43SJulian Elischer 
3720