xref: /freebsd/sys/netgraph/ng_base.c (revision 9852972bb5d1a7b581a8cca4478c1ae6e9c97ea2)
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 	0,	/* numhooks */
117954c4772SJulian Elischer 	NULL,	/* private */
118954c4772SJulian Elischer 	0,	/* ID */
119954c4772SJulian Elischer 	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
120954c4772SJulian Elischer 	{},	/* all_nodes list entry */
121954c4772SJulian Elischer 	{},	/* id hashtable list entry */
122954c4772SJulian Elischer 	{	0,
1239852972bSAlexander Motin 		0,
124954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
1259852972bSAlexander Motin 		{}, /* workqueue entry */
1269852972bSAlexander Motin 		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
127954c4772SJulian Elischer 	},
1289852972bSAlexander Motin 	1,	/* refs */
129954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
130954c4772SJulian Elischer 	ND_MAGIC,
131954c4772SJulian Elischer 	__FILE__,
132954c4772SJulian Elischer 	__LINE__,
133954c4772SJulian Elischer 	{NULL}
134954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
135954c4772SJulian Elischer };
136954c4772SJulian Elischer 
137954c4772SJulian Elischer struct ng_hook ng_deadhook = {
138954c4772SJulian Elischer 	"dead",
139954c4772SJulian Elischer 	NULL,		/* private */
140954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
141e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
142954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
143954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
144954c4772SJulian Elischer 	{},		/* hooks list */
145c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
146c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1479852972bSAlexander Motin 	1,		/* refs always >= 1 */
148954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
149954c4772SJulian Elischer 	HK_MAGIC,
150954c4772SJulian Elischer 	__FILE__,
151954c4772SJulian Elischer 	__LINE__,
152954c4772SJulian Elischer 	{NULL}
153954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
154954c4772SJulian Elischer };
155954c4772SJulian Elischer 
156954c4772SJulian Elischer /*
157954c4772SJulian Elischer  * END DEAD STRUCTURES
158954c4772SJulian Elischer  */
159069154d5SJulian Elischer /* List nodes with unallocated work */
1609852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
161b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1624cf49a43SJulian Elischer 
1634cf49a43SJulian Elischer /* List of installed types */
164069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
165069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
1664cf49a43SJulian Elischer 
167069154d5SJulian Elischer /* Hash related definitions */
1680f150d04SJulian Elischer /* XXX Don't need to initialise them because it's a LIST */
169cfea3f85SAlexander Motin #define NG_ID_HASH_SIZE 128 /* most systems wont need even this many */
1700f150d04SJulian Elischer static LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
171069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
1720f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
1730f150d04SJulian Elischer #define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
1740f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
1750f150d04SJulian Elischer 	do { 								\
17653f9c5e9SRobert Watson 		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
1770f150d04SJulian Elischer 		LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)],	\
1780f150d04SJulian Elischer 						nd_idnodes) {		\
1790f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
1800f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
1810f150d04SJulian Elischer 				break;					\
1820f150d04SJulian Elischer 			}						\
1830f150d04SJulian Elischer 		}							\
1840f150d04SJulian Elischer 	} while (0)
185069154d5SJulian Elischer 
186cfea3f85SAlexander Motin #define NG_NAME_HASH_SIZE 128 /* most systems wont need even this many */
187cfea3f85SAlexander Motin static LIST_HEAD(, ng_node) ng_name_hash[NG_NAME_HASH_SIZE];
188cfea3f85SAlexander Motin static struct mtx	ng_namehash_mtx;
189cfea3f85SAlexander Motin #define NG_NAMEHASH(NAME, HASH)				\
190cfea3f85SAlexander Motin 	do {						\
191cfea3f85SAlexander Motin 		u_char	h = 0;				\
192cfea3f85SAlexander Motin 		const u_char	*c;			\
193cfea3f85SAlexander Motin 		for (c = (const u_char*)(NAME); *c; c++)\
194cfea3f85SAlexander Motin 			h += *c;			\
195cfea3f85SAlexander Motin 		(HASH) = h % (NG_NAME_HASH_SIZE);	\
196cfea3f85SAlexander Motin 	} while (0)
197cfea3f85SAlexander Motin 
198dc90cad9SJulian Elischer 
1994cf49a43SJulian Elischer /* Internal functions */
2004cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
201069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
202dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2034cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
204394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
2054cf49a43SJulian Elischer static void	ngintr(void);
20627757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2079852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
208069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
209e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
210e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
211e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
212e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
2136b795970SJulian Elischer static int	ng_mkpeer(node_p node, const char *name,
2146b795970SJulian Elischer 						const char *name2, char *type);
215069154d5SJulian Elischer 
2164c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */
217069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
218069154d5SJulian Elischer node_p	ng_name2noderef(node_p node, const char *name);
219069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
220069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
221069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
222069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
2231acb27c6SJulian Elischer void	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
22430400f03SJulian Elischer void	ng_unname(node_p node);
225069154d5SJulian Elischer 
2264cf49a43SJulian Elischer 
2274cf49a43SJulian Elischer /* Our own netgraph malloc type */
2284cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
229069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
230069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
231069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
232069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
233069154d5SJulian Elischer 
234069154d5SJulian Elischer /* Should not be visible outside this file */
23530400f03SJulian Elischer 
23630400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
23730400f03SJulian Elischer 	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
23830400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
23930400f03SJulian Elischer 	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
24030400f03SJulian Elischer 
2412c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2424abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2432c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2444abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2452c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2464abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2472c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2484abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2492c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2504abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2512c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2524abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
2532c8dda8dSWojciech A. Koszek 
25430400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
25530400f03SJulian Elischer /*
25630400f03SJulian Elischer  * In debug mode:
25730400f03SJulian Elischer  * In an attempt to help track reference count screwups
25830400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
25930400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
26030400f03SJulian Elischer  * after they have been freed.
26130400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
26230400f03SJulian Elischer  */
26330400f03SJulian Elischer static __inline hook_p
26430400f03SJulian Elischer ng_alloc_hook(void)
26530400f03SJulian Elischer {
26630400f03SJulian Elischer 	hook_p hook;
26730400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2689ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
26930400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
27030400f03SJulian Elischer 	if (hook) {
27130400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
27230400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
27330400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
27430400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
2759ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
27630400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
27730400f03SJulian Elischer 	} else {
2789ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
27930400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
28030400f03SJulian Elischer 		if (hook) {
28130400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
2829ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
28330400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
2849ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
28530400f03SJulian Elischer 		}
28630400f03SJulian Elischer 	}
28730400f03SJulian Elischer 	return (hook);
28830400f03SJulian Elischer }
28930400f03SJulian Elischer 
29030400f03SJulian Elischer static __inline node_p
29130400f03SJulian Elischer ng_alloc_node(void)
29230400f03SJulian Elischer {
29330400f03SJulian Elischer 	node_p node;
29430400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
2959ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
29630400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
29730400f03SJulian Elischer 	if (node) {
29830400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
29930400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
30030400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
30130400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3029ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30330400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
30430400f03SJulian Elischer 	} else {
3059ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30630400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
30730400f03SJulian Elischer 		if (node) {
30830400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3099ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
31030400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3119ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
31230400f03SJulian Elischer 		}
31330400f03SJulian Elischer 	}
31430400f03SJulian Elischer 	return (node);
31530400f03SJulian Elischer }
31630400f03SJulian Elischer 
31730400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
31830400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
31930400f03SJulian Elischer 
32030400f03SJulian Elischer 
32130400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
32230400f03SJulian Elischer 	do {								\
3239ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
32430400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
32530400f03SJulian Elischer 		hook->hk_magic = 0;					\
3269ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
32730400f03SJulian Elischer 	} while (0)
32830400f03SJulian Elischer 
32930400f03SJulian Elischer #define NG_FREE_NODE(node)						\
33030400f03SJulian Elischer 	do {								\
3319ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
33230400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
33330400f03SJulian Elischer 		node->nd_magic = 0;					\
3349ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
33530400f03SJulian Elischer 	} while (0)
33630400f03SJulian Elischer 
33730400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
33830400f03SJulian Elischer 
33930400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
34030400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
34130400f03SJulian Elischer 
342069154d5SJulian Elischer #define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
343069154d5SJulian Elischer #define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
34430400f03SJulian Elischer 
34530400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
34630400f03SJulian Elischer 
347f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3484cf49a43SJulian Elischer #ifndef TRAP_ERROR
3496b795970SJulian Elischer #define TRAP_ERROR()
3504cf49a43SJulian Elischer #endif
3514cf49a43SJulian Elischer 
352dc90cad9SJulian Elischer static	ng_ID_t nextID = 1;
353dc90cad9SJulian Elischer 
354b2da83c2SArchie Cobbs #ifdef INVARIANTS
355b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
356b2da83c2SArchie Cobbs 		struct mbuf *n;						\
357b2da83c2SArchie Cobbs 		int total;						\
358b2da83c2SArchie Cobbs 									\
359fe584538SDag-Erling Smørgrav 		M_ASSERTPKTHDR(m);					\
360b32cfb32SGleb Smirnoff 		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
361b2da83c2SArchie Cobbs 			total += n->m_len;				\
362b32cfb32SGleb Smirnoff 			if (n->m_nextpkt != NULL)			\
363b32cfb32SGleb Smirnoff 				panic("%s: m_nextpkt", __func__);	\
364b32cfb32SGleb Smirnoff 		}							\
365ba5b359aSGleb Smirnoff 									\
366b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
367b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
3686e551fb6SDavid E. O'Brien 			    __func__, (m)->m_pkthdr.len, total);	\
369b2da83c2SArchie Cobbs 		}							\
370b2da83c2SArchie Cobbs 	} while (0)
371b2da83c2SArchie Cobbs #else
372b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
373b2da83c2SArchie Cobbs #endif
374b2da83c2SArchie Cobbs 
375e088dd4cSAlexander Motin #define ERROUT(x)	do { error = (x); goto done; } while (0)
376dc90cad9SJulian Elischer 
3774cf49a43SJulian Elischer /************************************************************************
378f8307e12SArchie Cobbs 	Parse type definitions for generic messages
379f8307e12SArchie Cobbs ************************************************************************/
380f8307e12SArchie Cobbs 
381f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
382f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
383f0184ff8SArchie Cobbs static const struct ng_parse_struct_field				\
384f0184ff8SArchie Cobbs 	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
385f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
386f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
387f0184ff8SArchie Cobbs 	&ng_ ## lo ## _type_fields					\
388f8307e12SArchie Cobbs }
389f8307e12SArchie Cobbs 
390f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
391f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
392f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
393f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
394f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
395f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
396f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
397f8307e12SArchie Cobbs 
398f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
399d7d97eb0SJeroen Ruigrok van der Werven    value immediately preceding the array -- as with struct namelist
400f8307e12SArchie Cobbs    and struct typelist. */
401f8307e12SArchie Cobbs static int
402f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
403f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
404f8307e12SArchie Cobbs {
405f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
406f8307e12SArchie Cobbs }
407f8307e12SArchie Cobbs 
408f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
409f8307e12SArchie Cobbs static int
410f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
411f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
412f8307e12SArchie Cobbs {
413f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
414f8307e12SArchie Cobbs 
415f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
416f8307e12SArchie Cobbs }
417f8307e12SArchie Cobbs 
418f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
419f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
420f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
421f8307e12SArchie Cobbs 	&ng_generic_list_getLength
422f8307e12SArchie Cobbs };
423f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
424f8307e12SArchie Cobbs 	&ng_parse_array_type,
425f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
426f8307e12SArchie Cobbs };
427f8307e12SArchie Cobbs 
428f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
429f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
430f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
431f8307e12SArchie Cobbs 	&ng_generic_list_getLength
432f8307e12SArchie Cobbs };
433f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
434f8307e12SArchie Cobbs 	&ng_parse_array_type,
435f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
436f8307e12SArchie Cobbs };
437f8307e12SArchie Cobbs 
438f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
439f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
440f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
441f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
442f8307e12SArchie Cobbs };
443f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
444f8307e12SArchie Cobbs 	&ng_parse_array_type,
445f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
446f8307e12SArchie Cobbs };
447f8307e12SArchie Cobbs 
448f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
449f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
450f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
451f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
452f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
453f8307e12SArchie Cobbs 
454f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
455f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
456f8307e12SArchie Cobbs 	{
457f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
458f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
459f8307e12SArchie Cobbs 	  "shutdown",
460f8307e12SArchie Cobbs 	  NULL,
461f8307e12SArchie Cobbs 	  NULL
462f8307e12SArchie Cobbs 	},
463f8307e12SArchie Cobbs 	{
464f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
465f8307e12SArchie Cobbs 	  NGM_MKPEER,
466f8307e12SArchie Cobbs 	  "mkpeer",
467f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
468f8307e12SArchie Cobbs 	  NULL
469f8307e12SArchie Cobbs 	},
470f8307e12SArchie Cobbs 	{
471f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
472f8307e12SArchie Cobbs 	  NGM_CONNECT,
473f8307e12SArchie Cobbs 	  "connect",
474f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
475f8307e12SArchie Cobbs 	  NULL
476f8307e12SArchie Cobbs 	},
477f8307e12SArchie Cobbs 	{
478f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
479f8307e12SArchie Cobbs 	  NGM_NAME,
480f8307e12SArchie Cobbs 	  "name",
481f8307e12SArchie Cobbs 	  &ng_generic_name_type,
482f8307e12SArchie Cobbs 	  NULL
483f8307e12SArchie Cobbs 	},
484f8307e12SArchie Cobbs 	{
485f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
486f8307e12SArchie Cobbs 	  NGM_RMHOOK,
487f8307e12SArchie Cobbs 	  "rmhook",
488f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
489f8307e12SArchie Cobbs 	  NULL
490f8307e12SArchie Cobbs 	},
491f8307e12SArchie Cobbs 	{
492f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
493f8307e12SArchie Cobbs 	  NGM_NODEINFO,
494f8307e12SArchie Cobbs 	  "nodeinfo",
495f8307e12SArchie Cobbs 	  NULL,
496f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
497f8307e12SArchie Cobbs 	},
498f8307e12SArchie Cobbs 	{
499f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
500f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
501f8307e12SArchie Cobbs 	  "listhooks",
502f8307e12SArchie Cobbs 	  NULL,
503f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
504f8307e12SArchie Cobbs 	},
505f8307e12SArchie Cobbs 	{
506f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
507f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
508f8307e12SArchie Cobbs 	  "listnames",
509f8307e12SArchie Cobbs 	  NULL,
510f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
511f8307e12SArchie Cobbs 	},
512f8307e12SArchie Cobbs 	{
513f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
514f8307e12SArchie Cobbs 	  NGM_LISTNODES,
515f8307e12SArchie Cobbs 	  "listnodes",
516f8307e12SArchie Cobbs 	  NULL,
517f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
518f8307e12SArchie Cobbs 	},
519f8307e12SArchie Cobbs 	{
520f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
521f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
522f8307e12SArchie Cobbs 	  "listtypes",
523f8307e12SArchie Cobbs 	  NULL,
524f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
525f8307e12SArchie Cobbs 	},
526f8307e12SArchie Cobbs 	{
527f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
5287095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
5297095e097SPoul-Henning Kamp 	  "textconfig",
5307095e097SPoul-Henning Kamp 	  NULL,
5317095e097SPoul-Henning Kamp 	  &ng_parse_string_type
5327095e097SPoul-Henning Kamp 	},
5337095e097SPoul-Henning Kamp 	{
5347095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
535f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
536f8307e12SArchie Cobbs 	  "textstatus",
537f8307e12SArchie Cobbs 	  NULL,
538f8307e12SArchie Cobbs 	  &ng_parse_string_type
539f8307e12SArchie Cobbs 	},
540f8307e12SArchie Cobbs 	{
541f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
542f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
543f8307e12SArchie Cobbs 	  "ascii2binary",
544f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
545f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
546f8307e12SArchie Cobbs 	},
547f8307e12SArchie Cobbs 	{
548f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
549f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
550f8307e12SArchie Cobbs 	  "binary2ascii",
551f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
552f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
553f8307e12SArchie Cobbs 	},
554f8307e12SArchie Cobbs 	{ 0 }
555f8307e12SArchie Cobbs };
556f8307e12SArchie Cobbs 
557f8307e12SArchie Cobbs /************************************************************************
5584cf49a43SJulian Elischer 			Node routines
5594cf49a43SJulian Elischer ************************************************************************/
5604cf49a43SJulian Elischer 
5614cf49a43SJulian Elischer /*
5624cf49a43SJulian Elischer  * Instantiate a node of the requested type
5634cf49a43SJulian Elischer  */
5644cf49a43SJulian Elischer int
5654cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
5664cf49a43SJulian Elischer {
5674cf49a43SJulian Elischer 	struct ng_type *type;
568069154d5SJulian Elischer 	int	error;
5694cf49a43SJulian Elischer 
5704cf49a43SJulian Elischer 	/* Check that the type makes sense */
5714cf49a43SJulian Elischer 	if (typename == NULL) {
5726b795970SJulian Elischer 		TRAP_ERROR();
5734cf49a43SJulian Elischer 		return (EINVAL);
5744cf49a43SJulian Elischer 	}
5754cf49a43SJulian Elischer 
5767610f574SGleb Smirnoff 	/* Locate the node type. If we fail we return. Do not try to load
5777610f574SGleb Smirnoff 	 * module.
5787610f574SGleb Smirnoff 	 */
5794cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL)
5804cf49a43SJulian Elischer 		return (ENXIO);
5814cf49a43SJulian Elischer 
582069154d5SJulian Elischer 	/*
583069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
584069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
585069154d5SJulian Elischer 	 */
586069154d5SJulian Elischer 	if (type->constructor != NULL) {
587069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
588069154d5SJulian Elischer 			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
58930400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
590069154d5SJulian Elischer 			}
591069154d5SJulian Elischer 		}
592069154d5SJulian Elischer 	} else {
593069154d5SJulian Elischer 		/*
594069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
59564efc707SRobert Watson 		 * to be made. It must be brought into existence by
596954c4772SJulian Elischer 		 * some external agency. The external agency should
597069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
598069154d5SJulian Elischer 		 * netgraph part initialised.
599069154d5SJulian Elischer 		 */
6006b795970SJulian Elischer 		TRAP_ERROR();
601069154d5SJulian Elischer 		error = EINVAL;
602069154d5SJulian Elischer 	}
603069154d5SJulian Elischer 	return (error);
6044cf49a43SJulian Elischer }
6054cf49a43SJulian Elischer 
6064cf49a43SJulian Elischer /*
607069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
608069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
6094cf49a43SJulian Elischer  * The returned node has a reference count of 1.
6104cf49a43SJulian Elischer  */
6114cf49a43SJulian Elischer int
6124cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
6134cf49a43SJulian Elischer {
6144cf49a43SJulian Elischer 	node_p node;
6154cf49a43SJulian Elischer 
6164cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6174cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6186b795970SJulian Elischer 		TRAP_ERROR();
6194cf49a43SJulian Elischer 		return (EINVAL);
6204cf49a43SJulian Elischer 	}
6214cf49a43SJulian Elischer 
6224cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
62330400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6244cf49a43SJulian Elischer 	if (node == NULL) {
6256b795970SJulian Elischer 		TRAP_ERROR();
6264cf49a43SJulian Elischer 		return (ENOMEM);
6274cf49a43SJulian Elischer 	}
62830400f03SJulian Elischer 	node->nd_type = type;
62930400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6304cf49a43SJulian Elischer 	type->refs++;
6314cf49a43SJulian Elischer 
6322c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6339852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
63430400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6354cf49a43SJulian Elischer 
6364cf49a43SJulian Elischer 	/* Initialize hook list for new node */
63730400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6384cf49a43SJulian Elischer 
639cfea3f85SAlexander Motin 	/* Link us into the name hash. */
640cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
641cfea3f85SAlexander Motin 	LIST_INSERT_HEAD(&ng_name_hash[0], node, nd_nodes);
642cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
643069154d5SJulian Elischer 
644dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
6459ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
64630400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
647069154d5SJulian Elischer 		node_p node2 = NULL;
64830400f03SJulian Elischer 		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
6490f150d04SJulian Elischer 
65030400f03SJulian Elischer 		/* Is there a problem with the new number? */
6510f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6520f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
65330400f03SJulian Elischer 			break;
654069154d5SJulian Elischer 		}
65530400f03SJulian Elischer 	}
6560f150d04SJulian Elischer 	LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
65730400f03SJulian Elischer 							node, nd_idnodes);
6589ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
659dc90cad9SJulian Elischer 
6604cf49a43SJulian Elischer 	/* Done */
6614cf49a43SJulian Elischer 	*nodepp = node;
6624cf49a43SJulian Elischer 	return (0);
6634cf49a43SJulian Elischer }
6644cf49a43SJulian Elischer 
6654cf49a43SJulian Elischer /*
6664cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
66764efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6684cf49a43SJulian Elischer  * no type-specific method.
6694cf49a43SJulian Elischer  *
67064efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6713e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6723e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
673069154d5SJulian Elischer  *
674069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
67564efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
6763e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
6773e4084c8SJulian Elischer  * in which case we allow the node to survive.
6783e4084c8SJulian Elischer  *
67964efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
6803e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
6813e4084c8SJulian Elischer  * are rebooting.... etc.
6824cf49a43SJulian Elischer  */
6834cf49a43SJulian Elischer void
6841acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
6854cf49a43SJulian Elischer {
6863e4084c8SJulian Elischer 	hook_p hook;
6873e4084c8SJulian Elischer 
6884cf49a43SJulian Elischer 	/* Check if it's already shutting down */
689be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
6904cf49a43SJulian Elischer 		return;
6914cf49a43SJulian Elischer 
6921acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
6931acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
6941acb27c6SJulian Elischer 		return;
6951acb27c6SJulian Elischer 	}
6961acb27c6SJulian Elischer 
6974cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
69830400f03SJulian Elischer 	NG_NODE_REF(node);
6994cf49a43SJulian Elischer 
70030400f03SJulian Elischer 	/*
70130400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
70230400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
703be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
70430400f03SJulian Elischer 	 * creation
70530400f03SJulian Elischer 	 */
706be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7074cf49a43SJulian Elischer 
708991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
709991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
710991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
711991fc65aSJulian Elischer 
7123e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7133e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7143e4084c8SJulian Elischer 		ng_destroy_hook(hook);
71530400f03SJulian Elischer 
716069154d5SJulian Elischer 	/*
717069154d5SJulian Elischer 	 * Drain the input queue forceably.
71830400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
71930400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
72030400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
721b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
722069154d5SJulian Elischer 	 */
7239852972bSAlexander Motin 	ng_flush_input_queue(node);
724069154d5SJulian Elischer 
725069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
72630400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
72730400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
72830400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
72930400f03SJulian Elischer 			/*
73030400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
73130400f03SJulian Elischer 			 * that it doesn't want to die.
73230400f03SJulian Elischer 			 * Presumably it is a persistant node.
7331acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7341acb27c6SJulian Elischer 			 *  e.g. hardware going away,
735be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
73630400f03SJulian Elischer 			 */
737be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7381acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
73930400f03SJulian Elischer 			return;
7404cf49a43SJulian Elischer 		}
7411acb27c6SJulian Elischer 	} else {				/* do the default thing */
7421acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7431acb27c6SJulian Elischer 	}
7444cf49a43SJulian Elischer 
74530400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
74630400f03SJulian Elischer 
74730400f03SJulian Elischer 	/*
74830400f03SJulian Elischer 	 * Remove extra reference, possibly the last
74930400f03SJulian Elischer 	 * Possible other holders of references may include
75030400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
75130400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
75230400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
75330400f03SJulian Elischer 	 */
75430400f03SJulian Elischer 	NG_NODE_UNREF(node);
7554cf49a43SJulian Elischer }
7564cf49a43SJulian Elischer 
7575951069aSJulian Elischer /*
7585951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7595951069aSJulian Elischer  * deadnode always acts as it it were the last.
7605951069aSJulian Elischer  */
7615951069aSJulian Elischer int
76230400f03SJulian Elischer ng_unref_node(node_p node)
7634cf49a43SJulian Elischer {
76430400f03SJulian Elischer 	int v;
7656b795970SJulian Elischer 
7666b795970SJulian Elischer 	if (node == &ng_deadnode) {
7675951069aSJulian Elischer 		return (0);
7686b795970SJulian Elischer 	}
7696b795970SJulian Elischer 
770018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&node->nd_refs, -1);
771e8a49db2SJulian Elischer 
772018fe3d1SAlexander Motin 	if (v == 1) { /* we were the last */
773069154d5SJulian Elischer 
774cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
77530400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
77630400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
777cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
778069154d5SJulian Elischer 
7799ed346baSBosko Milekic 		mtx_lock(&ng_idhash_mtx);
78030400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
7819ed346baSBosko Milekic 		mtx_unlock(&ng_idhash_mtx);
782069154d5SJulian Elischer 
78312574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
784069154d5SJulian Elischer 		NG_FREE_NODE(node);
7854cf49a43SJulian Elischer 	}
786018fe3d1SAlexander Motin 	return (v - 1);
7874cf49a43SJulian Elischer }
7884cf49a43SJulian Elischer 
7894cf49a43SJulian Elischer /************************************************************************
790dc90cad9SJulian Elischer 			Node ID handling
791dc90cad9SJulian Elischer ************************************************************************/
792dc90cad9SJulian Elischer static node_p
793069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
794dc90cad9SJulian Elischer {
79530400f03SJulian Elischer 	node_p node;
7969ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
7970f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
79830400f03SJulian Elischer 	if(node)
79930400f03SJulian Elischer 		NG_NODE_REF(node);
8009ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
80130400f03SJulian Elischer 	return(node);
802dc90cad9SJulian Elischer }
803dc90cad9SJulian Elischer 
804dc90cad9SJulian Elischer ng_ID_t
805dc90cad9SJulian Elischer ng_node2ID(node_p node)
806dc90cad9SJulian Elischer {
80770de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
808dc90cad9SJulian Elischer }
809dc90cad9SJulian Elischer 
810dc90cad9SJulian Elischer /************************************************************************
8114cf49a43SJulian Elischer 			Node name handling
8124cf49a43SJulian Elischer ************************************************************************/
8134cf49a43SJulian Elischer 
8144cf49a43SJulian Elischer /*
8154cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
8164cf49a43SJulian Elischer  */
8174cf49a43SJulian Elischer int
8184cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8194cf49a43SJulian Elischer {
820cfea3f85SAlexander Motin 	int i, hash;
821069154d5SJulian Elischer 	node_p node2;
8224cf49a43SJulian Elischer 
8234cf49a43SJulian Elischer 	/* Check the name is valid */
82487e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8254cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8264cf49a43SJulian Elischer 			break;
8274cf49a43SJulian Elischer 	}
8284cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8296b795970SJulian Elischer 		TRAP_ERROR();
8304cf49a43SJulian Elischer 		return (EINVAL);
8314cf49a43SJulian Elischer 	}
832dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8336b795970SJulian Elischer 		TRAP_ERROR();
8344cf49a43SJulian Elischer 		return (EINVAL);
8354cf49a43SJulian Elischer 	}
8364cf49a43SJulian Elischer 
8374cf49a43SJulian Elischer 	/* Check the name isn't already being used */
838069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
83930400f03SJulian Elischer 		NG_NODE_UNREF(node2);
8406b795970SJulian Elischer 		TRAP_ERROR();
8414cf49a43SJulian Elischer 		return (EADDRINUSE);
8424cf49a43SJulian Elischer 	}
8434cf49a43SJulian Elischer 
844069154d5SJulian Elischer 	/* copy it */
84587e2c66aSHartmut Brandt 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8464cf49a43SJulian Elischer 
847cfea3f85SAlexander Motin 	/* Update name hash. */
848cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
849cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
850cfea3f85SAlexander Motin 	LIST_REMOVE(node, nd_nodes);
851cfea3f85SAlexander Motin 	LIST_INSERT_HEAD(&ng_name_hash[hash], node, nd_nodes);
852cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
853cfea3f85SAlexander Motin 
8544cf49a43SJulian Elischer 	return (0);
8554cf49a43SJulian Elischer }
8564cf49a43SJulian Elischer 
8574cf49a43SJulian Elischer /*
8584cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8594cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8604cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8614cf49a43SJulian Elischer  *
8624cf49a43SJulian Elischer  * Returns the node if found, else NULL.
863069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
864e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
865e1e8f51bSRobert Watson  * there.
8664cf49a43SJulian Elischer  */
8674cf49a43SJulian Elischer node_p
868069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
8694cf49a43SJulian Elischer {
870dc90cad9SJulian Elischer 	node_p node;
871dc90cad9SJulian Elischer 	ng_ID_t temp;
872cfea3f85SAlexander Motin 	int	hash;
8734cf49a43SJulian Elischer 
8744cf49a43SJulian Elischer 	/* "." means "this node" */
875069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
87630400f03SJulian Elischer 		NG_NODE_REF(here);
877069154d5SJulian Elischer 		return(here);
878069154d5SJulian Elischer 	}
8794cf49a43SJulian Elischer 
8804cf49a43SJulian Elischer 	/* Check for name-by-ID */
881dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
882069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
8834cf49a43SJulian Elischer 	}
8844cf49a43SJulian Elischer 
8854cf49a43SJulian Elischer 	/* Find node by name */
886cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
887cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
888cfea3f85SAlexander Motin 	LIST_FOREACH(node, &ng_name_hash[hash], nd_nodes) {
889cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
890cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
8914cf49a43SJulian Elischer 			break;
8924cf49a43SJulian Elischer 		}
89370de87f2SJulian Elischer 	}
894069154d5SJulian Elischer 	if (node)
89530400f03SJulian Elischer 		NG_NODE_REF(node);
896cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
8974cf49a43SJulian Elischer 	return (node);
8984cf49a43SJulian Elischer }
8994cf49a43SJulian Elischer 
9004cf49a43SJulian Elischer /*
9019d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
902dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9034cf49a43SJulian Elischer  */
904dc90cad9SJulian Elischer static ng_ID_t
9054cf49a43SJulian Elischer ng_decodeidname(const char *name)
9064cf49a43SJulian Elischer {
9072b70adcbSArchie Cobbs 	const int len = strlen(name);
90825792ef3SArchie Cobbs 	char *eptr;
9092b70adcbSArchie Cobbs 	u_long val;
9104cf49a43SJulian Elischer 
9112b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
91270de87f2SJulian Elischer 	if ((len < 3)
91370de87f2SJulian Elischer 	|| (name[0] != '[')
91470de87f2SJulian Elischer 	|| (name[len - 1] != ']')
91570de87f2SJulian Elischer 	|| (!isxdigit(name[1]))) {
91670de87f2SJulian Elischer 		return ((ng_ID_t)0);
91770de87f2SJulian Elischer 	}
9184cf49a43SJulian Elischer 
9192b70adcbSArchie Cobbs 	/* Decode number */
9202b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
92170de87f2SJulian Elischer 	if ((eptr - name != len - 1)
92270de87f2SJulian Elischer 	|| (val == ULONG_MAX)
92370de87f2SJulian Elischer 	|| (val == 0)) {
92412f035e0SJulian Elischer 		return ((ng_ID_t)0);
92570de87f2SJulian Elischer 	}
9262b70adcbSArchie Cobbs 	return (ng_ID_t)val;
9274cf49a43SJulian Elischer }
9284cf49a43SJulian Elischer 
9294cf49a43SJulian Elischer /*
9304cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9314cf49a43SJulian Elischer  * when shutting down and removing the node.
93264efc707SRobert Watson  * IF we allow name changing this may be more resurrected.
9334cf49a43SJulian Elischer  */
9344cf49a43SJulian Elischer void
9354cf49a43SJulian Elischer ng_unname(node_p node)
9364cf49a43SJulian Elischer {
9374cf49a43SJulian Elischer }
9384cf49a43SJulian Elischer 
9394cf49a43SJulian Elischer /************************************************************************
9404cf49a43SJulian Elischer 			Hook routines
9414cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
9423e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
9433e4084c8SJulian Elischer  they are connected to the 'dead' hook.
9444cf49a43SJulian Elischer ************************************************************************/
9454cf49a43SJulian Elischer 
9464cf49a43SJulian Elischer /*
9474cf49a43SJulian Elischer  * Remove a hook reference
9484cf49a43SJulian Elischer  */
94930400f03SJulian Elischer void
9504cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
9514cf49a43SJulian Elischer {
95230400f03SJulian Elischer 	int v;
9536b795970SJulian Elischer 
9546b795970SJulian Elischer 	if (hook == &ng_deadhook) {
9556b795970SJulian Elischer 		return;
9566b795970SJulian Elischer 	}
957018fe3d1SAlexander Motin 
958018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&hook->hk_refs, -1);
959e8a49db2SJulian Elischer 
96030400f03SJulian Elischer 	if (v == 1) { /* we were the last */
961f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
9626b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
963069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
964069154d5SJulian Elischer 	}
9654cf49a43SJulian Elischer }
9664cf49a43SJulian Elischer 
9674cf49a43SJulian Elischer /*
9684cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
9693e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
9704cf49a43SJulian Elischer  */
9714cf49a43SJulian Elischer static int
9724cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
9734cf49a43SJulian Elischer {
9744cf49a43SJulian Elischer 	hook_p hook;
9754cf49a43SJulian Elischer 	int error = 0;
9764cf49a43SJulian Elischer 
9774cf49a43SJulian Elischer 	/* Check that the given name is good */
9784cf49a43SJulian Elischer 	if (name == NULL) {
9796b795970SJulian Elischer 		TRAP_ERROR();
9804cf49a43SJulian Elischer 		return (EINVAL);
9814cf49a43SJulian Elischer 	}
982899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
9836b795970SJulian Elischer 		TRAP_ERROR();
9844cf49a43SJulian Elischer 		return (EEXIST);
9854cf49a43SJulian Elischer 	}
9864cf49a43SJulian Elischer 
9874cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
98830400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
9894cf49a43SJulian Elischer 	if (hook == NULL) {
9906b795970SJulian Elischer 		TRAP_ERROR();
9914cf49a43SJulian Elischer 		return (ENOMEM);
9924cf49a43SJulian Elischer 	}
9933e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
99430400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
9953e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
99630400f03SJulian Elischer 	hook->hk_node = node;
99730400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
9984cf49a43SJulian Elischer 
9993e4084c8SJulian Elischer 	/* Set hook name */
100087e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10013e4084c8SJulian Elischer 
10023e4084c8SJulian Elischer 	/*
10033e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10043e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10053e4084c8SJulian Elischer 	 */
1006954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1007954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
100830400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1009069154d5SJulian Elischer 			return (error);
1010069154d5SJulian Elischer 		}
1011954c4772SJulian Elischer 	}
10124cf49a43SJulian Elischer 	/*
10134cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10144cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10154cf49a43SJulian Elischer 	 */
101630400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
101730400f03SJulian Elischer 	node->nd_numhooks++;
10183e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
10194cf49a43SJulian Elischer 
10204cf49a43SJulian Elischer 	if (hookp)
10214cf49a43SJulian Elischer 		*hookp = hook;
10223e4084c8SJulian Elischer 	return (0);
10234cf49a43SJulian Elischer }
10244cf49a43SJulian Elischer 
10254cf49a43SJulian Elischer /*
1026899e9c4eSArchie Cobbs  * Find a hook
1027899e9c4eSArchie Cobbs  *
1028899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1029899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
10303e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1031899e9c4eSArchie Cobbs  */
1032899e9c4eSArchie Cobbs hook_p
1033899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1034899e9c4eSArchie Cobbs {
1035899e9c4eSArchie Cobbs 	hook_p hook;
1036899e9c4eSArchie Cobbs 
103730400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
103830400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
103930400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
104070de87f2SJulian Elischer 		if (NG_HOOK_IS_VALID(hook)
1041ce5e5f99SArchie Cobbs 		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
1042899e9c4eSArchie Cobbs 			return (hook);
1043899e9c4eSArchie Cobbs 	}
1044899e9c4eSArchie Cobbs 	return (NULL);
1045899e9c4eSArchie Cobbs }
1046899e9c4eSArchie Cobbs 
1047899e9c4eSArchie Cobbs /*
10484cf49a43SJulian Elischer  * Destroy a hook
10494cf49a43SJulian Elischer  *
10504cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
10514cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
10523e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
10533e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
10543e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
10553e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
10563e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
10573e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
10583e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
10593e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
10603e4084c8SJulian Elischer  * attached to the 'dead' hook.
10616b795970SJulian Elischer  *
10626b795970SJulian Elischer  * This routine is called at all stages of hook creation
10636b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
10644cf49a43SJulian Elischer  */
10654cf49a43SJulian Elischer void
10664cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
10674cf49a43SJulian Elischer {
1068ac5dd141SGleb Smirnoff 	hook_p peer;
1069ac5dd141SGleb Smirnoff 	node_p node;
10704cf49a43SJulian Elischer 
10716b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
10726b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
10736b795970SJulian Elischer 		return;
10746b795970SJulian Elischer 	}
1075ac5dd141SGleb Smirnoff 
1076ac5dd141SGleb Smirnoff 	/*
1077ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1078ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1079ac5dd141SGleb Smirnoff 	 */
1080ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1081ac5dd141SGleb Smirnoff 
1082ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1083ac5dd141SGleb Smirnoff 
1084ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1085ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1086ac5dd141SGleb Smirnoff 
10873e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
10883e4084c8SJulian Elischer 		/*
10893e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
10903e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
10913e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
10923e4084c8SJulian Elischer 		 */
10933e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
10943e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
10956b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
10966b795970SJulian Elischer 			/*
10976b795970SJulian Elischer 			 * If it's already divorced from a node,
10986b795970SJulian Elischer 			 * just free it.
10996b795970SJulian Elischer 			 */
1100ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11016b795970SJulian Elischer 		} else {
1102ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11036b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11046b795970SJulian Elischer 		}
110552fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
110652fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1107ac5dd141SGleb Smirnoff 	} else
1108ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1109ac5dd141SGleb Smirnoff 
1110ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11114cf49a43SJulian Elischer 
11124cf49a43SJulian Elischer 	/*
11134cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11144cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11154cf49a43SJulian Elischer 	 */
11166b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11176b795970SJulian Elischer 		return;
11186b795970SJulian Elischer 	}
111930400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
112030400f03SJulian Elischer 	node->nd_numhooks--;
112130400f03SJulian Elischer 	if (node->nd_type->disconnect) {
11224cf49a43SJulian Elischer 		/*
11236b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
112464efc707SRobert Watson 		 * trust its existence after this point. (except
11256b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
11266b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
11274cf49a43SJulian Elischer 		 */
112830400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
11294cf49a43SJulian Elischer 	}
11306b795970SJulian Elischer 
11316b795970SJulian Elischer 	/*
11326b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
11336b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
11346b795970SJulian Elischer 	 */
11356b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
11366b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
11376b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
11384cf49a43SJulian Elischer }
11394cf49a43SJulian Elischer 
11404cf49a43SJulian Elischer /*
11414cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
11424cf49a43SJulian Elischer  * is effectively bypassed.
11434cf49a43SJulian Elischer  */
11444cf49a43SJulian Elischer int
11454cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
11464cf49a43SJulian Elischer {
114730400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
11486b795970SJulian Elischer 		TRAP_ERROR();
11494cf49a43SJulian Elischer 		return (EINVAL);
115030400f03SJulian Elischer 	}
115130400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
115230400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
11534cf49a43SJulian Elischer 
11543e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
11553e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
11563e4084c8SJulian Elischer 
1157cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1158cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1159cf3254aaSGleb Smirnoff 
11604cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
11614cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
11624cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
11634cf49a43SJulian Elischer 	return (0);
11644cf49a43SJulian Elischer }
11654cf49a43SJulian Elischer 
11664cf49a43SJulian Elischer /*
11674cf49a43SJulian Elischer  * Install a new netgraph type
11684cf49a43SJulian Elischer  */
11694cf49a43SJulian Elischer int
11704cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
11714cf49a43SJulian Elischer {
11724cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
11734cf49a43SJulian Elischer 
11744cf49a43SJulian Elischer 	/* Check version and type name fields */
1175589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
1176589f6ed8SJulian Elischer 	|| (namelen == 0)
117787e2c66aSHartmut Brandt 	|| (namelen >= NG_TYPESIZ)) {
11786b795970SJulian Elischer 		TRAP_ERROR();
11798ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
11808ed370fdSJulian Elischer 			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
11818ed370fdSJulian Elischer 		}
11824cf49a43SJulian Elischer 		return (EINVAL);
11834cf49a43SJulian Elischer 	}
11844cf49a43SJulian Elischer 
11854cf49a43SJulian Elischer 	/* Check for name collision */
11864cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
11876b795970SJulian Elischer 		TRAP_ERROR();
11884cf49a43SJulian Elischer 		return (EEXIST);
11894cf49a43SJulian Elischer 	}
11904cf49a43SJulian Elischer 
1191069154d5SJulian Elischer 
1192069154d5SJulian Elischer 	/* Link in new type */
11939ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1194069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1195c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
11969ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
11974cf49a43SJulian Elischer 	return (0);
11984cf49a43SJulian Elischer }
11994cf49a43SJulian Elischer 
12004cf49a43SJulian Elischer /*
1201c31b4a53SJulian Elischer  * unlink a netgraph type
1202c31b4a53SJulian Elischer  * If no examples exist
1203c31b4a53SJulian Elischer  */
1204c31b4a53SJulian Elischer int
1205c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1206c31b4a53SJulian Elischer {
1207c31b4a53SJulian Elischer 	/* Check for name collision */
1208c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1209c31b4a53SJulian Elischer 		TRAP_ERROR();
1210c31b4a53SJulian Elischer 		return (EBUSY);
1211c31b4a53SJulian Elischer 	}
1212c31b4a53SJulian Elischer 
1213c31b4a53SJulian Elischer 	/* Unlink type */
1214c31b4a53SJulian Elischer 	mtx_lock(&ng_typelist_mtx);
1215c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1216c31b4a53SJulian Elischer 	mtx_unlock(&ng_typelist_mtx);
1217c31b4a53SJulian Elischer 	return (0);
1218c31b4a53SJulian Elischer }
1219c31b4a53SJulian Elischer 
1220c31b4a53SJulian Elischer /*
12214cf49a43SJulian Elischer  * Look for a type of the name given
12224cf49a43SJulian Elischer  */
12234cf49a43SJulian Elischer struct ng_type *
12244cf49a43SJulian Elischer ng_findtype(const char *typename)
12254cf49a43SJulian Elischer {
12264cf49a43SJulian Elischer 	struct ng_type *type;
12274cf49a43SJulian Elischer 
12289ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1229069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
12304cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
12314cf49a43SJulian Elischer 			break;
12324cf49a43SJulian Elischer 	}
12339ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12344cf49a43SJulian Elischer 	return (type);
12354cf49a43SJulian Elischer }
12364cf49a43SJulian Elischer 
12374cf49a43SJulian Elischer /************************************************************************
12384cf49a43SJulian Elischer 			Composite routines
12394cf49a43SJulian Elischer ************************************************************************/
12404cf49a43SJulian Elischer /*
12416b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
12424cf49a43SJulian Elischer  */
1243e088dd4cSAlexander Motin static int
1244e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
12454cf49a43SJulian Elischer {
1246e088dd4cSAlexander Motin 	int	error = 0;
12474cf49a43SJulian Elischer 
12486b795970SJulian Elischer 	/*
12496b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12506b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12516b795970SJulian Elischer 	 * Our caller has a reference on the node.
12526b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12536b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12541acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
12551acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
12566b795970SJulian Elischer 	 */
12576b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
12586b795970SJulian Elischer 		/*
12596b795970SJulian Elischer 		 * The node must have been freed again since we last visited
12606b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
12616b795970SJulian Elischer 		 * We should just release our references and
12626b795970SJulian Elischer 		 * free anything we can think of.
12636b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
12646b795970SJulian Elischer 		 * that holds the references, just return.
12656b795970SJulian Elischer 		 */
1266e088dd4cSAlexander Motin 		ERROUT(ENOENT);
12676b795970SJulian Elischer 	}
12686b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1269e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
12706b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
12711acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1272e088dd4cSAlexander Motin 			ERROUT(error);
12734cf49a43SJulian Elischer 		}
12746b795970SJulian Elischer 	}
12756b795970SJulian Elischer 	/*
12766b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
12776b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
12786b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
12796b795970SJulian Elischer 	 */
12806b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1281e088dd4cSAlexander Motin done:
1282e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1283e088dd4cSAlexander Motin 	return (error);
12846b795970SJulian Elischer }
12856b795970SJulian Elischer 
1286e088dd4cSAlexander Motin static int
1287e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
12886b795970SJulian Elischer {
1289ac5dd141SGleb Smirnoff 	hook_p	peer;
1290e088dd4cSAlexander Motin 	int	error = 0;
12916b795970SJulian Elischer 
12926b795970SJulian Elischer 	/*
12936b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12946b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12956b795970SJulian Elischer 	 * Our caller has a reference on the node.
12966b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12976b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12986b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
12996b795970SJulian Elischer 	 * First check the hook name is unique.
13001acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13016b795970SJulian Elischer 	 */
13026b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13036b795970SJulian Elischer 		TRAP_ERROR();
13046b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
13051acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1306e088dd4cSAlexander Motin 		ERROUT(EEXIST);
13076b795970SJulian Elischer 	}
13086b795970SJulian Elischer 	/*
13096b795970SJulian Elischer 	 * Check if the node type code has something to say about it
13106b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
13116b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
13126b795970SJulian Elischer 	 * The peer hook will also be destroyed.
13136b795970SJulian Elischer 	 */
13146b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1315e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1316e088dd4cSAlexander Motin 		    hook->hk_name))) {
13176b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
13181acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1319e088dd4cSAlexander Motin 			ERROUT(error);
13206b795970SJulian Elischer 		}
13216b795970SJulian Elischer 	}
13226b795970SJulian Elischer 
13236b795970SJulian Elischer 	/*
13246b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
13256b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
13266b795970SJulian Elischer 	 */
13276b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
13286b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
13296b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
13306b795970SJulian Elischer 	node->nd_numhooks++;
13316b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
13326b795970SJulian Elischer 
13336b795970SJulian Elischer 	/*
133464efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
13355951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
13366b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
13376b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
13386b795970SJulian Elischer 	 * yet.
133964efc707SRobert Watson 	 * We can call the local one immediately as we have the
13406b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
13416b795970SJulian Elischer 	 */
13426b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1343e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13446b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13451acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1346e088dd4cSAlexander Motin 			ERROUT(error);
13476b795970SJulian Elischer 		}
13486b795970SJulian Elischer 	}
1349ac5dd141SGleb Smirnoff 
1350ac5dd141SGleb Smirnoff 	/*
1351ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1352ac5dd141SGleb Smirnoff 	 */
1353ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1354ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1355ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1356ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1357ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1358ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1359e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1360ac5dd141SGleb Smirnoff 	}
1361ac5dd141SGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
1362ac5dd141SGleb Smirnoff 
1363b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1364b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1365ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
13661acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1367e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
13681acb27c6SJulian Elischer 	}
13696b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1370e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1371e088dd4cSAlexander Motin done:
1372e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1373e088dd4cSAlexander Motin 	return (error);
13744cf49a43SJulian Elischer }
13754cf49a43SJulian Elischer 
13764cf49a43SJulian Elischer /*
13776b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
13786b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
13794cf49a43SJulian Elischer  */
13806b795970SJulian Elischer static int
1381e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1382e088dd4cSAlexander Motin     node_p node2, const char *name2)
13834cf49a43SJulian Elischer {
13844cf49a43SJulian Elischer 	int	error;
13854cf49a43SJulian Elischer 	hook_p	hook;
13864cf49a43SJulian Elischer 	hook_p	hook2;
13874cf49a43SJulian Elischer 
13881acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
13891acb27c6SJulian Elischer 		return(EEXIST);
13901acb27c6SJulian Elischer 	}
13913e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
13924cf49a43SJulian Elischer 		return (error);
13936b795970SJulian Elischer 	/* Allocate the other hook and link it up */
13946b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
139519724144SGleb Smirnoff 	if (hook2 == NULL) {
13966b795970SJulian Elischer 		TRAP_ERROR();
13976b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
13986b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
13996b795970SJulian Elischer 		return (ENOMEM);
14006b795970SJulian Elischer 	}
14016b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14026b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14036b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
14046b795970SJulian Elischer 	hook->hk_peer = hook2;
14056b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
14066b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14076b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
140887e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
14096b795970SJulian Elischer 
14106b795970SJulian Elischer 	/*
14116b795970SJulian Elischer 	 * Queue the function above.
14126b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
14136b795970SJulian Elischer 	 * the other node.
14146b795970SJulian Elischer 	 */
1415b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1416b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
14173fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
14183fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
14193fb87c24SAlexander Motin 	}
14206b795970SJulian Elischer 
14216b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
14226b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
14233fb87c24SAlexander Motin 	return (error);
14244cf49a43SJulian Elischer }
14256b795970SJulian Elischer 
14266b795970SJulian Elischer /*
14276b795970SJulian Elischer  * Make a peer and connect.
14286b795970SJulian Elischer  * We assume that the local node is locked.
14296b795970SJulian Elischer  * The new node probably doesn't need a lock until
14306b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
14316b795970SJulian Elischer  * but we should think about it a bit more.
14326b795970SJulian Elischer  *
14336b795970SJulian Elischer  * The problem may come if the other node also fires up
14346b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
14356b795970SJulian Elischer  * also it may already get a command msg via it's ID.
14366b795970SJulian Elischer  *
14376b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
14386b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
14396b795970SJulian Elischer  * make arg1 point to the node to remove).
14406b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
14416b795970SJulian Elischer  * an unconnected node?
14426b795970SJulian Elischer  */
14436b795970SJulian Elischer static int
14446b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
14456b795970SJulian Elischer {
14466b795970SJulian Elischer 	node_p	node2;
14474c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
14486b795970SJulian Elischer 	int	error;
14496b795970SJulian Elischer 
14506b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
14516b795970SJulian Elischer 		return (error);
14526b795970SJulian Elischer 	}
14536b795970SJulian Elischer 
14546b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
14551acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14566b795970SJulian Elischer 		return (error);
14576b795970SJulian Elischer 	}
14586b795970SJulian Elischer 
14596b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
14601acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14616b795970SJulian Elischer 		ng_destroy_hook(hook1);
14626b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
14636b795970SJulian Elischer 		return (error);
14646b795970SJulian Elischer 	}
14656b795970SJulian Elischer 
14666b795970SJulian Elischer 	/*
14676b795970SJulian Elischer 	 * Actually link the two hooks together.
14686b795970SJulian Elischer 	 */
14696b795970SJulian Elischer 	hook1->hk_peer = hook2;
14706b795970SJulian Elischer 	hook2->hk_peer = hook1;
14716b795970SJulian Elischer 
14726b795970SJulian Elischer 	/* Each hook is referenced by the other */
14736b795970SJulian Elischer 	NG_HOOK_REF(hook1);
14746b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14756b795970SJulian Elischer 
14766b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
14776b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
14786b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
14796b795970SJulian Elischer 	}
14806b795970SJulian Elischer 
14816b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
14826b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
14836b795970SJulian Elischer 
14846b795970SJulian Elischer 	}
14853e4084c8SJulian Elischer 
14863e4084c8SJulian Elischer 	/*
14873e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
14883e4084c8SJulian Elischer 	 */
14896b795970SJulian Elischer 	if (error) {
14906b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
14911acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14926b795970SJulian Elischer 	} else {
14936b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
14946b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
14956b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
14966b795970SJulian Elischer 	}
14976b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
14983e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
14993e4084c8SJulian Elischer 	return (error);
15004cf49a43SJulian Elischer }
15016b795970SJulian Elischer 
1502069154d5SJulian Elischer /************************************************************************
1503069154d5SJulian Elischer 		Utility routines to send self messages
1504069154d5SJulian Elischer ************************************************************************/
1505069154d5SJulian Elischer 
15061acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
150764efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1508069154d5SJulian Elischer int
15091acb27c6SJulian Elischer ng_rmnode_self(node_p node)
1510069154d5SJulian Elischer {
15111acb27c6SJulian Elischer 	int		error;
15124cf49a43SJulian Elischer 
15131acb27c6SJulian Elischer 	if (node == &ng_deadnode)
15141acb27c6SJulian Elischer 		return (0);
1515be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1516be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
15171acb27c6SJulian Elischer 		return (0);
1518069154d5SJulian Elischer 
15191acb27c6SJulian Elischer 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
15201acb27c6SJulian Elischer 	return (error);
1521069154d5SJulian Elischer }
1522069154d5SJulian Elischer 
15231acb27c6SJulian Elischer static void
15246b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
15256b795970SJulian Elischer {
15266b795970SJulian Elischer 	ng_destroy_hook(hook);
15271acb27c6SJulian Elischer 	return ;
15286b795970SJulian Elischer }
15296b795970SJulian Elischer 
1530954c4772SJulian Elischer int
1531954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1532954c4772SJulian Elischer {
15336b795970SJulian Elischer 	int		error;
1534954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1535954c4772SJulian Elischer 
15366b795970SJulian Elischer 	if (node == &ng_deadnode)
15376b795970SJulian Elischer 		return (0);
15386b795970SJulian Elischer 
15396b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
15406b795970SJulian Elischer 	return (error);
1541954c4772SJulian Elischer }
1542954c4772SJulian Elischer 
1543069154d5SJulian Elischer /***********************************************************************
15444cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
15454cf49a43SJulian Elischer  *
15464cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
15474cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
15484cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
15494cf49a43SJulian Elischer  *
15504cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
15514cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
15524cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
15534cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
15544cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
15554cf49a43SJulian Elischer  *
15564cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1557069154d5SJulian Elischer  ***********************************************************************/
15584cf49a43SJulian Elischer int
15594cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
15604cf49a43SJulian Elischer {
15614cf49a43SJulian Elischer 	char	*node, *path, *hook;
15624cf49a43SJulian Elischer 	int	k;
15634cf49a43SJulian Elischer 
15644cf49a43SJulian Elischer 	/*
15654cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
15664cf49a43SJulian Elischer 	 */
15674cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
15684cf49a43SJulian Elischer 	if (*path) {
15694cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
15704cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
15714cf49a43SJulian Elischer 
15724cf49a43SJulian Elischer 		/* Node name must not be empty */
15734cf49a43SJulian Elischer 		if (!*node)
15744cf49a43SJulian Elischer 			return -1;
15754cf49a43SJulian Elischer 
15764cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
15774cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
15784cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
15794cf49a43SJulian Elischer 				if (node[k] == '.')
15804cf49a43SJulian Elischer 					return -1;
15814cf49a43SJulian Elischer 		}
15824cf49a43SJulian Elischer 	} else {
15834cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
15844cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
15854cf49a43SJulian Elischer 	}
15864cf49a43SJulian Elischer 
15874cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
15884cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
15894cf49a43SJulian Elischer 		if (path[k] == ':')
15904cf49a43SJulian Elischer 			return -1;
15914cf49a43SJulian Elischer 
15924cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
15934cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
15944cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
15954cf49a43SJulian Elischer 			return -1;
15964cf49a43SJulian Elischer 
15974cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
15984cf49a43SJulian Elischer 	if (path[0] == '.')
15994cf49a43SJulian Elischer 		path++;
16004cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16014cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16024cf49a43SJulian Elischer 
16034cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
16044cf49a43SJulian Elischer 	if (*path) {
16054cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
16064cf49a43SJulian Elischer 			if (path[k] == '.') {
16074cf49a43SJulian Elischer 				hook = NULL;
16084cf49a43SJulian Elischer 				break;
16094cf49a43SJulian Elischer 			}
16104cf49a43SJulian Elischer 	} else
16114cf49a43SJulian Elischer 		path = hook = NULL;
16124cf49a43SJulian Elischer 
16134cf49a43SJulian Elischer 	/* Done */
16144cf49a43SJulian Elischer 	if (nodep)
16154cf49a43SJulian Elischer 		*nodep = node;
16164cf49a43SJulian Elischer 	if (pathp)
16174cf49a43SJulian Elischer 		*pathp = path;
16184cf49a43SJulian Elischer 	if (hookp)
16194cf49a43SJulian Elischer 		*hookp = hook;
16204cf49a43SJulian Elischer 	return (0);
16214cf49a43SJulian Elischer }
16224cf49a43SJulian Elischer 
16234cf49a43SJulian Elischer /*
16244cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1625069154d5SJulian Elischer  * return the destination node.
16264cf49a43SJulian Elischer  */
16274cf49a43SJulian Elischer int
1628069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1629069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
16304cf49a43SJulian Elischer {
163187e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
16324cf49a43SJulian Elischer 	char   *nodename, *path, pbuf[2];
1633069154d5SJulian Elischer 	node_p  node, oldnode;
16344cf49a43SJulian Elischer 	char   *cp;
1635a4ec03cfSJulian Elischer 	hook_p hook = NULL;
16364cf49a43SJulian Elischer 
16374cf49a43SJulian Elischer 	/* Initialize */
163830400f03SJulian Elischer 	if (destp == NULL) {
16396b795970SJulian Elischer 		TRAP_ERROR();
16404cf49a43SJulian Elischer 		return EINVAL;
164130400f03SJulian Elischer 	}
16424cf49a43SJulian Elischer 	*destp = NULL;
16434cf49a43SJulian Elischer 
16444cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
16454cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
16464cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
16474cf49a43SJulian Elischer 
16484cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
16494cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
16506b795970SJulian Elischer 		TRAP_ERROR();
16514cf49a43SJulian Elischer 		return EINVAL;
16524cf49a43SJulian Elischer 	}
16534cf49a43SJulian Elischer 	if (path == NULL) {
16544cf49a43SJulian Elischer 		pbuf[0] = '.';	/* Needs to be writable */
16554cf49a43SJulian Elischer 		pbuf[1] = '\0';
16564cf49a43SJulian Elischer 		path = pbuf;
16574cf49a43SJulian Elischer 	}
16584cf49a43SJulian Elischer 
1659069154d5SJulian Elischer 	/*
1660069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1661069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1662069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1663069154d5SJulian Elischer 	 */
16644cf49a43SJulian Elischer 	if (nodename) {
1665069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
16664cf49a43SJulian Elischer 		if (node == NULL) {
16676b795970SJulian Elischer 			TRAP_ERROR();
16684cf49a43SJulian Elischer 			return (ENOENT);
16694cf49a43SJulian Elischer 		}
1670069154d5SJulian Elischer 	} else {
1671069154d5SJulian Elischer 		if (here == NULL) {
16726b795970SJulian Elischer 			TRAP_ERROR();
1673069154d5SJulian Elischer 			return (EINVAL);
1674069154d5SJulian Elischer 		}
16754cf49a43SJulian Elischer 		node = here;
167630400f03SJulian Elischer 		NG_NODE_REF(node);
1677069154d5SJulian Elischer 	}
16784cf49a43SJulian Elischer 
1679069154d5SJulian Elischer 	/*
1680069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1681069154d5SJulian Elischer 	 * XXX
1682069154d5SJulian Elischer 	 * We actually cannot guarantee that the sequence
1683069154d5SJulian Elischer 	 * is not being demolished as we crawl along it
1684069154d5SJulian Elischer 	 * without extra-ordinary locking etc.
1685069154d5SJulian Elischer 	 * So this is a bit dodgy to say the least.
1686069154d5SJulian Elischer 	 * We can probably hold up some things by holding
1687069154d5SJulian Elischer 	 * the nodelist mutex for the time of this
1688069154d5SJulian Elischer 	 * crawl if we wanted.. At least that way we wouldn't have to
168964efc707SRobert Watson 	 * worry about the nodes disappearing, but the hooks would still
1690069154d5SJulian Elischer 	 * be a problem.
1691069154d5SJulian Elischer 	 */
16924cf49a43SJulian Elischer 	for (cp = path; node != NULL && *cp != '\0'; ) {
16934cf49a43SJulian Elischer 		char *segment;
16944cf49a43SJulian Elischer 
16954cf49a43SJulian Elischer 		/*
16964cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
16974cf49a43SJulian Elischer 		 * found with a NUL; "cp" points to the next segment (or the
16984cf49a43SJulian Elischer 		 * NUL at the end).
16994cf49a43SJulian Elischer 		 */
17004cf49a43SJulian Elischer 		for (segment = cp; *cp != '\0'; cp++) {
17014cf49a43SJulian Elischer 			if (*cp == '.') {
17024cf49a43SJulian Elischer 				*cp++ = '\0';
17034cf49a43SJulian Elischer 				break;
17044cf49a43SJulian Elischer 			}
17054cf49a43SJulian Elischer 		}
17064cf49a43SJulian Elischer 
17074cf49a43SJulian Elischer 		/* Empty segment */
17084cf49a43SJulian Elischer 		if (*segment == '\0')
17094cf49a43SJulian Elischer 			continue;
17104cf49a43SJulian Elischer 
17114cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1712899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17134cf49a43SJulian Elischer 
17144cf49a43SJulian Elischer 		/* Can't get there from here... */
17154cf49a43SJulian Elischer 		if (hook == NULL
171630400f03SJulian Elischer 		    || NG_HOOK_PEER(hook) == NULL
171730400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(hook)
171830400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17196b795970SJulian Elischer 			TRAP_ERROR();
172030400f03SJulian Elischer 			NG_NODE_UNREF(node);
172130400f03SJulian Elischer #if 0
172230400f03SJulian Elischer 			printf("hooknotvalid %s %s %d %d %d %d ",
172330400f03SJulian Elischer 					path,
172430400f03SJulian Elischer 					segment,
172530400f03SJulian Elischer 					hook == NULL,
172630400f03SJulian Elischer 					NG_HOOK_PEER(hook) == NULL,
172730400f03SJulian Elischer 					NG_HOOK_NOT_VALID(hook),
172830400f03SJulian Elischer 					NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
172930400f03SJulian Elischer #endif
17304cf49a43SJulian Elischer 			return (ENOENT);
17314cf49a43SJulian Elischer 		}
17324cf49a43SJulian Elischer 
1733069154d5SJulian Elischer 		/*
1734069154d5SJulian Elischer 		 * Hop on over to the next node
1735069154d5SJulian Elischer 		 * XXX
1736069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1737069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1738069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1739069154d5SJulian Elischer 		 */
1740069154d5SJulian Elischer 		oldnode = node;
174130400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
174230400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
174330400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
174430400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
174530400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1746069154d5SJulian Elischer 			node = NULL;
1747069154d5SJulian Elischer 		}
17484cf49a43SJulian Elischer 	}
17494cf49a43SJulian Elischer 
17504cf49a43SJulian Elischer 	/* If node somehow missing, fail here (probably this is not needed) */
17514cf49a43SJulian Elischer 	if (node == NULL) {
17526b795970SJulian Elischer 		TRAP_ERROR();
17534cf49a43SJulian Elischer 		return (ENXIO);
17544cf49a43SJulian Elischer 	}
17554cf49a43SJulian Elischer 
17564cf49a43SJulian Elischer 	/* Done */
17574cf49a43SJulian Elischer 	*destp = node;
17581bdebe4dSArchie Cobbs 	if (lasthook != NULL)
175930400f03SJulian Elischer 		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
1760069154d5SJulian Elischer 	return (0);
1761069154d5SJulian Elischer }
1762069154d5SJulian Elischer 
1763069154d5SJulian Elischer /***************************************************************\
1764069154d5SJulian Elischer * Input queue handling.
1765069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1766069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
176764efc707SRobert Watson * Items which cannot be handled immediately are queued.
1768069154d5SJulian Elischer *
1769069154d5SJulian Elischer * read-write queue locking inline functions			*
1770069154d5SJulian Elischer \***************************************************************/
1771069154d5SJulian Elischer 
17729852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
17739852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
17749852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
17759852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
17769852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
17779852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1778069154d5SJulian Elischer 
1779069154d5SJulian Elischer /*
1780069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1781069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1782069154d5SJulian Elischer  * with them.
1783069154d5SJulian Elischer  *
1784b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1785069154d5SJulian Elischer  */
1786069154d5SJulian Elischer /*-
1787069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1788069154d5SJulian Elischer                        |
1789069154d5SJulian Elischer                        V
1790069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17914be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
17924be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
17934be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1794069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17954be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
17964be59335SGleb Smirnoff                             V                                | |
17974be59335SGleb Smirnoff                   [active reader count]                      | |
1798069154d5SJulian Elischer                                                              | |
17994be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1800069154d5SJulian Elischer                                                                |
18014be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1802b57a7965SJulian Elischer 
18038f9ac44aSAlexander Motin Node queue has such semantics:
18048f9ac44aSAlexander Motin - All flags modifications are atomic.
18058f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18068f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18078f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18088f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18098f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18108f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18118f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18128f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18138f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1814069154d5SJulian Elischer */
18158f9ac44aSAlexander Motin 
18164be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18174be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18184be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18194be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18204be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1821b57a7965SJulian Elischer 
1822b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18234be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18244be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1825b57a7965SJulian Elischer 
18264be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1827b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1828b57a7965SJulian Elischer 
18294be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
18304be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
18314be59335SGleb Smirnoff 
18324be59335SGleb Smirnoff /* How to decide what the next queued item is. */
18339852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
18349852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
18354be59335SGleb Smirnoff 
18364be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
18374be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
18384be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
18394be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
18404be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1841b57a7965SJulian Elischer 
1842b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
18434be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
18444be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1845394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
18464be59335SGleb Smirnoff 
1847714fb865SGleb Smirnoff #define NGQRW_R 0
1848714fb865SGleb Smirnoff #define NGQRW_W 1
1849714fb865SGleb Smirnoff 
18509852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
18519852972bSAlexander Motin 
1852069154d5SJulian Elischer /*
1853069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1854069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1855069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1856069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1857069154d5SJulian Elischer  * on the queue.
1858069154d5SJulian Elischer  */
1859069154d5SJulian Elischer static __inline item_p
18609852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1861069154d5SJulian Elischer {
1862069154d5SJulian Elischer 	item_p item;
18639852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1864b57a7965SJulian Elischer 
18658f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1866d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
18678f9ac44aSAlexander Motin 
18688f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
18694be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
18702955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
18712955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
18729852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
18734be59335SGleb Smirnoff 		return (NULL);
18744be59335SGleb Smirnoff 	}
1875d58e678fSGleb Smirnoff 
18764be59335SGleb Smirnoff 	/*
18774be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
18784be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
18794be59335SGleb Smirnoff 	 * the current state of the node.
18804be59335SGleb Smirnoff 	 */
18814be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1882394cb30aSAlexander Motin 		while (1) {
1883394cb30aSAlexander Motin 			long t = ngq->q_flags;
1884394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
18858f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
18862955ee18SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
18872955ee18SGleb Smirnoff 				    "can't proceed; queue flags 0x%lx", __func__,
18889852972bSAlexander Motin 				    node->nd_ID, node, t);
18894be59335SGleb Smirnoff 				return (NULL);
18904be59335SGleb Smirnoff 			}
18919852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1892394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1893394cb30aSAlexander Motin 				break;
1894394cb30aSAlexander Motin 			cpu_spinwait();
1895394cb30aSAlexander Motin 		}
18968f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1897714fb865SGleb Smirnoff 		*rw = NGQRW_R;
18989852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1899394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19008f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1901714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1902069154d5SJulian Elischer 	} else {
19038f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
1904394cb30aSAlexander Motin 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer "
1905394cb30aSAlexander Motin 		    "can't proceed; queue flags 0x%lx", __func__,
19069852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19074be59335SGleb Smirnoff 		return (NULL);
19084cf49a43SJulian Elischer 	}
19094cf49a43SJulian Elischer 
19104cf49a43SJulian Elischer 	/*
1911069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1912069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19134cf49a43SJulian Elischer 	 */
19149852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19159852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
19169852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
19179852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
19182955ee18SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
19192955ee18SGleb Smirnoff 	    "queue flags 0x%lx", __func__,
19209852972bSAlexander Motin 	    node->nd_ID, node, item, *rw ? "WRITER" : "READER" ,
19213b33fbe7SGleb Smirnoff 	    ngq->q_flags);
1922069154d5SJulian Elischer 	return (item);
1923069154d5SJulian Elischer }
1924859a4d16SJulian Elischer 
1925859a4d16SJulian Elischer /*
19268f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
19278f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
1928859a4d16SJulian Elischer  */
1929069154d5SJulian Elischer static __inline void
19309852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
1931069154d5SJulian Elischer {
19329852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
19334be59335SGleb Smirnoff 	if (rw == NGQRW_W)
19344be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
19354be59335SGleb Smirnoff 	else
19364be59335SGleb Smirnoff 		NGI_SET_READER(item);
1937394cb30aSAlexander Motin 
1938394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
1939394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
19409852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
19419852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
1942394cb30aSAlexander Motin 
19432955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
19449852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
19454be59335SGleb Smirnoff 
19464be59335SGleb Smirnoff 	/*
19474be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
19484be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
19494be59335SGleb Smirnoff 	 */
19504be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
19519852972bSAlexander Motin 		ng_worklist_add(node);
1952394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
1953069154d5SJulian Elischer }
1954069154d5SJulian Elischer 
19558f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
1956069154d5SJulian Elischer static __inline item_p
19579852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
1958069154d5SJulian Elischer {
19599852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1960ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1961069154d5SJulian Elischer 
1962394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
1963394cb30aSAlexander Motin 	while (1) {
19649852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
1965394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
1966394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
19679852972bSAlexander Motin 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19689852972bSAlexander Motin 		    t, t + READER_INCREMENT)) {
1969069154d5SJulian Elischer 	    		/* Successfully grabbed node */
1970394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19719852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
1972069154d5SJulian Elischer 			return (item);
1973069154d5SJulian Elischer 		}
1974394cb30aSAlexander Motin 		cpu_spinwait();
1975394cb30aSAlexander Motin 	};
1976069154d5SJulian Elischer 
1977394cb30aSAlexander Motin 	/* Queue the request for later. */
19789852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
1979f912c0f7SGleb Smirnoff 
1980f912c0f7SGleb Smirnoff 	return (NULL);
1981069154d5SJulian Elischer }
1982069154d5SJulian Elischer 
19838f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
1984069154d5SJulian Elischer static __inline item_p
19859852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
1986069154d5SJulian Elischer {
19879852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1988ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1989ac5dd141SGleb Smirnoff 
1990394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
19919852972bSAlexander Motin 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19929852972bSAlexander Motin 	    0, WRITER_ACTIVE)) {
1993394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
19942955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19959852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
1996069154d5SJulian Elischer 		return (item);
1997069154d5SJulian Elischer 	}
1998069154d5SJulian Elischer 
1999394cb30aSAlexander Motin 	/* Queue the request for later. */
20009852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
2001f912c0f7SGleb Smirnoff 
2002f912c0f7SGleb Smirnoff 	return (NULL);
2003069154d5SJulian Elischer }
2004069154d5SJulian Elischer 
2005262dfd7fSJulian Elischer #if 0
2006262dfd7fSJulian Elischer static __inline item_p
20079852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
2008262dfd7fSJulian Elischer {
20099852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20109852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2011262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2012262dfd7fSJulian Elischer 
2013262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2014262dfd7fSJulian Elischer 
20159852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2016262dfd7fSJulian Elischer 
2017262dfd7fSJulian Elischer 	/*
2018262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2019262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2020262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2021262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2022262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2023262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2024262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2025262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2026262dfd7fSJulian Elischer 	 */
20279852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
20289852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
20299852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2030262dfd7fSJulian Elischer 
2031262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2032262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2033262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2034262dfd7fSJulian Elischer 
2035262dfd7fSJulian Elischer 		/*
2036262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
2037262dfd7fSJulian Elischer 		 * down grade back to READER and finish up
2038262dfd7fSJulian Elischer 	 	 */
20399852972bSAlexander Motin 		atomic_add_int(&ngq->q_flags,
2040262dfd7fSJulian Elischer 		    READER_INCREMENT - WRITER_ACTIVE);
2041262dfd7fSJulian Elischer 
2042262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2043262dfd7fSJulian Elischer 		return;
2044262dfd7fSJulian Elischer 	}
2045262dfd7fSJulian Elischer 	/*
2046262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2047262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2048262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2049262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2050262dfd7fSJulian Elischer 	 * set the correct flags.
2051262dfd7fSJulian Elischer 	 */
20529852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2053262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
20549852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2055262dfd7fSJulian Elischer 
2056262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
20579852972bSAlexander Motin 		    node->nd_ID, node);
2058262dfd7fSJulian Elischer 	};
20599852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
20609852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
20619852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2062262dfd7fSJulian Elischer 
2063262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
20649852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2065394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20669852972bSAlexander Motin 		ng_worklist_add(node);
20679852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2068262dfd7fSJulian Elischer 
2069262dfd7fSJulian Elischer 	return;
2070262dfd7fSJulian Elischer }
2071262dfd7fSJulian Elischer #endif
2072262dfd7fSJulian Elischer 
20738f9ac44aSAlexander Motin /* Release reader lock. */
2074069154d5SJulian Elischer static __inline void
20759852972bSAlexander Motin ng_leave_read(node_p node)
2076069154d5SJulian Elischer {
20779852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2078069154d5SJulian Elischer }
2079069154d5SJulian Elischer 
20808f9ac44aSAlexander Motin /* Release writer lock. */
2081069154d5SJulian Elischer static __inline void
20829852972bSAlexander Motin ng_leave_write(node_p node)
2083069154d5SJulian Elischer {
20849852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2085069154d5SJulian Elischer }
2086069154d5SJulian Elischer 
20878f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2088069154d5SJulian Elischer static void
20899852972bSAlexander Motin ng_flush_input_queue(node_p node)
2090069154d5SJulian Elischer {
20919852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2092069154d5SJulian Elischer 	item_p item;
2093069154d5SJulian Elischer 
20942c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
20959852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
20969852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
20979852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
20989852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
20992c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21004be59335SGleb Smirnoff 
21014be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
210210e87318SAlexander Motin 		if (item->apply != NULL) {
210310e87318SAlexander Motin 			if (item->depth == 1)
210410e87318SAlexander Motin 				item->apply->error = ENOENT;
210510e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
210610e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
210710e87318SAlexander Motin 				    item->apply->error);
210810e87318SAlexander Motin 			}
2109eb2405ddSGleb Smirnoff 		}
2110505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21112c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2112069154d5SJulian Elischer 	}
21132c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2114069154d5SJulian Elischer }
2115069154d5SJulian Elischer 
2116069154d5SJulian Elischer /***********************************************************************
2117069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2118069154d5SJulian Elischer ***********************************************************************/
2119069154d5SJulian Elischer 
2120069154d5SJulian Elischer /*
21211acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2122069154d5SJulian Elischer  * Common:
2123069154d5SJulian Elischer  *    reference to destination node.
2124069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2125e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2126069154d5SJulian Elischer  * Data:
2127069154d5SJulian Elischer  *    pointer to mbuf
2128069154d5SJulian Elischer  * Control_Message:
2129069154d5SJulian Elischer  *    pointer to msg.
2130069154d5SJulian Elischer  *    ID of original sender node. (return address)
21311acb27c6SJulian Elischer  * Function:
21321acb27c6SJulian Elischer  *    Function pointer
21331acb27c6SJulian Elischer  *    void * argument
21341acb27c6SJulian Elischer  *    integer argument
2135069154d5SJulian Elischer  *
2136069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2137069154d5SJulian Elischer  */
2138069154d5SJulian Elischer 
2139069154d5SJulian Elischer int
214042282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2141069154d5SJulian Elischer {
2142e088dd4cSAlexander Motin 	hook_p hook;
2143e088dd4cSAlexander Motin 	node_p node;
214442282202SGleb Smirnoff 	int queue, rw;
2145e088dd4cSAlexander Motin 	struct ng_queue *ngq;
214632b33288SGleb Smirnoff 	int error = 0;
2147069154d5SJulian Elischer 
2148f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2149f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2150e088dd4cSAlexander Motin 
2151e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2152e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2153e088dd4cSAlexander Motin #endif
2154e088dd4cSAlexander Motin 
2155f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2156e088dd4cSAlexander Motin 	if (item->apply)
2157e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2158e088dd4cSAlexander Motin 
2159e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2160f50597f5SAlexander Motin 	/* Node is never optional. */
2161f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2162e088dd4cSAlexander Motin 
2163e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2164f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2165f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2166f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
216710204449SJulian Elischer 		if (NGI_M(item) == NULL)
2168e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2169069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
21706f683eeeSGleb Smirnoff 	}
21716f683eeeSGleb Smirnoff 
2172069154d5SJulian Elischer 	/*
2173f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2174f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2175f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2176069154d5SJulian Elischer 	 */
2177db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2178f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2179f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2180069154d5SJulian Elischer 		rw = NGQRW_W;
2181f50597f5SAlexander Motin 	} else {
2182f50597f5SAlexander Motin 		rw = NGQRW_R;
2183f50597f5SAlexander Motin 	}
21846f683eeeSGleb Smirnoff 
218581a253a4SAlexander Motin 	/*
218681a253a4SAlexander Motin 	 * If sender or receiver requests queued delivery or stack usage
218781a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
218881a253a4SAlexander Motin 	 */
218981a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
219081a253a4SAlexander Motin 		queue = 1;
2191f50597f5SAlexander Motin 	} else {
2192f50597f5SAlexander Motin 		queue = 0;
219381a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2194d4529f98SAlexander Motin 		/*
2195942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2196f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2197d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2198f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2199f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2200d4529f98SAlexander Motin 		 */
2201f50597f5SAlexander Motin 		size_t	st, su, sl;
220281a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2203f50597f5SAlexander Motin 		sl = st - su;
2204f50597f5SAlexander Motin 		if ((sl * 4 < st) ||
2205f50597f5SAlexander Motin 		    ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) ||
220681a253a4SAlexander Motin 		      (hook && (hook->hk_flags & HK_HI_STACK))))) {
220781a253a4SAlexander Motin 			queue = 1;
220881a253a4SAlexander Motin 		}
220981a253a4SAlexander Motin #endif
2210f50597f5SAlexander Motin 	}
221181a253a4SAlexander Motin 
2212069154d5SJulian Elischer 	if (queue) {
221310e87318SAlexander Motin 		item->depth = 1;
2214394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
22159852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2216f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2217069154d5SJulian Elischer 	}
2218069154d5SJulian Elischer 
2219069154d5SJulian Elischer 	/*
2220069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2221069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2222069154d5SJulian Elischer 	 */
222332b33288SGleb Smirnoff  	if (rw == NGQRW_R)
22249852972bSAlexander Motin 		item = ng_acquire_read(node, item);
222532b33288SGleb Smirnoff 	else
22269852972bSAlexander Motin 		item = ng_acquire_write(node, item);
22274cf49a43SJulian Elischer 
2228f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2229f50597f5SAlexander Motin 	if (item == NULL)
2230f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2231069154d5SJulian Elischer 
22325951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
22335951069aSJulian Elischer 
223410e87318SAlexander Motin 	item->depth++;
223527757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2236069154d5SJulian Elischer 
2237394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
22389852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2239394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
22402c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2241394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
22429852972bSAlexander Motin 			ng_worklist_add(node);
22432c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2244394cb30aSAlexander Motin 	}
2245394cb30aSAlexander Motin 
2246394cb30aSAlexander Motin 	/*
2247394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2248394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2249394cb30aSAlexander Motin 	 */
2250394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2251069154d5SJulian Elischer 
22521acb27c6SJulian Elischer 	return (error);
2253e088dd4cSAlexander Motin 
2254e088dd4cSAlexander Motin done:
2255f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
225610e87318SAlexander Motin 	if (item->apply != NULL) {
225710e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
225810e87318SAlexander Motin 			item->apply->error = error;
225910e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
226010e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
226110e87318SAlexander Motin 			    item->apply->error);
226210e87318SAlexander Motin 		}
226310e87318SAlexander Motin 	}
2264e72a98f4SAlexander Motin 
2265e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2266e088dd4cSAlexander Motin 	return (error);
2267069154d5SJulian Elischer }
2268069154d5SJulian Elischer 
2269069154d5SJulian Elischer /*
2270069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2271069154d5SJulian Elischer  * It should contain all the information needed
2272069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2273e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
22744cf49a43SJulian Elischer  */
227527757487SGleb Smirnoff static int
2276714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2277069154d5SJulian Elischer {
2278069154d5SJulian Elischer 	hook_p  hook;
2279069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2280c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2281e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
228210e87318SAlexander Motin 	int	error = 0, depth;
2283069154d5SJulian Elischer 
2284f50597f5SAlexander Motin 	/* Node and item are never optional. */
2285f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2286f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2287f50597f5SAlexander Motin 
22881acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
228930400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2290069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2291069154d5SJulian Elischer #endif
22928afe16d5SGleb Smirnoff 
22938afe16d5SGleb Smirnoff 	apply = item->apply;
229410e87318SAlexander Motin 	depth = item->depth;
22958afe16d5SGleb Smirnoff 
22966b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2297069154d5SJulian Elischer 	case NGQF_DATA:
2298069154d5SJulian Elischer 		/*
2299069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2300069154d5SJulian Elischer 		 */
2301f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2302f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2303f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2304a54a69d7SJulian Elischer 			error = EIO;
2305069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2306c4b5eea4SJulian Elischer 			break;
2307069154d5SJulian Elischer 		}
2308c4b5eea4SJulian Elischer 		/*
2309c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
2310c4b5eea4SJulian Elischer 		 * Give preference to the hook over-ride method
2311c4b5eea4SJulian Elischer 		 */
2312c4b5eea4SJulian Elischer 		if ((!(rcvdata = hook->hk_rcvdata))
2313c4b5eea4SJulian Elischer 		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2314c4b5eea4SJulian Elischer 			error = 0;
2315c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2316c4b5eea4SJulian Elischer 			break;
2317c4b5eea4SJulian Elischer 		}
2318a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2319069154d5SJulian Elischer 		break;
2320069154d5SJulian Elischer 	case NGQF_MESG:
2321e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2322069154d5SJulian Elischer 			/*
2323e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2324e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2325069154d5SJulian Elischer 			 * The message may not need it.
2326069154d5SJulian Elischer 			 */
232730400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2328069154d5SJulian Elischer 			hook = NULL;
2329069154d5SJulian Elischer 		}
2330069154d5SJulian Elischer 		/*
2331069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2332069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2333069154d5SJulian Elischer 		 */
233430400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
23356b795970SJulian Elischer 			TRAP_ERROR();
2336a54a69d7SJulian Elischer 			error = EINVAL;
2337069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2338e72a98f4SAlexander Motin 			break;
2339e72a98f4SAlexander Motin 		}
2340069154d5SJulian Elischer 		/*
2341069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2342069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2343069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2344e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2345069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2346069154d5SJulian Elischer 		 * reference a node or hook that has just been
2347069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2348069154d5SJulian Elischer 		 * is holding a reference, but..
2349069154d5SJulian Elischer 		 */
2350e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2351e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2352a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2353c4b5eea4SJulian Elischer 			break;
2354c4b5eea4SJulian Elischer 		}
2355e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2356e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
23576b795970SJulian Elischer 			TRAP_ERROR();
2358a54a69d7SJulian Elischer 			error = 0;
2359069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2360c4b5eea4SJulian Elischer 			break;
2361069154d5SJulian Elischer 		}
2362a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2363069154d5SJulian Elischer 		break;
23646b795970SJulian Elischer 	case NGQF_FN:
2365e088dd4cSAlexander Motin 	case NGQF_FN2:
2366e088dd4cSAlexander Motin 		/*
2367e088dd4cSAlexander Motin 		 *  We have to implicitly trust the hook,
2368e088dd4cSAlexander Motin 		 * as some of these are used for system purposes
2369e088dd4cSAlexander Motin 		 * where the hook is invalid. In the case of
2370e088dd4cSAlexander Motin 		 * the shutdown message we allow it to hit
2371e088dd4cSAlexander Motin 		 * even if the node is invalid.
2372e088dd4cSAlexander Motin 		 */
2373e088dd4cSAlexander Motin 		if ((NG_NODE_NOT_VALID(node))
2374e088dd4cSAlexander Motin 		&& (NGI_FN(item) != &ng_rmnode)) {
2375e088dd4cSAlexander Motin 			TRAP_ERROR();
2376e088dd4cSAlexander Motin 			error = EINVAL;
2377e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2378e088dd4cSAlexander Motin 			break;
2379e088dd4cSAlexander Motin 		}
2380b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2381b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2382b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2383b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2384b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2385e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2386e088dd4cSAlexander Motin 		break;
2387069154d5SJulian Elischer 	}
2388069154d5SJulian Elischer 	/*
2389069154d5SJulian Elischer 	 * We held references on some of the resources
2390069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2391069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2392069154d5SJulian Elischer 	 */
2393e72a98f4SAlexander Motin 	if (hook)
239430400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2395069154d5SJulian Elischer 
2396f50597f5SAlexander Motin  	if (rw == NGQRW_R)
23979852972bSAlexander Motin 		ng_leave_read(node);
2398f50597f5SAlexander Motin 	else
23999852972bSAlexander Motin 		ng_leave_write(node);
24008afe16d5SGleb Smirnoff 
24018afe16d5SGleb Smirnoff 	/* Apply callback. */
240210e87318SAlexander Motin 	if (apply != NULL) {
240310e87318SAlexander Motin 		if (depth == 1 && error != 0)
240410e87318SAlexander Motin 			apply->error = error;
240510e87318SAlexander Motin 		if (refcount_release(&apply->refs))
240610e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
240710e87318SAlexander Motin 	}
24088afe16d5SGleb Smirnoff 
240927757487SGleb Smirnoff 	return (error);
2410069154d5SJulian Elischer }
2411069154d5SJulian Elischer 
2412069154d5SJulian Elischer /***********************************************************************
2413069154d5SJulian Elischer  * Implement the 'generic' control messages
2414069154d5SJulian Elischer  ***********************************************************************/
2415069154d5SJulian Elischer static int
2416069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
24174cf49a43SJulian Elischer {
24184cf49a43SJulian Elischer 	int error = 0;
2419069154d5SJulian Elischer 	struct ng_mesg *msg;
2420069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
24214cf49a43SJulian Elischer 
2422069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
24234cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
24246b795970SJulian Elischer 		TRAP_ERROR();
2425069154d5SJulian Elischer 		error = EINVAL;
2426069154d5SJulian Elischer 		goto out;
24274cf49a43SJulian Elischer 	}
24284cf49a43SJulian Elischer 	switch (msg->header.cmd) {
24294cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
24301acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
24314cf49a43SJulian Elischer 		break;
24324cf49a43SJulian Elischer 	case NGM_MKPEER:
24334cf49a43SJulian Elischer 	    {
24344cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
24354cf49a43SJulian Elischer 
24364cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
24376b795970SJulian Elischer 			TRAP_ERROR();
2438069154d5SJulian Elischer 			error = EINVAL;
2439069154d5SJulian Elischer 			break;
24404cf49a43SJulian Elischer 		}
24414cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
24424cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
24434cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
24444cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
24454cf49a43SJulian Elischer 		break;
24464cf49a43SJulian Elischer 	    }
24474cf49a43SJulian Elischer 	case NGM_CONNECT:
24484cf49a43SJulian Elischer 	    {
24494cf49a43SJulian Elischer 		struct ngm_connect *const con =
24504cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
24514cf49a43SJulian Elischer 		node_p node2;
24524cf49a43SJulian Elischer 
24534cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
24546b795970SJulian Elischer 			TRAP_ERROR();
2455069154d5SJulian Elischer 			error = EINVAL;
2456069154d5SJulian Elischer 			break;
24574cf49a43SJulian Elischer 		}
24584cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
24594cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
24604cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2461069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2462069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
24634cf49a43SJulian Elischer 		if (error)
24644cf49a43SJulian Elischer 			break;
2465e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2466e088dd4cSAlexander Motin 		    node2, con->peerhook);
246730400f03SJulian Elischer 		NG_NODE_UNREF(node2);
24684cf49a43SJulian Elischer 		break;
24694cf49a43SJulian Elischer 	    }
24704cf49a43SJulian Elischer 	case NGM_NAME:
24714cf49a43SJulian Elischer 	    {
24724cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
24734cf49a43SJulian Elischer 
24744cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
24756b795970SJulian Elischer 			TRAP_ERROR();
2476069154d5SJulian Elischer 			error = EINVAL;
2477069154d5SJulian Elischer 			break;
24784cf49a43SJulian Elischer 		}
24794cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
24804cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
24814cf49a43SJulian Elischer 		break;
24824cf49a43SJulian Elischer 	    }
24834cf49a43SJulian Elischer 	case NGM_RMHOOK:
24844cf49a43SJulian Elischer 	    {
24854cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
24864cf49a43SJulian Elischer 		hook_p hook;
24874cf49a43SJulian Elischer 
24884cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
24896b795970SJulian Elischer 			TRAP_ERROR();
2490069154d5SJulian Elischer 			error = EINVAL;
2491069154d5SJulian Elischer 			break;
24924cf49a43SJulian Elischer 		}
24934cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2494899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
24954cf49a43SJulian Elischer 			ng_destroy_hook(hook);
24964cf49a43SJulian Elischer 		break;
24974cf49a43SJulian Elischer 	    }
24984cf49a43SJulian Elischer 	case NGM_NODEINFO:
24994cf49a43SJulian Elischer 	    {
25004cf49a43SJulian Elischer 		struct nodeinfo *ni;
25014cf49a43SJulian Elischer 
2502069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25034cf49a43SJulian Elischer 		if (resp == NULL) {
25044cf49a43SJulian Elischer 			error = ENOMEM;
25054cf49a43SJulian Elischer 			break;
25064cf49a43SJulian Elischer 		}
25074cf49a43SJulian Elischer 
25084cf49a43SJulian Elischer 		/* Fill in node info */
2509069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
251030400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
251187e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
251287e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2513dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
251430400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25154cf49a43SJulian Elischer 		break;
25164cf49a43SJulian Elischer 	    }
25174cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
25184cf49a43SJulian Elischer 	    {
251930400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
25204cf49a43SJulian Elischer 		struct hooklist *hl;
25214cf49a43SJulian Elischer 		struct nodeinfo *ni;
25224cf49a43SJulian Elischer 		hook_p hook;
25234cf49a43SJulian Elischer 
25244cf49a43SJulian Elischer 		/* Get response struct */
2525069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
25264cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2527069154d5SJulian Elischer 		if (resp == NULL) {
25284cf49a43SJulian Elischer 			error = ENOMEM;
25294cf49a43SJulian Elischer 			break;
25304cf49a43SJulian Elischer 		}
2531069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
25324cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
25334cf49a43SJulian Elischer 
25344cf49a43SJulian Elischer 		/* Fill in node info */
253530400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
253687e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
253787e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2538dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
25394cf49a43SJulian Elischer 
25404cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
25414cf49a43SJulian Elischer 		ni->hooks = 0;
254230400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
25434cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
25444cf49a43SJulian Elischer 
25454cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
25464cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
25476e551fb6SDavid E. O'Brien 				    __func__, "hooks");
25484cf49a43SJulian Elischer 				break;
25494cf49a43SJulian Elischer 			}
255030400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
25514cf49a43SJulian Elischer 				continue;
255287e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
255387e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
255430400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
255587e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
255687e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
255787e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
255887e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
255930400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
256030400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
25614cf49a43SJulian Elischer 			ni->hooks++;
25624cf49a43SJulian Elischer 		}
25634cf49a43SJulian Elischer 		break;
25644cf49a43SJulian Elischer 	    }
25654cf49a43SJulian Elischer 
25664cf49a43SJulian Elischer 	case NGM_LISTNAMES:
25674cf49a43SJulian Elischer 	case NGM_LISTNODES:
25684cf49a43SJulian Elischer 	    {
25694cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
25704cf49a43SJulian Elischer 		struct namelist *nl;
25714cf49a43SJulian Elischer 		node_p node;
2572cfea3f85SAlexander Motin 		int num = 0, i;
25734cf49a43SJulian Elischer 
2574cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
25754cf49a43SJulian Elischer 		/* Count number of nodes */
2576cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2577cfea3f85SAlexander Motin 			LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) {
2578cfea3f85SAlexander Motin 				if (NG_NODE_IS_VALID(node) &&
2579cfea3f85SAlexander Motin 				    (unnamed || NG_NODE_HAS_NAME(node))) {
25804cf49a43SJulian Elischer 					num++;
25814cf49a43SJulian Elischer 				}
258270de87f2SJulian Elischer 			}
2583cfea3f85SAlexander Motin 		}
2584cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
25854cf49a43SJulian Elischer 
25864cf49a43SJulian Elischer 		/* Get response struct */
2587069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
25884cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2589069154d5SJulian Elischer 		if (resp == NULL) {
25904cf49a43SJulian Elischer 			error = ENOMEM;
25914cf49a43SJulian Elischer 			break;
25924cf49a43SJulian Elischer 		}
2593069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
25944cf49a43SJulian Elischer 
25954cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
25964cf49a43SJulian Elischer 		nl->numnames = 0;
2597cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
2598cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2599cfea3f85SAlexander Motin 			LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) {
2600cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2601cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
26024cf49a43SJulian Elischer 
2603b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2604b96baf0aSGleb Smirnoff 					continue;
2605b96baf0aSGleb Smirnoff 				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2606b96baf0aSGleb Smirnoff 					continue;
26074cf49a43SJulian Elischer 				if (nl->numnames >= num) {
2608cfea3f85SAlexander Motin 					log(LOG_ERR, "%s: number of nodes changed\n",
2609cfea3f85SAlexander Motin 					    __func__);
26104cf49a43SJulian Elischer 					break;
26114cf49a43SJulian Elischer 				}
261230400f03SJulian Elischer 				if (NG_NODE_HAS_NAME(node))
261387e2c66aSHartmut Brandt 					strcpy(np->name, NG_NODE_NAME(node));
261487e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2615dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
261630400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
26174cf49a43SJulian Elischer 				nl->numnames++;
26184cf49a43SJulian Elischer 			}
2619cfea3f85SAlexander Motin 		}
2620cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26214cf49a43SJulian Elischer 		break;
26224cf49a43SJulian Elischer 	    }
26234cf49a43SJulian Elischer 
26244cf49a43SJulian Elischer 	case NGM_LISTTYPES:
26254cf49a43SJulian Elischer 	    {
26264cf49a43SJulian Elischer 		struct typelist *tl;
26274cf49a43SJulian Elischer 		struct ng_type *type;
26284cf49a43SJulian Elischer 		int num = 0;
26294cf49a43SJulian Elischer 
26309ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
26314cf49a43SJulian Elischer 		/* Count number of types */
263270de87f2SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26334cf49a43SJulian Elischer 			num++;
263470de87f2SJulian Elischer 		}
26359ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26364cf49a43SJulian Elischer 
26374cf49a43SJulian Elischer 		/* Get response struct */
2638069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
26394cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2640069154d5SJulian Elischer 		if (resp == NULL) {
26414cf49a43SJulian Elischer 			error = ENOMEM;
26424cf49a43SJulian Elischer 			break;
26434cf49a43SJulian Elischer 		}
2644069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
26454cf49a43SJulian Elischer 
26464cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
26474cf49a43SJulian Elischer 		tl->numtypes = 0;
26489ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
2649069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26504cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
26514cf49a43SJulian Elischer 
26524cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
26534cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26546e551fb6SDavid E. O'Brien 				    __func__, "types");
26554cf49a43SJulian Elischer 				break;
26564cf49a43SJulian Elischer 			}
265787e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2658c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
26594cf49a43SJulian Elischer 			tl->numtypes++;
26604cf49a43SJulian Elischer 		}
26619ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26624cf49a43SJulian Elischer 		break;
26634cf49a43SJulian Elischer 	    }
26644cf49a43SJulian Elischer 
2665f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2666f8307e12SArchie Cobbs 	    {
26677133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2668f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2669f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2670069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2671f8307e12SArchie Cobbs 
2672f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2673f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
26744c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
26754c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
26764c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
26776b795970SJulian Elischer 			TRAP_ERROR();
2678f8307e12SArchie Cobbs 			error = EINVAL;
2679f8307e12SArchie Cobbs 			break;
2680f8307e12SArchie Cobbs 		}
2681f8307e12SArchie Cobbs 
2682f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2683069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2684069154d5SJulian Elischer 		if (resp == NULL) {
2685f8307e12SArchie Cobbs 			error = ENOMEM;
2686f8307e12SArchie Cobbs 			break;
2687f8307e12SArchie Cobbs 		}
2688069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2689f8307e12SArchie Cobbs 
2690f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2691f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2692f8307e12SArchie Cobbs 
2693f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
269430400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2695f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2696f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2697f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2698f8307e12SArchie Cobbs 				break;
2699f8307e12SArchie Cobbs 		}
2700f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2701f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2702f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2703f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2704f8307e12SArchie Cobbs 					break;
2705f8307e12SArchie Cobbs 			}
2706f8307e12SArchie Cobbs 			if (c->name == NULL) {
2707069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2708f8307e12SArchie Cobbs 				error = ENOSYS;
2709f8307e12SArchie Cobbs 				break;
2710f8307e12SArchie Cobbs 			}
2711f8307e12SArchie Cobbs 		}
2712f8307e12SArchie Cobbs 
2713f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2714f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2715f8307e12SArchie Cobbs 		    "%s", c->name);
2716f8307e12SArchie Cobbs 
2717f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2718f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2719f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
272070de87f2SJulian Elischer 		if (argstype == NULL) {
2721f8307e12SArchie Cobbs 			*ascii->data = '\0';
272270de87f2SJulian Elischer 		} else {
2723f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2724f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2725f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2726069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2727f8307e12SArchie Cobbs 				break;
2728f8307e12SArchie Cobbs 			}
2729f8307e12SArchie Cobbs 		}
2730f8307e12SArchie Cobbs 
2731f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2732f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2733f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2734069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2735f8307e12SArchie Cobbs 		break;
2736f8307e12SArchie Cobbs 	    }
2737f8307e12SArchie Cobbs 
2738f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2739f8307e12SArchie Cobbs 	    {
2740f8307e12SArchie Cobbs 		int bufSize = 2000;	/* XXX hard coded constant */
2741f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2742f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2743069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
274452ec4a03SArchie Cobbs 		int off = 0;
2745f8307e12SArchie Cobbs 
2746f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2747f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
27484c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
27494c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
27504c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
27514c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
27526b795970SJulian Elischer 			TRAP_ERROR();
2753f8307e12SArchie Cobbs 			error = EINVAL;
2754f8307e12SArchie Cobbs 			break;
2755f8307e12SArchie Cobbs 		}
2756f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2757f8307e12SArchie Cobbs 
2758f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2759069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2760069154d5SJulian Elischer 		if (resp == NULL) {
2761f8307e12SArchie Cobbs 			error = ENOMEM;
2762f8307e12SArchie Cobbs 			break;
2763f8307e12SArchie Cobbs 		}
2764069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2765f8307e12SArchie Cobbs 
2766f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2767f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2768f8307e12SArchie Cobbs 
2769f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
277030400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2771f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2772f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2773f8307e12SArchie Cobbs 				break;
2774f8307e12SArchie Cobbs 		}
2775f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2776f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2777f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2778f8307e12SArchie Cobbs 					break;
2779f8307e12SArchie Cobbs 			}
2780f8307e12SArchie Cobbs 			if (c->name == NULL) {
2781069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2782f8307e12SArchie Cobbs 				error = ENOSYS;
2783f8307e12SArchie Cobbs 				break;
2784f8307e12SArchie Cobbs 			}
2785f8307e12SArchie Cobbs 		}
2786f8307e12SArchie Cobbs 
2787f8307e12SArchie Cobbs 		/* Convert command name to binary */
2788f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2789f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2790f8307e12SArchie Cobbs 
2791f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2792f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2793f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
279470de87f2SJulian Elischer 		if (argstype == NULL) {
2795f8307e12SArchie Cobbs 			bufSize = 0;
279670de87f2SJulian Elischer 		} else {
2797f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2798f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2799069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2800f8307e12SArchie Cobbs 				break;
2801f8307e12SArchie Cobbs 			}
2802f8307e12SArchie Cobbs 		}
2803f8307e12SArchie Cobbs 
2804f8307e12SArchie Cobbs 		/* Return the result */
2805f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2806069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2807f8307e12SArchie Cobbs 		break;
2808f8307e12SArchie Cobbs 	    }
2809f8307e12SArchie Cobbs 
28107095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
28114cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
28124cf49a43SJulian Elischer 		/*
28134cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
28144cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2815069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
28164cf49a43SJulian Elischer 		 * when control passes back to us.
28174cf49a43SJulian Elischer 		 */
281830400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2819069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
282030400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
28214cf49a43SJulian Elischer 		}
28224cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
28234cf49a43SJulian Elischer 	default:
28246b795970SJulian Elischer 		TRAP_ERROR();
28254cf49a43SJulian Elischer 		error = EINVAL;
28264cf49a43SJulian Elischer 	}
2827069154d5SJulian Elischer 	/*
2828069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2829069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2830069154d5SJulian Elischer 	 * Don't free it if it is so.
2831069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2832069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2833069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2834069154d5SJulian Elischer 	 */
2835069154d5SJulian Elischer out:
2836069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
28371acb27c6SJulian Elischer 	if (msg)
2838069154d5SJulian Elischer 		NG_FREE_MSG(msg);
28394cf49a43SJulian Elischer 	return (error);
28404cf49a43SJulian Elischer }
28414cf49a43SJulian Elischer 
28424cf49a43SJulian Elischer /************************************************************************
28438253c060SGleb Smirnoff 			Queue element get/free routines
28448253c060SGleb Smirnoff ************************************************************************/
28458253c060SGleb Smirnoff 
28468253c060SGleb Smirnoff uma_zone_t			ng_qzone;
2847ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2848ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
2849ed75521fSAlexander Motin static int			useddata = 0;
28508253c060SGleb Smirnoff 
28518253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
28528253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
28538253c060SGleb Smirnoff     0, "Maximum number of queue items to allocate");
2854ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
2855ed75521fSAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RW | CTLFLAG_TUN, &maxdata,
2856ed75521fSAlexander Motin     0, "Maximum number of queue data items to allocate");
28578253c060SGleb Smirnoff 
28588253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
28598253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
28608253c060SGleb Smirnoff static int			allocated;	/* number of items malloc'd */
28618253c060SGleb Smirnoff #endif
28628253c060SGleb Smirnoff 
28638253c060SGleb Smirnoff /*
28648253c060SGleb Smirnoff  * Get a queue entry.
28658253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
28668253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
28678253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
28688253c060SGleb Smirnoff  * an interrupt.
28698253c060SGleb Smirnoff  */
28708253c060SGleb Smirnoff static __inline item_p
287142282202SGleb Smirnoff ng_getqblk(int flags)
28728253c060SGleb Smirnoff {
28738253c060SGleb Smirnoff 	item_p item = NULL;
287442282202SGleb Smirnoff 	int wait;
28758253c060SGleb Smirnoff 
287642282202SGleb Smirnoff 	wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT;
287742282202SGleb Smirnoff 
287842282202SGleb Smirnoff 	item = uma_zalloc(ng_qzone, wait | M_ZERO);
28798253c060SGleb Smirnoff 
28808253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
28818253c060SGleb Smirnoff 	if (item) {
28828253c060SGleb Smirnoff 			mtx_lock(&ngq_mtx);
28838253c060SGleb Smirnoff 			TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
28848253c060SGleb Smirnoff 			allocated++;
28858253c060SGleb Smirnoff 			mtx_unlock(&ngq_mtx);
28868253c060SGleb Smirnoff 	}
28878253c060SGleb Smirnoff #endif
28888253c060SGleb Smirnoff 
28898253c060SGleb Smirnoff 	return (item);
28908253c060SGleb Smirnoff }
28918253c060SGleb Smirnoff 
28928253c060SGleb Smirnoff /*
28938253c060SGleb Smirnoff  * Release a queue entry
28948253c060SGleb Smirnoff  */
28958253c060SGleb Smirnoff void
28968253c060SGleb Smirnoff ng_free_item(item_p item)
28978253c060SGleb Smirnoff {
28988253c060SGleb Smirnoff 	/*
28998253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
29008253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
29018253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
29028253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
29038253c060SGleb Smirnoff 	 * or we release them again here.
29048253c060SGleb Smirnoff 	 */
29058253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
29068253c060SGleb Smirnoff 	case NGQF_DATA:
2907ed75521fSAlexander Motin 		atomic_subtract_int(&useddata, 1);
29088253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
29098253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
29108253c060SGleb Smirnoff 		break;
29118253c060SGleb Smirnoff 	case NGQF_MESG:
29128253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
29138253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
29148253c060SGleb Smirnoff 		break;
29158253c060SGleb Smirnoff 	case NGQF_FN:
2916e088dd4cSAlexander Motin 	case NGQF_FN2:
29178253c060SGleb Smirnoff 		/* nothing to free really, */
29188253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
29198253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
29208253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
29218253c060SGleb Smirnoff 		break;
29228253c060SGleb Smirnoff 	}
29238253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
29248253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
29258253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
29268253c060SGleb Smirnoff 
29278253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29288253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
29298253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
29308253c060SGleb Smirnoff 	allocated--;
29318253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
29328253c060SGleb Smirnoff #endif
29338253c060SGleb Smirnoff 	uma_zfree(ng_qzone, item);
29348253c060SGleb Smirnoff }
29358253c060SGleb Smirnoff 
29368253c060SGleb Smirnoff /************************************************************************
29374cf49a43SJulian Elischer 			Module routines
29384cf49a43SJulian Elischer ************************************************************************/
29394cf49a43SJulian Elischer 
29404cf49a43SJulian Elischer /*
29414cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
29424cf49a43SJulian Elischer  */
29434cf49a43SJulian Elischer int
29444cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
29454cf49a43SJulian Elischer {
29464cf49a43SJulian Elischer 	struct ng_type *const type = data;
29474cf49a43SJulian Elischer 	int s, error = 0;
29484cf49a43SJulian Elischer 
29494cf49a43SJulian Elischer 	switch (event) {
29504cf49a43SJulian Elischer 	case MOD_LOAD:
29514cf49a43SJulian Elischer 
29524cf49a43SJulian Elischer 		/* Register new netgraph node type */
29534cf49a43SJulian Elischer 		s = splnet();
29544cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
29554cf49a43SJulian Elischer 			splx(s);
29564cf49a43SJulian Elischer 			break;
29574cf49a43SJulian Elischer 		}
29584cf49a43SJulian Elischer 
29594cf49a43SJulian Elischer 		/* Call type specific code */
29604cf49a43SJulian Elischer 		if (type->mod_event != NULL)
2961069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
29629ed346baSBosko Milekic 				mtx_lock(&ng_typelist_mtx);
2963c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
29644cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
29659ed346baSBosko Milekic 				mtx_unlock(&ng_typelist_mtx);
2966069154d5SJulian Elischer 			}
29674cf49a43SJulian Elischer 		splx(s);
29684cf49a43SJulian Elischer 		break;
29694cf49a43SJulian Elischer 
29704cf49a43SJulian Elischer 	case MOD_UNLOAD:
29714cf49a43SJulian Elischer 		s = splnet();
2972c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
29734cf49a43SJulian Elischer 			error = EBUSY;
2974c73b94a2SJulian Elischer 		} else {
2975c73b94a2SJulian Elischer 			if (type->refs == 0) {
2976c73b94a2SJulian Elischer 				/* failed load, nothing to undo */
2977c73b94a2SJulian Elischer 				splx(s);
2978c73b94a2SJulian Elischer 				break;
2979c73b94a2SJulian Elischer 			}
29804cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
29814cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
29824cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
29834cf49a43SJulian Elischer 					splx(s);
29844cf49a43SJulian Elischer 					break;
29854cf49a43SJulian Elischer 				}
29864cf49a43SJulian Elischer 			}
29879ed346baSBosko Milekic 			mtx_lock(&ng_typelist_mtx);
29884cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
29899ed346baSBosko Milekic 			mtx_unlock(&ng_typelist_mtx);
29904cf49a43SJulian Elischer 		}
29914cf49a43SJulian Elischer 		splx(s);
29924cf49a43SJulian Elischer 		break;
29934cf49a43SJulian Elischer 
29944cf49a43SJulian Elischer 	default:
29954cf49a43SJulian Elischer 		if (type->mod_event != NULL)
29964cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
29974cf49a43SJulian Elischer 		else
29983e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
29994cf49a43SJulian Elischer 		break;
30004cf49a43SJulian Elischer 	}
30014cf49a43SJulian Elischer 	return (error);
30024cf49a43SJulian Elischer }
30034cf49a43SJulian Elischer 
30044cf49a43SJulian Elischer /*
30054cf49a43SJulian Elischer  * Handle loading and unloading for this code.
30064cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
30074cf49a43SJulian Elischer  */
30084cf49a43SJulian Elischer static int
30094cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
30104cf49a43SJulian Elischer {
30111489164fSGleb Smirnoff 	int error = 0;
30124cf49a43SJulian Elischer 
30134cf49a43SJulian Elischer 	switch (event) {
30144cf49a43SJulian Elischer 	case MOD_LOAD:
30151489164fSGleb Smirnoff 		/* Initialize everything. */
30162c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3017efd8e7c9SDon Lewis 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3018efd8e7c9SDon Lewis 		    MTX_DEF);
3019efd8e7c9SDon Lewis 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3020efd8e7c9SDon Lewis 		    MTX_DEF);
3021cfea3f85SAlexander Motin 		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3022cfea3f85SAlexander Motin 		    MTX_DEF);
3023ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3024ac5dd141SGleb Smirnoff 		    MTX_DEF);
30251489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3026cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3027cfea3f85SAlexander Motin 		    MTX_DEF);
30281489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3029efd8e7c9SDon Lewis 		    MTX_DEF);
30301489164fSGleb Smirnoff #endif
30311489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
30321489164fSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
30331489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
303468780975SGleb Smirnoff 		netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL,
303568780975SGleb Smirnoff 		    NETISR_MPSAFE);
30364cf49a43SJulian Elischer 		break;
30374cf49a43SJulian Elischer 	case MOD_UNLOAD:
303864efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
30394cf49a43SJulian Elischer 		error = EBUSY;
30404cf49a43SJulian Elischer 		break;
30414cf49a43SJulian Elischer 	default:
30424cf49a43SJulian Elischer 		error = EOPNOTSUPP;
30434cf49a43SJulian Elischer 		break;
30444cf49a43SJulian Elischer 	}
30454cf49a43SJulian Elischer 	return (error);
30464cf49a43SJulian Elischer }
30474cf49a43SJulian Elischer 
30484cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
30494cf49a43SJulian Elischer 	"netgraph",
30504cf49a43SJulian Elischer 	ngb_mod_event,
30514cf49a43SJulian Elischer 	(NULL)
30524cf49a43SJulian Elischer };
3053aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
3054bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3055bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3056bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
30574cf49a43SJulian Elischer 
305830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
305930400f03SJulian Elischer void
306030400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
306130400f03SJulian Elischer {
306230400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
306330400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
306430400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
306530400f03SJulian Elischer 		hook->lastfile, hook->lastline);
306630400f03SJulian Elischer 	if (line) {
306730400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
306830400f03SJulian Elischer 	}
306930400f03SJulian Elischer }
307030400f03SJulian Elischer 
307130400f03SJulian Elischer void
307230400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
307330400f03SJulian Elischer {
307430400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
30756b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
307630400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
307730400f03SJulian Elischer 		node->nd_refs, node->nd_name);
307830400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
307930400f03SJulian Elischer 		node->lastfile, node->lastline);
308030400f03SJulian Elischer 	if (line) {
308130400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
308230400f03SJulian Elischer 	}
308330400f03SJulian Elischer }
308430400f03SJulian Elischer 
3085069154d5SJulian Elischer void
3086069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3087069154d5SJulian Elischer {
3088069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3089069154d5SJulian Elischer 		item->lastfile, item->lastline);
30906b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
30916b795970SJulian Elischer 	case NGQF_DATA:
3092069154d5SJulian Elischer 		printf(" - [data]\n");
30936b795970SJulian Elischer 		break;
30946b795970SJulian Elischer 	case NGQF_MESG:
30956b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
30966b795970SJulian Elischer 		break;
30976b795970SJulian Elischer 	case NGQF_FN:
3098857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3099857304e6SRuslan Ermilov 			_NGI_FN(item),
3100857304e6SRuslan Ermilov 			_NGI_NODE(item),
3101857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3102857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3103857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3104857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3105857304e6SRuslan Ermilov 		break;
3106e088dd4cSAlexander Motin 	case NGQF_FN2:
3107eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3108857304e6SRuslan Ermilov 			_NGI_FN2(item),
31096064e568SGleb Smirnoff 			_NGI_NODE(item),
31106064e568SGleb Smirnoff 			_NGI_HOOK(item),
31116b795970SJulian Elischer 			item->body.fn.fn_arg1,
31126b795970SJulian Elischer 			item->body.fn.fn_arg2,
31136b795970SJulian Elischer 			item->body.fn.fn_arg2);
31146b795970SJulian Elischer 		break;
3115069154d5SJulian Elischer 	}
311630400f03SJulian Elischer 	if (line) {
3117069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
31186064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
311930400f03SJulian Elischer 			printf("node %p ([%x])\n",
31206064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3121069154d5SJulian Elischer 		}
312230400f03SJulian Elischer 	}
312330400f03SJulian Elischer }
312430400f03SJulian Elischer 
312530400f03SJulian Elischer static void
312630400f03SJulian Elischer ng_dumpitems(void)
312730400f03SJulian Elischer {
312830400f03SJulian Elischer 	item_p item;
312930400f03SJulian Elischer 	int i = 1;
313030400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
313130400f03SJulian Elischer 		printf("[%d] ", i++);
313230400f03SJulian Elischer 		dumpitem(item, NULL, 0);
313330400f03SJulian Elischer 	}
313430400f03SJulian Elischer }
313530400f03SJulian Elischer 
313630400f03SJulian Elischer static void
313730400f03SJulian Elischer ng_dumpnodes(void)
313830400f03SJulian Elischer {
313930400f03SJulian Elischer 	node_p node;
314030400f03SJulian Elischer 	int i = 1;
314153f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
314230400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
314330400f03SJulian Elischer 		printf("[%d] ", i++);
314430400f03SJulian Elischer 		dumpnode(node, NULL, 0);
314530400f03SJulian Elischer 	}
314653f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
314730400f03SJulian Elischer }
314830400f03SJulian Elischer 
314930400f03SJulian Elischer static void
315030400f03SJulian Elischer ng_dumphooks(void)
315130400f03SJulian Elischer {
315230400f03SJulian Elischer 	hook_p hook;
315330400f03SJulian Elischer 	int i = 1;
315453f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
315530400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
315630400f03SJulian Elischer 		printf("[%d] ", i++);
315730400f03SJulian Elischer 		dumphook(hook, NULL, 0);
315830400f03SJulian Elischer 	}
315953f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
316030400f03SJulian Elischer }
3161069154d5SJulian Elischer 
3162069154d5SJulian Elischer static int
3163069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3164069154d5SJulian Elischer {
3165069154d5SJulian Elischer 	int error;
3166069154d5SJulian Elischer 	int val;
3167069154d5SJulian Elischer 	int i;
3168069154d5SJulian Elischer 
3169069154d5SJulian Elischer 	val = allocated;
3170069154d5SJulian Elischer 	i = 1;
3171041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
31726b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
31736b795970SJulian Elischer 		return (error);
31746b795970SJulian Elischer 	if (val == 42) {
317530400f03SJulian Elischer 		ng_dumpitems();
317630400f03SJulian Elischer 		ng_dumpnodes();
317730400f03SJulian Elischer 		ng_dumphooks();
3178069154d5SJulian Elischer 	}
31796b795970SJulian Elischer 	return (0);
3180069154d5SJulian Elischer }
3181069154d5SJulian Elischer 
31826b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
31836b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
318430400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3185069154d5SJulian Elischer 
3186069154d5SJulian Elischer 
3187069154d5SJulian Elischer /***********************************************************************
3188069154d5SJulian Elischer * Worklist routines
3189069154d5SJulian Elischer **********************************************************************/
3190069154d5SJulian Elischer /* NETISR thread enters here */
3191069154d5SJulian Elischer /*
3192069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3193069154d5SJulian Elischer  * try get an item to process off it.
3194069154d5SJulian Elischer  * If there are no more, remove the node from the list.
3195069154d5SJulian Elischer  */
3196069154d5SJulian Elischer static void
3197069154d5SJulian Elischer ngintr(void)
3198069154d5SJulian Elischer {
3199069154d5SJulian Elischer 	for (;;) {
3200394cb30aSAlexander Motin 		node_p  node;
3201394cb30aSAlexander Motin 
3202394cb30aSAlexander Motin 		/* Get node from the worklist. */
32032c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
32049852972bSAlexander Motin 		node = STAILQ_FIRST(&ng_worklist);
3205069154d5SJulian Elischer 		if (!node) {
32062c8dda8dSWojciech A. Koszek 			NG_WORKLIST_UNLOCK();
3207069154d5SJulian Elischer 			break;
3208069154d5SJulian Elischer 		}
32099852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
32102c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
32112955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
32122955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3213069154d5SJulian Elischer 		/*
3214069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3215069154d5SJulian Elischer 		 * that the list had on it.
3216069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3217069154d5SJulian Elischer 		 * let you have another item off the queue.
3218069154d5SJulian Elischer 		 * All this time, keep the reference
3219069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3220069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3221069154d5SJulian Elischer 		 */
3222069154d5SJulian Elischer 		for (;;) {
3223394cb30aSAlexander Motin 			item_p item;
3224714fb865SGleb Smirnoff 			int rw;
3225714fb865SGleb Smirnoff 
32262c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
32279852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3228069154d5SJulian Elischer 			if (item == NULL) {
32299852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
32302c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3231069154d5SJulian Elischer 				break; /* go look for another node */
3232069154d5SJulian Elischer 			} else {
32332c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
32345951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3235714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
32365951069aSJulian Elischer 				NG_NODE_UNREF(node);
3237069154d5SJulian Elischer 			}
3238069154d5SJulian Elischer 		}
3239a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3240069154d5SJulian Elischer 	}
3241069154d5SJulian Elischer }
3242069154d5SJulian Elischer 
324333338e73SJulian Elischer /*
324433338e73SJulian Elischer  * XXX
324533338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
324633338e73SJulian Elischer  * to be outside the mutex zone
324733338e73SJulian Elischer  */
3248069154d5SJulian Elischer static void
3249394cb30aSAlexander Motin ng_worklist_add(node_p node)
3250069154d5SJulian Elischer {
3251f912c0f7SGleb Smirnoff 
32525bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3253f912c0f7SGleb Smirnoff 
32549852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3255069154d5SJulian Elischer 		/*
3256069154d5SJulian Elischer 		 * If we are not already on the work queue,
3257069154d5SJulian Elischer 		 * then put us on.
3258069154d5SJulian Elischer 		 */
32599852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3260394cb30aSAlexander Motin 		NG_NODE_REF(node); /* XXX fafe in mutex? */
32612c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
32629852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
32632c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3264394cb30aSAlexander Motin 		schednetisr(NETISR_NETGRAPH);
32652955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
32662955ee18SGleb Smirnoff 		    node->nd_ID, node);
3267394cb30aSAlexander Motin 	} else {
32682955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
32692955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3270394cb30aSAlexander Motin 	}
3271069154d5SJulian Elischer }
3272069154d5SJulian Elischer 
3273069154d5SJulian Elischer 
3274069154d5SJulian Elischer /***********************************************************************
3275069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3276069154d5SJulian Elischer ***********************************************************************/
3277069154d5SJulian Elischer 
327830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
327930400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
32804cf49a43SJulian Elischer 	do {								\
32811acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3282069154d5SJulian Elischer 			printf("item already has node");		\
32833de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
32841acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3285069154d5SJulian Elischer 		}							\
32861acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3287069154d5SJulian Elischer 			printf("item already has hook");		\
32883de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
32891acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3290069154d5SJulian Elischer 		}							\
3291069154d5SJulian Elischer 	} while (0)
3292069154d5SJulian Elischer #else
329330400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3294069154d5SJulian Elischer #endif
3295069154d5SJulian Elischer 
3296069154d5SJulian Elischer /*
32978ed370fdSJulian Elischer  * Put mbuf into the item.
3298069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3299069154d5SJulian Elischer  * (or equivalent)
3300069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3301069154d5SJulian Elischer  * remote node might go away in this timescale.
3302069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3303069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
33044be59335SGleb Smirnoff  * here to be able to do this.
3305069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3306069154d5SJulian Elischer  *
3307069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3308069154d5SJulian Elischer  */
3309069154d5SJulian Elischer item_p
331042282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3311069154d5SJulian Elischer {
3312069154d5SJulian Elischer 	item_p item;
3313069154d5SJulian Elischer 
3314ed75521fSAlexander Motin 	if (atomic_fetchadd_int(&useddata, 1) >= maxdata) {
3315ed75521fSAlexander Motin 		atomic_subtract_int(&useddata, 1);
3316ed75521fSAlexander Motin 		NG_FREE_M(m);
3317ed75521fSAlexander Motin 		return (NULL);
3318ed75521fSAlexander Motin 	}
331942282202SGleb Smirnoff 	if ((item = ng_getqblk(flags)) == NULL) {
3320069154d5SJulian Elischer 		NG_FREE_M(m);
3321069154d5SJulian Elischer 		return (NULL);
3322069154d5SJulian Elischer 	}
332330400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
33246f683eeeSGleb Smirnoff 	item->el_flags = NGQF_DATA | NGQF_READER;
3325069154d5SJulian Elischer 	NGI_M(item) = m;
3326069154d5SJulian Elischer 	return (item);
3327069154d5SJulian Elischer }
3328069154d5SJulian Elischer 
3329069154d5SJulian Elischer /*
3330069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3331069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3332069154d5SJulian Elischer  * to work out what some of the fields should be.
3333069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3334069154d5SJulian Elischer  * (or equivalent)
3335069154d5SJulian Elischer  */
3336069154d5SJulian Elischer item_p
333742282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3338069154d5SJulian Elischer {
3339069154d5SJulian Elischer 	item_p item;
3340069154d5SJulian Elischer 
334142282202SGleb Smirnoff 	if ((item = ng_getqblk(flags)) == NULL) {
3342069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3343069154d5SJulian Elischer 		return (NULL);
3344069154d5SJulian Elischer 	}
334530400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
33466f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
33476f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
33486f683eeeSGleb Smirnoff 		item->el_flags = NGQF_MESG | NGQF_READER;
33496f683eeeSGleb Smirnoff 	else
33506f683eeeSGleb Smirnoff 		item->el_flags = NGQF_MESG | NGQF_WRITER;
3351069154d5SJulian Elischer 	/*
3352069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3353069154d5SJulian Elischer 	 */
3354069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3355facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3356069154d5SJulian Elischer 	return (item);
3357069154d5SJulian Elischer }
3358069154d5SJulian Elischer 
3359069154d5SJulian Elischer 
3360069154d5SJulian Elischer 
33611acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
33626b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
33636b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3364069154d5SJulian Elischer 			if (retaddr) {					\
3365069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
33664cf49a43SJulian Elischer 			} else {					\
3367069154d5SJulian Elischer 				/*					\
3368069154d5SJulian Elischer 				 * The old return address should be ok.	\
3369069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3370069154d5SJulian Elischer 				 * here.				\
3371069154d5SJulian Elischer 				 */					\
3372069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3373069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3374069154d5SJulian Elischer 						= ng_node2ID(here);	\
3375069154d5SJulian Elischer 				}					\
3376069154d5SJulian Elischer 			}						\
33774cf49a43SJulian Elischer 		}							\
33784cf49a43SJulian Elischer 	} while (0)
33794cf49a43SJulian Elischer 
33804cf49a43SJulian Elischer int
3381069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
33824cf49a43SJulian Elischer {
33831acb27c6SJulian Elischer 	hook_p peer;
33841acb27c6SJulian Elischer 	node_p peernode;
338530400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3386069154d5SJulian Elischer 	/*
3387069154d5SJulian Elischer 	 * Quick sanity check..
338830400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
338930400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
339030400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3391069154d5SJulian Elischer 	 */
3392069154d5SJulian Elischer 	if ((hook == NULL)
339330400f03SJulian Elischer 	|| NG_HOOK_NOT_VALID(hook)
339430400f03SJulian Elischer 	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
339530400f03SJulian Elischer 	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
3396069154d5SJulian Elischer 		NG_FREE_ITEM(item);
33976b795970SJulian Elischer 		TRAP_ERROR();
3398e08d3e3cSJulian Elischer 		return (ENETDOWN);
33994cf49a43SJulian Elischer 	}
34004cf49a43SJulian Elischer 
34014cf49a43SJulian Elischer 	/*
3402069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
34034cf49a43SJulian Elischer 	 */
34041acb27c6SJulian Elischer 	peer = NG_HOOK_PEER(hook);
34051acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
34061acb27c6SJulian Elischer 	NGI_SET_HOOK(item, peer);
34071acb27c6SJulian Elischer 	peernode = NG_PEER_NODE(hook);
34081acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
34091acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
34108b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3411069154d5SJulian Elischer 	return (0);
3412069154d5SJulian Elischer }
3413069154d5SJulian Elischer 
34144cf49a43SJulian Elischer int
3415069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
34164cf49a43SJulian Elischer {
34174cf49a43SJulian Elischer 	node_p	dest = NULL;
3418069154d5SJulian Elischer 	hook_p	hook = NULL;
34194cf49a43SJulian Elischer 	int	error;
3420069154d5SJulian Elischer 
342130400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3422069154d5SJulian Elischer 	/*
3423069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3424069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3425069154d5SJulian Elischer 	 */
3426069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3427069154d5SJulian Elischer 	if (error) {
3428069154d5SJulian Elischer 		NG_FREE_ITEM(item);
342930400f03SJulian Elischer 		return (error);
3430069154d5SJulian Elischer 	}
34311acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
34321acb27c6SJulian Elischer 	if ( hook) {
343330400f03SJulian Elischer 		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
34341acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
34351acb27c6SJulian Elischer 	}
34361acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3437069154d5SJulian Elischer 	return (0);
3438069154d5SJulian Elischer }
3439069154d5SJulian Elischer 
3440069154d5SJulian Elischer int
3441069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3442069154d5SJulian Elischer {
3443069154d5SJulian Elischer 	node_p dest;
3444069154d5SJulian Elischer 
344530400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3446069154d5SJulian Elischer 	/*
3447069154d5SJulian Elischer 	 * Find the target node.
3448069154d5SJulian Elischer 	 */
3449069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3450069154d5SJulian Elischer 	if (dest == NULL) {
3451069154d5SJulian Elischer 		NG_FREE_ITEM(item);
34526b795970SJulian Elischer 		TRAP_ERROR();
3453069154d5SJulian Elischer 		return(EINVAL);
3454069154d5SJulian Elischer 	}
3455069154d5SJulian Elischer 	/* Fill out the contents */
34561acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
34571acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
34581acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3459069154d5SJulian Elischer 	return (0);
3460069154d5SJulian Elischer }
3461069154d5SJulian Elischer 
3462069154d5SJulian Elischer /*
3463069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3464069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3465069154d5SJulian Elischer  * Useful for removing that hook :-)
3466069154d5SJulian Elischer  */
3467069154d5SJulian Elischer item_p
3468069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3469069154d5SJulian Elischer {
3470069154d5SJulian Elischer 	item_p item;
34714cf49a43SJulian Elischer 
3472859a4d16SJulian Elischer 	/*
3473859a4d16SJulian Elischer 	 * Find the target node.
3474859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3475859a4d16SJulian Elischer 	 * to the address.
3476859a4d16SJulian Elischer 	 */
347742282202SGleb Smirnoff 	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) {
3478069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3479069154d5SJulian Elischer 		return (NULL);
34804cf49a43SJulian Elischer 	}
34814cf49a43SJulian Elischer 
34824cf49a43SJulian Elischer 	/* Fill out the contents */
34836f683eeeSGleb Smirnoff 	item->el_flags = NGQF_MESG | NGQF_WRITER;
348430400f03SJulian Elischer 	NG_NODE_REF(here);
34851acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
34861acb27c6SJulian Elischer 	if (hook) {
348730400f03SJulian Elischer 		NG_HOOK_REF(hook);
34881acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
34891acb27c6SJulian Elischer 	}
3490069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3491069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3492069154d5SJulian Elischer 	return (item);
34934cf49a43SJulian Elischer }
34944cf49a43SJulian Elischer 
3495e088dd4cSAlexander Motin /*
3496e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3497e088dd4cSAlexander Motin  */
3498e088dd4cSAlexander Motin 
349942282202SGleb Smirnoff int
3500b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3501b332b91fSGleb Smirnoff {
3502b332b91fSGleb Smirnoff 
3503b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3504b332b91fSGleb Smirnoff }
3505b332b91fSGleb Smirnoff 
3506b332b91fSGleb Smirnoff int
3507aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
350842282202SGleb Smirnoff 	int flags)
35096b795970SJulian Elischer {
35106b795970SJulian Elischer 	item_p item;
35116b795970SJulian Elischer 
351242282202SGleb Smirnoff 	if ((item = ng_getqblk(flags)) == NULL) {
35136b795970SJulian Elischer 		return (ENOMEM);
35146b795970SJulian Elischer 	}
35156b795970SJulian Elischer 	item->el_flags = NGQF_FN | NGQF_WRITER;
3516a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
35171acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
35181acb27c6SJulian Elischer 	if (hook) {
35196b795970SJulian Elischer 		NG_HOOK_REF(hook);
35201acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35216b795970SJulian Elischer 	}
35226b795970SJulian Elischer 	NGI_FN(item) = fn;
35236b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
35246b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
352542282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
35266b795970SJulian Elischer }
35276b795970SJulian Elischer 
35284cf49a43SJulian Elischer /*
3529b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3530b332b91fSGleb Smirnoff  *
3531b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3532b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3533b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3534b332b91fSGleb Smirnoff  * be used.
3535e088dd4cSAlexander Motin  */
3536e088dd4cSAlexander Motin int
3537b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3538b332b91fSGleb Smirnoff 	int arg2, int flags)
3539e088dd4cSAlexander Motin {
3540e088dd4cSAlexander Motin 	item_p item;
3541e088dd4cSAlexander Motin 
3542b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3543b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3544e088dd4cSAlexander Motin 
3545e088dd4cSAlexander Motin 	/*
3546b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3547b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3548e088dd4cSAlexander Motin 	 */
3549b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
3550b332b91fSGleb Smirnoff 		if ((item = ng_getqblk(flags)) == NULL)
3551e088dd4cSAlexander Motin 			return (ENOMEM);
3552ed75521fSAlexander Motin 	} else {
3553ed75521fSAlexander Motin 		if ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA)
3554ed75521fSAlexander Motin 			atomic_subtract_int(&useddata, 1);
3555b332b91fSGleb Smirnoff 		item = pitem;
3556ed75521fSAlexander Motin 	}
3557b332b91fSGleb Smirnoff 
3558e088dd4cSAlexander Motin 	item->el_flags = NGQF_FN2 | NGQF_WRITER;
3559e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3560e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3561e088dd4cSAlexander Motin 	if (hook) {
3562e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3563e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3564e088dd4cSAlexander Motin 	}
3565e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3566e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3567e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3568b332b91fSGleb Smirnoff 	if (pitem != NULL && (flags & NG_REUSE_ITEM) == 0)
3569e088dd4cSAlexander Motin 		item->apply = pitem->apply;
3570e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3571e088dd4cSAlexander Motin }
3572e088dd4cSAlexander Motin 
3573e088dd4cSAlexander Motin /*
3574d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3575d2ca21a9SJulian Elischer  */
3576d2ca21a9SJulian Elischer static void
35771fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3578d2ca21a9SJulian Elischer {
3579d2ca21a9SJulian Elischer 	item_p item = arg;
3580d2ca21a9SJulian Elischer 
3581d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3582d2ca21a9SJulian Elischer }
3583d2ca21a9SJulian Elischer 
3584d2ca21a9SJulian Elischer 
358530bef41bSGleb Smirnoff int
3586f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3587d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3588d2ca21a9SJulian Elischer {
35891bf8e0faSGleb Smirnoff 	item_p item, oitem;
3590d2ca21a9SJulian Elischer 
359142282202SGleb Smirnoff 	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL)
359230bef41bSGleb Smirnoff 		return (ENOMEM);
359330bef41bSGleb Smirnoff 
3594d2ca21a9SJulian Elischer 	item->el_flags = NGQF_FN | NGQF_WRITER;
3595d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3596d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3597d2ca21a9SJulian Elischer 	if (hook) {
3598d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3599d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3600d2ca21a9SJulian Elischer 	}
3601d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3602d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3603d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
36041bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
36051bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
36061bf8e0faSGleb Smirnoff 	    oitem != NULL)
36071bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
360830bef41bSGleb Smirnoff 	return (0);
3609d2ca21a9SJulian Elischer }
3610d2ca21a9SJulian Elischer 
3611d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3612d2ca21a9SJulian Elischer int
3613f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3614d2ca21a9SJulian Elischer {
3615d2ca21a9SJulian Elischer 	item_p item;
361630bef41bSGleb Smirnoff 	int rval;
3617d2ca21a9SJulian Elischer 
361803b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
361903b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
362003b25f5dSGleb Smirnoff 
36213eadb26dSGleb Smirnoff 	rval = callout_stop(c);
362230bef41bSGleb Smirnoff 	item = c->c_arg;
362330bef41bSGleb Smirnoff 	/* Do an extra check */
36241fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
362530bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3626d2ca21a9SJulian Elischer 		/*
3627d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3628d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3629d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3630d2ca21a9SJulian Elischer 		 */
3631d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3632d2ca21a9SJulian Elischer 	}
36331bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
363430bef41bSGleb Smirnoff 
363530bef41bSGleb Smirnoff 	return (rval);
3636d2ca21a9SJulian Elischer }
3637d2ca21a9SJulian Elischer 
3638d2ca21a9SJulian Elischer /*
3639069154d5SJulian Elischer  * Set the address, if none given, give the node here.
36404cf49a43SJulian Elischer  */
3641069154d5SJulian Elischer void
3642069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
36434cf49a43SJulian Elischer {
3644069154d5SJulian Elischer 	if (retaddr) {
3645069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3646069154d5SJulian Elischer 	} else {
3647069154d5SJulian Elischer 		/*
3648069154d5SJulian Elischer 		 * The old return address should be ok.
3649069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3650069154d5SJulian Elischer 		 */
3651069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3652069154d5SJulian Elischer 	}
3653069154d5SJulian Elischer }
3654069154d5SJulian Elischer 
3655069154d5SJulian Elischer #define TESTING
3656069154d5SJulian Elischer #ifdef TESTING
3657069154d5SJulian Elischer /* just test all the macros */
3658069154d5SJulian Elischer void
3659069154d5SJulian Elischer ng_macro_test(item_p item);
3660069154d5SJulian Elischer void
3661069154d5SJulian Elischer ng_macro_test(item_p item)
3662069154d5SJulian Elischer {
3663069154d5SJulian Elischer 	node_p node = NULL;
3664069154d5SJulian Elischer 	hook_p hook = NULL;
36654cf49a43SJulian Elischer 	struct mbuf *m;
36664cf49a43SJulian Elischer 	struct ng_mesg *msg;
3667069154d5SJulian Elischer 	ng_ID_t retaddr;
3668069154d5SJulian Elischer 	int	error;
36694cf49a43SJulian Elischer 
3670069154d5SJulian Elischer 	NGI_GET_M(item, m);
3671069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3672069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
36738ed370fdSJulian Elischer 	NG_SEND_DATA(error, hook, m, NULL);
3674069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3675069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
367630400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3677069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3678069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3679069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3680069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
36814cf49a43SJulian Elischer }
3682069154d5SJulian Elischer #endif /* TESTING */
36834cf49a43SJulian Elischer 
3684