xref: /freebsd/sys/netgraph/ng_base.c (revision 4bd1b55756f92642d1d9b2a52d1cda1dce188d2a)
1c398230bSWarner Losh /*-
24cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
34cf49a43SJulian Elischer  * All rights reserved.
44cf49a43SJulian Elischer  *
54cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
64cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
74cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
84cf49a43SJulian Elischer  * provided, however, that:
94cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
104cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
114cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
124cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
134cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
144cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
154cf49a43SJulian Elischer  *
164cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
174cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
184cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
194cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
204cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
214cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
224cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
234cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
244cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
254cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
264cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
274cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
284cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
294cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
304cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
314cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
324cf49a43SJulian Elischer  * OF SUCH DAMAGE.
334cf49a43SJulian Elischer  *
34cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
35cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
364cf49a43SJulian Elischer  *
374cf49a43SJulian Elischer  * $FreeBSD$
384cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
394cf49a43SJulian Elischer  */
404cf49a43SJulian Elischer 
414cf49a43SJulian Elischer /*
424cf49a43SJulian Elischer  * This file implements the base netgraph code.
434cf49a43SJulian Elischer  */
444cf49a43SJulian Elischer 
454cf49a43SJulian Elischer #include <sys/param.h>
468e853a2cSGleb Smirnoff #include <sys/systm.h>
4777a58296SGleb Smirnoff #include <sys/ctype.h>
484cf49a43SJulian Elischer #include <sys/errno.h>
49f33ca0c9SMarcel Moolenaar #include <sys/kdb.h>
504cf49a43SJulian Elischer #include <sys/kernel.h>
513b33fbe7SGleb Smirnoff #include <sys/ktr.h>
52104a9b7eSAlexander Kabaev #include <sys/limits.h>
534cf49a43SJulian Elischer #include <sys/malloc.h>
544cf49a43SJulian Elischer #include <sys/mbuf.h>
5577a58296SGleb Smirnoff #include <sys/queue.h>
56bfa7e882SJulian Elischer #include <sys/sysctl.h>
5777a58296SGleb Smirnoff #include <sys/syslog.h>
58e088dd4cSAlexander Motin #include <sys/refcount.h>
5981a253a4SAlexander Motin #include <sys/proc.h>
60f2fbb838SAlexander Motin #include <sys/unistd.h>
61f2fbb838SAlexander Motin #include <sys/kthread.h>
62f2fbb838SAlexander Motin #include <sys/smp.h>
63394cb30aSAlexander Motin #include <machine/cpu.h>
644cf49a43SJulian Elischer 
654cf49a43SJulian Elischer #include <net/netisr.h>
66eddfbb76SRobert Watson #include <net/vnet.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 */
11913e403fdSAntoine Brodin 	LIST_HEAD_INITIALIZER(ng_deadnode.nd_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 */
129a40b7874SMarko Zec 	NULL,	/* vnet */
130954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
131954c4772SJulian Elischer 	ND_MAGIC,
132954c4772SJulian Elischer 	__FILE__,
133954c4772SJulian Elischer 	__LINE__,
134954c4772SJulian Elischer 	{NULL}
135954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
136954c4772SJulian Elischer };
137954c4772SJulian Elischer 
138954c4772SJulian Elischer struct ng_hook ng_deadhook = {
139954c4772SJulian Elischer 	"dead",
140954c4772SJulian Elischer 	NULL,		/* private */
141954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
142e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
143954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
144954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
145954c4772SJulian Elischer 	{},		/* hooks list */
146c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
147c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1489852972bSAlexander Motin 	1,		/* refs always >= 1 */
149954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
150954c4772SJulian Elischer 	HK_MAGIC,
151954c4772SJulian Elischer 	__FILE__,
152954c4772SJulian Elischer 	__LINE__,
153954c4772SJulian Elischer 	{NULL}
154954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
155954c4772SJulian Elischer };
156954c4772SJulian Elischer 
157954c4772SJulian Elischer /*
158954c4772SJulian Elischer  * END DEAD STRUCTURES
159954c4772SJulian Elischer  */
160069154d5SJulian Elischer /* List nodes with unallocated work */
1619852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
162b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1634cf49a43SJulian Elischer 
1644cf49a43SJulian Elischer /* List of installed types */
165069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
166069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
1674cf49a43SJulian Elischer 
168069154d5SJulian Elischer /* Hash related definitions */
1690f150d04SJulian Elischer /* XXX Don't need to initialise them because it's a LIST */
1703e288e62SDimitry Andric static VNET_DEFINE(LIST_HEAD(, ng_node), ng_ID_hash[NG_ID_HASH_SIZE]);
1711e77c105SRobert Watson #define	V_ng_ID_hash			VNET(ng_ID_hash)
172eddfbb76SRobert Watson 
173069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
1740f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
1750f150d04SJulian Elischer #define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
1760f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
1770f150d04SJulian Elischer 	do { 								\
17853f9c5e9SRobert Watson 		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
179603724d3SBjoern A. Zeeb 		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
1800f150d04SJulian Elischer 						nd_idnodes) {		\
1810f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
1820f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
1830f150d04SJulian Elischer 				break;					\
1840f150d04SJulian Elischer 			}						\
1850f150d04SJulian Elischer 		}							\
1860f150d04SJulian Elischer 	} while (0)
187069154d5SJulian Elischer 
1883e288e62SDimitry Andric static VNET_DEFINE(LIST_HEAD(, ng_node), ng_name_hash[NG_NAME_HASH_SIZE]);
1891e77c105SRobert Watson #define	V_ng_name_hash			VNET(ng_name_hash)
190eddfbb76SRobert Watson 
191cfea3f85SAlexander Motin static struct mtx	ng_namehash_mtx;
192cfea3f85SAlexander Motin #define NG_NAMEHASH(NAME, HASH)				\
193cfea3f85SAlexander Motin 	do {						\
194cfea3f85SAlexander Motin 		u_char	h = 0;				\
195cfea3f85SAlexander Motin 		const u_char	*c;			\
196cfea3f85SAlexander Motin 		for (c = (const u_char*)(NAME); *c; c++)\
197cfea3f85SAlexander Motin 			h += *c;			\
198cfea3f85SAlexander Motin 		(HASH) = h % (NG_NAME_HASH_SIZE);	\
199cfea3f85SAlexander Motin 	} while (0)
200cfea3f85SAlexander Motin 
201dc90cad9SJulian Elischer 
2024cf49a43SJulian Elischer /* Internal functions */
2034cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
204069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
205dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2064cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
207394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
208f2fbb838SAlexander Motin static void	ngthread(void *);
20927757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2109852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
211069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
212e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
213e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
214e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
215e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
2166b795970SJulian Elischer static int	ng_mkpeer(node_p node, const char *name,
2176b795970SJulian Elischer 						const char *name2, char *type);
218069154d5SJulian Elischer 
2194c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */
220069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
221069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
222069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
223069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
224069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
2251acb27c6SJulian Elischer void	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
22630400f03SJulian Elischer void	ng_unname(node_p node);
227069154d5SJulian Elischer 
2284cf49a43SJulian Elischer 
2294cf49a43SJulian Elischer /* Our own netgraph malloc type */
2304cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
231069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
232d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
233d745c852SEd Schouten     "netgraph hook structures");
234d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
235d745c852SEd Schouten     "netgraph node structures");
236d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
237d745c852SEd Schouten     "netgraph item structures");
238069154d5SJulian Elischer 
239069154d5SJulian Elischer /* Should not be visible outside this file */
24030400f03SJulian Elischer 
24130400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
2421ede983cSDag-Erling Smørgrav 	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
24330400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
2441ede983cSDag-Erling Smørgrav 	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
24530400f03SJulian Elischer 
2462c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2474abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2482c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2494abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2502c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2514abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2522c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2534abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2542c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2554abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2562c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2574abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
258f2fbb838SAlexander Motin #define	NG_WORKLIST_SLEEP()			\
259f2fbb838SAlexander Motin 	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
260f2fbb838SAlexander Motin #define	NG_WORKLIST_WAKEUP()			\
261f2fbb838SAlexander Motin 	wakeup_one(&ng_worklist)
2622c8dda8dSWojciech A. Koszek 
26330400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
26430400f03SJulian Elischer /*
26530400f03SJulian Elischer  * In debug mode:
26630400f03SJulian Elischer  * In an attempt to help track reference count screwups
26730400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
26830400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
26930400f03SJulian Elischer  * after they have been freed.
27030400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
27130400f03SJulian Elischer  */
27230400f03SJulian Elischer static __inline hook_p
27330400f03SJulian Elischer ng_alloc_hook(void)
27430400f03SJulian Elischer {
27530400f03SJulian Elischer 	hook_p hook;
27630400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2779ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
27830400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
27930400f03SJulian Elischer 	if (hook) {
28030400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
28130400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
28230400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
28330400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
2849ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
28530400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
28630400f03SJulian Elischer 	} else {
2879ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
28830400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
28930400f03SJulian Elischer 		if (hook) {
29030400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
2919ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
29230400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
2939ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
29430400f03SJulian Elischer 		}
29530400f03SJulian Elischer 	}
29630400f03SJulian Elischer 	return (hook);
29730400f03SJulian Elischer }
29830400f03SJulian Elischer 
29930400f03SJulian Elischer static __inline node_p
30030400f03SJulian Elischer ng_alloc_node(void)
30130400f03SJulian Elischer {
30230400f03SJulian Elischer 	node_p node;
30330400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
3049ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
30530400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
30630400f03SJulian Elischer 	if (node) {
30730400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
30830400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
30930400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
31030400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3119ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
31230400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
31330400f03SJulian Elischer 	} else {
3149ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
31530400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
31630400f03SJulian Elischer 		if (node) {
31730400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3189ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
31930400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3209ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
32130400f03SJulian Elischer 		}
32230400f03SJulian Elischer 	}
32330400f03SJulian Elischer 	return (node);
32430400f03SJulian Elischer }
32530400f03SJulian Elischer 
32630400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
32730400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
32830400f03SJulian Elischer 
32930400f03SJulian Elischer 
33030400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
33130400f03SJulian Elischer 	do {								\
3329ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);				\
33330400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
33430400f03SJulian Elischer 		hook->hk_magic = 0;					\
3359ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);				\
33630400f03SJulian Elischer 	} while (0)
33730400f03SJulian Elischer 
33830400f03SJulian Elischer #define NG_FREE_NODE(node)						\
33930400f03SJulian Elischer 	do {								\
3409ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);				\
34130400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
34230400f03SJulian Elischer 		node->nd_magic = 0;					\
3439ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);				\
34430400f03SJulian Elischer 	} while (0)
34530400f03SJulian Elischer 
34630400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
34730400f03SJulian Elischer 
34830400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
34930400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
35030400f03SJulian Elischer 
3511ede983cSDag-Erling Smørgrav #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
3521ede983cSDag-Erling Smørgrav #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
35330400f03SJulian Elischer 
35430400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
35530400f03SJulian Elischer 
356f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3574cf49a43SJulian Elischer #ifndef TRAP_ERROR
3586b795970SJulian Elischer #define TRAP_ERROR()
3594cf49a43SJulian Elischer #endif
3604cf49a43SJulian Elischer 
3613e288e62SDimitry Andric static VNET_DEFINE(ng_ID_t, nextID) = 1;
3621e77c105SRobert Watson #define	V_nextID			VNET(nextID)
363dc90cad9SJulian Elischer 
364b2da83c2SArchie Cobbs #ifdef INVARIANTS
365b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
366b2da83c2SArchie Cobbs 		struct mbuf *n;						\
367b2da83c2SArchie Cobbs 		int total;						\
368b2da83c2SArchie Cobbs 									\
369fe584538SDag-Erling Smørgrav 		M_ASSERTPKTHDR(m);					\
370b32cfb32SGleb Smirnoff 		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
371b2da83c2SArchie Cobbs 			total += n->m_len;				\
372b32cfb32SGleb Smirnoff 			if (n->m_nextpkt != NULL)			\
373b32cfb32SGleb Smirnoff 				panic("%s: m_nextpkt", __func__);	\
374b32cfb32SGleb Smirnoff 		}							\
375ba5b359aSGleb Smirnoff 									\
376b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
377b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
3786e551fb6SDavid E. O'Brien 			    __func__, (m)->m_pkthdr.len, total);	\
379b2da83c2SArchie Cobbs 		}							\
380b2da83c2SArchie Cobbs 	} while (0)
381b2da83c2SArchie Cobbs #else
382b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
383b2da83c2SArchie Cobbs #endif
384b2da83c2SArchie Cobbs 
385e088dd4cSAlexander Motin #define ERROUT(x)	do { error = (x); goto done; } while (0)
386dc90cad9SJulian Elischer 
3874cf49a43SJulian Elischer /************************************************************************
388f8307e12SArchie Cobbs 	Parse type definitions for generic messages
389f8307e12SArchie Cobbs ************************************************************************/
390f8307e12SArchie Cobbs 
391f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
392f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
393f0184ff8SArchie Cobbs static const struct ng_parse_struct_field				\
394f0184ff8SArchie Cobbs 	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
395f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
396f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
397f0184ff8SArchie Cobbs 	&ng_ ## lo ## _type_fields					\
398f8307e12SArchie Cobbs }
399f8307e12SArchie Cobbs 
400f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
401f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
402f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
403f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
404f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
405f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
406f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
407f8307e12SArchie Cobbs 
408f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
409d7d97eb0SJeroen Ruigrok van der Werven    value immediately preceding the array -- as with struct namelist
410f8307e12SArchie Cobbs    and struct typelist. */
411f8307e12SArchie Cobbs static int
412f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
413f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
414f8307e12SArchie Cobbs {
415f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
416f8307e12SArchie Cobbs }
417f8307e12SArchie Cobbs 
418f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
419f8307e12SArchie Cobbs static int
420f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
421f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
422f8307e12SArchie Cobbs {
423f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
424f8307e12SArchie Cobbs 
425f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
426f8307e12SArchie Cobbs }
427f8307e12SArchie Cobbs 
428f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
429f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
430f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
431f8307e12SArchie Cobbs 	&ng_generic_list_getLength
432f8307e12SArchie Cobbs };
433f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
434f8307e12SArchie Cobbs 	&ng_parse_array_type,
435f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
436f8307e12SArchie Cobbs };
437f8307e12SArchie Cobbs 
438f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
439f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
440f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
441f8307e12SArchie Cobbs 	&ng_generic_list_getLength
442f8307e12SArchie Cobbs };
443f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
444f8307e12SArchie Cobbs 	&ng_parse_array_type,
445f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
446f8307e12SArchie Cobbs };
447f8307e12SArchie Cobbs 
448f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
449f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
450f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
451f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
452f8307e12SArchie Cobbs };
453f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
454f8307e12SArchie Cobbs 	&ng_parse_array_type,
455f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
456f8307e12SArchie Cobbs };
457f8307e12SArchie Cobbs 
458f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
459f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
460f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
461f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
462f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
463f8307e12SArchie Cobbs 
464f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
465f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
466f8307e12SArchie Cobbs 	{
467f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
468f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
469f8307e12SArchie Cobbs 	  "shutdown",
470f8307e12SArchie Cobbs 	  NULL,
471f8307e12SArchie Cobbs 	  NULL
472f8307e12SArchie Cobbs 	},
473f8307e12SArchie Cobbs 	{
474f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
475f8307e12SArchie Cobbs 	  NGM_MKPEER,
476f8307e12SArchie Cobbs 	  "mkpeer",
477f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
478f8307e12SArchie Cobbs 	  NULL
479f8307e12SArchie Cobbs 	},
480f8307e12SArchie Cobbs 	{
481f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
482f8307e12SArchie Cobbs 	  NGM_CONNECT,
483f8307e12SArchie Cobbs 	  "connect",
484f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
485f8307e12SArchie Cobbs 	  NULL
486f8307e12SArchie Cobbs 	},
487f8307e12SArchie Cobbs 	{
488f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
489f8307e12SArchie Cobbs 	  NGM_NAME,
490f8307e12SArchie Cobbs 	  "name",
491f8307e12SArchie Cobbs 	  &ng_generic_name_type,
492f8307e12SArchie Cobbs 	  NULL
493f8307e12SArchie Cobbs 	},
494f8307e12SArchie Cobbs 	{
495f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
496f8307e12SArchie Cobbs 	  NGM_RMHOOK,
497f8307e12SArchie Cobbs 	  "rmhook",
498f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
499f8307e12SArchie Cobbs 	  NULL
500f8307e12SArchie Cobbs 	},
501f8307e12SArchie Cobbs 	{
502f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
503f8307e12SArchie Cobbs 	  NGM_NODEINFO,
504f8307e12SArchie Cobbs 	  "nodeinfo",
505f8307e12SArchie Cobbs 	  NULL,
506f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
507f8307e12SArchie Cobbs 	},
508f8307e12SArchie Cobbs 	{
509f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
510f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
511f8307e12SArchie Cobbs 	  "listhooks",
512f8307e12SArchie Cobbs 	  NULL,
513f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
514f8307e12SArchie Cobbs 	},
515f8307e12SArchie Cobbs 	{
516f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
517f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
518f8307e12SArchie Cobbs 	  "listnames",
519f8307e12SArchie Cobbs 	  NULL,
520f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
521f8307e12SArchie Cobbs 	},
522f8307e12SArchie Cobbs 	{
523f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
524f8307e12SArchie Cobbs 	  NGM_LISTNODES,
525f8307e12SArchie Cobbs 	  "listnodes",
526f8307e12SArchie Cobbs 	  NULL,
527f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
528f8307e12SArchie Cobbs 	},
529f8307e12SArchie Cobbs 	{
530f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
531f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
532f8307e12SArchie Cobbs 	  "listtypes",
533f8307e12SArchie Cobbs 	  NULL,
534f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
535f8307e12SArchie Cobbs 	},
536f8307e12SArchie Cobbs 	{
537f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
5387095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
5397095e097SPoul-Henning Kamp 	  "textconfig",
5407095e097SPoul-Henning Kamp 	  NULL,
5417095e097SPoul-Henning Kamp 	  &ng_parse_string_type
5427095e097SPoul-Henning Kamp 	},
5437095e097SPoul-Henning Kamp 	{
5447095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
545f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
546f8307e12SArchie Cobbs 	  "textstatus",
547f8307e12SArchie Cobbs 	  NULL,
548f8307e12SArchie Cobbs 	  &ng_parse_string_type
549f8307e12SArchie Cobbs 	},
550f8307e12SArchie Cobbs 	{
551f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
552f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
553f8307e12SArchie Cobbs 	  "ascii2binary",
554f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
555f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
556f8307e12SArchie Cobbs 	},
557f8307e12SArchie Cobbs 	{
558f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
559f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
560f8307e12SArchie Cobbs 	  "binary2ascii",
561f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
562f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
563f8307e12SArchie Cobbs 	},
564f8307e12SArchie Cobbs 	{ 0 }
565f8307e12SArchie Cobbs };
566f8307e12SArchie Cobbs 
567f8307e12SArchie Cobbs /************************************************************************
5684cf49a43SJulian Elischer 			Node routines
5694cf49a43SJulian Elischer ************************************************************************/
5704cf49a43SJulian Elischer 
5714cf49a43SJulian Elischer /*
5724cf49a43SJulian Elischer  * Instantiate a node of the requested type
5734cf49a43SJulian Elischer  */
5744cf49a43SJulian Elischer int
5754cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
5764cf49a43SJulian Elischer {
5774cf49a43SJulian Elischer 	struct ng_type *type;
578069154d5SJulian Elischer 	int	error;
5794cf49a43SJulian Elischer 
5804cf49a43SJulian Elischer 	/* Check that the type makes sense */
5814cf49a43SJulian Elischer 	if (typename == NULL) {
5826b795970SJulian Elischer 		TRAP_ERROR();
5834cf49a43SJulian Elischer 		return (EINVAL);
5844cf49a43SJulian Elischer 	}
5854cf49a43SJulian Elischer 
5867610f574SGleb Smirnoff 	/* Locate the node type. If we fail we return. Do not try to load
5877610f574SGleb Smirnoff 	 * module.
5887610f574SGleb Smirnoff 	 */
5894cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL)
5904cf49a43SJulian Elischer 		return (ENXIO);
5914cf49a43SJulian Elischer 
592069154d5SJulian Elischer 	/*
593069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
594069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
595069154d5SJulian Elischer 	 */
596069154d5SJulian Elischer 	if (type->constructor != NULL) {
597069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
5985633ca71SGleb Smirnoff 			if ((error = ((*type->constructor)(*nodepp))) != 0) {
59930400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
600069154d5SJulian Elischer 			}
601069154d5SJulian Elischer 		}
602069154d5SJulian Elischer 	} else {
603069154d5SJulian Elischer 		/*
604069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
60564efc707SRobert Watson 		 * to be made. It must be brought into existence by
606954c4772SJulian Elischer 		 * some external agency. The external agency should
607069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
608069154d5SJulian Elischer 		 * netgraph part initialised.
609069154d5SJulian Elischer 		 */
6106b795970SJulian Elischer 		TRAP_ERROR();
611069154d5SJulian Elischer 		error = EINVAL;
612069154d5SJulian Elischer 	}
613069154d5SJulian Elischer 	return (error);
6144cf49a43SJulian Elischer }
6154cf49a43SJulian Elischer 
6164cf49a43SJulian Elischer /*
617069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
618069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
6194cf49a43SJulian Elischer  * The returned node has a reference count of 1.
6204cf49a43SJulian Elischer  */
6214cf49a43SJulian Elischer int
6224cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
6234cf49a43SJulian Elischer {
6244cf49a43SJulian Elischer 	node_p node;
6254cf49a43SJulian Elischer 
6264cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6274cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6286b795970SJulian Elischer 		TRAP_ERROR();
6294cf49a43SJulian Elischer 		return (EINVAL);
6304cf49a43SJulian Elischer 	}
6314cf49a43SJulian Elischer 
6324cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
63330400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6344cf49a43SJulian Elischer 	if (node == NULL) {
6356b795970SJulian Elischer 		TRAP_ERROR();
6364cf49a43SJulian Elischer 		return (ENOMEM);
6374cf49a43SJulian Elischer 	}
63830400f03SJulian Elischer 	node->nd_type = type;
639bc29160dSMarko Zec #ifdef VIMAGE
640bc29160dSMarko Zec 	node->nd_vnet = curvnet;
641bc29160dSMarko Zec #endif
64230400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6434cf49a43SJulian Elischer 	type->refs++;
6444cf49a43SJulian Elischer 
6452c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6469852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
64730400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6484cf49a43SJulian Elischer 
6494cf49a43SJulian Elischer 	/* Initialize hook list for new node */
65030400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6514cf49a43SJulian Elischer 
652cfea3f85SAlexander Motin 	/* Link us into the name hash. */
653cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
654603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[0], node, nd_nodes);
655cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
656069154d5SJulian Elischer 
657dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
6589ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
65930400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
660069154d5SJulian Elischer 		node_p node2 = NULL;
661ac957cd2SJulian Elischer 		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6620f150d04SJulian Elischer 
66330400f03SJulian Elischer 		/* Is there a problem with the new number? */
6640f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6650f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
66630400f03SJulian Elischer 			break;
667069154d5SJulian Elischer 		}
66830400f03SJulian Elischer 	}
669*4bd1b557SGleb Smirnoff 	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
670*4bd1b557SGleb Smirnoff 	    nd_idnodes);
6719ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
672dc90cad9SJulian Elischer 
6734cf49a43SJulian Elischer 	/* Done */
6744cf49a43SJulian Elischer 	*nodepp = node;
6754cf49a43SJulian Elischer 	return (0);
6764cf49a43SJulian Elischer }
6774cf49a43SJulian Elischer 
6784cf49a43SJulian Elischer /*
6794cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
68064efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6814cf49a43SJulian Elischer  * no type-specific method.
6824cf49a43SJulian Elischer  *
68364efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6843e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6853e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
686069154d5SJulian Elischer  *
687069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
68864efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
6893e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
6903e4084c8SJulian Elischer  * in which case we allow the node to survive.
6913e4084c8SJulian Elischer  *
69264efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
6933e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
6943e4084c8SJulian Elischer  * are rebooting.... etc.
6954cf49a43SJulian Elischer  */
6964cf49a43SJulian Elischer void
6971acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
6984cf49a43SJulian Elischer {
6993e4084c8SJulian Elischer 	hook_p hook;
7003e4084c8SJulian Elischer 
7014cf49a43SJulian Elischer 	/* Check if it's already shutting down */
702be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
7034cf49a43SJulian Elischer 		return;
7044cf49a43SJulian Elischer 
7051acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
7061acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
7071acb27c6SJulian Elischer 		return;
7081acb27c6SJulian Elischer 	}
7091acb27c6SJulian Elischer 
7104cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
71130400f03SJulian Elischer 	NG_NODE_REF(node);
7124cf49a43SJulian Elischer 
71330400f03SJulian Elischer 	/*
71430400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
71530400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
716be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
71730400f03SJulian Elischer 	 * creation
71830400f03SJulian Elischer 	 */
719be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7204cf49a43SJulian Elischer 
721991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
722991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
723991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
724991fc65aSJulian Elischer 
7253e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7263e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7273e4084c8SJulian Elischer 		ng_destroy_hook(hook);
72830400f03SJulian Elischer 
729069154d5SJulian Elischer 	/*
730069154d5SJulian Elischer 	 * Drain the input queue forceably.
73130400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
73230400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
73330400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
734b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
735069154d5SJulian Elischer 	 */
7369852972bSAlexander Motin 	ng_flush_input_queue(node);
737069154d5SJulian Elischer 
738069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
73930400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
74030400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
74130400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
74230400f03SJulian Elischer 			/*
74330400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
74430400f03SJulian Elischer 			 * that it doesn't want to die.
74530400f03SJulian Elischer 			 * Presumably it is a persistant node.
7461acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7471acb27c6SJulian Elischer 			 *  e.g. hardware going away,
748be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
74930400f03SJulian Elischer 			 */
750be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7511acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
75230400f03SJulian Elischer 			return;
7534cf49a43SJulian Elischer 		}
7541acb27c6SJulian Elischer 	} else {				/* do the default thing */
7551acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7561acb27c6SJulian Elischer 	}
7574cf49a43SJulian Elischer 
75830400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
75930400f03SJulian Elischer 
76030400f03SJulian Elischer 	/*
76130400f03SJulian Elischer 	 * Remove extra reference, possibly the last
76230400f03SJulian Elischer 	 * Possible other holders of references may include
76330400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
76430400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
76530400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
76630400f03SJulian Elischer 	 */
76730400f03SJulian Elischer 	NG_NODE_UNREF(node);
7684cf49a43SJulian Elischer }
7694cf49a43SJulian Elischer 
7705951069aSJulian Elischer /*
7715951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7725951069aSJulian Elischer  * deadnode always acts as it it were the last.
7735951069aSJulian Elischer  */
7743fbdf774SGleb Smirnoff void
77530400f03SJulian Elischer ng_unref_node(node_p node)
7764cf49a43SJulian Elischer {
7776b795970SJulian Elischer 
7783fbdf774SGleb Smirnoff 	if (node == &ng_deadnode)
7793fbdf774SGleb Smirnoff 		return;
7806b795970SJulian Elischer 
7813fbdf774SGleb Smirnoff 	if (refcount_release(&node->nd_refs)) { /* we were the last */
782069154d5SJulian Elischer 
783cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
78430400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
78530400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
786cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
787069154d5SJulian Elischer 
7889ed346baSBosko Milekic 		mtx_lock(&ng_idhash_mtx);
78930400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
7909ed346baSBosko Milekic 		mtx_unlock(&ng_idhash_mtx);
791069154d5SJulian Elischer 
79212574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
793069154d5SJulian Elischer 		NG_FREE_NODE(node);
7944cf49a43SJulian Elischer 	}
7954cf49a43SJulian Elischer }
7964cf49a43SJulian Elischer 
7974cf49a43SJulian Elischer /************************************************************************
798dc90cad9SJulian Elischer 			Node ID handling
799dc90cad9SJulian Elischer ************************************************************************/
800dc90cad9SJulian Elischer static node_p
801069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
802dc90cad9SJulian Elischer {
80330400f03SJulian Elischer 	node_p node;
8049ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
8050f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
80630400f03SJulian Elischer 	if(node)
80730400f03SJulian Elischer 		NG_NODE_REF(node);
8089ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
80930400f03SJulian Elischer 	return(node);
810dc90cad9SJulian Elischer }
811dc90cad9SJulian Elischer 
812dc90cad9SJulian Elischer ng_ID_t
813dc90cad9SJulian Elischer ng_node2ID(node_p node)
814dc90cad9SJulian Elischer {
81570de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
816dc90cad9SJulian Elischer }
817dc90cad9SJulian Elischer 
818dc90cad9SJulian Elischer /************************************************************************
8194cf49a43SJulian Elischer 			Node name handling
8204cf49a43SJulian Elischer ************************************************************************/
8214cf49a43SJulian Elischer 
8224cf49a43SJulian Elischer /*
823*4bd1b557SGleb Smirnoff  * Assign a node a name.
8244cf49a43SJulian Elischer  */
8254cf49a43SJulian Elischer int
8264cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8274cf49a43SJulian Elischer {
828cfea3f85SAlexander Motin 	int i, hash;
829069154d5SJulian Elischer 	node_p node2;
8304cf49a43SJulian Elischer 
8314cf49a43SJulian Elischer 	/* Check the name is valid */
83287e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8334cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8344cf49a43SJulian Elischer 			break;
8354cf49a43SJulian Elischer 	}
8364cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8376b795970SJulian Elischer 		TRAP_ERROR();
8384cf49a43SJulian Elischer 		return (EINVAL);
8394cf49a43SJulian Elischer 	}
840dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8416b795970SJulian Elischer 		TRAP_ERROR();
8424cf49a43SJulian Elischer 		return (EINVAL);
8434cf49a43SJulian Elischer 	}
8444cf49a43SJulian Elischer 
8454cf49a43SJulian Elischer 	/* Check the name isn't already being used */
846069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
84730400f03SJulian Elischer 		NG_NODE_UNREF(node2);
8486b795970SJulian Elischer 		TRAP_ERROR();
8494cf49a43SJulian Elischer 		return (EADDRINUSE);
8504cf49a43SJulian Elischer 	}
8514cf49a43SJulian Elischer 
852069154d5SJulian Elischer 	/* copy it */
85387e2c66aSHartmut Brandt 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8544cf49a43SJulian Elischer 
855cfea3f85SAlexander Motin 	/* Update name hash. */
856cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
857cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
858cfea3f85SAlexander Motin 	LIST_REMOVE(node, nd_nodes);
859603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
860cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
861cfea3f85SAlexander Motin 
8624cf49a43SJulian Elischer 	return (0);
8634cf49a43SJulian Elischer }
8644cf49a43SJulian Elischer 
8654cf49a43SJulian Elischer /*
8664cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8674cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8684cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8694cf49a43SJulian Elischer  *
8704cf49a43SJulian Elischer  * Returns the node if found, else NULL.
871069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
872e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
873e1e8f51bSRobert Watson  * there.
8744cf49a43SJulian Elischer  */
8754cf49a43SJulian Elischer node_p
876069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
8774cf49a43SJulian Elischer {
878dc90cad9SJulian Elischer 	node_p node;
879dc90cad9SJulian Elischer 	ng_ID_t temp;
880cfea3f85SAlexander Motin 	int	hash;
8814cf49a43SJulian Elischer 
8824cf49a43SJulian Elischer 	/* "." means "this node" */
883069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
88430400f03SJulian Elischer 		NG_NODE_REF(here);
885069154d5SJulian Elischer 		return(here);
886069154d5SJulian Elischer 	}
8874cf49a43SJulian Elischer 
8884cf49a43SJulian Elischer 	/* Check for name-by-ID */
889dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
890069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
8914cf49a43SJulian Elischer 	}
8924cf49a43SJulian Elischer 
8934cf49a43SJulian Elischer 	/* Find node by name */
894cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
895cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
896603724d3SBjoern A. Zeeb 	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) {
897cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
898cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
8994cf49a43SJulian Elischer 			break;
9004cf49a43SJulian Elischer 		}
90170de87f2SJulian Elischer 	}
902069154d5SJulian Elischer 	if (node)
90330400f03SJulian Elischer 		NG_NODE_REF(node);
904cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
9054cf49a43SJulian Elischer 	return (node);
9064cf49a43SJulian Elischer }
9074cf49a43SJulian Elischer 
9084cf49a43SJulian Elischer /*
9099d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
910dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9114cf49a43SJulian Elischer  */
912dc90cad9SJulian Elischer static ng_ID_t
9134cf49a43SJulian Elischer ng_decodeidname(const char *name)
9144cf49a43SJulian Elischer {
9152b70adcbSArchie Cobbs 	const int len = strlen(name);
91625792ef3SArchie Cobbs 	char *eptr;
9172b70adcbSArchie Cobbs 	u_long val;
9184cf49a43SJulian Elischer 
9192b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
920*4bd1b557SGleb Smirnoff 	if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
921*4bd1b557SGleb Smirnoff 	    (!isxdigit(name[1])))
92270de87f2SJulian Elischer 		return ((ng_ID_t)0);
9234cf49a43SJulian Elischer 
9242b70adcbSArchie Cobbs 	/* Decode number */
9252b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
926*4bd1b557SGleb Smirnoff 	if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
92712f035e0SJulian Elischer 		return ((ng_ID_t)0);
928*4bd1b557SGleb Smirnoff 
929*4bd1b557SGleb Smirnoff 	return ((ng_ID_t)val);
9304cf49a43SJulian Elischer }
9314cf49a43SJulian Elischer 
9324cf49a43SJulian Elischer /*
9334cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9344cf49a43SJulian Elischer  * when shutting down and removing the node.
9354cf49a43SJulian Elischer  */
9364cf49a43SJulian Elischer void
9374cf49a43SJulian Elischer ng_unname(node_p node)
9384cf49a43SJulian Elischer {
9394cf49a43SJulian Elischer }
9404cf49a43SJulian Elischer 
9414cf49a43SJulian Elischer /************************************************************************
9424cf49a43SJulian Elischer 			Hook routines
9434cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
9443e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
9453e4084c8SJulian Elischer  they are connected to the 'dead' hook.
9464cf49a43SJulian Elischer ************************************************************************/
9474cf49a43SJulian Elischer 
9484cf49a43SJulian Elischer /*
9494cf49a43SJulian Elischer  * Remove a hook reference
9504cf49a43SJulian Elischer  */
95130400f03SJulian Elischer void
9524cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
9534cf49a43SJulian Elischer {
9546b795970SJulian Elischer 
9553fbdf774SGleb Smirnoff 	if (hook == &ng_deadhook)
9566b795970SJulian Elischer 		return;
957018fe3d1SAlexander Motin 
9583fbdf774SGleb Smirnoff 	if (refcount_release(&hook->hk_refs)) { /* we were the last */
959f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
9606b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
961069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
962069154d5SJulian Elischer 	}
9634cf49a43SJulian Elischer }
9644cf49a43SJulian Elischer 
9654cf49a43SJulian Elischer /*
9664cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
9673e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
9684cf49a43SJulian Elischer  */
9694cf49a43SJulian Elischer static int
9704cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
9714cf49a43SJulian Elischer {
9724cf49a43SJulian Elischer 	hook_p hook;
9734cf49a43SJulian Elischer 	int error = 0;
9744cf49a43SJulian Elischer 
9754cf49a43SJulian Elischer 	/* Check that the given name is good */
9764cf49a43SJulian Elischer 	if (name == NULL) {
9776b795970SJulian Elischer 		TRAP_ERROR();
9784cf49a43SJulian Elischer 		return (EINVAL);
9794cf49a43SJulian Elischer 	}
980899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
9816b795970SJulian Elischer 		TRAP_ERROR();
9824cf49a43SJulian Elischer 		return (EEXIST);
9834cf49a43SJulian Elischer 	}
9844cf49a43SJulian Elischer 
9854cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
98630400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
9874cf49a43SJulian Elischer 	if (hook == NULL) {
9886b795970SJulian Elischer 		TRAP_ERROR();
9894cf49a43SJulian Elischer 		return (ENOMEM);
9904cf49a43SJulian Elischer 	}
9913e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
99230400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
9933e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
99430400f03SJulian Elischer 	hook->hk_node = node;
99530400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
9964cf49a43SJulian Elischer 
9973e4084c8SJulian Elischer 	/* Set hook name */
99887e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
9993e4084c8SJulian Elischer 
10003e4084c8SJulian Elischer 	/*
10013e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10023e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10033e4084c8SJulian Elischer 	 */
1004954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1005954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
100630400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1007069154d5SJulian Elischer 			return (error);
1008069154d5SJulian Elischer 		}
1009954c4772SJulian Elischer 	}
10104cf49a43SJulian Elischer 	/*
10114cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10124cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10134cf49a43SJulian Elischer 	 */
101430400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
101530400f03SJulian Elischer 	node->nd_numhooks++;
10163e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
10174cf49a43SJulian Elischer 
10184cf49a43SJulian Elischer 	if (hookp)
10194cf49a43SJulian Elischer 		*hookp = hook;
10203e4084c8SJulian Elischer 	return (0);
10214cf49a43SJulian Elischer }
10224cf49a43SJulian Elischer 
10234cf49a43SJulian Elischer /*
1024899e9c4eSArchie Cobbs  * Find a hook
1025899e9c4eSArchie Cobbs  *
1026899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1027899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
10283e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1029899e9c4eSArchie Cobbs  */
1030899e9c4eSArchie Cobbs hook_p
1031899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1032899e9c4eSArchie Cobbs {
1033899e9c4eSArchie Cobbs 	hook_p hook;
1034899e9c4eSArchie Cobbs 
103530400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
103630400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
103730400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
1038*4bd1b557SGleb Smirnoff 		if (NG_HOOK_IS_VALID(hook) &&
1039*4bd1b557SGleb Smirnoff 		    (strcmp(NG_HOOK_NAME(hook), name) == 0))
1040899e9c4eSArchie Cobbs 			return (hook);
1041899e9c4eSArchie Cobbs 	}
1042899e9c4eSArchie Cobbs 	return (NULL);
1043899e9c4eSArchie Cobbs }
1044899e9c4eSArchie Cobbs 
1045899e9c4eSArchie Cobbs /*
10464cf49a43SJulian Elischer  * Destroy a hook
10474cf49a43SJulian Elischer  *
10484cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
10494cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
10503e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
10513e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
10523e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
10533e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
10543e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
10553e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
10563e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
10573e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
10583e4084c8SJulian Elischer  * attached to the 'dead' hook.
10596b795970SJulian Elischer  *
10606b795970SJulian Elischer  * This routine is called at all stages of hook creation
10616b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
10624cf49a43SJulian Elischer  */
10634cf49a43SJulian Elischer void
10644cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
10654cf49a43SJulian Elischer {
1066ac5dd141SGleb Smirnoff 	hook_p peer;
1067ac5dd141SGleb Smirnoff 	node_p node;
10684cf49a43SJulian Elischer 
10696b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
10706b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
10716b795970SJulian Elischer 		return;
10726b795970SJulian Elischer 	}
1073ac5dd141SGleb Smirnoff 
1074ac5dd141SGleb Smirnoff 	/*
1075ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1076ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1077ac5dd141SGleb Smirnoff 	 */
1078ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1079ac5dd141SGleb Smirnoff 
1080ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1081ac5dd141SGleb Smirnoff 
1082ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1083ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1084ac5dd141SGleb Smirnoff 
10853e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
10863e4084c8SJulian Elischer 		/*
10873e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
10883e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
10893e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
10903e4084c8SJulian Elischer 		 */
10913e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
10923e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
10936b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
10946b795970SJulian Elischer 			/*
10956b795970SJulian Elischer 			 * If it's already divorced from a node,
10966b795970SJulian Elischer 			 * just free it.
10976b795970SJulian Elischer 			 */
1098ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
10996b795970SJulian Elischer 		} else {
1100ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11016b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11026b795970SJulian Elischer 		}
110352fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
110452fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1105ac5dd141SGleb Smirnoff 	} else
1106ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1107ac5dd141SGleb Smirnoff 
1108ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11094cf49a43SJulian Elischer 
11104cf49a43SJulian Elischer 	/*
11114cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11124cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11134cf49a43SJulian Elischer 	 */
11146b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11156b795970SJulian Elischer 		return;
11166b795970SJulian Elischer 	}
111730400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
111830400f03SJulian Elischer 	node->nd_numhooks--;
111930400f03SJulian Elischer 	if (node->nd_type->disconnect) {
11204cf49a43SJulian Elischer 		/*
11216b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
112264efc707SRobert Watson 		 * trust its existence after this point. (except
11236b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
11246b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
11254cf49a43SJulian Elischer 		 */
112630400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
11274cf49a43SJulian Elischer 	}
11286b795970SJulian Elischer 
11296b795970SJulian Elischer 	/*
11306b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
11316b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
11326b795970SJulian Elischer 	 */
11336b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
11346b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
11356b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
11364cf49a43SJulian Elischer }
11374cf49a43SJulian Elischer 
11384cf49a43SJulian Elischer /*
11394cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
11404cf49a43SJulian Elischer  * is effectively bypassed.
11414cf49a43SJulian Elischer  */
11424cf49a43SJulian Elischer int
11434cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
11444cf49a43SJulian Elischer {
114530400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
11466b795970SJulian Elischer 		TRAP_ERROR();
11474cf49a43SJulian Elischer 		return (EINVAL);
114830400f03SJulian Elischer 	}
1149a7da736aSGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
115030400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
115130400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
11524cf49a43SJulian Elischer 
11533e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
11543e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
1155a7da736aSGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
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 */
1175*4bd1b557SGleb Smirnoff 	if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
1176*4bd1b557SGleb Smirnoff 	    (namelen >= NG_TYPESIZ)) {
11776b795970SJulian Elischer 		TRAP_ERROR();
11788ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
1179*4bd1b557SGleb Smirnoff 			printf("Netgraph: Node type rejected. ABI mismatch. "
1180*4bd1b557SGleb Smirnoff 			    "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
150915cea89fSAlexander Motin 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 
151915cea89fSAlexander Motin 	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
1628*4bd1b557SGleb Smirnoff ng_path2noderef(node_p here, const char *address, node_p *destp,
1629*4bd1b557SGleb Smirnoff     hook_p *lasthook)
16304cf49a43SJulian Elischer {
163187e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
1632a7da736aSGleb Smirnoff 	char   *nodename, *path;
1633069154d5SJulian Elischer 	node_p  node, oldnode;
16344cf49a43SJulian Elischer 
16354cf49a43SJulian Elischer 	/* Initialize */
163630400f03SJulian Elischer 	if (destp == NULL) {
16376b795970SJulian Elischer 		TRAP_ERROR();
16384cf49a43SJulian Elischer 		return EINVAL;
163930400f03SJulian Elischer 	}
16404cf49a43SJulian Elischer 	*destp = NULL;
16414cf49a43SJulian Elischer 
16424cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
16434cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
16444cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
16454cf49a43SJulian Elischer 
16464cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
16474cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
16486b795970SJulian Elischer 		TRAP_ERROR();
16494cf49a43SJulian Elischer 		return EINVAL;
16504cf49a43SJulian Elischer 	}
16514cf49a43SJulian Elischer 
1652069154d5SJulian Elischer 	/*
1653069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1654069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1655069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1656069154d5SJulian Elischer 	 */
16574cf49a43SJulian Elischer 	if (nodename) {
1658069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
16594cf49a43SJulian Elischer 		if (node == NULL) {
16606b795970SJulian Elischer 			TRAP_ERROR();
16614cf49a43SJulian Elischer 			return (ENOENT);
16624cf49a43SJulian Elischer 		}
1663069154d5SJulian Elischer 	} else {
1664069154d5SJulian Elischer 		if (here == NULL) {
16656b795970SJulian Elischer 			TRAP_ERROR();
1666069154d5SJulian Elischer 			return (EINVAL);
1667069154d5SJulian Elischer 		}
16684cf49a43SJulian Elischer 		node = here;
166930400f03SJulian Elischer 		NG_NODE_REF(node);
1670069154d5SJulian Elischer 	}
16714cf49a43SJulian Elischer 
1672a7da736aSGleb Smirnoff 	if (path == NULL) {
1673a7da736aSGleb Smirnoff 		if (lasthook != NULL)
1674a7da736aSGleb Smirnoff 			*lasthook = NULL;
1675a7da736aSGleb Smirnoff 		*destp = node;
1676a7da736aSGleb Smirnoff 		return (0);
1677a7da736aSGleb Smirnoff 	}
1678a7da736aSGleb Smirnoff 
1679069154d5SJulian Elischer 	/*
1680069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1681a7da736aSGleb Smirnoff 	 *
1682a7da736aSGleb Smirnoff 	 * XXXGL: The path may demolish as we go the sequence, but if
1683a7da736aSGleb Smirnoff 	 * we hold the topology mutex at critical places, then, I hope,
1684a7da736aSGleb Smirnoff 	 * we would always have valid pointers in hand, although the
1685a7da736aSGleb Smirnoff 	 * path behind us may no longer exist.
1686069154d5SJulian Elischer 	 */
1687a7da736aSGleb Smirnoff 	for (;;) {
1688a7da736aSGleb Smirnoff 		hook_p hook;
16894cf49a43SJulian Elischer 		char *segment;
16904cf49a43SJulian Elischer 
16914cf49a43SJulian Elischer 		/*
16924cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
1693a7da736aSGleb Smirnoff 		 * found with a NUL; "path" points to the next segment (or the
16944cf49a43SJulian Elischer 		 * NUL at the end).
16954cf49a43SJulian Elischer 		 */
1696a7da736aSGleb Smirnoff 		for (segment = path; *path != '\0'; path++) {
1697a7da736aSGleb Smirnoff 			if (*path == '.') {
1698a7da736aSGleb Smirnoff 				*path++ = '\0';
16994cf49a43SJulian Elischer 				break;
17004cf49a43SJulian Elischer 			}
17014cf49a43SJulian Elischer 		}
17024cf49a43SJulian Elischer 
17034cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1704899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17054cf49a43SJulian Elischer 
1706a7da736aSGleb Smirnoff 		mtx_lock(&ng_topo_mtx);
17074cf49a43SJulian Elischer 		/* Can't get there from here... */
1708*4bd1b557SGleb Smirnoff 		if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
1709*4bd1b557SGleb Smirnoff 		    NG_HOOK_NOT_VALID(hook) ||
1710*4bd1b557SGleb Smirnoff 		    NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17116b795970SJulian Elischer 			TRAP_ERROR();
171230400f03SJulian Elischer 			NG_NODE_UNREF(node);
1713a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
17144cf49a43SJulian Elischer 			return (ENOENT);
17154cf49a43SJulian Elischer 		}
17164cf49a43SJulian Elischer 
1717069154d5SJulian Elischer 		/*
1718069154d5SJulian Elischer 		 * Hop on over to the next node
1719069154d5SJulian Elischer 		 * XXX
1720069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1721069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1722069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1723069154d5SJulian Elischer 		 */
1724069154d5SJulian Elischer 		oldnode = node;
172530400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
172630400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
172730400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
172830400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
172930400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1730a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
17316b795970SJulian Elischer 			TRAP_ERROR();
17324cf49a43SJulian Elischer 			return (ENXIO);
17334cf49a43SJulian Elischer 		}
17344cf49a43SJulian Elischer 
1735a7da736aSGleb Smirnoff 		if (*path == '\0') {
1736a7da736aSGleb Smirnoff 			if (lasthook != NULL) {
1737a7da736aSGleb Smirnoff 				if (hook != NULL) {
1738a7da736aSGleb Smirnoff 					*lasthook = NG_HOOK_PEER(hook);
1739a7da736aSGleb Smirnoff 					NG_HOOK_REF(*lasthook);
1740a7da736aSGleb Smirnoff 				} else
1741a7da736aSGleb Smirnoff 					*lasthook = NULL;
1742a7da736aSGleb Smirnoff 			}
1743a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
17444cf49a43SJulian Elischer 			*destp = node;
1745069154d5SJulian Elischer 			return (0);
1746069154d5SJulian Elischer 		}
1747a7da736aSGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1748a7da736aSGleb Smirnoff 	}
1749a7da736aSGleb Smirnoff }
1750069154d5SJulian Elischer 
1751069154d5SJulian Elischer /***************************************************************\
1752069154d5SJulian Elischer * Input queue handling.
1753069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1754069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
175564efc707SRobert Watson * Items which cannot be handled immediately are queued.
1756069154d5SJulian Elischer *
1757069154d5SJulian Elischer * read-write queue locking inline functions			*
1758069154d5SJulian Elischer \***************************************************************/
1759069154d5SJulian Elischer 
17609852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
17619852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
17629852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
17639852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
17649852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
17659852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1766069154d5SJulian Elischer 
1767069154d5SJulian Elischer /*
1768069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1769069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1770069154d5SJulian Elischer  * with them.
1771069154d5SJulian Elischer  *
1772b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1773069154d5SJulian Elischer  */
1774069154d5SJulian Elischer /*-
1775069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1776069154d5SJulian Elischer                        |
1777069154d5SJulian Elischer                        V
1778069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17794be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
17804be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
17814be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1782069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17834be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
17844be59335SGleb Smirnoff                             V                                | |
17854be59335SGleb Smirnoff                   [active reader count]                      | |
1786069154d5SJulian Elischer                                                              | |
17874be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1788069154d5SJulian Elischer                                                                |
17894be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1790b57a7965SJulian Elischer 
17918f9ac44aSAlexander Motin Node queue has such semantics:
17928f9ac44aSAlexander Motin - All flags modifications are atomic.
17938f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
17948f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
17958f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
17968f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
17978f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
17988f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
17998f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18008f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18018f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1802069154d5SJulian Elischer */
18038f9ac44aSAlexander Motin 
18044be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18054be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18064be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18074be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18084be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1809b57a7965SJulian Elischer 
1810b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18114be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18124be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1813b57a7965SJulian Elischer 
18144be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1815b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1816b57a7965SJulian Elischer 
18174be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
18184be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
18194be59335SGleb Smirnoff 
18204be59335SGleb Smirnoff /* How to decide what the next queued item is. */
18219852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
18229852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
18234be59335SGleb Smirnoff 
18244be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
18254be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
18264be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
18274be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
18284be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1829b57a7965SJulian Elischer 
1830b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
18314be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
18324be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1833394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
18344be59335SGleb Smirnoff 
1835714fb865SGleb Smirnoff #define NGQRW_R 0
1836714fb865SGleb Smirnoff #define NGQRW_W 1
1837714fb865SGleb Smirnoff 
18389852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
18399852972bSAlexander Motin 
1840069154d5SJulian Elischer /*
1841069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1842069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1843069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1844069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1845069154d5SJulian Elischer  * on the queue.
1846069154d5SJulian Elischer  */
1847069154d5SJulian Elischer static __inline item_p
18489852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1849069154d5SJulian Elischer {
1850069154d5SJulian Elischer 	item_p item;
18519852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1852b57a7965SJulian Elischer 
18538f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1854d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
18558f9ac44aSAlexander Motin 
18568f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
18574be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
18582955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
18592955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
18609852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
18614be59335SGleb Smirnoff 		return (NULL);
18624be59335SGleb Smirnoff 	}
1863d58e678fSGleb Smirnoff 
18644be59335SGleb Smirnoff 	/*
18654be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
18664be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
18674be59335SGleb Smirnoff 	 * the current state of the node.
18684be59335SGleb Smirnoff 	 */
18694be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1870394cb30aSAlexander Motin 		while (1) {
1871394cb30aSAlexander Motin 			long t = ngq->q_flags;
1872394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
18738f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
1874*4bd1b557SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
1875*4bd1b557SGleb Smirnoff 				    "reader can't proceed; queue flags 0x%lx",
1876*4bd1b557SGleb Smirnoff 				    __func__, node->nd_ID, node, t);
18774be59335SGleb Smirnoff 				return (NULL);
18784be59335SGleb Smirnoff 			}
18799852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1880394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1881394cb30aSAlexander Motin 				break;
1882394cb30aSAlexander Motin 			cpu_spinwait();
1883394cb30aSAlexander Motin 		}
18848f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1885714fb865SGleb Smirnoff 		*rw = NGQRW_R;
18869852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1887394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
18888f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1889714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1890069154d5SJulian Elischer 	} else {
18918f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
1892*4bd1b557SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
1893*4bd1b557SGleb Smirnoff 		    "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
1894*4bd1b557SGleb Smirnoff 		    ngq->q_flags);
18954be59335SGleb Smirnoff 		return (NULL);
18964cf49a43SJulian Elischer 	}
18974cf49a43SJulian Elischer 
18984cf49a43SJulian Elischer 	/*
1899069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1900069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19014cf49a43SJulian Elischer 	 */
19029852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19039852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
19049852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
19059852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
1906*4bd1b557SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
1907*4bd1b557SGleb Smirnoff 	    "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
1908*4bd1b557SGleb Smirnoff 	    "READER", ngq->q_flags);
1909069154d5SJulian Elischer 	return (item);
1910069154d5SJulian Elischer }
1911859a4d16SJulian Elischer 
1912859a4d16SJulian Elischer /*
19138f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
19148f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
1915859a4d16SJulian Elischer  */
1916069154d5SJulian Elischer static __inline void
19179852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
1918069154d5SJulian Elischer {
19199852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
19204be59335SGleb Smirnoff 	if (rw == NGQRW_W)
19214be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
19224be59335SGleb Smirnoff 	else
19234be59335SGleb Smirnoff 		NGI_SET_READER(item);
1924394cb30aSAlexander Motin 
1925394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
1926394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
19279852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
19289852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
1929394cb30aSAlexander Motin 
19302955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
19319852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
19324be59335SGleb Smirnoff 
19334be59335SGleb Smirnoff 	/*
19344be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
19354be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
19364be59335SGleb Smirnoff 	 */
19374be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
19389852972bSAlexander Motin 		ng_worklist_add(node);
1939394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
1940069154d5SJulian Elischer }
1941069154d5SJulian Elischer 
19428f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
1943069154d5SJulian Elischer static __inline item_p
19449852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
1945069154d5SJulian Elischer {
19469852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1947ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1948069154d5SJulian Elischer 
1949394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
1950*4bd1b557SGleb Smirnoff 	for (;;) {
19519852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
1952394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
1953394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
1954*4bd1b557SGleb Smirnoff 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
1955*4bd1b557SGleb Smirnoff 		    t + READER_INCREMENT)) {
1956069154d5SJulian Elischer 	    		/* Successfully grabbed node */
1957394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19589852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
1959069154d5SJulian Elischer 			return (item);
1960069154d5SJulian Elischer 		}
1961394cb30aSAlexander Motin 		cpu_spinwait();
1962394cb30aSAlexander Motin 	};
1963069154d5SJulian Elischer 
1964394cb30aSAlexander Motin 	/* Queue the request for later. */
19659852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
1966f912c0f7SGleb Smirnoff 
1967f912c0f7SGleb Smirnoff 	return (NULL);
1968069154d5SJulian Elischer }
1969069154d5SJulian Elischer 
19708f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
1971069154d5SJulian Elischer static __inline item_p
19729852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
1973069154d5SJulian Elischer {
19749852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1975ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1976ac5dd141SGleb Smirnoff 
1977394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
1978*4bd1b557SGleb Smirnoff 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
1979*4bd1b557SGleb Smirnoff 	    WRITER_ACTIVE)) {
1980394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
19812955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19829852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
1983069154d5SJulian Elischer 		return (item);
1984069154d5SJulian Elischer 	}
1985069154d5SJulian Elischer 
1986394cb30aSAlexander Motin 	/* Queue the request for later. */
19879852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
1988f912c0f7SGleb Smirnoff 
1989f912c0f7SGleb Smirnoff 	return (NULL);
1990069154d5SJulian Elischer }
1991069154d5SJulian Elischer 
1992262dfd7fSJulian Elischer #if 0
1993262dfd7fSJulian Elischer static __inline item_p
19949852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
1995262dfd7fSJulian Elischer {
19969852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
19979852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1998262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
1999262dfd7fSJulian Elischer 
2000262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2001262dfd7fSJulian Elischer 
20029852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2003262dfd7fSJulian Elischer 
2004262dfd7fSJulian Elischer 	/*
2005262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2006262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2007262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2008262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2009262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2010262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2011262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2012262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2013262dfd7fSJulian Elischer 	 */
20149852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
20159852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
20169852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2017262dfd7fSJulian Elischer 
2018262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2019262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2020262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2021262dfd7fSJulian Elischer 
2022262dfd7fSJulian Elischer 		/*
2023262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
2024*4bd1b557SGleb Smirnoff 		 * downgrade back to READER and finish up.
2025262dfd7fSJulian Elischer 	 	 */
2026*4bd1b557SGleb Smirnoff 		atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2027262dfd7fSJulian Elischer 
2028262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2029262dfd7fSJulian Elischer 		return;
2030262dfd7fSJulian Elischer 	}
2031262dfd7fSJulian Elischer 	/*
2032262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2033262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2034262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2035262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2036262dfd7fSJulian Elischer 	 * set the correct flags.
2037262dfd7fSJulian Elischer 	 */
20389852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2039262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
20409852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2041262dfd7fSJulian Elischer 
2042262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
20439852972bSAlexander Motin 		    node->nd_ID, node);
2044262dfd7fSJulian Elischer 	};
20459852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
20469852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
20479852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2048262dfd7fSJulian Elischer 
2049262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
20509852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2051394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20529852972bSAlexander Motin 		ng_worklist_add(node);
20539852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2054262dfd7fSJulian Elischer 
2055262dfd7fSJulian Elischer 	return;
2056262dfd7fSJulian Elischer }
2057262dfd7fSJulian Elischer #endif
2058262dfd7fSJulian Elischer 
20598f9ac44aSAlexander Motin /* Release reader lock. */
2060069154d5SJulian Elischer static __inline void
20619852972bSAlexander Motin ng_leave_read(node_p node)
2062069154d5SJulian Elischer {
20639852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2064069154d5SJulian Elischer }
2065069154d5SJulian Elischer 
20668f9ac44aSAlexander Motin /* Release writer lock. */
2067069154d5SJulian Elischer static __inline void
20689852972bSAlexander Motin ng_leave_write(node_p node)
2069069154d5SJulian Elischer {
20709852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2071069154d5SJulian Elischer }
2072069154d5SJulian Elischer 
20738f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2074069154d5SJulian Elischer static void
20759852972bSAlexander Motin ng_flush_input_queue(node_p node)
2076069154d5SJulian Elischer {
20779852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2078069154d5SJulian Elischer 	item_p item;
2079069154d5SJulian Elischer 
20802c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
20819852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
20829852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
20839852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
20849852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
20852c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
20864be59335SGleb Smirnoff 
20874be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
208810e87318SAlexander Motin 		if (item->apply != NULL) {
208910e87318SAlexander Motin 			if (item->depth == 1)
209010e87318SAlexander Motin 				item->apply->error = ENOENT;
209110e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
209210e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
209310e87318SAlexander Motin 				    item->apply->error);
209410e87318SAlexander Motin 			}
2095eb2405ddSGleb Smirnoff 		}
2096505fad52SJulian Elischer 		NG_FREE_ITEM(item);
20972c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2098069154d5SJulian Elischer 	}
20992c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2100069154d5SJulian Elischer }
2101069154d5SJulian Elischer 
2102069154d5SJulian Elischer /***********************************************************************
2103069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2104069154d5SJulian Elischer ***********************************************************************/
2105069154d5SJulian Elischer 
2106069154d5SJulian Elischer /*
21071acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2108069154d5SJulian Elischer  * Common:
2109069154d5SJulian Elischer  *    reference to destination node.
2110069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2111e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2112069154d5SJulian Elischer  * Data:
2113069154d5SJulian Elischer  *    pointer to mbuf
2114069154d5SJulian Elischer  * Control_Message:
2115069154d5SJulian Elischer  *    pointer to msg.
2116069154d5SJulian Elischer  *    ID of original sender node. (return address)
21171acb27c6SJulian Elischer  * Function:
21181acb27c6SJulian Elischer  *    Function pointer
21191acb27c6SJulian Elischer  *    void * argument
21201acb27c6SJulian Elischer  *    integer argument
2121069154d5SJulian Elischer  *
2122069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2123069154d5SJulian Elischer  */
2124069154d5SJulian Elischer 
2125069154d5SJulian Elischer int
212642282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2127069154d5SJulian Elischer {
2128e088dd4cSAlexander Motin 	hook_p hook;
2129e088dd4cSAlexander Motin 	node_p node;
213042282202SGleb Smirnoff 	int queue, rw;
2131e088dd4cSAlexander Motin 	struct ng_queue *ngq;
213232b33288SGleb Smirnoff 	int error = 0;
2133069154d5SJulian Elischer 
2134f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2135f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2136e088dd4cSAlexander Motin 
2137e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2138e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2139e088dd4cSAlexander Motin #endif
2140e088dd4cSAlexander Motin 
2141f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2142e088dd4cSAlexander Motin 	if (item->apply)
2143e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2144e088dd4cSAlexander Motin 
2145e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2146f50597f5SAlexander Motin 	/* Node is never optional. */
2147f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2148e088dd4cSAlexander Motin 
2149e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2150f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2151f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2152f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
215310204449SJulian Elischer 		if (NGI_M(item) == NULL)
2154e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2155069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
21566f683eeeSGleb Smirnoff 	}
21576f683eeeSGleb Smirnoff 
2158069154d5SJulian Elischer 	/*
2159f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2160f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2161f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2162069154d5SJulian Elischer 	 */
2163db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2164f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2165f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2166069154d5SJulian Elischer 		rw = NGQRW_W;
2167f50597f5SAlexander Motin 	} else {
2168f50597f5SAlexander Motin 		rw = NGQRW_R;
2169f50597f5SAlexander Motin 	}
21706f683eeeSGleb Smirnoff 
217181a253a4SAlexander Motin 	/*
2172f089869fSMarko Zec 	 * If sender or receiver requests queued delivery, or call graph
2173f089869fSMarko Zec 	 * loops back from outbound to inbound path, or stack usage
217481a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
217581a253a4SAlexander Motin 	 */
217681a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
217781a253a4SAlexander Motin 		queue = 1;
2178f089869fSMarko Zec 	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2179f089869fSMarko Zec 	    curthread->td_ng_outbound) {
2180f089869fSMarko Zec 		queue = 1;
2181f50597f5SAlexander Motin 	} else {
2182f50597f5SAlexander Motin 		queue = 0;
218381a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2184d4529f98SAlexander Motin 		/*
2185942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2186f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2187d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2188f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2189f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2190d4529f98SAlexander Motin 		 */
2191f50597f5SAlexander Motin 		size_t	st, su, sl;
219281a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2193f50597f5SAlexander Motin 		sl = st - su;
2194*4bd1b557SGleb Smirnoff 		if ((sl * 4 < st) || ((sl * 2 < st) &&
2195*4bd1b557SGleb Smirnoff 		    ((node->nd_flags & NGF_HI_STACK) || (hook &&
2196*4bd1b557SGleb Smirnoff 		    (hook->hk_flags & HK_HI_STACK)))))
219781a253a4SAlexander Motin 			queue = 1;
219881a253a4SAlexander Motin #endif
2199f50597f5SAlexander Motin 	}
220081a253a4SAlexander Motin 
2201069154d5SJulian Elischer 	if (queue) {
220210e87318SAlexander Motin 		item->depth = 1;
2203394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
22049852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2205f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2206069154d5SJulian Elischer 	}
2207069154d5SJulian Elischer 
2208069154d5SJulian Elischer 	/*
2209069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2210069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2211069154d5SJulian Elischer 	 */
221232b33288SGleb Smirnoff  	if (rw == NGQRW_R)
22139852972bSAlexander Motin 		item = ng_acquire_read(node, item);
221432b33288SGleb Smirnoff 	else
22159852972bSAlexander Motin 		item = ng_acquire_write(node, item);
22164cf49a43SJulian Elischer 
2217f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2218f50597f5SAlexander Motin 	if (item == NULL)
2219f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2220069154d5SJulian Elischer 
22215951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
22225951069aSJulian Elischer 
222310e87318SAlexander Motin 	item->depth++;
222427757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2225069154d5SJulian Elischer 
2226394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
22279852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2228394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
22292c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2230394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
22319852972bSAlexander Motin 			ng_worklist_add(node);
22322c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2233394cb30aSAlexander Motin 	}
2234394cb30aSAlexander Motin 
2235394cb30aSAlexander Motin 	/*
2236394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2237394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2238394cb30aSAlexander Motin 	 */
2239394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2240069154d5SJulian Elischer 
22411acb27c6SJulian Elischer 	return (error);
2242e088dd4cSAlexander Motin 
2243e088dd4cSAlexander Motin done:
2244f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
224510e87318SAlexander Motin 	if (item->apply != NULL) {
224610e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
224710e87318SAlexander Motin 			item->apply->error = error;
224810e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
224910e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
225010e87318SAlexander Motin 			    item->apply->error);
225110e87318SAlexander Motin 		}
225210e87318SAlexander Motin 	}
2253e72a98f4SAlexander Motin 
2254e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2255e088dd4cSAlexander Motin 	return (error);
2256069154d5SJulian Elischer }
2257069154d5SJulian Elischer 
2258069154d5SJulian Elischer /*
2259069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2260069154d5SJulian Elischer  * It should contain all the information needed
2261069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2262e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
22634cf49a43SJulian Elischer  */
226427757487SGleb Smirnoff static int
2265714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2266069154d5SJulian Elischer {
2267069154d5SJulian Elischer 	hook_p  hook;
2268069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2269c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2270e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
227110e87318SAlexander Motin 	int	error = 0, depth;
2272069154d5SJulian Elischer 
2273f50597f5SAlexander Motin 	/* Node and item are never optional. */
2274f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2275f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2276f50597f5SAlexander Motin 
22771acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
227830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2279069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2280069154d5SJulian Elischer #endif
22818afe16d5SGleb Smirnoff 
22828afe16d5SGleb Smirnoff 	apply = item->apply;
228310e87318SAlexander Motin 	depth = item->depth;
22848afe16d5SGleb Smirnoff 
22856b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2286069154d5SJulian Elischer 	case NGQF_DATA:
2287069154d5SJulian Elischer 		/*
2288069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2289069154d5SJulian Elischer 		 */
2290f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2291f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2292f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2293a54a69d7SJulian Elischer 			error = EIO;
2294069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2295c4b5eea4SJulian Elischer 			break;
2296069154d5SJulian Elischer 		}
2297c4b5eea4SJulian Elischer 		/*
2298c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
2299*4bd1b557SGleb Smirnoff 		 * Give preference to the hook over-ride method.
2300c4b5eea4SJulian Elischer 		 */
2301*4bd1b557SGleb Smirnoff 		if ((!(rcvdata = hook->hk_rcvdata)) &&
2302*4bd1b557SGleb Smirnoff 		    (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2303c4b5eea4SJulian Elischer 			error = 0;
2304c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2305c4b5eea4SJulian Elischer 			break;
2306c4b5eea4SJulian Elischer 		}
2307a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2308069154d5SJulian Elischer 		break;
2309069154d5SJulian Elischer 	case NGQF_MESG:
2310e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2311069154d5SJulian Elischer 			/*
2312e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2313e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2314069154d5SJulian Elischer 			 * The message may not need it.
2315069154d5SJulian Elischer 			 */
231630400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2317069154d5SJulian Elischer 			hook = NULL;
2318069154d5SJulian Elischer 		}
2319069154d5SJulian Elischer 		/*
2320069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2321069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2322069154d5SJulian Elischer 		 */
232330400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
23246b795970SJulian Elischer 			TRAP_ERROR();
2325a54a69d7SJulian Elischer 			error = EINVAL;
2326069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2327e72a98f4SAlexander Motin 			break;
2328e72a98f4SAlexander Motin 		}
2329069154d5SJulian Elischer 		/*
2330069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2331069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2332069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2333e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2334069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2335069154d5SJulian Elischer 		 * reference a node or hook that has just been
2336069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2337069154d5SJulian Elischer 		 * is holding a reference, but..
2338069154d5SJulian Elischer 		 */
2339e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2340e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2341a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2342c4b5eea4SJulian Elischer 			break;
2343c4b5eea4SJulian Elischer 		}
2344e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2345e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
23466b795970SJulian Elischer 			TRAP_ERROR();
2347a54a69d7SJulian Elischer 			error = 0;
2348069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2349c4b5eea4SJulian Elischer 			break;
2350069154d5SJulian Elischer 		}
2351a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2352069154d5SJulian Elischer 		break;
23536b795970SJulian Elischer 	case NGQF_FN:
2354e088dd4cSAlexander Motin 	case NGQF_FN2:
2355e088dd4cSAlexander Motin 		/*
235674c9119dSAlexander Motin 		 * In the case of the shutdown message we allow it to hit
2357e088dd4cSAlexander Motin 		 * even if the node is invalid.
2358e088dd4cSAlexander Motin 		 */
235974c9119dSAlexander Motin 		if (NG_NODE_NOT_VALID(node) &&
236074c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmnode) {
2361e088dd4cSAlexander Motin 			TRAP_ERROR();
2362e088dd4cSAlexander Motin 			error = EINVAL;
2363e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2364e088dd4cSAlexander Motin 			break;
2365e088dd4cSAlexander Motin 		}
236674c9119dSAlexander Motin 		/* Same is about some internal functions and invalid hook. */
236774c9119dSAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook) &&
236874c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part2 &&
236974c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part3 &&
237074c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmhook_part2) {
237174c9119dSAlexander Motin 			TRAP_ERROR();
237274c9119dSAlexander Motin 			error = EINVAL;
237374c9119dSAlexander Motin 			NG_FREE_ITEM(item);
237474c9119dSAlexander Motin 			break;
237574c9119dSAlexander Motin 		}
237674c9119dSAlexander Motin 
2377b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2378b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2379b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2380b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2381b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2382e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2383e088dd4cSAlexander Motin 		break;
2384069154d5SJulian Elischer 	}
2385069154d5SJulian Elischer 	/*
2386069154d5SJulian Elischer 	 * We held references on some of the resources
2387069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2388069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2389069154d5SJulian Elischer 	 */
2390e72a98f4SAlexander Motin 	if (hook)
239130400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2392069154d5SJulian Elischer 
2393f50597f5SAlexander Motin  	if (rw == NGQRW_R)
23949852972bSAlexander Motin 		ng_leave_read(node);
2395f50597f5SAlexander Motin 	else
23969852972bSAlexander Motin 		ng_leave_write(node);
23978afe16d5SGleb Smirnoff 
23988afe16d5SGleb Smirnoff 	/* Apply callback. */
239910e87318SAlexander Motin 	if (apply != NULL) {
240010e87318SAlexander Motin 		if (depth == 1 && error != 0)
240110e87318SAlexander Motin 			apply->error = error;
240210e87318SAlexander Motin 		if (refcount_release(&apply->refs))
240310e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
240410e87318SAlexander Motin 	}
24058afe16d5SGleb Smirnoff 
240627757487SGleb Smirnoff 	return (error);
2407069154d5SJulian Elischer }
2408069154d5SJulian Elischer 
2409069154d5SJulian Elischer /***********************************************************************
2410069154d5SJulian Elischer  * Implement the 'generic' control messages
2411069154d5SJulian Elischer  ***********************************************************************/
2412069154d5SJulian Elischer static int
2413069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
24144cf49a43SJulian Elischer {
24154cf49a43SJulian Elischer 	int error = 0;
2416069154d5SJulian Elischer 	struct ng_mesg *msg;
2417069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
24184cf49a43SJulian Elischer 
2419069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
24204cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
24216b795970SJulian Elischer 		TRAP_ERROR();
2422069154d5SJulian Elischer 		error = EINVAL;
2423069154d5SJulian Elischer 		goto out;
24244cf49a43SJulian Elischer 	}
24254cf49a43SJulian Elischer 	switch (msg->header.cmd) {
24264cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
24271acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
24284cf49a43SJulian Elischer 		break;
24294cf49a43SJulian Elischer 	case NGM_MKPEER:
24304cf49a43SJulian Elischer 	    {
24314cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
24324cf49a43SJulian Elischer 
24334cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
24346b795970SJulian Elischer 			TRAP_ERROR();
2435069154d5SJulian Elischer 			error = EINVAL;
2436069154d5SJulian Elischer 			break;
24374cf49a43SJulian Elischer 		}
24384cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
24394cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
24404cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
24414cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
24424cf49a43SJulian Elischer 		break;
24434cf49a43SJulian Elischer 	    }
24444cf49a43SJulian Elischer 	case NGM_CONNECT:
24454cf49a43SJulian Elischer 	    {
24464cf49a43SJulian Elischer 		struct ngm_connect *const con =
24474cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
24484cf49a43SJulian Elischer 		node_p node2;
24494cf49a43SJulian Elischer 
24504cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
24516b795970SJulian Elischer 			TRAP_ERROR();
2452069154d5SJulian Elischer 			error = EINVAL;
2453069154d5SJulian Elischer 			break;
24544cf49a43SJulian Elischer 		}
24554cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
24564cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
24574cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2458069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2459069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
24604cf49a43SJulian Elischer 		if (error)
24614cf49a43SJulian Elischer 			break;
2462e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2463e088dd4cSAlexander Motin 		    node2, con->peerhook);
246430400f03SJulian Elischer 		NG_NODE_UNREF(node2);
24654cf49a43SJulian Elischer 		break;
24664cf49a43SJulian Elischer 	    }
24674cf49a43SJulian Elischer 	case NGM_NAME:
24684cf49a43SJulian Elischer 	    {
24694cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
24704cf49a43SJulian Elischer 
24714cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
24726b795970SJulian Elischer 			TRAP_ERROR();
2473069154d5SJulian Elischer 			error = EINVAL;
2474069154d5SJulian Elischer 			break;
24754cf49a43SJulian Elischer 		}
24764cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
24774cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
24784cf49a43SJulian Elischer 		break;
24794cf49a43SJulian Elischer 	    }
24804cf49a43SJulian Elischer 	case NGM_RMHOOK:
24814cf49a43SJulian Elischer 	    {
24824cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
24834cf49a43SJulian Elischer 		hook_p hook;
24844cf49a43SJulian Elischer 
24854cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
24866b795970SJulian Elischer 			TRAP_ERROR();
2487069154d5SJulian Elischer 			error = EINVAL;
2488069154d5SJulian Elischer 			break;
24894cf49a43SJulian Elischer 		}
24904cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2491899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
24924cf49a43SJulian Elischer 			ng_destroy_hook(hook);
24934cf49a43SJulian Elischer 		break;
24944cf49a43SJulian Elischer 	    }
24954cf49a43SJulian Elischer 	case NGM_NODEINFO:
24964cf49a43SJulian Elischer 	    {
24974cf49a43SJulian Elischer 		struct nodeinfo *ni;
24984cf49a43SJulian Elischer 
2499069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25004cf49a43SJulian Elischer 		if (resp == NULL) {
25014cf49a43SJulian Elischer 			error = ENOMEM;
25024cf49a43SJulian Elischer 			break;
25034cf49a43SJulian Elischer 		}
25044cf49a43SJulian Elischer 
25054cf49a43SJulian Elischer 		/* Fill in node info */
2506069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
250730400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
250887e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
250987e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2510dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
251130400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25124cf49a43SJulian Elischer 		break;
25134cf49a43SJulian Elischer 	    }
25144cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
25154cf49a43SJulian Elischer 	    {
251630400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
25174cf49a43SJulian Elischer 		struct hooklist *hl;
25184cf49a43SJulian Elischer 		struct nodeinfo *ni;
25194cf49a43SJulian Elischer 		hook_p hook;
25204cf49a43SJulian Elischer 
25214cf49a43SJulian Elischer 		/* Get response struct */
2522*4bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*hl) +
2523*4bd1b557SGleb Smirnoff 		    (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2524069154d5SJulian Elischer 		if (resp == NULL) {
25254cf49a43SJulian Elischer 			error = ENOMEM;
25264cf49a43SJulian Elischer 			break;
25274cf49a43SJulian Elischer 		}
2528069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
25294cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
25304cf49a43SJulian Elischer 
25314cf49a43SJulian Elischer 		/* Fill in node info */
253230400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
253387e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
253487e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2535dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
25364cf49a43SJulian Elischer 
25374cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
25384cf49a43SJulian Elischer 		ni->hooks = 0;
253930400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
25404cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
25414cf49a43SJulian Elischer 
25424cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
25434cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
25446e551fb6SDavid E. O'Brien 				    __func__, "hooks");
25454cf49a43SJulian Elischer 				break;
25464cf49a43SJulian Elischer 			}
254730400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
25484cf49a43SJulian Elischer 				continue;
254987e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
255087e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
255130400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
255287e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
255387e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
255487e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
255587e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
255630400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
255730400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
25584cf49a43SJulian Elischer 			ni->hooks++;
25594cf49a43SJulian Elischer 		}
25604cf49a43SJulian Elischer 		break;
25614cf49a43SJulian Elischer 	    }
25624cf49a43SJulian Elischer 
25634cf49a43SJulian Elischer 	case NGM_LISTNAMES:
25644cf49a43SJulian Elischer 	case NGM_LISTNODES:
25654cf49a43SJulian Elischer 	    {
25664cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
25674cf49a43SJulian Elischer 		struct namelist *nl;
25684cf49a43SJulian Elischer 		node_p node;
2569cfea3f85SAlexander Motin 		int num = 0, i;
25704cf49a43SJulian Elischer 
2571cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
25724cf49a43SJulian Elischer 		/* Count number of nodes */
2573cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2574603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2575cfea3f85SAlexander Motin 				if (NG_NODE_IS_VALID(node) &&
2576cfea3f85SAlexander Motin 				    (unnamed || NG_NODE_HAS_NAME(node))) {
25774cf49a43SJulian Elischer 					num++;
25784cf49a43SJulian Elischer 				}
257970de87f2SJulian Elischer 			}
2580cfea3f85SAlexander Motin 		}
2581cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
25824cf49a43SJulian Elischer 
25834cf49a43SJulian Elischer 		/* Get response struct */
2584*4bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2585*4bd1b557SGleb Smirnoff 		    (num * sizeof(struct nodeinfo)), M_NOWAIT);
2586069154d5SJulian Elischer 		if (resp == NULL) {
25874cf49a43SJulian Elischer 			error = ENOMEM;
25884cf49a43SJulian Elischer 			break;
25894cf49a43SJulian Elischer 		}
2590069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
25914cf49a43SJulian Elischer 
25924cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
25934cf49a43SJulian Elischer 		nl->numnames = 0;
2594cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
2595cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2596603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2597cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2598cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
25994cf49a43SJulian Elischer 
2600b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2601b96baf0aSGleb Smirnoff 					continue;
2602b96baf0aSGleb Smirnoff 				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2603b96baf0aSGleb Smirnoff 					continue;
26044cf49a43SJulian Elischer 				if (nl->numnames >= num) {
2605cfea3f85SAlexander Motin 					log(LOG_ERR, "%s: number of nodes changed\n",
2606cfea3f85SAlexander Motin 					    __func__);
26074cf49a43SJulian Elischer 					break;
26084cf49a43SJulian Elischer 				}
260930400f03SJulian Elischer 				if (NG_NODE_HAS_NAME(node))
261087e2c66aSHartmut Brandt 					strcpy(np->name, NG_NODE_NAME(node));
261187e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2612dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
261330400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
26144cf49a43SJulian Elischer 				nl->numnames++;
26154cf49a43SJulian Elischer 			}
2616cfea3f85SAlexander Motin 		}
2617cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26184cf49a43SJulian Elischer 		break;
26194cf49a43SJulian Elischer 	    }
26204cf49a43SJulian Elischer 
26214cf49a43SJulian Elischer 	case NGM_LISTTYPES:
26224cf49a43SJulian Elischer 	    {
26234cf49a43SJulian Elischer 		struct typelist *tl;
26244cf49a43SJulian Elischer 		struct ng_type *type;
26254cf49a43SJulian Elischer 		int num = 0;
26264cf49a43SJulian Elischer 
26279ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
26284cf49a43SJulian Elischer 		/* Count number of types */
262970de87f2SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26304cf49a43SJulian Elischer 			num++;
263170de87f2SJulian Elischer 		}
26329ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26334cf49a43SJulian Elischer 
26344cf49a43SJulian Elischer 		/* Get response struct */
2635*4bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*tl) +
2636*4bd1b557SGleb Smirnoff 		    (num * sizeof(struct typeinfo)), M_NOWAIT);
2637069154d5SJulian Elischer 		if (resp == NULL) {
26384cf49a43SJulian Elischer 			error = ENOMEM;
26394cf49a43SJulian Elischer 			break;
26404cf49a43SJulian Elischer 		}
2641069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
26424cf49a43SJulian Elischer 
26434cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
26444cf49a43SJulian Elischer 		tl->numtypes = 0;
26459ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
2646069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26474cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
26484cf49a43SJulian Elischer 
26494cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
26504cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26516e551fb6SDavid E. O'Brien 				    __func__, "types");
26524cf49a43SJulian Elischer 				break;
26534cf49a43SJulian Elischer 			}
265487e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2655c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
26564cf49a43SJulian Elischer 			tl->numtypes++;
26574cf49a43SJulian Elischer 		}
26589ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26594cf49a43SJulian Elischer 		break;
26604cf49a43SJulian Elischer 	    }
26614cf49a43SJulian Elischer 
2662f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2663f8307e12SArchie Cobbs 	    {
26647133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2665f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2666f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2667069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2668f8307e12SArchie Cobbs 
2669f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2670f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
26714c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
26724c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
26734c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
26746b795970SJulian Elischer 			TRAP_ERROR();
2675f8307e12SArchie Cobbs 			error = EINVAL;
2676f8307e12SArchie Cobbs 			break;
2677f8307e12SArchie Cobbs 		}
2678f8307e12SArchie Cobbs 
2679f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2680069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2681069154d5SJulian Elischer 		if (resp == NULL) {
2682f8307e12SArchie Cobbs 			error = ENOMEM;
2683f8307e12SArchie Cobbs 			break;
2684f8307e12SArchie Cobbs 		}
2685069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2686f8307e12SArchie Cobbs 
2687f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2688f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2689f8307e12SArchie Cobbs 
2690f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
2691*4bd1b557SGleb Smirnoff 		for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
2692*4bd1b557SGleb Smirnoff 		    c++) {
2693*4bd1b557SGleb Smirnoff 			if (binary->header.typecookie == c->cookie &&
2694*4bd1b557SGleb Smirnoff 			    binary->header.cmd == c->cmd)
2695f8307e12SArchie Cobbs 				break;
2696f8307e12SArchie Cobbs 		}
2697f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2698f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2699*4bd1b557SGleb Smirnoff 				if (binary->header.typecookie == c->cookie &&
2700*4bd1b557SGleb Smirnoff 				    binary->header.cmd == c->cmd)
2701f8307e12SArchie Cobbs 					break;
2702f8307e12SArchie Cobbs 			}
2703f8307e12SArchie Cobbs 			if (c->name == NULL) {
2704069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2705f8307e12SArchie Cobbs 				error = ENOSYS;
2706f8307e12SArchie Cobbs 				break;
2707f8307e12SArchie Cobbs 			}
2708f8307e12SArchie Cobbs 		}
2709f8307e12SArchie Cobbs 
2710f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2711f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2712f8307e12SArchie Cobbs 		    "%s", c->name);
2713f8307e12SArchie Cobbs 
2714f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2715f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2716f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
271770de87f2SJulian Elischer 		if (argstype == NULL) {
2718f8307e12SArchie Cobbs 			*ascii->data = '\0';
271970de87f2SJulian Elischer 		} else {
2720f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2721f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2722f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2723069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2724f8307e12SArchie Cobbs 				break;
2725f8307e12SArchie Cobbs 			}
2726f8307e12SArchie Cobbs 		}
2727f8307e12SArchie Cobbs 
2728f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2729f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2730f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2731069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2732f8307e12SArchie Cobbs 		break;
2733f8307e12SArchie Cobbs 	    }
2734f8307e12SArchie Cobbs 
2735f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2736f8307e12SArchie Cobbs 	    {
273798a5a343SMarko Zec 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2738f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2739f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2740069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
274152ec4a03SArchie Cobbs 		int off = 0;
2742f8307e12SArchie Cobbs 
2743f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2744f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
27454c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
27464c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
27474c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
27484c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
27496b795970SJulian Elischer 			TRAP_ERROR();
2750f8307e12SArchie Cobbs 			error = EINVAL;
2751f8307e12SArchie Cobbs 			break;
2752f8307e12SArchie Cobbs 		}
2753f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2754f8307e12SArchie Cobbs 
2755f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2756069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2757069154d5SJulian Elischer 		if (resp == NULL) {
2758f8307e12SArchie Cobbs 			error = ENOMEM;
2759f8307e12SArchie Cobbs 			break;
2760f8307e12SArchie Cobbs 		}
2761069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2762f8307e12SArchie Cobbs 
2763f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2764f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2765f8307e12SArchie Cobbs 
2766f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
276730400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2768f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2769f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2770f8307e12SArchie Cobbs 				break;
2771f8307e12SArchie Cobbs 		}
2772f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2773f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2774f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2775f8307e12SArchie Cobbs 					break;
2776f8307e12SArchie Cobbs 			}
2777f8307e12SArchie Cobbs 			if (c->name == NULL) {
2778069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2779f8307e12SArchie Cobbs 				error = ENOSYS;
2780f8307e12SArchie Cobbs 				break;
2781f8307e12SArchie Cobbs 			}
2782f8307e12SArchie Cobbs 		}
2783f8307e12SArchie Cobbs 
2784f8307e12SArchie Cobbs 		/* Convert command name to binary */
2785f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2786f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2787f8307e12SArchie Cobbs 
2788f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2789f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2790f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
279170de87f2SJulian Elischer 		if (argstype == NULL) {
2792f8307e12SArchie Cobbs 			bufSize = 0;
279370de87f2SJulian Elischer 		} else {
2794*4bd1b557SGleb Smirnoff 			if ((error = ng_parse(argstype, ascii->data, &off,
2795*4bd1b557SGleb Smirnoff 			    (u_char *)binary->data, &bufSize)) != 0) {
2796069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2797f8307e12SArchie Cobbs 				break;
2798f8307e12SArchie Cobbs 			}
2799f8307e12SArchie Cobbs 		}
2800f8307e12SArchie Cobbs 
2801f8307e12SArchie Cobbs 		/* Return the result */
2802f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2803069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2804f8307e12SArchie Cobbs 		break;
2805f8307e12SArchie Cobbs 	    }
2806f8307e12SArchie Cobbs 
28077095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
28084cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
28094cf49a43SJulian Elischer 		/*
28104cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
28114cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2812069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
28134cf49a43SJulian Elischer 		 * when control passes back to us.
28144cf49a43SJulian Elischer 		 */
281530400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2816069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
281730400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
28184cf49a43SJulian Elischer 		}
28194cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
28204cf49a43SJulian Elischer 	default:
28216b795970SJulian Elischer 		TRAP_ERROR();
28224cf49a43SJulian Elischer 		error = EINVAL;
28234cf49a43SJulian Elischer 	}
2824069154d5SJulian Elischer 	/*
2825069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2826*4bd1b557SGleb Smirnoff 	 * to avoid problems with allocating when in tight memory situations.
2827069154d5SJulian Elischer 	 * Don't free it if it is so.
2828069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2829069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2830069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2831069154d5SJulian Elischer 	 */
2832069154d5SJulian Elischer out:
2833069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2834069154d5SJulian Elischer 	NG_FREE_MSG(msg);
28354cf49a43SJulian Elischer 	return (error);
28364cf49a43SJulian Elischer }
28374cf49a43SJulian Elischer 
28384cf49a43SJulian Elischer /************************************************************************
28398253c060SGleb Smirnoff 			Queue element get/free routines
28408253c060SGleb Smirnoff ************************************************************************/
28418253c060SGleb Smirnoff 
28428253c060SGleb Smirnoff uma_zone_t			ng_qzone;
28436aa6d011SAlexander Motin uma_zone_t			ng_qdzone;
2844f2fbb838SAlexander Motin static int			numthreads = 0; /* number of queue threads */
2845ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2846ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
28478253c060SGleb Smirnoff 
2848f2fbb838SAlexander Motin TUNABLE_INT("net.graph.threads", &numthreads);
2849f2fbb838SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2850f2fbb838SAlexander Motin     0, "Number of queue processing threads");
28518253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
28528253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
28536aa6d011SAlexander Motin     0, "Maximum number of non-data queue items to allocate");
2854ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
28556aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
28566aa6d011SAlexander Motin     0, "Maximum number of data queue 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
28716aa6d011SAlexander Motin ng_alloc_item(int type, int flags)
28728253c060SGleb Smirnoff {
28736aa6d011SAlexander Motin 	item_p item;
28748253c060SGleb Smirnoff 
28756aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
28766aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
287742282202SGleb Smirnoff 
28786aa6d011SAlexander Motin 	item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
28796aa6d011SAlexander Motin 	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
28808253c060SGleb Smirnoff 
28818253c060SGleb Smirnoff 	if (item) {
28826aa6d011SAlexander Motin 		item->el_flags = type;
28836aa6d011SAlexander Motin #ifdef	NETGRAPH_DEBUG
28848253c060SGleb Smirnoff 		mtx_lock(&ngq_mtx);
28858253c060SGleb Smirnoff 		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
28868253c060SGleb Smirnoff 		allocated++;
28878253c060SGleb Smirnoff 		mtx_unlock(&ngq_mtx);
28888253c060SGleb Smirnoff #endif
28896aa6d011SAlexander Motin 	}
28908253c060SGleb Smirnoff 
28918253c060SGleb Smirnoff 	return (item);
28928253c060SGleb Smirnoff }
28938253c060SGleb Smirnoff 
28948253c060SGleb Smirnoff /*
28958253c060SGleb Smirnoff  * Release a queue entry
28968253c060SGleb Smirnoff  */
28978253c060SGleb Smirnoff void
28988253c060SGleb Smirnoff ng_free_item(item_p item)
28998253c060SGleb Smirnoff {
29008253c060SGleb Smirnoff 	/*
29018253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
29028253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
29038253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
29048253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
29058253c060SGleb Smirnoff 	 * or we release them again here.
29068253c060SGleb Smirnoff 	 */
29078253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
29088253c060SGleb Smirnoff 	case NGQF_DATA:
29098253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
29108253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
29118253c060SGleb Smirnoff 		break;
29128253c060SGleb Smirnoff 	case NGQF_MESG:
29138253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
29148253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
29158253c060SGleb Smirnoff 		break;
29168253c060SGleb Smirnoff 	case NGQF_FN:
2917e088dd4cSAlexander Motin 	case NGQF_FN2:
29188253c060SGleb Smirnoff 		/* nothing to free really, */
29198253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
29208253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
29218253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
29228253c060SGleb Smirnoff 		break;
29238253c060SGleb Smirnoff 	}
29248253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
29258253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
29268253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
29278253c060SGleb Smirnoff 
29288253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29298253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
29308253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
29318253c060SGleb Smirnoff 	allocated--;
29328253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
29338253c060SGleb Smirnoff #endif
29346aa6d011SAlexander Motin 	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
29356aa6d011SAlexander Motin 	    ng_qdzone : ng_qzone, item);
29366aa6d011SAlexander Motin }
29376aa6d011SAlexander Motin 
29386aa6d011SAlexander Motin /*
29396aa6d011SAlexander Motin  * Change type of the queue entry.
29406aa6d011SAlexander Motin  * Possibly reallocates it from another UMA zone.
29416aa6d011SAlexander Motin  */
29426aa6d011SAlexander Motin static __inline item_p
29436aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags)
29446aa6d011SAlexander Motin {
29456aa6d011SAlexander Motin 	item_p item;
29466aa6d011SAlexander Motin 	int from, to;
29476aa6d011SAlexander Motin 
29486aa6d011SAlexander Motin 	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
29496aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29506aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
29516aa6d011SAlexander Motin 
29526aa6d011SAlexander Motin 	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
29536aa6d011SAlexander Motin 	to = (type == NGQF_DATA);
29546aa6d011SAlexander Motin 	if (from != to) {
29556aa6d011SAlexander Motin 		/* If reallocation is required do it and copy item. */
29566aa6d011SAlexander Motin 		if ((item = ng_alloc_item(type, flags)) == NULL) {
29576aa6d011SAlexander Motin 			ng_free_item(pitem);
29586aa6d011SAlexander Motin 			return (NULL);
29596aa6d011SAlexander Motin 		}
29606aa6d011SAlexander Motin 		*item = *pitem;
29616aa6d011SAlexander Motin 		ng_free_item(pitem);
29626aa6d011SAlexander Motin 	} else
29636aa6d011SAlexander Motin 		item = pitem;
29646aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
29656aa6d011SAlexander Motin 
29666aa6d011SAlexander Motin 	return (item);
29678253c060SGleb Smirnoff }
29688253c060SGleb Smirnoff 
29698253c060SGleb Smirnoff /************************************************************************
29704cf49a43SJulian Elischer 			Module routines
29714cf49a43SJulian Elischer ************************************************************************/
29724cf49a43SJulian Elischer 
29734cf49a43SJulian Elischer /*
29744cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
29754cf49a43SJulian Elischer  */
29764cf49a43SJulian Elischer int
29774cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
29784cf49a43SJulian Elischer {
29794cf49a43SJulian Elischer 	struct ng_type *const type = data;
2980*4bd1b557SGleb Smirnoff 	int error = 0;
29814cf49a43SJulian Elischer 
29824cf49a43SJulian Elischer 	switch (event) {
29834cf49a43SJulian Elischer 	case MOD_LOAD:
29844cf49a43SJulian Elischer 
29854cf49a43SJulian Elischer 		/* Register new netgraph node type */
2986*4bd1b557SGleb Smirnoff 		if ((error = ng_newtype(type)) != 0)
29874cf49a43SJulian Elischer 			break;
29884cf49a43SJulian Elischer 
29894cf49a43SJulian Elischer 		/* Call type specific code */
29904cf49a43SJulian Elischer 		if (type->mod_event != NULL)
2991069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
29929ed346baSBosko Milekic 				mtx_lock(&ng_typelist_mtx);
2993c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
29944cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
29959ed346baSBosko Milekic 				mtx_unlock(&ng_typelist_mtx);
2996069154d5SJulian Elischer 			}
29974cf49a43SJulian Elischer 		break;
29984cf49a43SJulian Elischer 
29994cf49a43SJulian Elischer 	case MOD_UNLOAD:
3000c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
30014cf49a43SJulian Elischer 			error = EBUSY;
3002c73b94a2SJulian Elischer 		} else {
3003*4bd1b557SGleb Smirnoff 			if (type->refs == 0) /* failed load, nothing to undo */
3004c73b94a2SJulian Elischer 				break;
30054cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
30064cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
3007*4bd1b557SGleb Smirnoff 				if (error != 0)	/* type refuses.. */
30084cf49a43SJulian Elischer 					break;
30094cf49a43SJulian Elischer 			}
30109ed346baSBosko Milekic 			mtx_lock(&ng_typelist_mtx);
30114cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
30129ed346baSBosko Milekic 			mtx_unlock(&ng_typelist_mtx);
30134cf49a43SJulian Elischer 		}
30144cf49a43SJulian Elischer 		break;
30154cf49a43SJulian Elischer 
30164cf49a43SJulian Elischer 	default:
30174cf49a43SJulian Elischer 		if (type->mod_event != NULL)
30184cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
30194cf49a43SJulian Elischer 		else
30203e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
30214cf49a43SJulian Elischer 		break;
30224cf49a43SJulian Elischer 	}
30234cf49a43SJulian Elischer 	return (error);
30244cf49a43SJulian Elischer }
30254cf49a43SJulian Elischer 
3026eddfbb76SRobert Watson #ifdef VIMAGE
3027d0728d71SRobert Watson static void
3028d0728d71SRobert Watson vnet_netgraph_uninit(const void *unused __unused)
3029bc29160dSMarko Zec {
3030a3f93b72SMarko Zec 	node_p node = NULL, last_killed = NULL;
3031a3f93b72SMarko Zec 	int i;
3032bc29160dSMarko Zec 
3033a3f93b72SMarko Zec 	do {
3034a3f93b72SMarko Zec 		/* Find a node to kill */
3035a3f93b72SMarko Zec 		mtx_lock(&ng_namehash_mtx);
3036a3f93b72SMarko Zec 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
3037a3f93b72SMarko Zec 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
3038a3f93b72SMarko Zec 				if (node != &ng_deadnode) {
3039a3f93b72SMarko Zec 					NG_NODE_REF(node);
3040a3f93b72SMarko Zec 					break;
3041a3f93b72SMarko Zec 				}
3042a3f93b72SMarko Zec 			}
3043a3f93b72SMarko Zec 			if (node != NULL)
3044a3f93b72SMarko Zec 				break;
3045a3f93b72SMarko Zec 		}
3046a3f93b72SMarko Zec 		mtx_unlock(&ng_namehash_mtx);
3047a3f93b72SMarko Zec 
3048a3f93b72SMarko Zec 		/* Attempt to kill it only if it is a regular node */
3049a3f93b72SMarko Zec 		if (node != NULL) {
3050bc29160dSMarko Zec 			if (node == last_killed) {
3051bc29160dSMarko Zec 				/* This should never happen */
3052*4bd1b557SGleb Smirnoff 				printf("ng node %s needs NGF_REALLY_DIE\n",
3053*4bd1b557SGleb Smirnoff 				    node->nd_name);
3054a3f93b72SMarko Zec 				if (node->nd_flags & NGF_REALLY_DIE)
3055a3f93b72SMarko Zec 					panic("ng node %s won't die",
3056a3f93b72SMarko Zec 					    node->nd_name);
3057bc29160dSMarko Zec 				node->nd_flags |= NGF_REALLY_DIE;
3058bc29160dSMarko Zec 			}
3059bc29160dSMarko Zec 			ng_rmnode(node, NULL, NULL, 0);
3060a3f93b72SMarko Zec 			NG_NODE_UNREF(node);
3061bc29160dSMarko Zec 			last_killed = node;
3062bc29160dSMarko Zec 		}
3063a3f93b72SMarko Zec 	} while (node != NULL);
3064bc29160dSMarko Zec }
3065a3f93b72SMarko Zec VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
3066d0728d71SRobert Watson     vnet_netgraph_uninit, NULL);
3067bc29160dSMarko Zec #endif /* VIMAGE */
3068bc29160dSMarko Zec 
30694cf49a43SJulian Elischer /*
30704cf49a43SJulian Elischer  * Handle loading and unloading for this code.
30714cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
30724cf49a43SJulian Elischer  */
30734cf49a43SJulian Elischer static int
30744cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
30754cf49a43SJulian Elischer {
3076f2fbb838SAlexander Motin 	struct proc *p;
3077f2fbb838SAlexander Motin 	struct thread *td;
3078f2fbb838SAlexander Motin 	int i, error = 0;
30794cf49a43SJulian Elischer 
30804cf49a43SJulian Elischer 	switch (event) {
30814cf49a43SJulian Elischer 	case MOD_LOAD:
30821489164fSGleb Smirnoff 		/* Initialize everything. */
30832c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3084efd8e7c9SDon Lewis 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3085efd8e7c9SDon Lewis 		    MTX_DEF);
3086efd8e7c9SDon Lewis 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3087efd8e7c9SDon Lewis 		    MTX_DEF);
3088cfea3f85SAlexander Motin 		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3089cfea3f85SAlexander Motin 		    MTX_DEF);
3090ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3091ac5dd141SGleb Smirnoff 		    MTX_DEF);
30921489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3093cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3094cfea3f85SAlexander Motin 		    MTX_DEF);
30951489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3096efd8e7c9SDon Lewis 		    MTX_DEF);
30971489164fSGleb Smirnoff #endif
30981489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
30991489164fSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
31001489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
3101*4bd1b557SGleb Smirnoff 		ng_qdzone = uma_zcreate("NetGraph data items",
3102*4bd1b557SGleb Smirnoff 		    sizeof(struct ng_item), NULL, NULL, NULL, NULL,
3103*4bd1b557SGleb Smirnoff 		    UMA_ALIGN_CACHE, 0);
31046aa6d011SAlexander Motin 		uma_zone_set_max(ng_qdzone, maxdata);
3105f2fbb838SAlexander Motin 		/* Autoconfigure number of threads. */
3106f2fbb838SAlexander Motin 		if (numthreads <= 0)
3107f2fbb838SAlexander Motin 			numthreads = mp_ncpus;
3108f2fbb838SAlexander Motin 		/* Create threads. */
3109f2fbb838SAlexander Motin     		p = NULL; /* start with no process */
3110f2fbb838SAlexander Motin 		for (i = 0; i < numthreads; i++) {
3111f2fbb838SAlexander Motin 			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3112f2fbb838SAlexander Motin 			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3113f2fbb838SAlexander Motin 				numthreads = i;
3114f2fbb838SAlexander Motin 				break;
3115f2fbb838SAlexander Motin 			}
3116f2fbb838SAlexander Motin 		}
31174cf49a43SJulian Elischer 		break;
31184cf49a43SJulian Elischer 	case MOD_UNLOAD:
311964efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
31204cf49a43SJulian Elischer 		error = EBUSY;
31214cf49a43SJulian Elischer 		break;
31224cf49a43SJulian Elischer 	default:
31234cf49a43SJulian Elischer 		error = EOPNOTSUPP;
31244cf49a43SJulian Elischer 		break;
31254cf49a43SJulian Elischer 	}
31264cf49a43SJulian Elischer 	return (error);
31274cf49a43SJulian Elischer }
31284cf49a43SJulian Elischer 
31294cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
31304cf49a43SJulian Elischer 	"netgraph",
31314cf49a43SJulian Elischer 	ngb_mod_event,
31324cf49a43SJulian Elischer 	(NULL)
31334cf49a43SJulian Elischer };
3134aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
3135bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3136bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3137bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
31384cf49a43SJulian Elischer 
313930400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
314030400f03SJulian Elischer void
314130400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
314230400f03SJulian Elischer {
314330400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
314430400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
314530400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
314630400f03SJulian Elischer 		hook->lastfile, hook->lastline);
314730400f03SJulian Elischer 	if (line) {
314830400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
3149e5fe87b3SGleb Smirnoff #ifdef KDB
3150e5fe87b3SGleb Smirnoff 		kdb_backtrace();
3151e5fe87b3SGleb Smirnoff #endif
315230400f03SJulian Elischer 	}
315330400f03SJulian Elischer }
315430400f03SJulian Elischer 
315530400f03SJulian Elischer void
315630400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
315730400f03SJulian Elischer {
315830400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
31596b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
316030400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
316130400f03SJulian Elischer 		node->nd_refs, node->nd_name);
316230400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
316330400f03SJulian Elischer 		node->lastfile, node->lastline);
316430400f03SJulian Elischer 	if (line) {
316530400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
3166e5fe87b3SGleb Smirnoff #ifdef KDB
3167e5fe87b3SGleb Smirnoff 		kdb_backtrace();
3168e5fe87b3SGleb Smirnoff #endif
316930400f03SJulian Elischer 	}
317030400f03SJulian Elischer }
317130400f03SJulian Elischer 
3172069154d5SJulian Elischer void
3173069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3174069154d5SJulian Elischer {
3175069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3176069154d5SJulian Elischer 		item->lastfile, item->lastline);
31776b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
31786b795970SJulian Elischer 	case NGQF_DATA:
3179069154d5SJulian Elischer 		printf(" - [data]\n");
31806b795970SJulian Elischer 		break;
31816b795970SJulian Elischer 	case NGQF_MESG:
31826b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
31836b795970SJulian Elischer 		break;
31846b795970SJulian Elischer 	case NGQF_FN:
3185857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3186857304e6SRuslan Ermilov 			_NGI_FN(item),
3187857304e6SRuslan Ermilov 			_NGI_NODE(item),
3188857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3189857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3190857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3191857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3192857304e6SRuslan Ermilov 		break;
3193e088dd4cSAlexander Motin 	case NGQF_FN2:
3194eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3195857304e6SRuslan Ermilov 			_NGI_FN2(item),
31966064e568SGleb Smirnoff 			_NGI_NODE(item),
31976064e568SGleb Smirnoff 			_NGI_HOOK(item),
31986b795970SJulian Elischer 			item->body.fn.fn_arg1,
31996b795970SJulian Elischer 			item->body.fn.fn_arg2,
32006b795970SJulian Elischer 			item->body.fn.fn_arg2);
32016b795970SJulian Elischer 		break;
3202069154d5SJulian Elischer 	}
320330400f03SJulian Elischer 	if (line) {
3204069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
32056064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
320630400f03SJulian Elischer 			printf("node %p ([%x])\n",
32076064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3208069154d5SJulian Elischer 		}
320930400f03SJulian Elischer 	}
321030400f03SJulian Elischer }
321130400f03SJulian Elischer 
321230400f03SJulian Elischer static void
321330400f03SJulian Elischer ng_dumpitems(void)
321430400f03SJulian Elischer {
321530400f03SJulian Elischer 	item_p item;
321630400f03SJulian Elischer 	int i = 1;
321730400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
321830400f03SJulian Elischer 		printf("[%d] ", i++);
321930400f03SJulian Elischer 		dumpitem(item, NULL, 0);
322030400f03SJulian Elischer 	}
322130400f03SJulian Elischer }
322230400f03SJulian Elischer 
322330400f03SJulian Elischer static void
322430400f03SJulian Elischer ng_dumpnodes(void)
322530400f03SJulian Elischer {
322630400f03SJulian Elischer 	node_p node;
322730400f03SJulian Elischer 	int i = 1;
322853f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
322930400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
323030400f03SJulian Elischer 		printf("[%d] ", i++);
323130400f03SJulian Elischer 		dumpnode(node, NULL, 0);
323230400f03SJulian Elischer 	}
323353f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
323430400f03SJulian Elischer }
323530400f03SJulian Elischer 
323630400f03SJulian Elischer static void
323730400f03SJulian Elischer ng_dumphooks(void)
323830400f03SJulian Elischer {
323930400f03SJulian Elischer 	hook_p hook;
324030400f03SJulian Elischer 	int i = 1;
324153f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
324230400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
324330400f03SJulian Elischer 		printf("[%d] ", i++);
324430400f03SJulian Elischer 		dumphook(hook, NULL, 0);
324530400f03SJulian Elischer 	}
324653f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
324730400f03SJulian Elischer }
3248069154d5SJulian Elischer 
3249069154d5SJulian Elischer static int
3250069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3251069154d5SJulian Elischer {
3252069154d5SJulian Elischer 	int error;
3253069154d5SJulian Elischer 	int val;
3254069154d5SJulian Elischer 	int i;
3255069154d5SJulian Elischer 
3256069154d5SJulian Elischer 	val = allocated;
3257069154d5SJulian Elischer 	i = 1;
3258041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
32596b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
32606b795970SJulian Elischer 		return (error);
32616b795970SJulian Elischer 	if (val == 42) {
326230400f03SJulian Elischer 		ng_dumpitems();
326330400f03SJulian Elischer 		ng_dumpnodes();
326430400f03SJulian Elischer 		ng_dumphooks();
3265069154d5SJulian Elischer 	}
32666b795970SJulian Elischer 	return (0);
3267069154d5SJulian Elischer }
3268069154d5SJulian Elischer 
32696b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
32706b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
327130400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3272069154d5SJulian Elischer 
3273069154d5SJulian Elischer 
3274069154d5SJulian Elischer /***********************************************************************
3275069154d5SJulian Elischer * Worklist routines
3276069154d5SJulian Elischer **********************************************************************/
3277069154d5SJulian Elischer /*
3278069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3279f2fbb838SAlexander Motin  * try get an item to process off it. Remove the node from the list.
3280069154d5SJulian Elischer  */
3281069154d5SJulian Elischer static void
3282f2fbb838SAlexander Motin ngthread(void *arg)
3283069154d5SJulian Elischer {
3284069154d5SJulian Elischer 	for (;;) {
3285394cb30aSAlexander Motin 		node_p  node;
3286394cb30aSAlexander Motin 
3287394cb30aSAlexander Motin 		/* Get node from the worklist. */
32882c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
3289f2fbb838SAlexander Motin 		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3290f2fbb838SAlexander Motin 			NG_WORKLIST_SLEEP();
32919852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
32922c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3293bc29160dSMarko Zec 		CURVNET_SET(node->nd_vnet);
32942955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
32952955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3296069154d5SJulian Elischer 		/*
3297069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3298069154d5SJulian Elischer 		 * that the list had on it.
3299069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3300069154d5SJulian Elischer 		 * let you have another item off the queue.
3301069154d5SJulian Elischer 		 * All this time, keep the reference
3302069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3303069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3304069154d5SJulian Elischer 		 */
3305069154d5SJulian Elischer 		for (;;) {
3306394cb30aSAlexander Motin 			item_p item;
3307714fb865SGleb Smirnoff 			int rw;
3308714fb865SGleb Smirnoff 
33092c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
33109852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3311069154d5SJulian Elischer 			if (item == NULL) {
33129852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
33132c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3314069154d5SJulian Elischer 				break; /* go look for another node */
3315069154d5SJulian Elischer 			} else {
33162c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
33175951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3318714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
33195951069aSJulian Elischer 				NG_NODE_UNREF(node);
3320069154d5SJulian Elischer 			}
3321069154d5SJulian Elischer 		}
3322a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3323bc29160dSMarko Zec 		CURVNET_RESTORE();
3324069154d5SJulian Elischer 	}
3325069154d5SJulian Elischer }
3326069154d5SJulian Elischer 
332733338e73SJulian Elischer /*
332833338e73SJulian Elischer  * XXX
332933338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
333033338e73SJulian Elischer  * to be outside the mutex zone
333133338e73SJulian Elischer  */
3332069154d5SJulian Elischer static void
3333394cb30aSAlexander Motin ng_worklist_add(node_p node)
3334069154d5SJulian Elischer {
3335f912c0f7SGleb Smirnoff 
33365bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3337f912c0f7SGleb Smirnoff 
33389852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3339069154d5SJulian Elischer 		/*
3340069154d5SJulian Elischer 		 * If we are not already on the work queue,
3341069154d5SJulian Elischer 		 * then put us on.
3342069154d5SJulian Elischer 		 */
33439852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3344*4bd1b557SGleb Smirnoff 		NG_NODE_REF(node); /* XXX safe in mutex? */
33452c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
33469852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
33472c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
33482955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
33492955ee18SGleb Smirnoff 		    node->nd_ID, node);
3350f2fbb838SAlexander Motin 		NG_WORKLIST_WAKEUP();
3351394cb30aSAlexander Motin 	} else {
33522955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
33532955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3354394cb30aSAlexander Motin 	}
3355069154d5SJulian Elischer }
3356069154d5SJulian Elischer 
3357069154d5SJulian Elischer 
3358069154d5SJulian Elischer /***********************************************************************
3359069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3360069154d5SJulian Elischer ***********************************************************************/
3361069154d5SJulian Elischer 
336230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
336330400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
33644cf49a43SJulian Elischer 	do {								\
33651acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3366069154d5SJulian Elischer 			printf("item already has node");		\
33673de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
33681acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3369069154d5SJulian Elischer 		}							\
33701acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3371069154d5SJulian Elischer 			printf("item already has hook");		\
33723de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
33731acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3374069154d5SJulian Elischer 		}							\
3375069154d5SJulian Elischer 	} while (0)
3376069154d5SJulian Elischer #else
337730400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3378069154d5SJulian Elischer #endif
3379069154d5SJulian Elischer 
3380069154d5SJulian Elischer /*
33818ed370fdSJulian Elischer  * Put mbuf into the item.
3382069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3383069154d5SJulian Elischer  * (or equivalent)
3384069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3385069154d5SJulian Elischer  * remote node might go away in this timescale.
3386069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3387069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
33884be59335SGleb Smirnoff  * here to be able to do this.
3389069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3390069154d5SJulian Elischer  *
3391069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3392069154d5SJulian Elischer  */
3393069154d5SJulian Elischer item_p
339442282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3395069154d5SJulian Elischer {
3396069154d5SJulian Elischer 	item_p item;
3397069154d5SJulian Elischer 
33986aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3399069154d5SJulian Elischer 		NG_FREE_M(m);
3400069154d5SJulian Elischer 		return (NULL);
3401069154d5SJulian Elischer 	}
340230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34036aa6d011SAlexander Motin 	item->el_flags |= NGQF_READER;
3404069154d5SJulian Elischer 	NGI_M(item) = m;
3405069154d5SJulian Elischer 	return (item);
3406069154d5SJulian Elischer }
3407069154d5SJulian Elischer 
3408069154d5SJulian Elischer /*
3409069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3410069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3411069154d5SJulian Elischer  * to work out what some of the fields should be.
3412069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3413069154d5SJulian Elischer  * (or equivalent)
3414069154d5SJulian Elischer  */
3415069154d5SJulian Elischer item_p
341642282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3417069154d5SJulian Elischer {
3418069154d5SJulian Elischer 	item_p item;
3419069154d5SJulian Elischer 
34206aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3421069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3422069154d5SJulian Elischer 		return (NULL);
3423069154d5SJulian Elischer 	}
342430400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34256f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
34266f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
34276aa6d011SAlexander Motin 		item->el_flags |= NGQF_READER;
34286f683eeeSGleb Smirnoff 	else
34296aa6d011SAlexander Motin 		item->el_flags |= NGQF_WRITER;
3430069154d5SJulian Elischer 	/*
3431069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3432069154d5SJulian Elischer 	 */
3433069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3434facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3435069154d5SJulian Elischer 	return (item);
3436069154d5SJulian Elischer }
3437069154d5SJulian Elischer 
3438069154d5SJulian Elischer 
3439069154d5SJulian Elischer 
34401acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
34416b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
34426b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3443069154d5SJulian Elischer 			if (retaddr) {					\
3444069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
34454cf49a43SJulian Elischer 			} else {					\
3446069154d5SJulian Elischer 				/*					\
3447069154d5SJulian Elischer 				 * The old return address should be ok.	\
3448069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3449069154d5SJulian Elischer 				 * here.				\
3450069154d5SJulian Elischer 				 */					\
3451069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3452069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3453069154d5SJulian Elischer 						= ng_node2ID(here);	\
3454069154d5SJulian Elischer 				}					\
3455069154d5SJulian Elischer 			}						\
34564cf49a43SJulian Elischer 		}							\
34574cf49a43SJulian Elischer 	} while (0)
34584cf49a43SJulian Elischer 
34594cf49a43SJulian Elischer int
3460069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
34614cf49a43SJulian Elischer {
34621acb27c6SJulian Elischer 	hook_p peer;
34631acb27c6SJulian Elischer 	node_p peernode;
346430400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3465069154d5SJulian Elischer 	/*
3466069154d5SJulian Elischer 	 * Quick sanity check..
346730400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
346830400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
346930400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3470069154d5SJulian Elischer 	 */
3471a7da736aSGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
3472*4bd1b557SGleb Smirnoff 	if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
3473a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3474a04e9846SAlexander Motin 	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3475069154d5SJulian Elischer 		NG_FREE_ITEM(item);
34766b795970SJulian Elischer 		TRAP_ERROR();
3477a7da736aSGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
3478e08d3e3cSJulian Elischer 		return (ENETDOWN);
34794cf49a43SJulian Elischer 	}
34804cf49a43SJulian Elischer 
34814cf49a43SJulian Elischer 	/*
3482069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
34834cf49a43SJulian Elischer 	 */
34841acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
34851acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
3486a04e9846SAlexander Motin 	NGI_SET_HOOK(item, peer);
34871acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
34888b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3489a7da736aSGleb Smirnoff 
3490a7da736aSGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
3491a7da736aSGleb Smirnoff 
3492069154d5SJulian Elischer 	return (0);
3493069154d5SJulian Elischer }
3494069154d5SJulian Elischer 
34954cf49a43SJulian Elischer int
3496707d2058SMax Khon ng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
34974cf49a43SJulian Elischer {
34984cf49a43SJulian Elischer 	node_p	dest = NULL;
3499069154d5SJulian Elischer 	hook_p	hook = NULL;
35004cf49a43SJulian Elischer 	int	error;
3501069154d5SJulian Elischer 
350230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3503069154d5SJulian Elischer 	/*
3504069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3505069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3506069154d5SJulian Elischer 	 */
3507069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3508069154d5SJulian Elischer 	if (error) {
3509069154d5SJulian Elischer 		NG_FREE_ITEM(item);
351030400f03SJulian Elischer 		return (error);
3511069154d5SJulian Elischer 	}
35121acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
3513a7da736aSGleb Smirnoff 	if (hook)
35141acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
3515a7da736aSGleb Smirnoff 
35161acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3517069154d5SJulian Elischer 	return (0);
3518069154d5SJulian Elischer }
3519069154d5SJulian Elischer 
3520069154d5SJulian Elischer int
3521069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3522069154d5SJulian Elischer {
3523069154d5SJulian Elischer 	node_p dest;
3524069154d5SJulian Elischer 
352530400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3526069154d5SJulian Elischer 	/*
3527069154d5SJulian Elischer 	 * Find the target node.
3528069154d5SJulian Elischer 	 */
3529069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3530069154d5SJulian Elischer 	if (dest == NULL) {
3531069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35326b795970SJulian Elischer 		TRAP_ERROR();
3533069154d5SJulian Elischer 		return(EINVAL);
3534069154d5SJulian Elischer 	}
3535069154d5SJulian Elischer 	/* Fill out the contents */
35361acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
35371acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
35381acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3539069154d5SJulian Elischer 	return (0);
3540069154d5SJulian Elischer }
3541069154d5SJulian Elischer 
3542069154d5SJulian Elischer /*
3543069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3544069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3545069154d5SJulian Elischer  * Useful for removing that hook :-)
3546069154d5SJulian Elischer  */
3547069154d5SJulian Elischer item_p
3548069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3549069154d5SJulian Elischer {
3550069154d5SJulian Elischer 	item_p item;
35514cf49a43SJulian Elischer 
3552859a4d16SJulian Elischer 	/*
3553859a4d16SJulian Elischer 	 * Find the target node.
3554859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3555859a4d16SJulian Elischer 	 * to the address.
3556859a4d16SJulian Elischer 	 */
35576aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3558069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3559069154d5SJulian Elischer 		return (NULL);
35604cf49a43SJulian Elischer 	}
35614cf49a43SJulian Elischer 
35624cf49a43SJulian Elischer 	/* Fill out the contents */
35636aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
356430400f03SJulian Elischer 	NG_NODE_REF(here);
35651acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
35661acb27c6SJulian Elischer 	if (hook) {
356730400f03SJulian Elischer 		NG_HOOK_REF(hook);
35681acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35691acb27c6SJulian Elischer 	}
3570069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3571069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3572069154d5SJulian Elischer 	return (item);
35734cf49a43SJulian Elischer }
35744cf49a43SJulian Elischer 
3575e088dd4cSAlexander Motin /*
3576e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3577e088dd4cSAlexander Motin  */
3578e088dd4cSAlexander Motin 
357942282202SGleb Smirnoff int
3580b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3581b332b91fSGleb Smirnoff {
3582b332b91fSGleb Smirnoff 
3583b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3584b332b91fSGleb Smirnoff }
3585b332b91fSGleb Smirnoff 
3586b332b91fSGleb Smirnoff int
3587aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
358842282202SGleb Smirnoff 	int flags)
35896b795970SJulian Elischer {
35906b795970SJulian Elischer 	item_p item;
35916b795970SJulian Elischer 
35926aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
35936b795970SJulian Elischer 		return (ENOMEM);
35946b795970SJulian Elischer 	}
35956aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3596a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
35971acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
35981acb27c6SJulian Elischer 	if (hook) {
35996b795970SJulian Elischer 		NG_HOOK_REF(hook);
36001acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36016b795970SJulian Elischer 	}
36026b795970SJulian Elischer 	NGI_FN(item) = fn;
36036b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
36046b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
360542282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
36066b795970SJulian Elischer }
36076b795970SJulian Elischer 
36084cf49a43SJulian Elischer /*
3609b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3610b332b91fSGleb Smirnoff  *
3611b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3612b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3613b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3614b332b91fSGleb Smirnoff  * be used.
3615e088dd4cSAlexander Motin  */
3616e088dd4cSAlexander Motin int
3617b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3618b332b91fSGleb Smirnoff 	int arg2, int flags)
3619e088dd4cSAlexander Motin {
3620e088dd4cSAlexander Motin 	item_p item;
3621e088dd4cSAlexander Motin 
3622b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3623b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3624e088dd4cSAlexander Motin 
3625e088dd4cSAlexander Motin 	/*
3626b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3627b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3628e088dd4cSAlexander Motin 	 */
3629b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
36306aa6d011SAlexander Motin 		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3631e088dd4cSAlexander Motin 			return (ENOMEM);
36326aa6d011SAlexander Motin 		if (pitem != NULL)
36336aa6d011SAlexander Motin 			item->apply = pitem->apply;
3634ed75521fSAlexander Motin 	} else {
36356aa6d011SAlexander Motin 		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
36366aa6d011SAlexander Motin 			return (ENOMEM);
3637ed75521fSAlexander Motin 	}
3638b332b91fSGleb Smirnoff 
36396aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3640e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3641e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3642e088dd4cSAlexander Motin 	if (hook) {
3643e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3644e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3645e088dd4cSAlexander Motin 	}
3646e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3647e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3648e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3649e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3650e088dd4cSAlexander Motin }
3651e088dd4cSAlexander Motin 
3652e088dd4cSAlexander Motin /*
3653d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3654d2ca21a9SJulian Elischer  */
3655d2ca21a9SJulian Elischer static void
36561fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3657d2ca21a9SJulian Elischer {
3658d2ca21a9SJulian Elischer 	item_p item = arg;
3659d2ca21a9SJulian Elischer 
3660bc29160dSMarko Zec 	CURVNET_SET(NGI_NODE(item)->nd_vnet);
3661d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3662bc29160dSMarko Zec 	CURVNET_RESTORE();
3663d2ca21a9SJulian Elischer }
3664d2ca21a9SJulian Elischer 
3665d2ca21a9SJulian Elischer 
366630bef41bSGleb Smirnoff int
3667f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3668d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3669d2ca21a9SJulian Elischer {
36701bf8e0faSGleb Smirnoff 	item_p item, oitem;
3671d2ca21a9SJulian Elischer 
36726aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
367330bef41bSGleb Smirnoff 		return (ENOMEM);
367430bef41bSGleb Smirnoff 
36756aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3676d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3677d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3678d2ca21a9SJulian Elischer 	if (hook) {
3679d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3680d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3681d2ca21a9SJulian Elischer 	}
3682d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3683d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3684d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
36851bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
36861bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
36871bf8e0faSGleb Smirnoff 	    oitem != NULL)
36881bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
368930bef41bSGleb Smirnoff 	return (0);
3690d2ca21a9SJulian Elischer }
3691d2ca21a9SJulian Elischer 
3692d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3693d2ca21a9SJulian Elischer int
3694f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3695d2ca21a9SJulian Elischer {
3696d2ca21a9SJulian Elischer 	item_p item;
369730bef41bSGleb Smirnoff 	int rval;
3698d2ca21a9SJulian Elischer 
369903b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
370003b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
370103b25f5dSGleb Smirnoff 
37023eadb26dSGleb Smirnoff 	rval = callout_stop(c);
370330bef41bSGleb Smirnoff 	item = c->c_arg;
370430bef41bSGleb Smirnoff 	/* Do an extra check */
37051fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
370630bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3707d2ca21a9SJulian Elischer 		/*
3708d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3709d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3710d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3711d2ca21a9SJulian Elischer 		 */
3712d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3713d2ca21a9SJulian Elischer 	}
37141bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
371530bef41bSGleb Smirnoff 
371630bef41bSGleb Smirnoff 	return (rval);
3717d2ca21a9SJulian Elischer }
3718d2ca21a9SJulian Elischer 
3719d2ca21a9SJulian Elischer /*
3720069154d5SJulian Elischer  * Set the address, if none given, give the node here.
37214cf49a43SJulian Elischer  */
3722069154d5SJulian Elischer void
3723069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
37244cf49a43SJulian Elischer {
3725069154d5SJulian Elischer 	if (retaddr) {
3726069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3727069154d5SJulian Elischer 	} else {
3728069154d5SJulian Elischer 		/*
3729069154d5SJulian Elischer 		 * The old return address should be ok.
3730069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3731069154d5SJulian Elischer 		 */
3732069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3733069154d5SJulian Elischer 	}
3734069154d5SJulian Elischer }
3735069154d5SJulian Elischer 
3736069154d5SJulian Elischer #define TESTING
3737069154d5SJulian Elischer #ifdef TESTING
3738069154d5SJulian Elischer /* just test all the macros */
3739069154d5SJulian Elischer void
3740069154d5SJulian Elischer ng_macro_test(item_p item);
3741069154d5SJulian Elischer void
3742069154d5SJulian Elischer ng_macro_test(item_p item)
3743069154d5SJulian Elischer {
3744069154d5SJulian Elischer 	node_p node = NULL;
3745069154d5SJulian Elischer 	hook_p hook = NULL;
37464cf49a43SJulian Elischer 	struct mbuf *m;
37474cf49a43SJulian Elischer 	struct ng_mesg *msg;
3748069154d5SJulian Elischer 	ng_ID_t retaddr;
3749069154d5SJulian Elischer 	int	error;
37504cf49a43SJulian Elischer 
3751069154d5SJulian Elischer 	NGI_GET_M(item, m);
3752069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3753069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
37548ed370fdSJulian Elischer 	NG_SEND_DATA(error, hook, m, NULL);
3755069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3756069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
375730400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3758069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3759069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3760069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3761069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
37624cf49a43SJulian Elischer }
3763069154d5SJulian Elischer #endif /* TESTING */
3764