xref: /freebsd/sys/netgraph/ng_base.c (revision 8f9ac44aa7f97c97d0376db68107435f49cc40c6)
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>
64394cb30aSAlexander Motin #include <machine/cpu.h>
654cf49a43SJulian Elischer 
664cf49a43SJulian Elischer #include <net/netisr.h>
674cf49a43SJulian Elischer 
684cf49a43SJulian Elischer #include <netgraph/ng_message.h>
694cf49a43SJulian Elischer #include <netgraph/netgraph.h>
70f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
714cf49a43SJulian Elischer 
729d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION);
7399ff8176SPeter Wemm 
74ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */
75ac5dd141SGleb Smirnoff static struct mtx	ng_topo_mtx;
76ac5dd141SGleb Smirnoff 
7730400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
78cfea3f85SAlexander Motin static struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
791489164fSGleb Smirnoff static struct mtx	ngq_mtx;	/* protects the queue item list */
8030400f03SJulian Elischer 
8130400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
8230400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8330400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
8430400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8530400f03SJulian Elischer 
8630400f03SJulian Elischer static void ng_dumpitems(void);
8730400f03SJulian Elischer static void ng_dumpnodes(void);
8830400f03SJulian Elischer static void ng_dumphooks(void);
8930400f03SJulian Elischer 
9030400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
91954c4772SJulian Elischer /*
92954c4772SJulian Elischer  * DEAD versions of the structures.
93954c4772SJulian Elischer  * In order to avoid races, it is sometimes neccesary to point
94954c4772SJulian Elischer  * at SOMETHING even though theoretically, the current entity is
95954c4772SJulian Elischer  * INVALID. Use these to avoid these races.
96954c4772SJulian Elischer  */
97954c4772SJulian Elischer struct ng_type ng_deadtype = {
98954c4772SJulian Elischer 	NG_ABI_VERSION,
99954c4772SJulian Elischer 	"dead",
100954c4772SJulian Elischer 	NULL,	/* modevent */
101954c4772SJulian Elischer 	NULL,	/* constructor */
102954c4772SJulian Elischer 	NULL,	/* rcvmsg */
103954c4772SJulian Elischer 	NULL,	/* shutdown */
104954c4772SJulian Elischer 	NULL,	/* newhook */
105954c4772SJulian Elischer 	NULL,	/* findhook */
106954c4772SJulian Elischer 	NULL,	/* connect */
107954c4772SJulian Elischer 	NULL,	/* rcvdata */
108954c4772SJulian Elischer 	NULL,	/* disconnect */
109954c4772SJulian Elischer 	NULL, 	/* cmdlist */
110954c4772SJulian Elischer };
11130400f03SJulian Elischer 
112954c4772SJulian Elischer struct ng_node ng_deadnode = {
113954c4772SJulian Elischer 	"dead",
114954c4772SJulian Elischer 	&ng_deadtype,
115be4252b3SJulian Elischer 	NGF_INVALID,
116954c4772SJulian Elischer 	1,	/* refs */
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 	{},	/* workqueue entry */
124954c4772SJulian Elischer 	{	0,
125954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
126954c4772SJulian Elischer 		NULL,
127954c4772SJulian Elischer 		&ng_deadnode.nd_input_queue.queue,
128954c4772SJulian Elischer 		&ng_deadnode
129954c4772SJulian Elischer 	},
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,
142954c4772SJulian Elischer 	1,		/* refs always >= 1 */
143e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
144954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
145954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
146954c4772SJulian Elischer 	{},		/* hooks list */
147c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
148c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
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 */
161069154d5SJulian Elischer static TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_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);			\
1780f150d04SJulian Elischer 		LIST_FOREACH(node, &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);
208069154d5SJulian Elischer static void	ng_flush_input_queue(struct ng_queue * ngq);
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);
63430400f03SJulian Elischer 	node->nd_input_queue.queue = NULL;
63530400f03SJulian Elischer 	node->nd_input_queue.last = &node->nd_input_queue.queue;
63630400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
63730400f03SJulian Elischer 	node->nd_input_queue.q_node = node;
6384cf49a43SJulian Elischer 
6394cf49a43SJulian Elischer 	/* Initialize hook list for new node */
64030400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6414cf49a43SJulian Elischer 
642cfea3f85SAlexander Motin 	/* Link us into the name hash. */
643cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
644cfea3f85SAlexander Motin 	LIST_INSERT_HEAD(&ng_name_hash[0], node, nd_nodes);
645cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
646069154d5SJulian Elischer 
647dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
6489ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
64930400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
650069154d5SJulian Elischer 		node_p node2 = NULL;
65130400f03SJulian Elischer 		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
6520f150d04SJulian Elischer 
65330400f03SJulian Elischer 		/* Is there a problem with the new number? */
6540f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6550f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
65630400f03SJulian Elischer 			break;
657069154d5SJulian Elischer 		}
65830400f03SJulian Elischer 	}
6590f150d04SJulian Elischer 	LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
66030400f03SJulian Elischer 							node, nd_idnodes);
6619ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
662dc90cad9SJulian Elischer 
6634cf49a43SJulian Elischer 	/* Done */
6644cf49a43SJulian Elischer 	*nodepp = node;
6654cf49a43SJulian Elischer 	return (0);
6664cf49a43SJulian Elischer }
6674cf49a43SJulian Elischer 
6684cf49a43SJulian Elischer /*
6694cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
67064efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6714cf49a43SJulian Elischer  * no type-specific method.
6724cf49a43SJulian Elischer  *
67364efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6743e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6753e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
676069154d5SJulian Elischer  *
677069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
67864efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
6793e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
6803e4084c8SJulian Elischer  * in which case we allow the node to survive.
6813e4084c8SJulian Elischer  *
68264efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
6833e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
6843e4084c8SJulian Elischer  * are rebooting.... etc.
6854cf49a43SJulian Elischer  */
6864cf49a43SJulian Elischer void
6871acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
6884cf49a43SJulian Elischer {
6893e4084c8SJulian Elischer 	hook_p hook;
6903e4084c8SJulian Elischer 
6914cf49a43SJulian Elischer 	/* Check if it's already shutting down */
692be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
6934cf49a43SJulian Elischer 		return;
6944cf49a43SJulian Elischer 
6951acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
6961acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
6971acb27c6SJulian Elischer 		return;
6981acb27c6SJulian Elischer 	}
6991acb27c6SJulian Elischer 
7004cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
70130400f03SJulian Elischer 	NG_NODE_REF(node);
7024cf49a43SJulian Elischer 
70330400f03SJulian Elischer 	/*
70430400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
70530400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
706be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
70730400f03SJulian Elischer 	 * creation
70830400f03SJulian Elischer 	 */
709be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7104cf49a43SJulian Elischer 
711991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
712991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
713991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
714991fc65aSJulian Elischer 
7153e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7163e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7173e4084c8SJulian Elischer 		ng_destroy_hook(hook);
71830400f03SJulian Elischer 
719069154d5SJulian Elischer 	/*
720069154d5SJulian Elischer 	 * Drain the input queue forceably.
72130400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
72230400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
72330400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
724b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
725069154d5SJulian Elischer 	 */
72630400f03SJulian Elischer 	ng_flush_input_queue(&node->nd_input_queue);
727069154d5SJulian Elischer 
728069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
72930400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
73030400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
73130400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
73230400f03SJulian Elischer 			/*
73330400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
73430400f03SJulian Elischer 			 * that it doesn't want to die.
73530400f03SJulian Elischer 			 * Presumably it is a persistant node.
7361acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7371acb27c6SJulian Elischer 			 *  e.g. hardware going away,
738be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
73930400f03SJulian Elischer 			 */
740be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7411acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
74230400f03SJulian Elischer 			return;
7434cf49a43SJulian Elischer 		}
7441acb27c6SJulian Elischer 	} else {				/* do the default thing */
7451acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7461acb27c6SJulian Elischer 	}
7474cf49a43SJulian Elischer 
74830400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
74930400f03SJulian Elischer 
75030400f03SJulian Elischer 	/*
75130400f03SJulian Elischer 	 * Remove extra reference, possibly the last
75230400f03SJulian Elischer 	 * Possible other holders of references may include
75330400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
75430400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
75530400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
75630400f03SJulian Elischer 	 */
75730400f03SJulian Elischer 	NG_NODE_UNREF(node);
7584cf49a43SJulian Elischer }
7594cf49a43SJulian Elischer 
7605951069aSJulian Elischer /*
7615951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7625951069aSJulian Elischer  * deadnode always acts as it it were the last.
7635951069aSJulian Elischer  */
7645951069aSJulian Elischer int
76530400f03SJulian Elischer ng_unref_node(node_p node)
7664cf49a43SJulian Elischer {
76730400f03SJulian Elischer 	int v;
7686b795970SJulian Elischer 
7696b795970SJulian Elischer 	if (node == &ng_deadnode) {
7705951069aSJulian Elischer 		return (0);
7716b795970SJulian Elischer 	}
7726b795970SJulian Elischer 
773018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&node->nd_refs, -1);
774e8a49db2SJulian Elischer 
775018fe3d1SAlexander Motin 	if (v == 1) { /* we were the last */
776069154d5SJulian Elischer 
777cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
77830400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
77930400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
780cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
781069154d5SJulian Elischer 
7829ed346baSBosko Milekic 		mtx_lock(&ng_idhash_mtx);
78330400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
7849ed346baSBosko Milekic 		mtx_unlock(&ng_idhash_mtx);
785069154d5SJulian Elischer 
78612574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
787069154d5SJulian Elischer 		NG_FREE_NODE(node);
7884cf49a43SJulian Elischer 	}
789018fe3d1SAlexander Motin 	return (v - 1);
7904cf49a43SJulian Elischer }
7914cf49a43SJulian Elischer 
7924cf49a43SJulian Elischer /************************************************************************
793dc90cad9SJulian Elischer 			Node ID handling
794dc90cad9SJulian Elischer ************************************************************************/
795dc90cad9SJulian Elischer static node_p
796069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
797dc90cad9SJulian Elischer {
79830400f03SJulian Elischer 	node_p node;
7999ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
8000f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
80130400f03SJulian Elischer 	if(node)
80230400f03SJulian Elischer 		NG_NODE_REF(node);
8039ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
80430400f03SJulian Elischer 	return(node);
805dc90cad9SJulian Elischer }
806dc90cad9SJulian Elischer 
807dc90cad9SJulian Elischer ng_ID_t
808dc90cad9SJulian Elischer ng_node2ID(node_p node)
809dc90cad9SJulian Elischer {
81070de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
811dc90cad9SJulian Elischer }
812dc90cad9SJulian Elischer 
813dc90cad9SJulian Elischer /************************************************************************
8144cf49a43SJulian Elischer 			Node name handling
8154cf49a43SJulian Elischer ************************************************************************/
8164cf49a43SJulian Elischer 
8174cf49a43SJulian Elischer /*
8184cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
8194cf49a43SJulian Elischer  */
8204cf49a43SJulian Elischer int
8214cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8224cf49a43SJulian Elischer {
823cfea3f85SAlexander Motin 	int i, hash;
824069154d5SJulian Elischer 	node_p node2;
8254cf49a43SJulian Elischer 
8264cf49a43SJulian Elischer 	/* Check the name is valid */
82787e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8284cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8294cf49a43SJulian Elischer 			break;
8304cf49a43SJulian Elischer 	}
8314cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8326b795970SJulian Elischer 		TRAP_ERROR();
8334cf49a43SJulian Elischer 		return (EINVAL);
8344cf49a43SJulian Elischer 	}
835dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8366b795970SJulian Elischer 		TRAP_ERROR();
8374cf49a43SJulian Elischer 		return (EINVAL);
8384cf49a43SJulian Elischer 	}
8394cf49a43SJulian Elischer 
8404cf49a43SJulian Elischer 	/* Check the name isn't already being used */
841069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
84230400f03SJulian Elischer 		NG_NODE_UNREF(node2);
8436b795970SJulian Elischer 		TRAP_ERROR();
8444cf49a43SJulian Elischer 		return (EADDRINUSE);
8454cf49a43SJulian Elischer 	}
8464cf49a43SJulian Elischer 
847069154d5SJulian Elischer 	/* copy it */
84887e2c66aSHartmut Brandt 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8494cf49a43SJulian Elischer 
850cfea3f85SAlexander Motin 	/* Update name hash. */
851cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
852cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
853cfea3f85SAlexander Motin 	LIST_REMOVE(node, nd_nodes);
854cfea3f85SAlexander Motin 	LIST_INSERT_HEAD(&ng_name_hash[hash], node, nd_nodes);
855cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
856cfea3f85SAlexander Motin 
8574cf49a43SJulian Elischer 	return (0);
8584cf49a43SJulian Elischer }
8594cf49a43SJulian Elischer 
8604cf49a43SJulian Elischer /*
8614cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8624cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8634cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8644cf49a43SJulian Elischer  *
8654cf49a43SJulian Elischer  * Returns the node if found, else NULL.
866069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
867e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
868e1e8f51bSRobert Watson  * there.
8694cf49a43SJulian Elischer  */
8704cf49a43SJulian Elischer node_p
871069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
8724cf49a43SJulian Elischer {
873dc90cad9SJulian Elischer 	node_p node;
874dc90cad9SJulian Elischer 	ng_ID_t temp;
875cfea3f85SAlexander Motin 	int	hash;
8764cf49a43SJulian Elischer 
8774cf49a43SJulian Elischer 	/* "." means "this node" */
878069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
87930400f03SJulian Elischer 		NG_NODE_REF(here);
880069154d5SJulian Elischer 		return(here);
881069154d5SJulian Elischer 	}
8824cf49a43SJulian Elischer 
8834cf49a43SJulian Elischer 	/* Check for name-by-ID */
884dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
885069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
8864cf49a43SJulian Elischer 	}
8874cf49a43SJulian Elischer 
8884cf49a43SJulian Elischer 	/* Find node by name */
889cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
890cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
891cfea3f85SAlexander Motin 	LIST_FOREACH(node, &ng_name_hash[hash], nd_nodes) {
892cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
893cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
8944cf49a43SJulian Elischer 			break;
8954cf49a43SJulian Elischer 		}
89670de87f2SJulian Elischer 	}
897069154d5SJulian Elischer 	if (node)
89830400f03SJulian Elischer 		NG_NODE_REF(node);
899cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
9004cf49a43SJulian Elischer 	return (node);
9014cf49a43SJulian Elischer }
9024cf49a43SJulian Elischer 
9034cf49a43SJulian Elischer /*
9049d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
905dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9064cf49a43SJulian Elischer  */
907dc90cad9SJulian Elischer static ng_ID_t
9084cf49a43SJulian Elischer ng_decodeidname(const char *name)
9094cf49a43SJulian Elischer {
9102b70adcbSArchie Cobbs 	const int len = strlen(name);
91125792ef3SArchie Cobbs 	char *eptr;
9122b70adcbSArchie Cobbs 	u_long val;
9134cf49a43SJulian Elischer 
9142b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
91570de87f2SJulian Elischer 	if ((len < 3)
91670de87f2SJulian Elischer 	|| (name[0] != '[')
91770de87f2SJulian Elischer 	|| (name[len - 1] != ']')
91870de87f2SJulian Elischer 	|| (!isxdigit(name[1]))) {
91970de87f2SJulian Elischer 		return ((ng_ID_t)0);
92070de87f2SJulian Elischer 	}
9214cf49a43SJulian Elischer 
9222b70adcbSArchie Cobbs 	/* Decode number */
9232b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
92470de87f2SJulian Elischer 	if ((eptr - name != len - 1)
92570de87f2SJulian Elischer 	|| (val == ULONG_MAX)
92670de87f2SJulian Elischer 	|| (val == 0)) {
92712f035e0SJulian Elischer 		return ((ng_ID_t)0);
92870de87f2SJulian Elischer 	}
9292b70adcbSArchie Cobbs 	return (ng_ID_t)val;
9304cf49a43SJulian Elischer }
9314cf49a43SJulian Elischer 
9324cf49a43SJulian Elischer /*
9334cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9344cf49a43SJulian Elischer  * when shutting down and removing the node.
93564efc707SRobert Watson  * IF we allow name changing this may be more resurrected.
9364cf49a43SJulian Elischer  */
9374cf49a43SJulian Elischer void
9384cf49a43SJulian Elischer ng_unname(node_p node)
9394cf49a43SJulian Elischer {
9404cf49a43SJulian Elischer }
9414cf49a43SJulian Elischer 
9424cf49a43SJulian Elischer /************************************************************************
9434cf49a43SJulian Elischer 			Hook routines
9444cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
9453e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
9463e4084c8SJulian Elischer  they are connected to the 'dead' hook.
9474cf49a43SJulian Elischer ************************************************************************/
9484cf49a43SJulian Elischer 
9494cf49a43SJulian Elischer /*
9504cf49a43SJulian Elischer  * Remove a hook reference
9514cf49a43SJulian Elischer  */
95230400f03SJulian Elischer void
9534cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
9544cf49a43SJulian Elischer {
95530400f03SJulian Elischer 	int v;
9566b795970SJulian Elischer 
9576b795970SJulian Elischer 	if (hook == &ng_deadhook) {
9586b795970SJulian Elischer 		return;
9596b795970SJulian Elischer 	}
960018fe3d1SAlexander Motin 
961018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&hook->hk_refs, -1);
962e8a49db2SJulian Elischer 
96330400f03SJulian Elischer 	if (v == 1) { /* we were the last */
964f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
9656b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
966069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
967069154d5SJulian Elischer 	}
9684cf49a43SJulian Elischer }
9694cf49a43SJulian Elischer 
9704cf49a43SJulian Elischer /*
9714cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
9723e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
9734cf49a43SJulian Elischer  */
9744cf49a43SJulian Elischer static int
9754cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
9764cf49a43SJulian Elischer {
9774cf49a43SJulian Elischer 	hook_p hook;
9784cf49a43SJulian Elischer 	int error = 0;
9794cf49a43SJulian Elischer 
9804cf49a43SJulian Elischer 	/* Check that the given name is good */
9814cf49a43SJulian Elischer 	if (name == NULL) {
9826b795970SJulian Elischer 		TRAP_ERROR();
9834cf49a43SJulian Elischer 		return (EINVAL);
9844cf49a43SJulian Elischer 	}
985899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
9866b795970SJulian Elischer 		TRAP_ERROR();
9874cf49a43SJulian Elischer 		return (EEXIST);
9884cf49a43SJulian Elischer 	}
9894cf49a43SJulian Elischer 
9904cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
99130400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
9924cf49a43SJulian Elischer 	if (hook == NULL) {
9936b795970SJulian Elischer 		TRAP_ERROR();
9944cf49a43SJulian Elischer 		return (ENOMEM);
9954cf49a43SJulian Elischer 	}
9963e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
99730400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
9983e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
99930400f03SJulian Elischer 	hook->hk_node = node;
100030400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
10014cf49a43SJulian Elischer 
10023e4084c8SJulian Elischer 	/* Set hook name */
100387e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10043e4084c8SJulian Elischer 
10053e4084c8SJulian Elischer 	/*
10063e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10073e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10083e4084c8SJulian Elischer 	 */
1009954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1010954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
101130400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1012069154d5SJulian Elischer 			return (error);
1013069154d5SJulian Elischer 		}
1014954c4772SJulian Elischer 	}
10154cf49a43SJulian Elischer 	/*
10164cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10174cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10184cf49a43SJulian Elischer 	 */
101930400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
102030400f03SJulian Elischer 	node->nd_numhooks++;
10213e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
10224cf49a43SJulian Elischer 
10234cf49a43SJulian Elischer 	if (hookp)
10244cf49a43SJulian Elischer 		*hookp = hook;
10253e4084c8SJulian Elischer 	return (0);
10264cf49a43SJulian Elischer }
10274cf49a43SJulian Elischer 
10284cf49a43SJulian Elischer /*
1029899e9c4eSArchie Cobbs  * Find a hook
1030899e9c4eSArchie Cobbs  *
1031899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1032899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
10333e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1034899e9c4eSArchie Cobbs  */
1035899e9c4eSArchie Cobbs hook_p
1036899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1037899e9c4eSArchie Cobbs {
1038899e9c4eSArchie Cobbs 	hook_p hook;
1039899e9c4eSArchie Cobbs 
104030400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
104130400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
104230400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
104370de87f2SJulian Elischer 		if (NG_HOOK_IS_VALID(hook)
1044ce5e5f99SArchie Cobbs 		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
1045899e9c4eSArchie Cobbs 			return (hook);
1046899e9c4eSArchie Cobbs 	}
1047899e9c4eSArchie Cobbs 	return (NULL);
1048899e9c4eSArchie Cobbs }
1049899e9c4eSArchie Cobbs 
1050899e9c4eSArchie Cobbs /*
10514cf49a43SJulian Elischer  * Destroy a hook
10524cf49a43SJulian Elischer  *
10534cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
10544cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
10553e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
10563e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
10573e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
10583e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
10593e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
10603e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
10613e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
10623e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
10633e4084c8SJulian Elischer  * attached to the 'dead' hook.
10646b795970SJulian Elischer  *
10656b795970SJulian Elischer  * This routine is called at all stages of hook creation
10666b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
10674cf49a43SJulian Elischer  */
10684cf49a43SJulian Elischer void
10694cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
10704cf49a43SJulian Elischer {
1071ac5dd141SGleb Smirnoff 	hook_p peer;
1072ac5dd141SGleb Smirnoff 	node_p node;
10734cf49a43SJulian Elischer 
10746b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
10756b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
10766b795970SJulian Elischer 		return;
10776b795970SJulian Elischer 	}
1078ac5dd141SGleb Smirnoff 
1079ac5dd141SGleb Smirnoff 	/*
1080ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1081ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1082ac5dd141SGleb Smirnoff 	 */
1083ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1084ac5dd141SGleb Smirnoff 
1085ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1086ac5dd141SGleb Smirnoff 
1087ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1088ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1089ac5dd141SGleb Smirnoff 
10903e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
10913e4084c8SJulian Elischer 		/*
10923e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
10933e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
10943e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
10953e4084c8SJulian Elischer 		 */
10963e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
10973e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
10986b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
10996b795970SJulian Elischer 			/*
11006b795970SJulian Elischer 			 * If it's already divorced from a node,
11016b795970SJulian Elischer 			 * just free it.
11026b795970SJulian Elischer 			 */
1103ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11046b795970SJulian Elischer 		} else {
1105ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11066b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11076b795970SJulian Elischer 		}
110852fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
110952fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1110ac5dd141SGleb Smirnoff 	} else
1111ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1112ac5dd141SGleb Smirnoff 
1113ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11144cf49a43SJulian Elischer 
11154cf49a43SJulian Elischer 	/*
11164cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11174cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11184cf49a43SJulian Elischer 	 */
11196b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11206b795970SJulian Elischer 		return;
11216b795970SJulian Elischer 	}
112230400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
112330400f03SJulian Elischer 	node->nd_numhooks--;
112430400f03SJulian Elischer 	if (node->nd_type->disconnect) {
11254cf49a43SJulian Elischer 		/*
11266b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
112764efc707SRobert Watson 		 * trust its existence after this point. (except
11286b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
11296b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
11304cf49a43SJulian Elischer 		 */
113130400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
11324cf49a43SJulian Elischer 	}
11336b795970SJulian Elischer 
11346b795970SJulian Elischer 	/*
11356b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
11366b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
11376b795970SJulian Elischer 	 */
11386b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
11396b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
11406b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
11414cf49a43SJulian Elischer }
11424cf49a43SJulian Elischer 
11434cf49a43SJulian Elischer /*
11444cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
11454cf49a43SJulian Elischer  * is effectively bypassed.
11464cf49a43SJulian Elischer  */
11474cf49a43SJulian Elischer int
11484cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
11494cf49a43SJulian Elischer {
115030400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
11516b795970SJulian Elischer 		TRAP_ERROR();
11524cf49a43SJulian Elischer 		return (EINVAL);
115330400f03SJulian Elischer 	}
115430400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
115530400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
11564cf49a43SJulian Elischer 
11573e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
11583e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
11593e4084c8SJulian Elischer 
1160cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1161cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1162cf3254aaSGleb Smirnoff 
11634cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
11644cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
11654cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
11664cf49a43SJulian Elischer 	return (0);
11674cf49a43SJulian Elischer }
11684cf49a43SJulian Elischer 
11694cf49a43SJulian Elischer /*
11704cf49a43SJulian Elischer  * Install a new netgraph type
11714cf49a43SJulian Elischer  */
11724cf49a43SJulian Elischer int
11734cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
11744cf49a43SJulian Elischer {
11754cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
11764cf49a43SJulian Elischer 
11774cf49a43SJulian Elischer 	/* Check version and type name fields */
1178589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
1179589f6ed8SJulian Elischer 	|| (namelen == 0)
118087e2c66aSHartmut Brandt 	|| (namelen >= NG_TYPESIZ)) {
11816b795970SJulian Elischer 		TRAP_ERROR();
11828ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
11838ed370fdSJulian Elischer 			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
11848ed370fdSJulian Elischer 		}
11854cf49a43SJulian Elischer 		return (EINVAL);
11864cf49a43SJulian Elischer 	}
11874cf49a43SJulian Elischer 
11884cf49a43SJulian Elischer 	/* Check for name collision */
11894cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
11906b795970SJulian Elischer 		TRAP_ERROR();
11914cf49a43SJulian Elischer 		return (EEXIST);
11924cf49a43SJulian Elischer 	}
11934cf49a43SJulian Elischer 
1194069154d5SJulian Elischer 
1195069154d5SJulian Elischer 	/* Link in new type */
11969ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1197069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1198c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
11999ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12004cf49a43SJulian Elischer 	return (0);
12014cf49a43SJulian Elischer }
12024cf49a43SJulian Elischer 
12034cf49a43SJulian Elischer /*
1204c31b4a53SJulian Elischer  * unlink a netgraph type
1205c31b4a53SJulian Elischer  * If no examples exist
1206c31b4a53SJulian Elischer  */
1207c31b4a53SJulian Elischer int
1208c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1209c31b4a53SJulian Elischer {
1210c31b4a53SJulian Elischer 	/* Check for name collision */
1211c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1212c31b4a53SJulian Elischer 		TRAP_ERROR();
1213c31b4a53SJulian Elischer 		return (EBUSY);
1214c31b4a53SJulian Elischer 	}
1215c31b4a53SJulian Elischer 
1216c31b4a53SJulian Elischer 	/* Unlink type */
1217c31b4a53SJulian Elischer 	mtx_lock(&ng_typelist_mtx);
1218c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1219c31b4a53SJulian Elischer 	mtx_unlock(&ng_typelist_mtx);
1220c31b4a53SJulian Elischer 	return (0);
1221c31b4a53SJulian Elischer }
1222c31b4a53SJulian Elischer 
1223c31b4a53SJulian Elischer /*
12244cf49a43SJulian Elischer  * Look for a type of the name given
12254cf49a43SJulian Elischer  */
12264cf49a43SJulian Elischer struct ng_type *
12274cf49a43SJulian Elischer ng_findtype(const char *typename)
12284cf49a43SJulian Elischer {
12294cf49a43SJulian Elischer 	struct ng_type *type;
12304cf49a43SJulian Elischer 
12319ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1232069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
12334cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
12344cf49a43SJulian Elischer 			break;
12354cf49a43SJulian Elischer 	}
12369ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12374cf49a43SJulian Elischer 	return (type);
12384cf49a43SJulian Elischer }
12394cf49a43SJulian Elischer 
12404cf49a43SJulian Elischer /************************************************************************
12414cf49a43SJulian Elischer 			Composite routines
12424cf49a43SJulian Elischer ************************************************************************/
12434cf49a43SJulian Elischer /*
12446b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
12454cf49a43SJulian Elischer  */
1246e088dd4cSAlexander Motin static int
1247e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
12484cf49a43SJulian Elischer {
1249e088dd4cSAlexander Motin 	int	error = 0;
12504cf49a43SJulian Elischer 
12516b795970SJulian Elischer 	/*
12526b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12536b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12546b795970SJulian Elischer 	 * Our caller has a reference on the node.
12556b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12566b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12571acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
12581acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
12596b795970SJulian Elischer 	 */
12606b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
12616b795970SJulian Elischer 		/*
12626b795970SJulian Elischer 		 * The node must have been freed again since we last visited
12636b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
12646b795970SJulian Elischer 		 * We should just release our references and
12656b795970SJulian Elischer 		 * free anything we can think of.
12666b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
12676b795970SJulian Elischer 		 * that holds the references, just return.
12686b795970SJulian Elischer 		 */
1269e088dd4cSAlexander Motin 		ERROUT(ENOENT);
12706b795970SJulian Elischer 	}
12716b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1272e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
12736b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
12741acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1275e088dd4cSAlexander Motin 			ERROUT(error);
12764cf49a43SJulian Elischer 		}
12776b795970SJulian Elischer 	}
12786b795970SJulian Elischer 	/*
12796b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
12806b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
12816b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
12826b795970SJulian Elischer 	 */
12836b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1284e088dd4cSAlexander Motin done:
1285e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1286e088dd4cSAlexander Motin 	return (error);
12876b795970SJulian Elischer }
12886b795970SJulian Elischer 
1289e088dd4cSAlexander Motin static int
1290e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
12916b795970SJulian Elischer {
1292ac5dd141SGleb Smirnoff 	hook_p	peer;
1293e088dd4cSAlexander Motin 	int	error = 0;
12946b795970SJulian Elischer 
12956b795970SJulian Elischer 	/*
12966b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12976b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12986b795970SJulian Elischer 	 * Our caller has a reference on the node.
12996b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13006b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13016b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
13026b795970SJulian Elischer 	 * First check the hook name is unique.
13031acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13046b795970SJulian Elischer 	 */
13056b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13066b795970SJulian Elischer 		TRAP_ERROR();
13076b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
13081acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1309e088dd4cSAlexander Motin 		ERROUT(EEXIST);
13106b795970SJulian Elischer 	}
13116b795970SJulian Elischer 	/*
13126b795970SJulian Elischer 	 * Check if the node type code has something to say about it
13136b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
13146b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
13156b795970SJulian Elischer 	 * The peer hook will also be destroyed.
13166b795970SJulian Elischer 	 */
13176b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1318e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1319e088dd4cSAlexander Motin 		    hook->hk_name))) {
13206b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
13211acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1322e088dd4cSAlexander Motin 			ERROUT(error);
13236b795970SJulian Elischer 		}
13246b795970SJulian Elischer 	}
13256b795970SJulian Elischer 
13266b795970SJulian Elischer 	/*
13276b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
13286b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
13296b795970SJulian Elischer 	 */
13306b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
13316b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
13326b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
13336b795970SJulian Elischer 	node->nd_numhooks++;
13346b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
13356b795970SJulian Elischer 
13366b795970SJulian Elischer 	/*
133764efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
13385951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
13396b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
13406b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
13416b795970SJulian Elischer 	 * yet.
134264efc707SRobert Watson 	 * We can call the local one immediately as we have the
13436b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
13446b795970SJulian Elischer 	 */
13456b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1346e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13476b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13481acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1349e088dd4cSAlexander Motin 			ERROUT(error);
13506b795970SJulian Elischer 		}
13516b795970SJulian Elischer 	}
1352ac5dd141SGleb Smirnoff 
1353ac5dd141SGleb Smirnoff 	/*
1354ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1355ac5dd141SGleb Smirnoff 	 */
1356ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1357ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1358ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1359ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1360ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1361ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1362e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1363ac5dd141SGleb Smirnoff 	}
1364ac5dd141SGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
1365ac5dd141SGleb Smirnoff 
1366b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1367b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1368ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
13691acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1370e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
13711acb27c6SJulian Elischer 	}
13726b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1373e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1374e088dd4cSAlexander Motin done:
1375e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1376e088dd4cSAlexander Motin 	return (error);
13774cf49a43SJulian Elischer }
13784cf49a43SJulian Elischer 
13794cf49a43SJulian Elischer /*
13806b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
13816b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
13824cf49a43SJulian Elischer  */
13836b795970SJulian Elischer static int
1384e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1385e088dd4cSAlexander Motin     node_p node2, const char *name2)
13864cf49a43SJulian Elischer {
13874cf49a43SJulian Elischer 	int	error;
13884cf49a43SJulian Elischer 	hook_p	hook;
13894cf49a43SJulian Elischer 	hook_p	hook2;
13904cf49a43SJulian Elischer 
13911acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
13921acb27c6SJulian Elischer 		return(EEXIST);
13931acb27c6SJulian Elischer 	}
13943e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
13954cf49a43SJulian Elischer 		return (error);
13966b795970SJulian Elischer 	/* Allocate the other hook and link it up */
13976b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
139819724144SGleb Smirnoff 	if (hook2 == NULL) {
13996b795970SJulian Elischer 		TRAP_ERROR();
14006b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
14016b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
14026b795970SJulian Elischer 		return (ENOMEM);
14036b795970SJulian Elischer 	}
14046b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14056b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14066b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
14076b795970SJulian Elischer 	hook->hk_peer = hook2;
14086b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
14096b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14106b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
141187e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
14126b795970SJulian Elischer 
14136b795970SJulian Elischer 	/*
14146b795970SJulian Elischer 	 * Queue the function above.
14156b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
14166b795970SJulian Elischer 	 * the other node.
14176b795970SJulian Elischer 	 */
1418b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1419b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
14203fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
14213fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
14223fb87c24SAlexander Motin 	}
14236b795970SJulian Elischer 
14246b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
14256b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
14263fb87c24SAlexander Motin 	return (error);
14274cf49a43SJulian Elischer }
14286b795970SJulian Elischer 
14296b795970SJulian Elischer /*
14306b795970SJulian Elischer  * Make a peer and connect.
14316b795970SJulian Elischer  * We assume that the local node is locked.
14326b795970SJulian Elischer  * The new node probably doesn't need a lock until
14336b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
14346b795970SJulian Elischer  * but we should think about it a bit more.
14356b795970SJulian Elischer  *
14366b795970SJulian Elischer  * The problem may come if the other node also fires up
14376b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
14386b795970SJulian Elischer  * also it may already get a command msg via it's ID.
14396b795970SJulian Elischer  *
14406b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
14416b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
14426b795970SJulian Elischer  * make arg1 point to the node to remove).
14436b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
14446b795970SJulian Elischer  * an unconnected node?
14456b795970SJulian Elischer  */
14466b795970SJulian Elischer static int
14476b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
14486b795970SJulian Elischer {
14496b795970SJulian Elischer 	node_p	node2;
14504c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
14516b795970SJulian Elischer 	int	error;
14526b795970SJulian Elischer 
14536b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
14546b795970SJulian Elischer 		return (error);
14556b795970SJulian Elischer 	}
14566b795970SJulian Elischer 
14576b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
14581acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14596b795970SJulian Elischer 		return (error);
14606b795970SJulian Elischer 	}
14616b795970SJulian Elischer 
14626b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
14631acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14646b795970SJulian Elischer 		ng_destroy_hook(hook1);
14656b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
14666b795970SJulian Elischer 		return (error);
14676b795970SJulian Elischer 	}
14686b795970SJulian Elischer 
14696b795970SJulian Elischer 	/*
14706b795970SJulian Elischer 	 * Actually link the two hooks together.
14716b795970SJulian Elischer 	 */
14726b795970SJulian Elischer 	hook1->hk_peer = hook2;
14736b795970SJulian Elischer 	hook2->hk_peer = hook1;
14746b795970SJulian Elischer 
14756b795970SJulian Elischer 	/* Each hook is referenced by the other */
14766b795970SJulian Elischer 	NG_HOOK_REF(hook1);
14776b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14786b795970SJulian Elischer 
14796b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
14806b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
14816b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
14826b795970SJulian Elischer 	}
14836b795970SJulian Elischer 
14846b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
14856b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
14866b795970SJulian Elischer 
14876b795970SJulian Elischer 	}
14883e4084c8SJulian Elischer 
14893e4084c8SJulian Elischer 	/*
14903e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
14913e4084c8SJulian Elischer 	 */
14926b795970SJulian Elischer 	if (error) {
14936b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
14941acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14956b795970SJulian Elischer 	} else {
14966b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
14976b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
14986b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
14996b795970SJulian Elischer 	}
15006b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
15013e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
15023e4084c8SJulian Elischer 	return (error);
15034cf49a43SJulian Elischer }
15046b795970SJulian Elischer 
1505069154d5SJulian Elischer /************************************************************************
1506069154d5SJulian Elischer 		Utility routines to send self messages
1507069154d5SJulian Elischer ************************************************************************/
1508069154d5SJulian Elischer 
15091acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
151064efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1511069154d5SJulian Elischer int
15121acb27c6SJulian Elischer ng_rmnode_self(node_p node)
1513069154d5SJulian Elischer {
15141acb27c6SJulian Elischer 	int		error;
15154cf49a43SJulian Elischer 
15161acb27c6SJulian Elischer 	if (node == &ng_deadnode)
15171acb27c6SJulian Elischer 		return (0);
1518be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1519be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
15201acb27c6SJulian Elischer 		return (0);
1521069154d5SJulian Elischer 
15221acb27c6SJulian Elischer 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
15231acb27c6SJulian Elischer 	return (error);
1524069154d5SJulian Elischer }
1525069154d5SJulian Elischer 
15261acb27c6SJulian Elischer static void
15276b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
15286b795970SJulian Elischer {
15296b795970SJulian Elischer 	ng_destroy_hook(hook);
15301acb27c6SJulian Elischer 	return ;
15316b795970SJulian Elischer }
15326b795970SJulian Elischer 
1533954c4772SJulian Elischer int
1534954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1535954c4772SJulian Elischer {
15366b795970SJulian Elischer 	int		error;
1537954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1538954c4772SJulian Elischer 
15396b795970SJulian Elischer 	if (node == &ng_deadnode)
15406b795970SJulian Elischer 		return (0);
15416b795970SJulian Elischer 
15426b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
15436b795970SJulian Elischer 	return (error);
1544954c4772SJulian Elischer }
1545954c4772SJulian Elischer 
1546069154d5SJulian Elischer /***********************************************************************
15474cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
15484cf49a43SJulian Elischer  *
15494cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
15504cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
15514cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
15524cf49a43SJulian Elischer  *
15534cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
15544cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
15554cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
15564cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
15574cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
15584cf49a43SJulian Elischer  *
15594cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1560069154d5SJulian Elischer  ***********************************************************************/
15614cf49a43SJulian Elischer int
15624cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
15634cf49a43SJulian Elischer {
15644cf49a43SJulian Elischer 	char	*node, *path, *hook;
15654cf49a43SJulian Elischer 	int	k;
15664cf49a43SJulian Elischer 
15674cf49a43SJulian Elischer 	/*
15684cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
15694cf49a43SJulian Elischer 	 */
15704cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
15714cf49a43SJulian Elischer 	if (*path) {
15724cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
15734cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
15744cf49a43SJulian Elischer 
15754cf49a43SJulian Elischer 		/* Node name must not be empty */
15764cf49a43SJulian Elischer 		if (!*node)
15774cf49a43SJulian Elischer 			return -1;
15784cf49a43SJulian Elischer 
15794cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
15804cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
15814cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
15824cf49a43SJulian Elischer 				if (node[k] == '.')
15834cf49a43SJulian Elischer 					return -1;
15844cf49a43SJulian Elischer 		}
15854cf49a43SJulian Elischer 	} else {
15864cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
15874cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
15884cf49a43SJulian Elischer 	}
15894cf49a43SJulian Elischer 
15904cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
15914cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
15924cf49a43SJulian Elischer 		if (path[k] == ':')
15934cf49a43SJulian Elischer 			return -1;
15944cf49a43SJulian Elischer 
15954cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
15964cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
15974cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
15984cf49a43SJulian Elischer 			return -1;
15994cf49a43SJulian Elischer 
16004cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
16014cf49a43SJulian Elischer 	if (path[0] == '.')
16024cf49a43SJulian Elischer 		path++;
16034cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16044cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16054cf49a43SJulian Elischer 
16064cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
16074cf49a43SJulian Elischer 	if (*path) {
16084cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
16094cf49a43SJulian Elischer 			if (path[k] == '.') {
16104cf49a43SJulian Elischer 				hook = NULL;
16114cf49a43SJulian Elischer 				break;
16124cf49a43SJulian Elischer 			}
16134cf49a43SJulian Elischer 	} else
16144cf49a43SJulian Elischer 		path = hook = NULL;
16154cf49a43SJulian Elischer 
16164cf49a43SJulian Elischer 	/* Done */
16174cf49a43SJulian Elischer 	if (nodep)
16184cf49a43SJulian Elischer 		*nodep = node;
16194cf49a43SJulian Elischer 	if (pathp)
16204cf49a43SJulian Elischer 		*pathp = path;
16214cf49a43SJulian Elischer 	if (hookp)
16224cf49a43SJulian Elischer 		*hookp = hook;
16234cf49a43SJulian Elischer 	return (0);
16244cf49a43SJulian Elischer }
16254cf49a43SJulian Elischer 
16264cf49a43SJulian Elischer /*
16274cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1628069154d5SJulian Elischer  * return the destination node.
16294cf49a43SJulian Elischer  */
16304cf49a43SJulian Elischer int
1631069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1632069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
16334cf49a43SJulian Elischer {
163487e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
16354cf49a43SJulian Elischer 	char   *nodename, *path, pbuf[2];
1636069154d5SJulian Elischer 	node_p  node, oldnode;
16374cf49a43SJulian Elischer 	char   *cp;
1638a4ec03cfSJulian Elischer 	hook_p hook = NULL;
16394cf49a43SJulian Elischer 
16404cf49a43SJulian Elischer 	/* Initialize */
164130400f03SJulian Elischer 	if (destp == NULL) {
16426b795970SJulian Elischer 		TRAP_ERROR();
16434cf49a43SJulian Elischer 		return EINVAL;
164430400f03SJulian Elischer 	}
16454cf49a43SJulian Elischer 	*destp = NULL;
16464cf49a43SJulian Elischer 
16474cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
16484cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
16494cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
16504cf49a43SJulian Elischer 
16514cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
16524cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
16536b795970SJulian Elischer 		TRAP_ERROR();
16544cf49a43SJulian Elischer 		return EINVAL;
16554cf49a43SJulian Elischer 	}
16564cf49a43SJulian Elischer 	if (path == NULL) {
16574cf49a43SJulian Elischer 		pbuf[0] = '.';	/* Needs to be writable */
16584cf49a43SJulian Elischer 		pbuf[1] = '\0';
16594cf49a43SJulian Elischer 		path = pbuf;
16604cf49a43SJulian Elischer 	}
16614cf49a43SJulian Elischer 
1662069154d5SJulian Elischer 	/*
1663069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1664069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1665069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1666069154d5SJulian Elischer 	 */
16674cf49a43SJulian Elischer 	if (nodename) {
1668069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
16694cf49a43SJulian Elischer 		if (node == NULL) {
16706b795970SJulian Elischer 			TRAP_ERROR();
16714cf49a43SJulian Elischer 			return (ENOENT);
16724cf49a43SJulian Elischer 		}
1673069154d5SJulian Elischer 	} else {
1674069154d5SJulian Elischer 		if (here == NULL) {
16756b795970SJulian Elischer 			TRAP_ERROR();
1676069154d5SJulian Elischer 			return (EINVAL);
1677069154d5SJulian Elischer 		}
16784cf49a43SJulian Elischer 		node = here;
167930400f03SJulian Elischer 		NG_NODE_REF(node);
1680069154d5SJulian Elischer 	}
16814cf49a43SJulian Elischer 
1682069154d5SJulian Elischer 	/*
1683069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1684069154d5SJulian Elischer 	 * XXX
1685069154d5SJulian Elischer 	 * We actually cannot guarantee that the sequence
1686069154d5SJulian Elischer 	 * is not being demolished as we crawl along it
1687069154d5SJulian Elischer 	 * without extra-ordinary locking etc.
1688069154d5SJulian Elischer 	 * So this is a bit dodgy to say the least.
1689069154d5SJulian Elischer 	 * We can probably hold up some things by holding
1690069154d5SJulian Elischer 	 * the nodelist mutex for the time of this
1691069154d5SJulian Elischer 	 * crawl if we wanted.. At least that way we wouldn't have to
169264efc707SRobert Watson 	 * worry about the nodes disappearing, but the hooks would still
1693069154d5SJulian Elischer 	 * be a problem.
1694069154d5SJulian Elischer 	 */
16954cf49a43SJulian Elischer 	for (cp = path; node != NULL && *cp != '\0'; ) {
16964cf49a43SJulian Elischer 		char *segment;
16974cf49a43SJulian Elischer 
16984cf49a43SJulian Elischer 		/*
16994cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
17004cf49a43SJulian Elischer 		 * found with a NUL; "cp" points to the next segment (or the
17014cf49a43SJulian Elischer 		 * NUL at the end).
17024cf49a43SJulian Elischer 		 */
17034cf49a43SJulian Elischer 		for (segment = cp; *cp != '\0'; cp++) {
17044cf49a43SJulian Elischer 			if (*cp == '.') {
17054cf49a43SJulian Elischer 				*cp++ = '\0';
17064cf49a43SJulian Elischer 				break;
17074cf49a43SJulian Elischer 			}
17084cf49a43SJulian Elischer 		}
17094cf49a43SJulian Elischer 
17104cf49a43SJulian Elischer 		/* Empty segment */
17114cf49a43SJulian Elischer 		if (*segment == '\0')
17124cf49a43SJulian Elischer 			continue;
17134cf49a43SJulian Elischer 
17144cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1715899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17164cf49a43SJulian Elischer 
17174cf49a43SJulian Elischer 		/* Can't get there from here... */
17184cf49a43SJulian Elischer 		if (hook == NULL
171930400f03SJulian Elischer 		    || NG_HOOK_PEER(hook) == NULL
172030400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(hook)
172130400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17226b795970SJulian Elischer 			TRAP_ERROR();
172330400f03SJulian Elischer 			NG_NODE_UNREF(node);
172430400f03SJulian Elischer #if 0
172530400f03SJulian Elischer 			printf("hooknotvalid %s %s %d %d %d %d ",
172630400f03SJulian Elischer 					path,
172730400f03SJulian Elischer 					segment,
172830400f03SJulian Elischer 					hook == NULL,
172930400f03SJulian Elischer 					NG_HOOK_PEER(hook) == NULL,
173030400f03SJulian Elischer 					NG_HOOK_NOT_VALID(hook),
173130400f03SJulian Elischer 					NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
173230400f03SJulian Elischer #endif
17334cf49a43SJulian Elischer 			return (ENOENT);
17344cf49a43SJulian Elischer 		}
17354cf49a43SJulian Elischer 
1736069154d5SJulian Elischer 		/*
1737069154d5SJulian Elischer 		 * Hop on over to the next node
1738069154d5SJulian Elischer 		 * XXX
1739069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1740069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1741069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1742069154d5SJulian Elischer 		 */
1743069154d5SJulian Elischer 		oldnode = node;
174430400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
174530400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
174630400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
174730400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
174830400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1749069154d5SJulian Elischer 			node = NULL;
1750069154d5SJulian Elischer 		}
17514cf49a43SJulian Elischer 	}
17524cf49a43SJulian Elischer 
17534cf49a43SJulian Elischer 	/* If node somehow missing, fail here (probably this is not needed) */
17544cf49a43SJulian Elischer 	if (node == NULL) {
17556b795970SJulian Elischer 		TRAP_ERROR();
17564cf49a43SJulian Elischer 		return (ENXIO);
17574cf49a43SJulian Elischer 	}
17584cf49a43SJulian Elischer 
17594cf49a43SJulian Elischer 	/* Done */
17604cf49a43SJulian Elischer 	*destp = node;
17611bdebe4dSArchie Cobbs 	if (lasthook != NULL)
176230400f03SJulian Elischer 		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
1763069154d5SJulian Elischer 	return (0);
1764069154d5SJulian Elischer }
1765069154d5SJulian Elischer 
1766069154d5SJulian Elischer /***************************************************************\
1767069154d5SJulian Elischer * Input queue handling.
1768069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1769069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
177064efc707SRobert Watson * Items which cannot be handled immediately are queued.
1771069154d5SJulian Elischer *
1772069154d5SJulian Elischer * read-write queue locking inline functions			*
1773069154d5SJulian Elischer \***************************************************************/
1774069154d5SJulian Elischer 
1775714fb865SGleb Smirnoff static __inline item_p ng_dequeue(struct ng_queue * ngq, int *rw);
1776069154d5SJulian Elischer static __inline item_p ng_acquire_read(struct ng_queue * ngq,
1777069154d5SJulian Elischer 					item_p  item);
1778069154d5SJulian Elischer static __inline item_p ng_acquire_write(struct ng_queue * ngq,
1779069154d5SJulian Elischer 					item_p  item);
1780069154d5SJulian Elischer static __inline void	ng_leave_read(struct ng_queue * ngq);
1781069154d5SJulian Elischer static __inline void	ng_leave_write(struct ng_queue * ngq);
1782069154d5SJulian Elischer static __inline void	ng_queue_rw(struct ng_queue * ngq,
1783069154d5SJulian Elischer 					item_p  item, int rw);
1784069154d5SJulian Elischer 
1785069154d5SJulian Elischer /*
1786069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1787069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1788069154d5SJulian Elischer  * with them.
1789069154d5SJulian Elischer  *
1790b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1791069154d5SJulian Elischer  */
1792069154d5SJulian Elischer /*-
1793069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1794069154d5SJulian Elischer                        |
1795069154d5SJulian Elischer                        V
1796069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17974be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
17984be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
17994be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1800069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18014be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
18024be59335SGleb Smirnoff                             V                                | |
18034be59335SGleb Smirnoff                   [active reader count]                      | |
1804069154d5SJulian Elischer                                                              | |
18054be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1806069154d5SJulian Elischer                                                                |
18074be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1808b57a7965SJulian Elischer 
18098f9ac44aSAlexander Motin Node queue has such semantics:
18108f9ac44aSAlexander Motin - All flags modifications are atomic.
18118f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18128f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18138f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18148f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18158f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18168f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18178f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18188f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18198f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1820069154d5SJulian Elischer */
18218f9ac44aSAlexander Motin 
18224be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18234be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18244be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18254be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18264be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1827b57a7965SJulian Elischer 
1828b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18294be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18304be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1831b57a7965SJulian Elischer 
18324be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1833b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1834b57a7965SJulian Elischer 
18354be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
18364be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
18374be59335SGleb Smirnoff 
18384be59335SGleb Smirnoff /* How to decide what the next queued item is. */
18394be59335SGleb Smirnoff #define HEAD_IS_READER(QP)  NGI_QUEUED_READER((QP)->queue)
18404be59335SGleb Smirnoff #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER((QP)->queue) /* notused */
18414be59335SGleb Smirnoff 
18424be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
18434be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
18444be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
18454be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
18464be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1847b57a7965SJulian Elischer 
1848b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
18494be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
18504be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1851394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
18524be59335SGleb Smirnoff 
1853b57a7965SJulian Elischer 
1854714fb865SGleb Smirnoff #define NGQRW_R 0
1855714fb865SGleb Smirnoff #define NGQRW_W 1
1856714fb865SGleb Smirnoff 
1857069154d5SJulian Elischer /*
1858069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1859069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1860069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1861069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1862069154d5SJulian Elischer  * on the queue.
1863069154d5SJulian Elischer  */
1864069154d5SJulian Elischer static __inline item_p
1865714fb865SGleb Smirnoff ng_dequeue(struct ng_queue *ngq, int *rw)
1866069154d5SJulian Elischer {
1867069154d5SJulian Elischer 	item_p item;
1868b57a7965SJulian Elischer 
18698f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1870d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
18718f9ac44aSAlexander Motin 
18728f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
18734be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
18742955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
18752955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
18762955ee18SGleb Smirnoff 		    ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags);
18774be59335SGleb Smirnoff 		return (NULL);
18784be59335SGleb Smirnoff 	}
1879d58e678fSGleb Smirnoff 
18804be59335SGleb Smirnoff 	/*
18814be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
18824be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
18834be59335SGleb Smirnoff 	 * the current state of the node.
18844be59335SGleb Smirnoff 	 */
18854be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1886394cb30aSAlexander Motin 		while (1) {
1887394cb30aSAlexander Motin 			long t = ngq->q_flags;
1888394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
18898f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
18902955ee18SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
18912955ee18SGleb Smirnoff 				    "can't proceed; queue flags 0x%lx", __func__,
1892394cb30aSAlexander Motin 				    ngq->q_node->nd_ID, ngq->q_node, t);
18934be59335SGleb Smirnoff 				return (NULL);
18944be59335SGleb Smirnoff 			}
18958f9ac44aSAlexander Motin 			if (atomic_cmpset_acq_long(&ngq->q_flags, t,
1896394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1897394cb30aSAlexander Motin 				break;
1898394cb30aSAlexander Motin 			cpu_spinwait();
1899394cb30aSAlexander Motin 		}
19008f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1901714fb865SGleb Smirnoff 		*rw = NGQRW_R;
19028f9ac44aSAlexander Motin 	} else if (atomic_cmpset_acq_long(&ngq->q_flags, OP_PENDING,
1903394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19048f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1905714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1906069154d5SJulian Elischer 	} else {
19078f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
1908394cb30aSAlexander Motin 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer "
1909394cb30aSAlexander Motin 		    "can't proceed; queue flags 0x%lx", __func__,
19102955ee18SGleb Smirnoff 		    ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags);
19114be59335SGleb Smirnoff 		return (NULL);
19124cf49a43SJulian Elischer 	}
19134cf49a43SJulian Elischer 
19144cf49a43SJulian Elischer 	/*
1915069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1916069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19174cf49a43SJulian Elischer 	 */
1918069154d5SJulian Elischer 	item = ngq->queue;
1919069154d5SJulian Elischer 	ngq->queue = item->el_next;
1920069154d5SJulian Elischer 	if (ngq->last == &(item->el_next)) {
1921069154d5SJulian Elischer 		ngq->last = &(ngq->queue);
1922394cb30aSAlexander Motin 		atomic_clear_long(&ngq->q_flags, OP_PENDING);
1923b57a7965SJulian Elischer 	}
19242955ee18SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
19252955ee18SGleb Smirnoff 	    "queue flags 0x%lx", __func__,
19262955ee18SGleb Smirnoff 	    ngq->q_node->nd_ID, ngq->q_node, item, *rw ? "WRITER" : "READER" ,
19273b33fbe7SGleb Smirnoff 	    ngq->q_flags);
1928069154d5SJulian Elischer 	return (item);
1929069154d5SJulian Elischer }
1930859a4d16SJulian Elischer 
1931859a4d16SJulian Elischer /*
19328f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
19338f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
1934859a4d16SJulian Elischer  */
1935069154d5SJulian Elischer static __inline void
1936069154d5SJulian Elischer ng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
1937069154d5SJulian Elischer {
19384be59335SGleb Smirnoff 	if (rw == NGQRW_W)
19394be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
19404be59335SGleb Smirnoff 	else
19414be59335SGleb Smirnoff 		NGI_SET_READER(item);
1942069154d5SJulian Elischer 	item->el_next = NULL;	/* maybe not needed */
1943394cb30aSAlexander Motin 
1944394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
1945394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
1946394cb30aSAlexander Motin 	atomic_set_long(&ngq->q_flags, OP_PENDING);
1947069154d5SJulian Elischer 	*ngq->last = item;
1948394cb30aSAlexander Motin 	ngq->last = &(item->el_next);
1949394cb30aSAlexander Motin 
19502955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
19512955ee18SGleb Smirnoff 	    ngq->q_node->nd_ID, ngq->q_node, item, rw ? "WRITER" : "READER" );
19524be59335SGleb Smirnoff 
19534be59335SGleb Smirnoff 	/*
19544be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
19554be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
19564be59335SGleb Smirnoff 	 */
19574be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
1958394cb30aSAlexander Motin 		ng_worklist_add(ngq->q_node);
1959394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
1960069154d5SJulian Elischer }
1961069154d5SJulian Elischer 
19628f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
1963069154d5SJulian Elischer static __inline item_p
1964069154d5SJulian Elischer ng_acquire_read(struct ng_queue *ngq, item_p item)
1965069154d5SJulian Elischer {
1966ac5dd141SGleb Smirnoff 	KASSERT(ngq != &ng_deadnode.nd_input_queue,
1967ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1968069154d5SJulian Elischer 
1969394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
1970394cb30aSAlexander Motin 	while (1) {
1971394cb30aSAlexander Motin 		long t = ngq->q_flags;
1972394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
1973394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
19748f9ac44aSAlexander Motin 		if (atomic_cmpset_acq_long(&ngq->q_flags, t, t + READER_INCREMENT)) {
1975069154d5SJulian Elischer 	    		/* Successfully grabbed node */
1976394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19772955ee18SGleb Smirnoff 			    __func__, ngq->q_node->nd_ID, ngq->q_node, item);
1978069154d5SJulian Elischer 			return (item);
1979069154d5SJulian Elischer 		}
1980394cb30aSAlexander Motin 		cpu_spinwait();
1981394cb30aSAlexander Motin 	};
1982069154d5SJulian Elischer 
1983394cb30aSAlexander Motin 	/* Queue the request for later. */
1984069154d5SJulian Elischer 	ng_queue_rw(ngq, item, NGQRW_R);
1985f912c0f7SGleb Smirnoff 
1986f912c0f7SGleb Smirnoff 	return (NULL);
1987069154d5SJulian Elischer }
1988069154d5SJulian Elischer 
19898f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
1990069154d5SJulian Elischer static __inline item_p
1991069154d5SJulian Elischer ng_acquire_write(struct ng_queue *ngq, item_p item)
1992069154d5SJulian Elischer {
1993ac5dd141SGleb Smirnoff 	KASSERT(ngq != &ng_deadnode.nd_input_queue,
1994ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1995ac5dd141SGleb Smirnoff 
1996394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
19978f9ac44aSAlexander Motin 	if (atomic_cmpset_acq_long(&ngq->q_flags, 0, WRITER_ACTIVE)) {
1998394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
19992955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20002955ee18SGleb Smirnoff 		    __func__, ngq->q_node->nd_ID, ngq->q_node, item);
2001069154d5SJulian Elischer 		return (item);
2002069154d5SJulian Elischer 	}
2003069154d5SJulian Elischer 
2004394cb30aSAlexander Motin 	/* Queue the request for later. */
2005069154d5SJulian Elischer 	ng_queue_rw(ngq, item, NGQRW_W);
2006f912c0f7SGleb Smirnoff 
2007f912c0f7SGleb Smirnoff 	return (NULL);
2008069154d5SJulian Elischer }
2009069154d5SJulian Elischer 
2010262dfd7fSJulian Elischer #if 0
2011262dfd7fSJulian Elischer static __inline item_p
2012262dfd7fSJulian Elischer ng_upgrade_write(struct ng_queue *ngq, item_p item)
2013262dfd7fSJulian Elischer {
2014262dfd7fSJulian Elischer 	KASSERT(ngq != &ng_deadnode.nd_input_queue,
2015262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2016262dfd7fSJulian Elischer 
2017262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2018262dfd7fSJulian Elischer 
2019262dfd7fSJulian Elischer 	mtx_lock_spin(&(ngq->q_mtx));
2020262dfd7fSJulian Elischer 
2021262dfd7fSJulian Elischer 	/*
2022262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2023262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2024262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2025262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2026262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2027262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2028262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2029262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2030262dfd7fSJulian Elischer 	 */
2031262dfd7fSJulian Elischer 	atomic_add_long(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
2032262dfd7fSJulian Elischer 	if (ngq->q_flags & (NGQ_WMASK & ~OP_PENDING) == WRITER_ACTIVE) {
2033262dfd7fSJulian Elischer 		mtx_unlock_spin(&(ngq->q_mtx));
2034262dfd7fSJulian Elischer 
2035262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2036262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2037262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2038262dfd7fSJulian Elischer 
2039262dfd7fSJulian Elischer 		/*
2040262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
2041262dfd7fSJulian Elischer 		 * down grade back to READER and finish up
2042262dfd7fSJulian Elischer 	 	 */
2043262dfd7fSJulian Elischer 		atomic_add_long(&ngq->q_flags,
2044262dfd7fSJulian Elischer 		    READER_INCREMENT - WRITER_ACTIVE);
2045262dfd7fSJulian Elischer 
2046262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2047262dfd7fSJulian Elischer 		return;
2048262dfd7fSJulian Elischer 	}
2049262dfd7fSJulian Elischer 	/*
2050262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2051262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2052262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2053262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2054262dfd7fSJulian Elischer 	 * set the correct flags.
2055262dfd7fSJulian Elischer 	 */
2056262dfd7fSJulian Elischer 	if ((item->el_next = ngq->queue) == NULL) {
2057262dfd7fSJulian Elischer 		/*
2058262dfd7fSJulian Elischer 		 * Set up the "last" pointer.
2059262dfd7fSJulian Elischer 		 * We are the only (and thus last) item
2060262dfd7fSJulian Elischer 		 */
2061262dfd7fSJulian Elischer 		ngq->last = &(item->el_next);
2062262dfd7fSJulian Elischer 
2063262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
2064394cb30aSAlexander Motin 		atomic_set_long(&ngq->q_flags, OP_PENDING);
2065262dfd7fSJulian Elischer 
2066262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
2067262dfd7fSJulian Elischer 		    ngq->q_node->nd_ID, ngq->q_node);
2068262dfd7fSJulian Elischer 	};
2069262dfd7fSJulian Elischer 	ngq->queue = item;
2070262dfd7fSJulian Elischer 	CTR5(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
2071262dfd7fSJulian Elischer 	    __func__, ngq->q_node->nd_ID, ngq->q_node, item );
2072262dfd7fSJulian Elischer 
2073262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
2074262dfd7fSJulian Elischer 	atomic_add_long(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2075394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2076394cb30aSAlexander Motin 		ng_worklist_add(ngq->q_node);
2077262dfd7fSJulian Elischer 	mtx_unlock_spin(&(ngq->q_mtx));
2078262dfd7fSJulian Elischer 
2079262dfd7fSJulian Elischer 	return;
2080262dfd7fSJulian Elischer }
2081262dfd7fSJulian Elischer 
2082262dfd7fSJulian Elischer #endif
2083262dfd7fSJulian Elischer 
20848f9ac44aSAlexander Motin /* Release reader lock. */
2085069154d5SJulian Elischer static __inline void
2086069154d5SJulian Elischer ng_leave_read(struct ng_queue *ngq)
2087069154d5SJulian Elischer {
20888f9ac44aSAlexander Motin 	atomic_subtract_rel_long(&ngq->q_flags, READER_INCREMENT);
2089069154d5SJulian Elischer }
2090069154d5SJulian Elischer 
20918f9ac44aSAlexander Motin /* Release writer lock. */
2092069154d5SJulian Elischer static __inline void
2093069154d5SJulian Elischer ng_leave_write(struct ng_queue *ngq)
2094069154d5SJulian Elischer {
20958f9ac44aSAlexander Motin 	atomic_clear_rel_long(&ngq->q_flags, WRITER_ACTIVE);
2096069154d5SJulian Elischer }
2097069154d5SJulian Elischer 
20988f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2099069154d5SJulian Elischer static void
2100069154d5SJulian Elischer ng_flush_input_queue(struct ng_queue * ngq)
2101069154d5SJulian Elischer {
2102069154d5SJulian Elischer 	item_p item;
2103069154d5SJulian Elischer 
21042c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
21054be59335SGleb Smirnoff 	while (ngq->queue) {
2106069154d5SJulian Elischer 		item = ngq->queue;
2107069154d5SJulian Elischer 		ngq->queue = item->el_next;
2108069154d5SJulian Elischer 		if (ngq->last == &(item->el_next)) {
2109069154d5SJulian Elischer 			ngq->last = &(ngq->queue);
2110394cb30aSAlexander Motin 			atomic_clear_long(&ngq->q_flags, OP_PENDING);
2111069154d5SJulian Elischer 		}
21122c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21134be59335SGleb Smirnoff 
21144be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
211510e87318SAlexander Motin 		if (item->apply != NULL) {
211610e87318SAlexander Motin 			if (item->depth == 1)
211710e87318SAlexander Motin 				item->apply->error = ENOENT;
211810e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
211910e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
212010e87318SAlexander Motin 				    item->apply->error);
212110e87318SAlexander Motin 			}
2122eb2405ddSGleb Smirnoff 		}
2123505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21242c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2125069154d5SJulian Elischer 	}
21262c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2127069154d5SJulian Elischer }
2128069154d5SJulian Elischer 
2129069154d5SJulian Elischer /***********************************************************************
2130069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2131069154d5SJulian Elischer ***********************************************************************/
2132069154d5SJulian Elischer 
2133069154d5SJulian Elischer /*
21341acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2135069154d5SJulian Elischer  * Common:
2136069154d5SJulian Elischer  *    reference to destination node.
2137069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2138e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2139069154d5SJulian Elischer  * Data:
2140069154d5SJulian Elischer  *    pointer to mbuf
2141069154d5SJulian Elischer  * Control_Message:
2142069154d5SJulian Elischer  *    pointer to msg.
2143069154d5SJulian Elischer  *    ID of original sender node. (return address)
21441acb27c6SJulian Elischer  * Function:
21451acb27c6SJulian Elischer  *    Function pointer
21461acb27c6SJulian Elischer  *    void * argument
21471acb27c6SJulian Elischer  *    integer argument
2148069154d5SJulian Elischer  *
2149069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2150069154d5SJulian Elischer  */
2151069154d5SJulian Elischer 
2152069154d5SJulian Elischer int
215342282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2154069154d5SJulian Elischer {
2155e088dd4cSAlexander Motin 	hook_p hook;
2156e088dd4cSAlexander Motin 	node_p node;
215742282202SGleb Smirnoff 	int queue, rw;
2158e088dd4cSAlexander Motin 	struct ng_queue *ngq;
215932b33288SGleb Smirnoff 	int error = 0;
2160069154d5SJulian Elischer 
2161f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2162f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2163e088dd4cSAlexander Motin 
2164e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2165e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2166e088dd4cSAlexander Motin #endif
2167e088dd4cSAlexander Motin 
2168f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2169e088dd4cSAlexander Motin 	if (item->apply)
2170e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2171e088dd4cSAlexander Motin 
2172e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2173f50597f5SAlexander Motin 	/* Node is never optional. */
2174f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2175e088dd4cSAlexander Motin 
2176e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2177f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2178f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2179f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
218010204449SJulian Elischer 		if (NGI_M(item) == NULL)
2181e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2182069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
21836f683eeeSGleb Smirnoff 	}
21846f683eeeSGleb Smirnoff 
2185069154d5SJulian Elischer 	/*
2186f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2187f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2188f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2189069154d5SJulian Elischer 	 */
2190db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2191f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2192f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2193069154d5SJulian Elischer 		rw = NGQRW_W;
2194f50597f5SAlexander Motin 	} else {
2195f50597f5SAlexander Motin 		rw = NGQRW_R;
2196f50597f5SAlexander Motin 	}
21976f683eeeSGleb Smirnoff 
219881a253a4SAlexander Motin 	/*
219981a253a4SAlexander Motin 	 * If sender or receiver requests queued delivery or stack usage
220081a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
220181a253a4SAlexander Motin 	 */
220281a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
220381a253a4SAlexander Motin 		queue = 1;
2204f50597f5SAlexander Motin 	} else {
2205f50597f5SAlexander Motin 		queue = 0;
220681a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2207d4529f98SAlexander Motin 		/*
2208942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2209f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2210d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2211f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2212f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2213d4529f98SAlexander Motin 		 */
2214f50597f5SAlexander Motin 		size_t	st, su, sl;
221581a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2216f50597f5SAlexander Motin 		sl = st - su;
2217f50597f5SAlexander Motin 		if ((sl * 4 < st) ||
2218f50597f5SAlexander Motin 		    ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) ||
221981a253a4SAlexander Motin 		      (hook && (hook->hk_flags & HK_HI_STACK))))) {
222081a253a4SAlexander Motin 			queue = 1;
222181a253a4SAlexander Motin 		}
222281a253a4SAlexander Motin #endif
2223f50597f5SAlexander Motin 	}
222481a253a4SAlexander Motin 
2225e72a98f4SAlexander Motin 	ngq = &node->nd_input_queue;
2226069154d5SJulian Elischer 	if (queue) {
222710e87318SAlexander Motin 		item->depth = 1;
2228394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
2229069154d5SJulian Elischer 		ng_queue_rw(ngq, item, rw);
2230f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2231069154d5SJulian Elischer 	}
2232069154d5SJulian Elischer 
2233069154d5SJulian Elischer 	/*
2234069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2235069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2236069154d5SJulian Elischer 	 */
223732b33288SGleb Smirnoff  	if (rw == NGQRW_R)
2238069154d5SJulian Elischer 		item = ng_acquire_read(ngq, item);
223932b33288SGleb Smirnoff 	else
2240069154d5SJulian Elischer 		item = ng_acquire_write(ngq, item);
22414cf49a43SJulian Elischer 
2242f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2243f50597f5SAlexander Motin 	if (item == NULL)
2244f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2245069154d5SJulian Elischer 
22465951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
22475951069aSJulian Elischer 
224810e87318SAlexander Motin 	item->depth++;
224927757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2250069154d5SJulian Elischer 
2251394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
2252394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
22532c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2254394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2255394cb30aSAlexander Motin 			ng_worklist_add(ngq->q_node);
22562c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2257394cb30aSAlexander Motin 	}
2258394cb30aSAlexander Motin 
2259394cb30aSAlexander Motin 	/*
2260394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2261394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2262394cb30aSAlexander Motin 	 */
2263394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2264069154d5SJulian Elischer 
22651acb27c6SJulian Elischer 	return (error);
2266e088dd4cSAlexander Motin 
2267e088dd4cSAlexander Motin done:
2268f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
226910e87318SAlexander Motin 	if (item->apply != NULL) {
227010e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
227110e87318SAlexander Motin 			item->apply->error = error;
227210e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
227310e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
227410e87318SAlexander Motin 			    item->apply->error);
227510e87318SAlexander Motin 		}
227610e87318SAlexander Motin 	}
2277e72a98f4SAlexander Motin 
2278e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2279e088dd4cSAlexander Motin 	return (error);
2280069154d5SJulian Elischer }
2281069154d5SJulian Elischer 
2282069154d5SJulian Elischer /*
2283069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2284069154d5SJulian Elischer  * It should contain all the information needed
2285069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2286e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
22874cf49a43SJulian Elischer  */
228827757487SGleb Smirnoff static int
2289714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2290069154d5SJulian Elischer {
2291069154d5SJulian Elischer 	hook_p  hook;
2292069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2293c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2294e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
229510e87318SAlexander Motin 	int	error = 0, depth;
2296069154d5SJulian Elischer 
2297f50597f5SAlexander Motin 	/* Node and item are never optional. */
2298f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2299f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2300f50597f5SAlexander Motin 
23011acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
230230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2303069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2304069154d5SJulian Elischer #endif
23058afe16d5SGleb Smirnoff 
23068afe16d5SGleb Smirnoff 	apply = item->apply;
230710e87318SAlexander Motin 	depth = item->depth;
23088afe16d5SGleb Smirnoff 
23096b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2310069154d5SJulian Elischer 	case NGQF_DATA:
2311069154d5SJulian Elischer 		/*
2312069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2313069154d5SJulian Elischer 		 */
2314f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2315f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2316f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2317a54a69d7SJulian Elischer 			error = EIO;
2318069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2319c4b5eea4SJulian Elischer 			break;
2320069154d5SJulian Elischer 		}
2321c4b5eea4SJulian Elischer 		/*
2322c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
2323c4b5eea4SJulian Elischer 		 * Give preference to the hook over-ride method
2324c4b5eea4SJulian Elischer 		 */
2325c4b5eea4SJulian Elischer 		if ((!(rcvdata = hook->hk_rcvdata))
2326c4b5eea4SJulian Elischer 		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2327c4b5eea4SJulian Elischer 			error = 0;
2328c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2329c4b5eea4SJulian Elischer 			break;
2330c4b5eea4SJulian Elischer 		}
2331a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2332069154d5SJulian Elischer 		break;
2333069154d5SJulian Elischer 	case NGQF_MESG:
2334e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2335069154d5SJulian Elischer 			/*
2336e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2337e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2338069154d5SJulian Elischer 			 * The message may not need it.
2339069154d5SJulian Elischer 			 */
234030400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2341069154d5SJulian Elischer 			hook = NULL;
2342069154d5SJulian Elischer 		}
2343069154d5SJulian Elischer 		/*
2344069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2345069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2346069154d5SJulian Elischer 		 */
234730400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
23486b795970SJulian Elischer 			TRAP_ERROR();
2349a54a69d7SJulian Elischer 			error = EINVAL;
2350069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2351e72a98f4SAlexander Motin 			break;
2352e72a98f4SAlexander Motin 		}
2353069154d5SJulian Elischer 		/*
2354069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2355069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2356069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2357e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2358069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2359069154d5SJulian Elischer 		 * reference a node or hook that has just been
2360069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2361069154d5SJulian Elischer 		 * is holding a reference, but..
2362069154d5SJulian Elischer 		 */
2363e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2364e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2365a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2366c4b5eea4SJulian Elischer 			break;
2367c4b5eea4SJulian Elischer 		}
2368e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2369e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
23706b795970SJulian Elischer 			TRAP_ERROR();
2371a54a69d7SJulian Elischer 			error = 0;
2372069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2373c4b5eea4SJulian Elischer 			break;
2374069154d5SJulian Elischer 		}
2375a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2376069154d5SJulian Elischer 		break;
23776b795970SJulian Elischer 	case NGQF_FN:
2378e088dd4cSAlexander Motin 	case NGQF_FN2:
2379e088dd4cSAlexander Motin 		/*
2380e088dd4cSAlexander Motin 		 *  We have to implicitly trust the hook,
2381e088dd4cSAlexander Motin 		 * as some of these are used for system purposes
2382e088dd4cSAlexander Motin 		 * where the hook is invalid. In the case of
2383e088dd4cSAlexander Motin 		 * the shutdown message we allow it to hit
2384e088dd4cSAlexander Motin 		 * even if the node is invalid.
2385e088dd4cSAlexander Motin 		 */
2386e088dd4cSAlexander Motin 		if ((NG_NODE_NOT_VALID(node))
2387e088dd4cSAlexander Motin 		&& (NGI_FN(item) != &ng_rmnode)) {
2388e088dd4cSAlexander Motin 			TRAP_ERROR();
2389e088dd4cSAlexander Motin 			error = EINVAL;
2390e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2391e088dd4cSAlexander Motin 			break;
2392e088dd4cSAlexander Motin 		}
2393b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2394b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2395b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2396b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2397b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2398e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2399e088dd4cSAlexander Motin 		break;
2400069154d5SJulian Elischer 	}
2401069154d5SJulian Elischer 	/*
2402069154d5SJulian Elischer 	 * We held references on some of the resources
2403069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2404069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2405069154d5SJulian Elischer 	 */
2406e72a98f4SAlexander Motin 	if (hook)
240730400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2408069154d5SJulian Elischer 
2409f50597f5SAlexander Motin  	if (rw == NGQRW_R)
241030400f03SJulian Elischer 		ng_leave_read(&node->nd_input_queue);
2411f50597f5SAlexander Motin 	else
241230400f03SJulian Elischer 		ng_leave_write(&node->nd_input_queue);
24138afe16d5SGleb Smirnoff 
24148afe16d5SGleb Smirnoff 	/* Apply callback. */
241510e87318SAlexander Motin 	if (apply != NULL) {
241610e87318SAlexander Motin 		if (depth == 1 && error != 0)
241710e87318SAlexander Motin 			apply->error = error;
241810e87318SAlexander Motin 		if (refcount_release(&apply->refs))
241910e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
242010e87318SAlexander Motin 	}
24218afe16d5SGleb Smirnoff 
242227757487SGleb Smirnoff 	return (error);
2423069154d5SJulian Elischer }
2424069154d5SJulian Elischer 
2425069154d5SJulian Elischer /***********************************************************************
2426069154d5SJulian Elischer  * Implement the 'generic' control messages
2427069154d5SJulian Elischer  ***********************************************************************/
2428069154d5SJulian Elischer static int
2429069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
24304cf49a43SJulian Elischer {
24314cf49a43SJulian Elischer 	int error = 0;
2432069154d5SJulian Elischer 	struct ng_mesg *msg;
2433069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
24344cf49a43SJulian Elischer 
2435069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
24364cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
24376b795970SJulian Elischer 		TRAP_ERROR();
2438069154d5SJulian Elischer 		error = EINVAL;
2439069154d5SJulian Elischer 		goto out;
24404cf49a43SJulian Elischer 	}
24414cf49a43SJulian Elischer 	switch (msg->header.cmd) {
24424cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
24431acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
24444cf49a43SJulian Elischer 		break;
24454cf49a43SJulian Elischer 	case NGM_MKPEER:
24464cf49a43SJulian Elischer 	    {
24474cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
24484cf49a43SJulian Elischer 
24494cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
24506b795970SJulian Elischer 			TRAP_ERROR();
2451069154d5SJulian Elischer 			error = EINVAL;
2452069154d5SJulian Elischer 			break;
24534cf49a43SJulian Elischer 		}
24544cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
24554cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
24564cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
24574cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
24584cf49a43SJulian Elischer 		break;
24594cf49a43SJulian Elischer 	    }
24604cf49a43SJulian Elischer 	case NGM_CONNECT:
24614cf49a43SJulian Elischer 	    {
24624cf49a43SJulian Elischer 		struct ngm_connect *const con =
24634cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
24644cf49a43SJulian Elischer 		node_p node2;
24654cf49a43SJulian Elischer 
24664cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
24676b795970SJulian Elischer 			TRAP_ERROR();
2468069154d5SJulian Elischer 			error = EINVAL;
2469069154d5SJulian Elischer 			break;
24704cf49a43SJulian Elischer 		}
24714cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
24724cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
24734cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2474069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2475069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
24764cf49a43SJulian Elischer 		if (error)
24774cf49a43SJulian Elischer 			break;
2478e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2479e088dd4cSAlexander Motin 		    node2, con->peerhook);
248030400f03SJulian Elischer 		NG_NODE_UNREF(node2);
24814cf49a43SJulian Elischer 		break;
24824cf49a43SJulian Elischer 	    }
24834cf49a43SJulian Elischer 	case NGM_NAME:
24844cf49a43SJulian Elischer 	    {
24854cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
24864cf49a43SJulian Elischer 
24874cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
24886b795970SJulian Elischer 			TRAP_ERROR();
2489069154d5SJulian Elischer 			error = EINVAL;
2490069154d5SJulian Elischer 			break;
24914cf49a43SJulian Elischer 		}
24924cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
24934cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
24944cf49a43SJulian Elischer 		break;
24954cf49a43SJulian Elischer 	    }
24964cf49a43SJulian Elischer 	case NGM_RMHOOK:
24974cf49a43SJulian Elischer 	    {
24984cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
24994cf49a43SJulian Elischer 		hook_p hook;
25004cf49a43SJulian Elischer 
25014cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
25026b795970SJulian Elischer 			TRAP_ERROR();
2503069154d5SJulian Elischer 			error = EINVAL;
2504069154d5SJulian Elischer 			break;
25054cf49a43SJulian Elischer 		}
25064cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2507899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
25084cf49a43SJulian Elischer 			ng_destroy_hook(hook);
25094cf49a43SJulian Elischer 		break;
25104cf49a43SJulian Elischer 	    }
25114cf49a43SJulian Elischer 	case NGM_NODEINFO:
25124cf49a43SJulian Elischer 	    {
25134cf49a43SJulian Elischer 		struct nodeinfo *ni;
25144cf49a43SJulian Elischer 
2515069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25164cf49a43SJulian Elischer 		if (resp == NULL) {
25174cf49a43SJulian Elischer 			error = ENOMEM;
25184cf49a43SJulian Elischer 			break;
25194cf49a43SJulian Elischer 		}
25204cf49a43SJulian Elischer 
25214cf49a43SJulian Elischer 		/* Fill in node info */
2522069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
252330400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
252487e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
252587e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2526dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
252730400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25284cf49a43SJulian Elischer 		break;
25294cf49a43SJulian Elischer 	    }
25304cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
25314cf49a43SJulian Elischer 	    {
253230400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
25334cf49a43SJulian Elischer 		struct hooklist *hl;
25344cf49a43SJulian Elischer 		struct nodeinfo *ni;
25354cf49a43SJulian Elischer 		hook_p hook;
25364cf49a43SJulian Elischer 
25374cf49a43SJulian Elischer 		/* Get response struct */
2538069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
25394cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2540069154d5SJulian Elischer 		if (resp == NULL) {
25414cf49a43SJulian Elischer 			error = ENOMEM;
25424cf49a43SJulian Elischer 			break;
25434cf49a43SJulian Elischer 		}
2544069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
25454cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
25464cf49a43SJulian Elischer 
25474cf49a43SJulian Elischer 		/* Fill in node info */
254830400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
254987e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
255087e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2551dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
25524cf49a43SJulian Elischer 
25534cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
25544cf49a43SJulian Elischer 		ni->hooks = 0;
255530400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
25564cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
25574cf49a43SJulian Elischer 
25584cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
25594cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
25606e551fb6SDavid E. O'Brien 				    __func__, "hooks");
25614cf49a43SJulian Elischer 				break;
25624cf49a43SJulian Elischer 			}
256330400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
25644cf49a43SJulian Elischer 				continue;
256587e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
256687e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
256730400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
256887e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
256987e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
257087e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
257187e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
257230400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
257330400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
25744cf49a43SJulian Elischer 			ni->hooks++;
25754cf49a43SJulian Elischer 		}
25764cf49a43SJulian Elischer 		break;
25774cf49a43SJulian Elischer 	    }
25784cf49a43SJulian Elischer 
25794cf49a43SJulian Elischer 	case NGM_LISTNAMES:
25804cf49a43SJulian Elischer 	case NGM_LISTNODES:
25814cf49a43SJulian Elischer 	    {
25824cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
25834cf49a43SJulian Elischer 		struct namelist *nl;
25844cf49a43SJulian Elischer 		node_p node;
2585cfea3f85SAlexander Motin 		int num = 0, i;
25864cf49a43SJulian Elischer 
2587cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
25884cf49a43SJulian Elischer 		/* Count number of nodes */
2589cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2590cfea3f85SAlexander Motin 			LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) {
2591cfea3f85SAlexander Motin 				if (NG_NODE_IS_VALID(node) &&
2592cfea3f85SAlexander Motin 				    (unnamed || NG_NODE_HAS_NAME(node))) {
25934cf49a43SJulian Elischer 					num++;
25944cf49a43SJulian Elischer 				}
259570de87f2SJulian Elischer 			}
2596cfea3f85SAlexander Motin 		}
2597cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
25984cf49a43SJulian Elischer 
25994cf49a43SJulian Elischer 		/* Get response struct */
2600069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
26014cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2602069154d5SJulian Elischer 		if (resp == NULL) {
26034cf49a43SJulian Elischer 			error = ENOMEM;
26044cf49a43SJulian Elischer 			break;
26054cf49a43SJulian Elischer 		}
2606069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
26074cf49a43SJulian Elischer 
26084cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
26094cf49a43SJulian Elischer 		nl->numnames = 0;
2610cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
2611cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2612cfea3f85SAlexander Motin 			LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) {
2613cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2614cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
26154cf49a43SJulian Elischer 
2616b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2617b96baf0aSGleb Smirnoff 					continue;
2618b96baf0aSGleb Smirnoff 				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2619b96baf0aSGleb Smirnoff 					continue;
26204cf49a43SJulian Elischer 				if (nl->numnames >= num) {
2621cfea3f85SAlexander Motin 					log(LOG_ERR, "%s: number of nodes changed\n",
2622cfea3f85SAlexander Motin 					    __func__);
26234cf49a43SJulian Elischer 					break;
26244cf49a43SJulian Elischer 				}
262530400f03SJulian Elischer 				if (NG_NODE_HAS_NAME(node))
262687e2c66aSHartmut Brandt 					strcpy(np->name, NG_NODE_NAME(node));
262787e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2628dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
262930400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
26304cf49a43SJulian Elischer 				nl->numnames++;
26314cf49a43SJulian Elischer 			}
2632cfea3f85SAlexander Motin 		}
2633cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26344cf49a43SJulian Elischer 		break;
26354cf49a43SJulian Elischer 	    }
26364cf49a43SJulian Elischer 
26374cf49a43SJulian Elischer 	case NGM_LISTTYPES:
26384cf49a43SJulian Elischer 	    {
26394cf49a43SJulian Elischer 		struct typelist *tl;
26404cf49a43SJulian Elischer 		struct ng_type *type;
26414cf49a43SJulian Elischer 		int num = 0;
26424cf49a43SJulian Elischer 
26439ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
26444cf49a43SJulian Elischer 		/* Count number of types */
264570de87f2SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26464cf49a43SJulian Elischer 			num++;
264770de87f2SJulian Elischer 		}
26489ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26494cf49a43SJulian Elischer 
26504cf49a43SJulian Elischer 		/* Get response struct */
2651069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
26524cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2653069154d5SJulian Elischer 		if (resp == NULL) {
26544cf49a43SJulian Elischer 			error = ENOMEM;
26554cf49a43SJulian Elischer 			break;
26564cf49a43SJulian Elischer 		}
2657069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
26584cf49a43SJulian Elischer 
26594cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
26604cf49a43SJulian Elischer 		tl->numtypes = 0;
26619ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
2662069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26634cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
26644cf49a43SJulian Elischer 
26654cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
26664cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26676e551fb6SDavid E. O'Brien 				    __func__, "types");
26684cf49a43SJulian Elischer 				break;
26694cf49a43SJulian Elischer 			}
267087e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2671c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
26724cf49a43SJulian Elischer 			tl->numtypes++;
26734cf49a43SJulian Elischer 		}
26749ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26754cf49a43SJulian Elischer 		break;
26764cf49a43SJulian Elischer 	    }
26774cf49a43SJulian Elischer 
2678f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2679f8307e12SArchie Cobbs 	    {
26807133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2681f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2682f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2683069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2684f8307e12SArchie Cobbs 
2685f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2686f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
26874c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
26884c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
26894c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
26906b795970SJulian Elischer 			TRAP_ERROR();
2691f8307e12SArchie Cobbs 			error = EINVAL;
2692f8307e12SArchie Cobbs 			break;
2693f8307e12SArchie Cobbs 		}
2694f8307e12SArchie Cobbs 
2695f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2696069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2697069154d5SJulian Elischer 		if (resp == NULL) {
2698f8307e12SArchie Cobbs 			error = ENOMEM;
2699f8307e12SArchie Cobbs 			break;
2700f8307e12SArchie Cobbs 		}
2701069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2702f8307e12SArchie Cobbs 
2703f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2704f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2705f8307e12SArchie Cobbs 
2706f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
270730400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2708f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2709f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2710f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2711f8307e12SArchie Cobbs 				break;
2712f8307e12SArchie Cobbs 		}
2713f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2714f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2715f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2716f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2717f8307e12SArchie Cobbs 					break;
2718f8307e12SArchie Cobbs 			}
2719f8307e12SArchie Cobbs 			if (c->name == NULL) {
2720069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2721f8307e12SArchie Cobbs 				error = ENOSYS;
2722f8307e12SArchie Cobbs 				break;
2723f8307e12SArchie Cobbs 			}
2724f8307e12SArchie Cobbs 		}
2725f8307e12SArchie Cobbs 
2726f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2727f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2728f8307e12SArchie Cobbs 		    "%s", c->name);
2729f8307e12SArchie Cobbs 
2730f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2731f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2732f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
273370de87f2SJulian Elischer 		if (argstype == NULL) {
2734f8307e12SArchie Cobbs 			*ascii->data = '\0';
273570de87f2SJulian Elischer 		} else {
2736f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2737f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2738f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2739069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2740f8307e12SArchie Cobbs 				break;
2741f8307e12SArchie Cobbs 			}
2742f8307e12SArchie Cobbs 		}
2743f8307e12SArchie Cobbs 
2744f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2745f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2746f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2747069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2748f8307e12SArchie Cobbs 		break;
2749f8307e12SArchie Cobbs 	    }
2750f8307e12SArchie Cobbs 
2751f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2752f8307e12SArchie Cobbs 	    {
2753f8307e12SArchie Cobbs 		int bufSize = 2000;	/* XXX hard coded constant */
2754f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2755f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2756069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
275752ec4a03SArchie Cobbs 		int off = 0;
2758f8307e12SArchie Cobbs 
2759f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2760f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
27614c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
27624c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
27634c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
27644c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
27656b795970SJulian Elischer 			TRAP_ERROR();
2766f8307e12SArchie Cobbs 			error = EINVAL;
2767f8307e12SArchie Cobbs 			break;
2768f8307e12SArchie Cobbs 		}
2769f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2770f8307e12SArchie Cobbs 
2771f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2772069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2773069154d5SJulian Elischer 		if (resp == NULL) {
2774f8307e12SArchie Cobbs 			error = ENOMEM;
2775f8307e12SArchie Cobbs 			break;
2776f8307e12SArchie Cobbs 		}
2777069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2778f8307e12SArchie Cobbs 
2779f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2780f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2781f8307e12SArchie Cobbs 
2782f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
278330400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2784f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2785f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2786f8307e12SArchie Cobbs 				break;
2787f8307e12SArchie Cobbs 		}
2788f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2789f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2790f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2791f8307e12SArchie Cobbs 					break;
2792f8307e12SArchie Cobbs 			}
2793f8307e12SArchie Cobbs 			if (c->name == NULL) {
2794069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2795f8307e12SArchie Cobbs 				error = ENOSYS;
2796f8307e12SArchie Cobbs 				break;
2797f8307e12SArchie Cobbs 			}
2798f8307e12SArchie Cobbs 		}
2799f8307e12SArchie Cobbs 
2800f8307e12SArchie Cobbs 		/* Convert command name to binary */
2801f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2802f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2803f8307e12SArchie Cobbs 
2804f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2805f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2806f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
280770de87f2SJulian Elischer 		if (argstype == NULL) {
2808f8307e12SArchie Cobbs 			bufSize = 0;
280970de87f2SJulian Elischer 		} else {
2810f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2811f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2812069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2813f8307e12SArchie Cobbs 				break;
2814f8307e12SArchie Cobbs 			}
2815f8307e12SArchie Cobbs 		}
2816f8307e12SArchie Cobbs 
2817f8307e12SArchie Cobbs 		/* Return the result */
2818f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2819069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2820f8307e12SArchie Cobbs 		break;
2821f8307e12SArchie Cobbs 	    }
2822f8307e12SArchie Cobbs 
28237095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
28244cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
28254cf49a43SJulian Elischer 		/*
28264cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
28274cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2828069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
28294cf49a43SJulian Elischer 		 * when control passes back to us.
28304cf49a43SJulian Elischer 		 */
283130400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2832069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
283330400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
28344cf49a43SJulian Elischer 		}
28354cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
28364cf49a43SJulian Elischer 	default:
28376b795970SJulian Elischer 		TRAP_ERROR();
28384cf49a43SJulian Elischer 		error = EINVAL;
28394cf49a43SJulian Elischer 	}
2840069154d5SJulian Elischer 	/*
2841069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2842069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2843069154d5SJulian Elischer 	 * Don't free it if it is so.
2844069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2845069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2846069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2847069154d5SJulian Elischer 	 */
2848069154d5SJulian Elischer out:
2849069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
28501acb27c6SJulian Elischer 	if (msg)
2851069154d5SJulian Elischer 		NG_FREE_MSG(msg);
28524cf49a43SJulian Elischer 	return (error);
28534cf49a43SJulian Elischer }
28544cf49a43SJulian Elischer 
28554cf49a43SJulian Elischer /************************************************************************
28568253c060SGleb Smirnoff 			Queue element get/free routines
28578253c060SGleb Smirnoff ************************************************************************/
28588253c060SGleb Smirnoff 
28598253c060SGleb Smirnoff uma_zone_t			ng_qzone;
2860ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2861ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
2862ed75521fSAlexander Motin static int			useddata = 0;
28638253c060SGleb Smirnoff 
28648253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
28658253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
28668253c060SGleb Smirnoff     0, "Maximum number of queue items to allocate");
2867ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
2868ed75521fSAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RW | CTLFLAG_TUN, &maxdata,
2869ed75521fSAlexander Motin     0, "Maximum number of queue data items to allocate");
28708253c060SGleb Smirnoff 
28718253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
28728253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
28738253c060SGleb Smirnoff static int			allocated;	/* number of items malloc'd */
28748253c060SGleb Smirnoff #endif
28758253c060SGleb Smirnoff 
28768253c060SGleb Smirnoff /*
28778253c060SGleb Smirnoff  * Get a queue entry.
28788253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
28798253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
28808253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
28818253c060SGleb Smirnoff  * an interrupt.
28828253c060SGleb Smirnoff  */
28838253c060SGleb Smirnoff static __inline item_p
288442282202SGleb Smirnoff ng_getqblk(int flags)
28858253c060SGleb Smirnoff {
28868253c060SGleb Smirnoff 	item_p item = NULL;
288742282202SGleb Smirnoff 	int wait;
28888253c060SGleb Smirnoff 
288942282202SGleb Smirnoff 	wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT;
289042282202SGleb Smirnoff 
289142282202SGleb Smirnoff 	item = uma_zalloc(ng_qzone, wait | M_ZERO);
28928253c060SGleb Smirnoff 
28938253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
28948253c060SGleb Smirnoff 	if (item) {
28958253c060SGleb Smirnoff 			mtx_lock(&ngq_mtx);
28968253c060SGleb Smirnoff 			TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
28978253c060SGleb Smirnoff 			allocated++;
28988253c060SGleb Smirnoff 			mtx_unlock(&ngq_mtx);
28998253c060SGleb Smirnoff 	}
29008253c060SGleb Smirnoff #endif
29018253c060SGleb Smirnoff 
29028253c060SGleb Smirnoff 	return (item);
29038253c060SGleb Smirnoff }
29048253c060SGleb Smirnoff 
29058253c060SGleb Smirnoff /*
29068253c060SGleb Smirnoff  * Release a queue entry
29078253c060SGleb Smirnoff  */
29088253c060SGleb Smirnoff void
29098253c060SGleb Smirnoff ng_free_item(item_p item)
29108253c060SGleb Smirnoff {
29118253c060SGleb Smirnoff 	/*
29128253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
29138253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
29148253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
29158253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
29168253c060SGleb Smirnoff 	 * or we release them again here.
29178253c060SGleb Smirnoff 	 */
29188253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
29198253c060SGleb Smirnoff 	case NGQF_DATA:
2920ed75521fSAlexander Motin 		atomic_subtract_int(&useddata, 1);
29218253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
29228253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
29238253c060SGleb Smirnoff 		break;
29248253c060SGleb Smirnoff 	case NGQF_MESG:
29258253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
29268253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
29278253c060SGleb Smirnoff 		break;
29288253c060SGleb Smirnoff 	case NGQF_FN:
2929e088dd4cSAlexander Motin 	case NGQF_FN2:
29308253c060SGleb Smirnoff 		/* nothing to free really, */
29318253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
29328253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
29338253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
29348253c060SGleb Smirnoff 		break;
29358253c060SGleb Smirnoff 	}
29368253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
29378253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
29388253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
29398253c060SGleb Smirnoff 
29408253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29418253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
29428253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
29438253c060SGleb Smirnoff 	allocated--;
29448253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
29458253c060SGleb Smirnoff #endif
29468253c060SGleb Smirnoff 	uma_zfree(ng_qzone, item);
29478253c060SGleb Smirnoff }
29488253c060SGleb Smirnoff 
29498253c060SGleb Smirnoff /************************************************************************
29504cf49a43SJulian Elischer 			Module routines
29514cf49a43SJulian Elischer ************************************************************************/
29524cf49a43SJulian Elischer 
29534cf49a43SJulian Elischer /*
29544cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
29554cf49a43SJulian Elischer  */
29564cf49a43SJulian Elischer int
29574cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
29584cf49a43SJulian Elischer {
29594cf49a43SJulian Elischer 	struct ng_type *const type = data;
29604cf49a43SJulian Elischer 	int s, error = 0;
29614cf49a43SJulian Elischer 
29624cf49a43SJulian Elischer 	switch (event) {
29634cf49a43SJulian Elischer 	case MOD_LOAD:
29644cf49a43SJulian Elischer 
29654cf49a43SJulian Elischer 		/* Register new netgraph node type */
29664cf49a43SJulian Elischer 		s = splnet();
29674cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
29684cf49a43SJulian Elischer 			splx(s);
29694cf49a43SJulian Elischer 			break;
29704cf49a43SJulian Elischer 		}
29714cf49a43SJulian Elischer 
29724cf49a43SJulian Elischer 		/* Call type specific code */
29734cf49a43SJulian Elischer 		if (type->mod_event != NULL)
2974069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
29759ed346baSBosko Milekic 				mtx_lock(&ng_typelist_mtx);
2976c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
29774cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
29789ed346baSBosko Milekic 				mtx_unlock(&ng_typelist_mtx);
2979069154d5SJulian Elischer 			}
29804cf49a43SJulian Elischer 		splx(s);
29814cf49a43SJulian Elischer 		break;
29824cf49a43SJulian Elischer 
29834cf49a43SJulian Elischer 	case MOD_UNLOAD:
29844cf49a43SJulian Elischer 		s = splnet();
2985c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
29864cf49a43SJulian Elischer 			error = EBUSY;
2987c73b94a2SJulian Elischer 		} else {
2988c73b94a2SJulian Elischer 			if (type->refs == 0) {
2989c73b94a2SJulian Elischer 				/* failed load, nothing to undo */
2990c73b94a2SJulian Elischer 				splx(s);
2991c73b94a2SJulian Elischer 				break;
2992c73b94a2SJulian Elischer 			}
29934cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
29944cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
29954cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
29964cf49a43SJulian Elischer 					splx(s);
29974cf49a43SJulian Elischer 					break;
29984cf49a43SJulian Elischer 				}
29994cf49a43SJulian Elischer 			}
30009ed346baSBosko Milekic 			mtx_lock(&ng_typelist_mtx);
30014cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
30029ed346baSBosko Milekic 			mtx_unlock(&ng_typelist_mtx);
30034cf49a43SJulian Elischer 		}
30044cf49a43SJulian Elischer 		splx(s);
30054cf49a43SJulian Elischer 		break;
30064cf49a43SJulian Elischer 
30074cf49a43SJulian Elischer 	default:
30084cf49a43SJulian Elischer 		if (type->mod_event != NULL)
30094cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
30104cf49a43SJulian Elischer 		else
30113e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
30124cf49a43SJulian Elischer 		break;
30134cf49a43SJulian Elischer 	}
30144cf49a43SJulian Elischer 	return (error);
30154cf49a43SJulian Elischer }
30164cf49a43SJulian Elischer 
30174cf49a43SJulian Elischer /*
30184cf49a43SJulian Elischer  * Handle loading and unloading for this code.
30194cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
30204cf49a43SJulian Elischer  */
30214cf49a43SJulian Elischer static int
30224cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
30234cf49a43SJulian Elischer {
30241489164fSGleb Smirnoff 	int error = 0;
30254cf49a43SJulian Elischer 
30264cf49a43SJulian Elischer 	switch (event) {
30274cf49a43SJulian Elischer 	case MOD_LOAD:
30281489164fSGleb Smirnoff 		/* Initialize everything. */
30292c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3030efd8e7c9SDon Lewis 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3031efd8e7c9SDon Lewis 		    MTX_DEF);
3032efd8e7c9SDon Lewis 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3033efd8e7c9SDon Lewis 		    MTX_DEF);
3034cfea3f85SAlexander Motin 		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3035cfea3f85SAlexander Motin 		    MTX_DEF);
3036ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3037ac5dd141SGleb Smirnoff 		    MTX_DEF);
30381489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3039cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3040cfea3f85SAlexander Motin 		    MTX_DEF);
30411489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3042efd8e7c9SDon Lewis 		    MTX_DEF);
30431489164fSGleb Smirnoff #endif
30441489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
30451489164fSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
30461489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
304768780975SGleb Smirnoff 		netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL,
304868780975SGleb Smirnoff 		    NETISR_MPSAFE);
30494cf49a43SJulian Elischer 		break;
30504cf49a43SJulian Elischer 	case MOD_UNLOAD:
305164efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
30524cf49a43SJulian Elischer 		error = EBUSY;
30534cf49a43SJulian Elischer 		break;
30544cf49a43SJulian Elischer 	default:
30554cf49a43SJulian Elischer 		error = EOPNOTSUPP;
30564cf49a43SJulian Elischer 		break;
30574cf49a43SJulian Elischer 	}
30584cf49a43SJulian Elischer 	return (error);
30594cf49a43SJulian Elischer }
30604cf49a43SJulian Elischer 
30614cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
30624cf49a43SJulian Elischer 	"netgraph",
30634cf49a43SJulian Elischer 	ngb_mod_event,
30644cf49a43SJulian Elischer 	(NULL)
30654cf49a43SJulian Elischer };
3066aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
3067bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3068bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3069bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
30704cf49a43SJulian Elischer 
307130400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
307230400f03SJulian Elischer void
307330400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
307430400f03SJulian Elischer {
307530400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
307630400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
307730400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
307830400f03SJulian Elischer 		hook->lastfile, hook->lastline);
307930400f03SJulian Elischer 	if (line) {
308030400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
308130400f03SJulian Elischer 	}
308230400f03SJulian Elischer }
308330400f03SJulian Elischer 
308430400f03SJulian Elischer void
308530400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
308630400f03SJulian Elischer {
308730400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
30886b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
308930400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
309030400f03SJulian Elischer 		node->nd_refs, node->nd_name);
309130400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
309230400f03SJulian Elischer 		node->lastfile, node->lastline);
309330400f03SJulian Elischer 	if (line) {
309430400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
309530400f03SJulian Elischer 	}
309630400f03SJulian Elischer }
309730400f03SJulian Elischer 
3098069154d5SJulian Elischer void
3099069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3100069154d5SJulian Elischer {
3101069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3102069154d5SJulian Elischer 		item->lastfile, item->lastline);
31036b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
31046b795970SJulian Elischer 	case NGQF_DATA:
3105069154d5SJulian Elischer 		printf(" - [data]\n");
31066b795970SJulian Elischer 		break;
31076b795970SJulian Elischer 	case NGQF_MESG:
31086b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
31096b795970SJulian Elischer 		break;
31106b795970SJulian Elischer 	case NGQF_FN:
3111857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3112857304e6SRuslan Ermilov 			_NGI_FN(item),
3113857304e6SRuslan Ermilov 			_NGI_NODE(item),
3114857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3115857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3116857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3117857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3118857304e6SRuslan Ermilov 		break;
3119e088dd4cSAlexander Motin 	case NGQF_FN2:
3120eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3121857304e6SRuslan Ermilov 			_NGI_FN2(item),
31226064e568SGleb Smirnoff 			_NGI_NODE(item),
31236064e568SGleb Smirnoff 			_NGI_HOOK(item),
31246b795970SJulian Elischer 			item->body.fn.fn_arg1,
31256b795970SJulian Elischer 			item->body.fn.fn_arg2,
31266b795970SJulian Elischer 			item->body.fn.fn_arg2);
31276b795970SJulian Elischer 		break;
3128069154d5SJulian Elischer 	}
312930400f03SJulian Elischer 	if (line) {
3130069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
31316064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
313230400f03SJulian Elischer 			printf("node %p ([%x])\n",
31336064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3134069154d5SJulian Elischer 		}
313530400f03SJulian Elischer 	}
313630400f03SJulian Elischer }
313730400f03SJulian Elischer 
313830400f03SJulian Elischer static void
313930400f03SJulian Elischer ng_dumpitems(void)
314030400f03SJulian Elischer {
314130400f03SJulian Elischer 	item_p item;
314230400f03SJulian Elischer 	int i = 1;
314330400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
314430400f03SJulian Elischer 		printf("[%d] ", i++);
314530400f03SJulian Elischer 		dumpitem(item, NULL, 0);
314630400f03SJulian Elischer 	}
314730400f03SJulian Elischer }
314830400f03SJulian Elischer 
314930400f03SJulian Elischer static void
315030400f03SJulian Elischer ng_dumpnodes(void)
315130400f03SJulian Elischer {
315230400f03SJulian Elischer 	node_p node;
315330400f03SJulian Elischer 	int i = 1;
315453f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
315530400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
315630400f03SJulian Elischer 		printf("[%d] ", i++);
315730400f03SJulian Elischer 		dumpnode(node, NULL, 0);
315830400f03SJulian Elischer 	}
315953f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
316030400f03SJulian Elischer }
316130400f03SJulian Elischer 
316230400f03SJulian Elischer static void
316330400f03SJulian Elischer ng_dumphooks(void)
316430400f03SJulian Elischer {
316530400f03SJulian Elischer 	hook_p hook;
316630400f03SJulian Elischer 	int i = 1;
316753f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
316830400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
316930400f03SJulian Elischer 		printf("[%d] ", i++);
317030400f03SJulian Elischer 		dumphook(hook, NULL, 0);
317130400f03SJulian Elischer 	}
317253f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
317330400f03SJulian Elischer }
3174069154d5SJulian Elischer 
3175069154d5SJulian Elischer static int
3176069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3177069154d5SJulian Elischer {
3178069154d5SJulian Elischer 	int error;
3179069154d5SJulian Elischer 	int val;
3180069154d5SJulian Elischer 	int i;
3181069154d5SJulian Elischer 
3182069154d5SJulian Elischer 	val = allocated;
3183069154d5SJulian Elischer 	i = 1;
3184041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
31856b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
31866b795970SJulian Elischer 		return (error);
31876b795970SJulian Elischer 	if (val == 42) {
318830400f03SJulian Elischer 		ng_dumpitems();
318930400f03SJulian Elischer 		ng_dumpnodes();
319030400f03SJulian Elischer 		ng_dumphooks();
3191069154d5SJulian Elischer 	}
31926b795970SJulian Elischer 	return (0);
3193069154d5SJulian Elischer }
3194069154d5SJulian Elischer 
31956b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
31966b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
319730400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3198069154d5SJulian Elischer 
3199069154d5SJulian Elischer 
3200069154d5SJulian Elischer /***********************************************************************
3201069154d5SJulian Elischer * Worklist routines
3202069154d5SJulian Elischer **********************************************************************/
3203069154d5SJulian Elischer /* NETISR thread enters here */
3204069154d5SJulian Elischer /*
3205069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3206069154d5SJulian Elischer  * try get an item to process off it.
3207069154d5SJulian Elischer  * If there are no more, remove the node from the list.
3208069154d5SJulian Elischer  */
3209069154d5SJulian Elischer static void
3210069154d5SJulian Elischer ngintr(void)
3211069154d5SJulian Elischer {
3212069154d5SJulian Elischer 	for (;;) {
3213394cb30aSAlexander Motin 		node_p  node;
3214394cb30aSAlexander Motin 
3215394cb30aSAlexander Motin 		/* Get node from the worklist. */
32162c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
3217069154d5SJulian Elischer 		node = TAILQ_FIRST(&ng_worklist);
3218069154d5SJulian Elischer 		if (!node) {
32192c8dda8dSWojciech A. Koszek 			NG_WORKLIST_UNLOCK();
3220069154d5SJulian Elischer 			break;
3221069154d5SJulian Elischer 		}
322230400f03SJulian Elischer 		TAILQ_REMOVE(&ng_worklist, node, nd_work);
32232c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
32242955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
32252955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3226069154d5SJulian Elischer 		/*
3227069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3228069154d5SJulian Elischer 		 * that the list had on it.
3229069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3230069154d5SJulian Elischer 		 * let you have another item off the queue.
3231069154d5SJulian Elischer 		 * All this time, keep the reference
3232069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3233069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3234069154d5SJulian Elischer 		 */
3235069154d5SJulian Elischer 		for (;;) {
3236394cb30aSAlexander Motin 			item_p item;
3237714fb865SGleb Smirnoff 			int rw;
3238714fb865SGleb Smirnoff 
32392c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
3240714fb865SGleb Smirnoff 			item = ng_dequeue(&node->nd_input_queue, &rw);
3241069154d5SJulian Elischer 			if (item == NULL) {
3242394cb30aSAlexander Motin 				atomic_clear_int(&node->nd_flags, NGF_WORKQ);
32432c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3244069154d5SJulian Elischer 				break; /* go look for another node */
3245069154d5SJulian Elischer 			} else {
32462c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
32475951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3248714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
32495951069aSJulian Elischer 				NG_NODE_UNREF(node);
3250069154d5SJulian Elischer 			}
3251069154d5SJulian Elischer 		}
3252a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3253069154d5SJulian Elischer 	}
3254069154d5SJulian Elischer }
3255069154d5SJulian Elischer 
325633338e73SJulian Elischer /*
325733338e73SJulian Elischer  * XXX
325833338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
325933338e73SJulian Elischer  * to be outside the mutex zone
326033338e73SJulian Elischer  */
3261069154d5SJulian Elischer static void
3262394cb30aSAlexander Motin ng_worklist_add(node_p node)
3263069154d5SJulian Elischer {
3264f912c0f7SGleb Smirnoff 
32655bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3266f912c0f7SGleb Smirnoff 
3267be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_WORKQ) == 0) {
3268069154d5SJulian Elischer 		/*
3269069154d5SJulian Elischer 		 * If we are not already on the work queue,
3270069154d5SJulian Elischer 		 * then put us on.
3271069154d5SJulian Elischer 		 */
3272394cb30aSAlexander Motin 		atomic_set_int(&node->nd_flags, NGF_WORKQ);
3273394cb30aSAlexander Motin 		NG_NODE_REF(node); /* XXX fafe in mutex? */
32742c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
327530400f03SJulian Elischer 		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
32762c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3277394cb30aSAlexander Motin 		schednetisr(NETISR_NETGRAPH);
32782955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
32792955ee18SGleb Smirnoff 		    node->nd_ID, node);
3280394cb30aSAlexander Motin 	} else {
32812955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
32822955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3283394cb30aSAlexander Motin 	}
3284069154d5SJulian Elischer }
3285069154d5SJulian Elischer 
3286069154d5SJulian Elischer 
3287069154d5SJulian Elischer /***********************************************************************
3288069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3289069154d5SJulian Elischer ***********************************************************************/
3290069154d5SJulian Elischer 
329130400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
329230400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
32934cf49a43SJulian Elischer 	do {								\
32941acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3295069154d5SJulian Elischer 			printf("item already has node");		\
32963de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
32971acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3298069154d5SJulian Elischer 		}							\
32991acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3300069154d5SJulian Elischer 			printf("item already has hook");		\
33013de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
33021acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3303069154d5SJulian Elischer 		}							\
3304069154d5SJulian Elischer 	} while (0)
3305069154d5SJulian Elischer #else
330630400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3307069154d5SJulian Elischer #endif
3308069154d5SJulian Elischer 
3309069154d5SJulian Elischer /*
33108ed370fdSJulian Elischer  * Put mbuf into the item.
3311069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3312069154d5SJulian Elischer  * (or equivalent)
3313069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3314069154d5SJulian Elischer  * remote node might go away in this timescale.
3315069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3316069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
33174be59335SGleb Smirnoff  * here to be able to do this.
3318069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3319069154d5SJulian Elischer  *
3320069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3321069154d5SJulian Elischer  */
3322069154d5SJulian Elischer item_p
332342282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3324069154d5SJulian Elischer {
3325069154d5SJulian Elischer 	item_p item;
3326069154d5SJulian Elischer 
3327ed75521fSAlexander Motin 	if (atomic_fetchadd_int(&useddata, 1) >= maxdata) {
3328ed75521fSAlexander Motin 		atomic_subtract_int(&useddata, 1);
3329ed75521fSAlexander Motin 		NG_FREE_M(m);
3330ed75521fSAlexander Motin 		return (NULL);
3331ed75521fSAlexander Motin 	}
333242282202SGleb Smirnoff 	if ((item = ng_getqblk(flags)) == NULL) {
3333069154d5SJulian Elischer 		NG_FREE_M(m);
3334069154d5SJulian Elischer 		return (NULL);
3335069154d5SJulian Elischer 	}
333630400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
33376f683eeeSGleb Smirnoff 	item->el_flags = NGQF_DATA | NGQF_READER;
3338069154d5SJulian Elischer 	NGI_M(item) = m;
3339069154d5SJulian Elischer 	return (item);
3340069154d5SJulian Elischer }
3341069154d5SJulian Elischer 
3342069154d5SJulian Elischer /*
3343069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3344069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3345069154d5SJulian Elischer  * to work out what some of the fields should be.
3346069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3347069154d5SJulian Elischer  * (or equivalent)
3348069154d5SJulian Elischer  */
3349069154d5SJulian Elischer item_p
335042282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3351069154d5SJulian Elischer {
3352069154d5SJulian Elischer 	item_p item;
3353069154d5SJulian Elischer 
335442282202SGleb Smirnoff 	if ((item = ng_getqblk(flags)) == NULL) {
3355069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3356069154d5SJulian Elischer 		return (NULL);
3357069154d5SJulian Elischer 	}
335830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
33596f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
33606f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
33616f683eeeSGleb Smirnoff 		item->el_flags = NGQF_MESG | NGQF_READER;
33626f683eeeSGleb Smirnoff 	else
33636f683eeeSGleb Smirnoff 		item->el_flags = NGQF_MESG | NGQF_WRITER;
3364069154d5SJulian Elischer 	/*
3365069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3366069154d5SJulian Elischer 	 */
3367069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3368facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3369069154d5SJulian Elischer 	return (item);
3370069154d5SJulian Elischer }
3371069154d5SJulian Elischer 
3372069154d5SJulian Elischer 
3373069154d5SJulian Elischer 
33741acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
33756b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
33766b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3377069154d5SJulian Elischer 			if (retaddr) {					\
3378069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
33794cf49a43SJulian Elischer 			} else {					\
3380069154d5SJulian Elischer 				/*					\
3381069154d5SJulian Elischer 				 * The old return address should be ok.	\
3382069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3383069154d5SJulian Elischer 				 * here.				\
3384069154d5SJulian Elischer 				 */					\
3385069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3386069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3387069154d5SJulian Elischer 						= ng_node2ID(here);	\
3388069154d5SJulian Elischer 				}					\
3389069154d5SJulian Elischer 			}						\
33904cf49a43SJulian Elischer 		}							\
33914cf49a43SJulian Elischer 	} while (0)
33924cf49a43SJulian Elischer 
33934cf49a43SJulian Elischer int
3394069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
33954cf49a43SJulian Elischer {
33961acb27c6SJulian Elischer 	hook_p peer;
33971acb27c6SJulian Elischer 	node_p peernode;
339830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3399069154d5SJulian Elischer 	/*
3400069154d5SJulian Elischer 	 * Quick sanity check..
340130400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
340230400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
340330400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3404069154d5SJulian Elischer 	 */
3405069154d5SJulian Elischer 	if ((hook == NULL)
340630400f03SJulian Elischer 	|| NG_HOOK_NOT_VALID(hook)
340730400f03SJulian Elischer 	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
340830400f03SJulian Elischer 	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
3409069154d5SJulian Elischer 		NG_FREE_ITEM(item);
34106b795970SJulian Elischer 		TRAP_ERROR();
3411e08d3e3cSJulian Elischer 		return (ENETDOWN);
34124cf49a43SJulian Elischer 	}
34134cf49a43SJulian Elischer 
34144cf49a43SJulian Elischer 	/*
3415069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
34164cf49a43SJulian Elischer 	 */
34171acb27c6SJulian Elischer 	peer = NG_HOOK_PEER(hook);
34181acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
34191acb27c6SJulian Elischer 	NGI_SET_HOOK(item, peer);
34201acb27c6SJulian Elischer 	peernode = NG_PEER_NODE(hook);
34211acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
34221acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
34238b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3424069154d5SJulian Elischer 	return (0);
3425069154d5SJulian Elischer }
3426069154d5SJulian Elischer 
34274cf49a43SJulian Elischer int
3428069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
34294cf49a43SJulian Elischer {
34304cf49a43SJulian Elischer 	node_p	dest = NULL;
3431069154d5SJulian Elischer 	hook_p	hook = NULL;
34324cf49a43SJulian Elischer 	int	error;
3433069154d5SJulian Elischer 
343430400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3435069154d5SJulian Elischer 	/*
3436069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3437069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3438069154d5SJulian Elischer 	 */
3439069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3440069154d5SJulian Elischer 	if (error) {
3441069154d5SJulian Elischer 		NG_FREE_ITEM(item);
344230400f03SJulian Elischer 		return (error);
3443069154d5SJulian Elischer 	}
34441acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
34451acb27c6SJulian Elischer 	if ( hook) {
344630400f03SJulian Elischer 		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
34471acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
34481acb27c6SJulian Elischer 	}
34491acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3450069154d5SJulian Elischer 	return (0);
3451069154d5SJulian Elischer }
3452069154d5SJulian Elischer 
3453069154d5SJulian Elischer int
3454069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3455069154d5SJulian Elischer {
3456069154d5SJulian Elischer 	node_p dest;
3457069154d5SJulian Elischer 
345830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3459069154d5SJulian Elischer 	/*
3460069154d5SJulian Elischer 	 * Find the target node.
3461069154d5SJulian Elischer 	 */
3462069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3463069154d5SJulian Elischer 	if (dest == NULL) {
3464069154d5SJulian Elischer 		NG_FREE_ITEM(item);
34656b795970SJulian Elischer 		TRAP_ERROR();
3466069154d5SJulian Elischer 		return(EINVAL);
3467069154d5SJulian Elischer 	}
3468069154d5SJulian Elischer 	/* Fill out the contents */
34691acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
34701acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
34711acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3472069154d5SJulian Elischer 	return (0);
3473069154d5SJulian Elischer }
3474069154d5SJulian Elischer 
3475069154d5SJulian Elischer /*
3476069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3477069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3478069154d5SJulian Elischer  * Useful for removing that hook :-)
3479069154d5SJulian Elischer  */
3480069154d5SJulian Elischer item_p
3481069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3482069154d5SJulian Elischer {
3483069154d5SJulian Elischer 	item_p item;
34844cf49a43SJulian Elischer 
3485859a4d16SJulian Elischer 	/*
3486859a4d16SJulian Elischer 	 * Find the target node.
3487859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3488859a4d16SJulian Elischer 	 * to the address.
3489859a4d16SJulian Elischer 	 */
349042282202SGleb Smirnoff 	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) {
3491069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3492069154d5SJulian Elischer 		return (NULL);
34934cf49a43SJulian Elischer 	}
34944cf49a43SJulian Elischer 
34954cf49a43SJulian Elischer 	/* Fill out the contents */
34966f683eeeSGleb Smirnoff 	item->el_flags = NGQF_MESG | NGQF_WRITER;
349730400f03SJulian Elischer 	NG_NODE_REF(here);
34981acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
34991acb27c6SJulian Elischer 	if (hook) {
350030400f03SJulian Elischer 		NG_HOOK_REF(hook);
35011acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35021acb27c6SJulian Elischer 	}
3503069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3504069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3505069154d5SJulian Elischer 	return (item);
35064cf49a43SJulian Elischer }
35074cf49a43SJulian Elischer 
3508e088dd4cSAlexander Motin /*
3509e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3510e088dd4cSAlexander Motin  */
3511e088dd4cSAlexander Motin 
351242282202SGleb Smirnoff int
3513b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3514b332b91fSGleb Smirnoff {
3515b332b91fSGleb Smirnoff 
3516b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3517b332b91fSGleb Smirnoff }
3518b332b91fSGleb Smirnoff 
3519b332b91fSGleb Smirnoff int
3520aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
352142282202SGleb Smirnoff 	int flags)
35226b795970SJulian Elischer {
35236b795970SJulian Elischer 	item_p item;
35246b795970SJulian Elischer 
352542282202SGleb Smirnoff 	if ((item = ng_getqblk(flags)) == NULL) {
35266b795970SJulian Elischer 		return (ENOMEM);
35276b795970SJulian Elischer 	}
35286b795970SJulian Elischer 	item->el_flags = NGQF_FN | NGQF_WRITER;
3529a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
35301acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
35311acb27c6SJulian Elischer 	if (hook) {
35326b795970SJulian Elischer 		NG_HOOK_REF(hook);
35331acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35346b795970SJulian Elischer 	}
35356b795970SJulian Elischer 	NGI_FN(item) = fn;
35366b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
35376b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
353842282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
35396b795970SJulian Elischer }
35406b795970SJulian Elischer 
35414cf49a43SJulian Elischer /*
3542b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3543b332b91fSGleb Smirnoff  *
3544b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3545b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3546b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3547b332b91fSGleb Smirnoff  * be used.
3548e088dd4cSAlexander Motin  */
3549e088dd4cSAlexander Motin int
3550b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3551b332b91fSGleb Smirnoff 	int arg2, int flags)
3552e088dd4cSAlexander Motin {
3553e088dd4cSAlexander Motin 	item_p item;
3554e088dd4cSAlexander Motin 
3555b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3556b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3557e088dd4cSAlexander Motin 
3558e088dd4cSAlexander Motin 	/*
3559b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3560b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3561e088dd4cSAlexander Motin 	 */
3562b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
3563b332b91fSGleb Smirnoff 		if ((item = ng_getqblk(flags)) == NULL)
3564e088dd4cSAlexander Motin 			return (ENOMEM);
3565ed75521fSAlexander Motin 	} else {
3566ed75521fSAlexander Motin 		if ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA)
3567ed75521fSAlexander Motin 			atomic_subtract_int(&useddata, 1);
3568b332b91fSGleb Smirnoff 		item = pitem;
3569ed75521fSAlexander Motin 	}
3570b332b91fSGleb Smirnoff 
3571e088dd4cSAlexander Motin 	item->el_flags = NGQF_FN2 | NGQF_WRITER;
3572e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3573e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3574e088dd4cSAlexander Motin 	if (hook) {
3575e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3576e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3577e088dd4cSAlexander Motin 	}
3578e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3579e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3580e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3581b332b91fSGleb Smirnoff 	if (pitem != NULL && (flags & NG_REUSE_ITEM) == 0)
3582e088dd4cSAlexander Motin 		item->apply = pitem->apply;
3583e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3584e088dd4cSAlexander Motin }
3585e088dd4cSAlexander Motin 
3586e088dd4cSAlexander Motin /*
3587d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3588d2ca21a9SJulian Elischer  */
3589d2ca21a9SJulian Elischer static void
35901fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3591d2ca21a9SJulian Elischer {
3592d2ca21a9SJulian Elischer 	item_p item = arg;
3593d2ca21a9SJulian Elischer 
3594d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3595d2ca21a9SJulian Elischer }
3596d2ca21a9SJulian Elischer 
3597d2ca21a9SJulian Elischer 
359830bef41bSGleb Smirnoff int
3599f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3600d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3601d2ca21a9SJulian Elischer {
36021bf8e0faSGleb Smirnoff 	item_p item, oitem;
3603d2ca21a9SJulian Elischer 
360442282202SGleb Smirnoff 	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL)
360530bef41bSGleb Smirnoff 		return (ENOMEM);
360630bef41bSGleb Smirnoff 
3607d2ca21a9SJulian Elischer 	item->el_flags = NGQF_FN | NGQF_WRITER;
3608d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3609d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3610d2ca21a9SJulian Elischer 	if (hook) {
3611d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3612d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3613d2ca21a9SJulian Elischer 	}
3614d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3615d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3616d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
36171bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
36181bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
36191bf8e0faSGleb Smirnoff 	    oitem != NULL)
36201bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
362130bef41bSGleb Smirnoff 	return (0);
3622d2ca21a9SJulian Elischer }
3623d2ca21a9SJulian Elischer 
3624d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3625d2ca21a9SJulian Elischer int
3626f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3627d2ca21a9SJulian Elischer {
3628d2ca21a9SJulian Elischer 	item_p item;
362930bef41bSGleb Smirnoff 	int rval;
3630d2ca21a9SJulian Elischer 
363103b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
363203b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
363303b25f5dSGleb Smirnoff 
36343eadb26dSGleb Smirnoff 	rval = callout_stop(c);
363530bef41bSGleb Smirnoff 	item = c->c_arg;
363630bef41bSGleb Smirnoff 	/* Do an extra check */
36371fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
363830bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3639d2ca21a9SJulian Elischer 		/*
3640d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3641d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3642d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3643d2ca21a9SJulian Elischer 		 */
3644d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3645d2ca21a9SJulian Elischer 	}
36461bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
364730bef41bSGleb Smirnoff 
364830bef41bSGleb Smirnoff 	return (rval);
3649d2ca21a9SJulian Elischer }
3650d2ca21a9SJulian Elischer 
3651d2ca21a9SJulian Elischer /*
3652069154d5SJulian Elischer  * Set the address, if none given, give the node here.
36534cf49a43SJulian Elischer  */
3654069154d5SJulian Elischer void
3655069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
36564cf49a43SJulian Elischer {
3657069154d5SJulian Elischer 	if (retaddr) {
3658069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3659069154d5SJulian Elischer 	} else {
3660069154d5SJulian Elischer 		/*
3661069154d5SJulian Elischer 		 * The old return address should be ok.
3662069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3663069154d5SJulian Elischer 		 */
3664069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3665069154d5SJulian Elischer 	}
3666069154d5SJulian Elischer }
3667069154d5SJulian Elischer 
3668069154d5SJulian Elischer #define TESTING
3669069154d5SJulian Elischer #ifdef TESTING
3670069154d5SJulian Elischer /* just test all the macros */
3671069154d5SJulian Elischer void
3672069154d5SJulian Elischer ng_macro_test(item_p item);
3673069154d5SJulian Elischer void
3674069154d5SJulian Elischer ng_macro_test(item_p item)
3675069154d5SJulian Elischer {
3676069154d5SJulian Elischer 	node_p node = NULL;
3677069154d5SJulian Elischer 	hook_p hook = NULL;
36784cf49a43SJulian Elischer 	struct mbuf *m;
36794cf49a43SJulian Elischer 	struct ng_mesg *msg;
3680069154d5SJulian Elischer 	ng_ID_t retaddr;
3681069154d5SJulian Elischer 	int	error;
36824cf49a43SJulian Elischer 
3683069154d5SJulian Elischer 	NGI_GET_M(item, m);
3684069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3685069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
36868ed370fdSJulian Elischer 	NG_SEND_DATA(error, hook, m, NULL);
3687069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3688069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
368930400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3690069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3691069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3692069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3693069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
36944cf49a43SJulian Elischer }
3695069154d5SJulian Elischer #endif /* TESTING */
36964cf49a43SJulian Elischer 
3697