xref: /freebsd/sys/netgraph/ng_base.c (revision 5633ca7116dd4025f6f085bf849c2e18abf9d593)
14cf49a43SJulian Elischer /*
24cf49a43SJulian Elischer  * ng_base.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
64cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
74cf49a43SJulian Elischer  * All rights reserved.
84cf49a43SJulian Elischer  *
94cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
104cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
114cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
124cf49a43SJulian Elischer  * provided, however, that:
134cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
144cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
154cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
164cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
174cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
184cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
194cf49a43SJulian Elischer  *
204cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
214cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
224cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
234cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
244cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
254cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
264cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
274cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
284cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
294cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
304cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
314cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
324cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
334cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
344cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
354cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
364cf49a43SJulian Elischer  * OF SUCH DAMAGE.
374cf49a43SJulian Elischer  *
38cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
39cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
404cf49a43SJulian Elischer  *
414cf49a43SJulian Elischer  * $FreeBSD$
424cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
434cf49a43SJulian Elischer  */
444cf49a43SJulian Elischer 
454cf49a43SJulian Elischer /*
464cf49a43SJulian Elischer  * This file implements the base netgraph code.
474cf49a43SJulian Elischer  */
484cf49a43SJulian Elischer 
494cf49a43SJulian Elischer #include <sys/param.h>
508e853a2cSGleb Smirnoff #include <sys/systm.h>
5177a58296SGleb Smirnoff #include <sys/ctype.h>
524cf49a43SJulian Elischer #include <sys/errno.h>
53f33ca0c9SMarcel Moolenaar #include <sys/kdb.h>
544cf49a43SJulian Elischer #include <sys/kernel.h>
553b33fbe7SGleb Smirnoff #include <sys/ktr.h>
56104a9b7eSAlexander Kabaev #include <sys/limits.h>
574cf49a43SJulian Elischer #include <sys/malloc.h>
584cf49a43SJulian Elischer #include <sys/mbuf.h>
5977a58296SGleb Smirnoff #include <sys/queue.h>
60bfa7e882SJulian Elischer #include <sys/sysctl.h>
6177a58296SGleb Smirnoff #include <sys/syslog.h>
62e088dd4cSAlexander Motin #include <sys/refcount.h>
6381a253a4SAlexander Motin #include <sys/proc.h>
64f2fbb838SAlexander Motin #include <sys/unistd.h>
65f2fbb838SAlexander Motin #include <sys/kthread.h>
66f2fbb838SAlexander Motin #include <sys/smp.h>
67394cb30aSAlexander Motin #include <machine/cpu.h>
684cf49a43SJulian Elischer 
694cf49a43SJulian Elischer #include <net/netisr.h>
70eddfbb76SRobert Watson #include <net/vnet.h>
714cf49a43SJulian Elischer 
724cf49a43SJulian Elischer #include <netgraph/ng_message.h>
734cf49a43SJulian Elischer #include <netgraph/netgraph.h>
74f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
754cf49a43SJulian Elischer 
769d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION);
7799ff8176SPeter Wemm 
78ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */
79ac5dd141SGleb Smirnoff static struct mtx	ng_topo_mtx;
80ac5dd141SGleb Smirnoff 
8130400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
82cfea3f85SAlexander Motin static struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
831489164fSGleb Smirnoff static struct mtx	ngq_mtx;	/* protects the queue item list */
8430400f03SJulian Elischer 
8530400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
8630400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8730400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
8830400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8930400f03SJulian Elischer 
9030400f03SJulian Elischer static void ng_dumpitems(void);
9130400f03SJulian Elischer static void ng_dumpnodes(void);
9230400f03SJulian Elischer static void ng_dumphooks(void);
9330400f03SJulian Elischer 
9430400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
95954c4772SJulian Elischer /*
96954c4772SJulian Elischer  * DEAD versions of the structures.
97954c4772SJulian Elischer  * In order to avoid races, it is sometimes neccesary to point
98954c4772SJulian Elischer  * at SOMETHING even though theoretically, the current entity is
99954c4772SJulian Elischer  * INVALID. Use these to avoid these races.
100954c4772SJulian Elischer  */
101954c4772SJulian Elischer struct ng_type ng_deadtype = {
102954c4772SJulian Elischer 	NG_ABI_VERSION,
103954c4772SJulian Elischer 	"dead",
104954c4772SJulian Elischer 	NULL,	/* modevent */
105954c4772SJulian Elischer 	NULL,	/* constructor */
106954c4772SJulian Elischer 	NULL,	/* rcvmsg */
107954c4772SJulian Elischer 	NULL,	/* shutdown */
108954c4772SJulian Elischer 	NULL,	/* newhook */
109954c4772SJulian Elischer 	NULL,	/* findhook */
110954c4772SJulian Elischer 	NULL,	/* connect */
111954c4772SJulian Elischer 	NULL,	/* rcvdata */
112954c4772SJulian Elischer 	NULL,	/* disconnect */
113954c4772SJulian Elischer 	NULL, 	/* cmdlist */
114954c4772SJulian Elischer };
11530400f03SJulian Elischer 
116954c4772SJulian Elischer struct ng_node ng_deadnode = {
117954c4772SJulian Elischer 	"dead",
118954c4772SJulian Elischer 	&ng_deadtype,
119be4252b3SJulian Elischer 	NGF_INVALID,
120954c4772SJulian Elischer 	0,	/* numhooks */
121954c4772SJulian Elischer 	NULL,	/* private */
122954c4772SJulian Elischer 	0,	/* ID */
12313e403fdSAntoine Brodin 	LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
124954c4772SJulian Elischer 	{},	/* all_nodes list entry */
125954c4772SJulian Elischer 	{},	/* id hashtable list entry */
126954c4772SJulian Elischer 	{	0,
1279852972bSAlexander Motin 		0,
128954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
1299852972bSAlexander Motin 		{}, /* workqueue entry */
1309852972bSAlexander Motin 		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
131954c4772SJulian Elischer 	},
1329852972bSAlexander Motin 	1,	/* refs */
133a40b7874SMarko Zec 	NULL,	/* vnet */
134954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
135954c4772SJulian Elischer 	ND_MAGIC,
136954c4772SJulian Elischer 	__FILE__,
137954c4772SJulian Elischer 	__LINE__,
138954c4772SJulian Elischer 	{NULL}
139954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
140954c4772SJulian Elischer };
141954c4772SJulian Elischer 
142954c4772SJulian Elischer struct ng_hook ng_deadhook = {
143954c4772SJulian Elischer 	"dead",
144954c4772SJulian Elischer 	NULL,		/* private */
145954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
146e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
147954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
148954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
149954c4772SJulian Elischer 	{},		/* hooks list */
150c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
151c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1529852972bSAlexander Motin 	1,		/* refs always >= 1 */
153954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
154954c4772SJulian Elischer 	HK_MAGIC,
155954c4772SJulian Elischer 	__FILE__,
156954c4772SJulian Elischer 	__LINE__,
157954c4772SJulian Elischer 	{NULL}
158954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
159954c4772SJulian Elischer };
160954c4772SJulian Elischer 
161954c4772SJulian Elischer /*
162954c4772SJulian Elischer  * END DEAD STRUCTURES
163954c4772SJulian Elischer  */
164069154d5SJulian Elischer /* List nodes with unallocated work */
1659852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
166b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1674cf49a43SJulian Elischer 
1684cf49a43SJulian Elischer /* List of installed types */
169069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
170069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
1714cf49a43SJulian Elischer 
172069154d5SJulian Elischer /* Hash related definitions */
1730f150d04SJulian Elischer /* XXX Don't need to initialise them because it's a LIST */
1743e288e62SDimitry Andric static VNET_DEFINE(LIST_HEAD(, ng_node), ng_ID_hash[NG_ID_HASH_SIZE]);
1751e77c105SRobert Watson #define	V_ng_ID_hash			VNET(ng_ID_hash)
176eddfbb76SRobert Watson 
177069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
1780f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
1790f150d04SJulian Elischer #define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
1800f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
1810f150d04SJulian Elischer 	do { 								\
18253f9c5e9SRobert Watson 		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
183603724d3SBjoern A. Zeeb 		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
1840f150d04SJulian Elischer 						nd_idnodes) {		\
1850f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
1860f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
1870f150d04SJulian Elischer 				break;					\
1880f150d04SJulian Elischer 			}						\
1890f150d04SJulian Elischer 		}							\
1900f150d04SJulian Elischer 	} while (0)
191069154d5SJulian Elischer 
1923e288e62SDimitry Andric static VNET_DEFINE(LIST_HEAD(, ng_node), ng_name_hash[NG_NAME_HASH_SIZE]);
1931e77c105SRobert Watson #define	V_ng_name_hash			VNET(ng_name_hash)
194eddfbb76SRobert Watson 
195cfea3f85SAlexander Motin static struct mtx	ng_namehash_mtx;
196cfea3f85SAlexander Motin #define NG_NAMEHASH(NAME, HASH)				\
197cfea3f85SAlexander Motin 	do {						\
198cfea3f85SAlexander Motin 		u_char	h = 0;				\
199cfea3f85SAlexander Motin 		const u_char	*c;			\
200cfea3f85SAlexander Motin 		for (c = (const u_char*)(NAME); *c; c++)\
201cfea3f85SAlexander Motin 			h += *c;			\
202cfea3f85SAlexander Motin 		(HASH) = h % (NG_NAME_HASH_SIZE);	\
203cfea3f85SAlexander Motin 	} while (0)
204cfea3f85SAlexander Motin 
205dc90cad9SJulian Elischer 
2064cf49a43SJulian Elischer /* Internal functions */
2074cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
208069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
209dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2104cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
211394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
212f2fbb838SAlexander Motin static void	ngthread(void *);
21327757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2149852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
215069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
216e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
217e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
218e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
219e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
2206b795970SJulian Elischer static int	ng_mkpeer(node_p node, const char *name,
2216b795970SJulian Elischer 						const char *name2, char *type);
222069154d5SJulian Elischer 
2234c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */
224069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
225069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
226069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
227069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
228069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
2291acb27c6SJulian Elischer void	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
23030400f03SJulian Elischer void	ng_unname(node_p node);
231069154d5SJulian Elischer 
2324cf49a43SJulian Elischer 
2334cf49a43SJulian Elischer /* Our own netgraph malloc type */
2344cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
235069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
236069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
237069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
238069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
239069154d5SJulian Elischer 
240069154d5SJulian Elischer /* Should not be visible outside this file */
24130400f03SJulian Elischer 
24230400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
2431ede983cSDag-Erling Smørgrav 	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
24430400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
2451ede983cSDag-Erling Smørgrav 	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
24630400f03SJulian Elischer 
2472c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2484abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2492c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2504abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2512c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2524abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2532c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2544abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2552c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2564abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2572c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2584abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
259f2fbb838SAlexander Motin #define	NG_WORKLIST_SLEEP()			\
260f2fbb838SAlexander Motin 	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
261f2fbb838SAlexander Motin #define	NG_WORKLIST_WAKEUP()			\
262f2fbb838SAlexander Motin 	wakeup_one(&ng_worklist)
2632c8dda8dSWojciech A. Koszek 
26430400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
26530400f03SJulian Elischer /*
26630400f03SJulian Elischer  * In debug mode:
26730400f03SJulian Elischer  * In an attempt to help track reference count screwups
26830400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
26930400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
27030400f03SJulian Elischer  * after they have been freed.
27130400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
27230400f03SJulian Elischer  */
27330400f03SJulian Elischer static __inline hook_p
27430400f03SJulian Elischer ng_alloc_hook(void)
27530400f03SJulian Elischer {
27630400f03SJulian Elischer 	hook_p hook;
27730400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2789ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
27930400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
28030400f03SJulian Elischer 	if (hook) {
28130400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
28230400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
28330400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
28430400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
2859ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
28630400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
28730400f03SJulian Elischer 	} else {
2889ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
28930400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
29030400f03SJulian Elischer 		if (hook) {
29130400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
2929ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
29330400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
2949ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
29530400f03SJulian Elischer 		}
29630400f03SJulian Elischer 	}
29730400f03SJulian Elischer 	return (hook);
29830400f03SJulian Elischer }
29930400f03SJulian Elischer 
30030400f03SJulian Elischer static __inline node_p
30130400f03SJulian Elischer ng_alloc_node(void)
30230400f03SJulian Elischer {
30330400f03SJulian Elischer 	node_p node;
30430400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
3059ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
30630400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
30730400f03SJulian Elischer 	if (node) {
30830400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
30930400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
31030400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
31130400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3129ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
31330400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
31430400f03SJulian Elischer 	} else {
3159ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
31630400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
31730400f03SJulian Elischer 		if (node) {
31830400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3199ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
32030400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3219ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
32230400f03SJulian Elischer 		}
32330400f03SJulian Elischer 	}
32430400f03SJulian Elischer 	return (node);
32530400f03SJulian Elischer }
32630400f03SJulian Elischer 
32730400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
32830400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
32930400f03SJulian Elischer 
33030400f03SJulian Elischer 
33130400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
33230400f03SJulian Elischer 	do {								\
3339ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
33430400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
33530400f03SJulian Elischer 		hook->hk_magic = 0;					\
3369ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
33730400f03SJulian Elischer 	} while (0)
33830400f03SJulian Elischer 
33930400f03SJulian Elischer #define NG_FREE_NODE(node)						\
34030400f03SJulian Elischer 	do {								\
3419ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
34230400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
34330400f03SJulian Elischer 		node->nd_magic = 0;					\
3449ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
34530400f03SJulian Elischer 	} while (0)
34630400f03SJulian Elischer 
34730400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
34830400f03SJulian Elischer 
34930400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
35030400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
35130400f03SJulian Elischer 
3521ede983cSDag-Erling Smørgrav #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
3531ede983cSDag-Erling Smørgrav #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
35430400f03SJulian Elischer 
35530400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
35630400f03SJulian Elischer 
357f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3584cf49a43SJulian Elischer #ifndef TRAP_ERROR
3596b795970SJulian Elischer #define TRAP_ERROR()
3604cf49a43SJulian Elischer #endif
3614cf49a43SJulian Elischer 
3623e288e62SDimitry Andric static VNET_DEFINE(ng_ID_t, nextID) = 1;
3631e77c105SRobert Watson #define	V_nextID			VNET(nextID)
364dc90cad9SJulian Elischer 
365b2da83c2SArchie Cobbs #ifdef INVARIANTS
366b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
367b2da83c2SArchie Cobbs 		struct mbuf *n;						\
368b2da83c2SArchie Cobbs 		int total;						\
369b2da83c2SArchie Cobbs 									\
370fe584538SDag-Erling Smørgrav 		M_ASSERTPKTHDR(m);					\
371b32cfb32SGleb Smirnoff 		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
372b2da83c2SArchie Cobbs 			total += n->m_len;				\
373b32cfb32SGleb Smirnoff 			if (n->m_nextpkt != NULL)			\
374b32cfb32SGleb Smirnoff 				panic("%s: m_nextpkt", __func__);	\
375b32cfb32SGleb Smirnoff 		}							\
376ba5b359aSGleb Smirnoff 									\
377b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
378b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
3796e551fb6SDavid E. O'Brien 			    __func__, (m)->m_pkthdr.len, total);	\
380b2da83c2SArchie Cobbs 		}							\
381b2da83c2SArchie Cobbs 	} while (0)
382b2da83c2SArchie Cobbs #else
383b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
384b2da83c2SArchie Cobbs #endif
385b2da83c2SArchie Cobbs 
386e088dd4cSAlexander Motin #define ERROUT(x)	do { error = (x); goto done; } while (0)
387dc90cad9SJulian Elischer 
3884cf49a43SJulian Elischer /************************************************************************
389f8307e12SArchie Cobbs 	Parse type definitions for generic messages
390f8307e12SArchie Cobbs ************************************************************************/
391f8307e12SArchie Cobbs 
392f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
393f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
394f0184ff8SArchie Cobbs static const struct ng_parse_struct_field				\
395f0184ff8SArchie Cobbs 	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
396f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
397f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
398f0184ff8SArchie Cobbs 	&ng_ ## lo ## _type_fields					\
399f8307e12SArchie Cobbs }
400f8307e12SArchie Cobbs 
401f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
402f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
403f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
404f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
405f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
406f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
407f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
408f8307e12SArchie Cobbs 
409f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
410d7d97eb0SJeroen Ruigrok van der Werven    value immediately preceding the array -- as with struct namelist
411f8307e12SArchie Cobbs    and struct typelist. */
412f8307e12SArchie Cobbs static int
413f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
414f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
415f8307e12SArchie Cobbs {
416f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
417f8307e12SArchie Cobbs }
418f8307e12SArchie Cobbs 
419f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
420f8307e12SArchie Cobbs static int
421f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
422f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
423f8307e12SArchie Cobbs {
424f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
425f8307e12SArchie Cobbs 
426f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
427f8307e12SArchie Cobbs }
428f8307e12SArchie Cobbs 
429f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
430f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
431f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
432f8307e12SArchie Cobbs 	&ng_generic_list_getLength
433f8307e12SArchie Cobbs };
434f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
435f8307e12SArchie Cobbs 	&ng_parse_array_type,
436f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
437f8307e12SArchie Cobbs };
438f8307e12SArchie Cobbs 
439f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
440f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
441f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
442f8307e12SArchie Cobbs 	&ng_generic_list_getLength
443f8307e12SArchie Cobbs };
444f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
445f8307e12SArchie Cobbs 	&ng_parse_array_type,
446f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
447f8307e12SArchie Cobbs };
448f8307e12SArchie Cobbs 
449f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
450f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
451f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
452f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
453f8307e12SArchie Cobbs };
454f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
455f8307e12SArchie Cobbs 	&ng_parse_array_type,
456f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
457f8307e12SArchie Cobbs };
458f8307e12SArchie Cobbs 
459f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
460f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
461f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
462f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
463f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
464f8307e12SArchie Cobbs 
465f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
466f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
467f8307e12SArchie Cobbs 	{
468f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
469f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
470f8307e12SArchie Cobbs 	  "shutdown",
471f8307e12SArchie Cobbs 	  NULL,
472f8307e12SArchie Cobbs 	  NULL
473f8307e12SArchie Cobbs 	},
474f8307e12SArchie Cobbs 	{
475f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
476f8307e12SArchie Cobbs 	  NGM_MKPEER,
477f8307e12SArchie Cobbs 	  "mkpeer",
478f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
479f8307e12SArchie Cobbs 	  NULL
480f8307e12SArchie Cobbs 	},
481f8307e12SArchie Cobbs 	{
482f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
483f8307e12SArchie Cobbs 	  NGM_CONNECT,
484f8307e12SArchie Cobbs 	  "connect",
485f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
486f8307e12SArchie Cobbs 	  NULL
487f8307e12SArchie Cobbs 	},
488f8307e12SArchie Cobbs 	{
489f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
490f8307e12SArchie Cobbs 	  NGM_NAME,
491f8307e12SArchie Cobbs 	  "name",
492f8307e12SArchie Cobbs 	  &ng_generic_name_type,
493f8307e12SArchie Cobbs 	  NULL
494f8307e12SArchie Cobbs 	},
495f8307e12SArchie Cobbs 	{
496f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
497f8307e12SArchie Cobbs 	  NGM_RMHOOK,
498f8307e12SArchie Cobbs 	  "rmhook",
499f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
500f8307e12SArchie Cobbs 	  NULL
501f8307e12SArchie Cobbs 	},
502f8307e12SArchie Cobbs 	{
503f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
504f8307e12SArchie Cobbs 	  NGM_NODEINFO,
505f8307e12SArchie Cobbs 	  "nodeinfo",
506f8307e12SArchie Cobbs 	  NULL,
507f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
508f8307e12SArchie Cobbs 	},
509f8307e12SArchie Cobbs 	{
510f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
511f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
512f8307e12SArchie Cobbs 	  "listhooks",
513f8307e12SArchie Cobbs 	  NULL,
514f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
515f8307e12SArchie Cobbs 	},
516f8307e12SArchie Cobbs 	{
517f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
518f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
519f8307e12SArchie Cobbs 	  "listnames",
520f8307e12SArchie Cobbs 	  NULL,
521f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
522f8307e12SArchie Cobbs 	},
523f8307e12SArchie Cobbs 	{
524f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
525f8307e12SArchie Cobbs 	  NGM_LISTNODES,
526f8307e12SArchie Cobbs 	  "listnodes",
527f8307e12SArchie Cobbs 	  NULL,
528f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
529f8307e12SArchie Cobbs 	},
530f8307e12SArchie Cobbs 	{
531f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
532f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
533f8307e12SArchie Cobbs 	  "listtypes",
534f8307e12SArchie Cobbs 	  NULL,
535f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
536f8307e12SArchie Cobbs 	},
537f8307e12SArchie Cobbs 	{
538f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
5397095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
5407095e097SPoul-Henning Kamp 	  "textconfig",
5417095e097SPoul-Henning Kamp 	  NULL,
5427095e097SPoul-Henning Kamp 	  &ng_parse_string_type
5437095e097SPoul-Henning Kamp 	},
5447095e097SPoul-Henning Kamp 	{
5457095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
546f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
547f8307e12SArchie Cobbs 	  "textstatus",
548f8307e12SArchie Cobbs 	  NULL,
549f8307e12SArchie Cobbs 	  &ng_parse_string_type
550f8307e12SArchie Cobbs 	},
551f8307e12SArchie Cobbs 	{
552f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
553f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
554f8307e12SArchie Cobbs 	  "ascii2binary",
555f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
556f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
557f8307e12SArchie Cobbs 	},
558f8307e12SArchie Cobbs 	{
559f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
560f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
561f8307e12SArchie Cobbs 	  "binary2ascii",
562f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
563f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
564f8307e12SArchie Cobbs 	},
565f8307e12SArchie Cobbs 	{ 0 }
566f8307e12SArchie Cobbs };
567f8307e12SArchie Cobbs 
568f8307e12SArchie Cobbs /************************************************************************
5694cf49a43SJulian Elischer 			Node routines
5704cf49a43SJulian Elischer ************************************************************************/
5714cf49a43SJulian Elischer 
5724cf49a43SJulian Elischer /*
5734cf49a43SJulian Elischer  * Instantiate a node of the requested type
5744cf49a43SJulian Elischer  */
5754cf49a43SJulian Elischer int
5764cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
5774cf49a43SJulian Elischer {
5784cf49a43SJulian Elischer 	struct ng_type *type;
579069154d5SJulian Elischer 	int	error;
5804cf49a43SJulian Elischer 
5814cf49a43SJulian Elischer 	/* Check that the type makes sense */
5824cf49a43SJulian Elischer 	if (typename == NULL) {
5836b795970SJulian Elischer 		TRAP_ERROR();
5844cf49a43SJulian Elischer 		return (EINVAL);
5854cf49a43SJulian Elischer 	}
5864cf49a43SJulian Elischer 
5877610f574SGleb Smirnoff 	/* Locate the node type. If we fail we return. Do not try to load
5887610f574SGleb Smirnoff 	 * module.
5897610f574SGleb Smirnoff 	 */
5904cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL)
5914cf49a43SJulian Elischer 		return (ENXIO);
5924cf49a43SJulian Elischer 
593069154d5SJulian Elischer 	/*
594069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
595069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
596069154d5SJulian Elischer 	 */
597069154d5SJulian Elischer 	if (type->constructor != NULL) {
598069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
599*5633ca71SGleb Smirnoff 			if ((error = ((*type->constructor)(*nodepp))) != 0) {
60030400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
601069154d5SJulian Elischer 			}
602069154d5SJulian Elischer 		}
603069154d5SJulian Elischer 	} else {
604069154d5SJulian Elischer 		/*
605069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
60664efc707SRobert Watson 		 * to be made. It must be brought into existence by
607954c4772SJulian Elischer 		 * some external agency. The external agency should
608069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
609069154d5SJulian Elischer 		 * netgraph part initialised.
610069154d5SJulian Elischer 		 */
6116b795970SJulian Elischer 		TRAP_ERROR();
612069154d5SJulian Elischer 		error = EINVAL;
613069154d5SJulian Elischer 	}
614069154d5SJulian Elischer 	return (error);
6154cf49a43SJulian Elischer }
6164cf49a43SJulian Elischer 
6174cf49a43SJulian Elischer /*
618069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
619069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
6204cf49a43SJulian Elischer  * The returned node has a reference count of 1.
6214cf49a43SJulian Elischer  */
6224cf49a43SJulian Elischer int
6234cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
6244cf49a43SJulian Elischer {
6254cf49a43SJulian Elischer 	node_p node;
6264cf49a43SJulian Elischer 
6274cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6284cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6296b795970SJulian Elischer 		TRAP_ERROR();
6304cf49a43SJulian Elischer 		return (EINVAL);
6314cf49a43SJulian Elischer 	}
6324cf49a43SJulian Elischer 
6334cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
63430400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6354cf49a43SJulian Elischer 	if (node == NULL) {
6366b795970SJulian Elischer 		TRAP_ERROR();
6374cf49a43SJulian Elischer 		return (ENOMEM);
6384cf49a43SJulian Elischer 	}
63930400f03SJulian Elischer 	node->nd_type = type;
640bc29160dSMarko Zec #ifdef VIMAGE
641bc29160dSMarko Zec 	node->nd_vnet = curvnet;
642bc29160dSMarko Zec #endif
64330400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6444cf49a43SJulian Elischer 	type->refs++;
6454cf49a43SJulian Elischer 
6462c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6479852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
64830400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6494cf49a43SJulian Elischer 
6504cf49a43SJulian Elischer 	/* Initialize hook list for new node */
65130400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6524cf49a43SJulian Elischer 
653cfea3f85SAlexander Motin 	/* Link us into the name hash. */
654cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
655603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[0], node, nd_nodes);
656cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
657069154d5SJulian Elischer 
658dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
6599ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
66030400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
661069154d5SJulian Elischer 		node_p node2 = NULL;
662ac957cd2SJulian Elischer 		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6630f150d04SJulian Elischer 
66430400f03SJulian Elischer 		/* Is there a problem with the new number? */
6650f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6660f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
66730400f03SJulian Elischer 			break;
668069154d5SJulian Elischer 		}
66930400f03SJulian Elischer 	}
670603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
67130400f03SJulian Elischer 							node, nd_idnodes);
6729ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
673dc90cad9SJulian Elischer 
6744cf49a43SJulian Elischer 	/* Done */
6754cf49a43SJulian Elischer 	*nodepp = node;
6764cf49a43SJulian Elischer 	return (0);
6774cf49a43SJulian Elischer }
6784cf49a43SJulian Elischer 
6794cf49a43SJulian Elischer /*
6804cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
68164efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6824cf49a43SJulian Elischer  * no type-specific method.
6834cf49a43SJulian Elischer  *
68464efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6853e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6863e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
687069154d5SJulian Elischer  *
688069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
68964efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
6903e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
6913e4084c8SJulian Elischer  * in which case we allow the node to survive.
6923e4084c8SJulian Elischer  *
69364efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
6943e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
6953e4084c8SJulian Elischer  * are rebooting.... etc.
6964cf49a43SJulian Elischer  */
6974cf49a43SJulian Elischer void
6981acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
6994cf49a43SJulian Elischer {
7003e4084c8SJulian Elischer 	hook_p hook;
7013e4084c8SJulian Elischer 
7024cf49a43SJulian Elischer 	/* Check if it's already shutting down */
703be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
7044cf49a43SJulian Elischer 		return;
7054cf49a43SJulian Elischer 
7061acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
7071acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
7081acb27c6SJulian Elischer 		return;
7091acb27c6SJulian Elischer 	}
7101acb27c6SJulian Elischer 
7114cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
71230400f03SJulian Elischer 	NG_NODE_REF(node);
7134cf49a43SJulian Elischer 
71430400f03SJulian Elischer 	/*
71530400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
71630400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
717be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
71830400f03SJulian Elischer 	 * creation
71930400f03SJulian Elischer 	 */
720be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7214cf49a43SJulian Elischer 
722991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
723991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
724991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
725991fc65aSJulian Elischer 
7263e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7273e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7283e4084c8SJulian Elischer 		ng_destroy_hook(hook);
72930400f03SJulian Elischer 
730069154d5SJulian Elischer 	/*
731069154d5SJulian Elischer 	 * Drain the input queue forceably.
73230400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
73330400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
73430400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
735b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
736069154d5SJulian Elischer 	 */
7379852972bSAlexander Motin 	ng_flush_input_queue(node);
738069154d5SJulian Elischer 
739069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
74030400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
74130400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
74230400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
74330400f03SJulian Elischer 			/*
74430400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
74530400f03SJulian Elischer 			 * that it doesn't want to die.
74630400f03SJulian Elischer 			 * Presumably it is a persistant node.
7471acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7481acb27c6SJulian Elischer 			 *  e.g. hardware going away,
749be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
75030400f03SJulian Elischer 			 */
751be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7521acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
75330400f03SJulian Elischer 			return;
7544cf49a43SJulian Elischer 		}
7551acb27c6SJulian Elischer 	} else {				/* do the default thing */
7561acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7571acb27c6SJulian Elischer 	}
7584cf49a43SJulian Elischer 
75930400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
76030400f03SJulian Elischer 
76130400f03SJulian Elischer 	/*
76230400f03SJulian Elischer 	 * Remove extra reference, possibly the last
76330400f03SJulian Elischer 	 * Possible other holders of references may include
76430400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
76530400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
76630400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
76730400f03SJulian Elischer 	 */
76830400f03SJulian Elischer 	NG_NODE_UNREF(node);
7694cf49a43SJulian Elischer }
7704cf49a43SJulian Elischer 
7715951069aSJulian Elischer /*
7725951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7735951069aSJulian Elischer  * deadnode always acts as it it were the last.
7745951069aSJulian Elischer  */
7755951069aSJulian Elischer int
77630400f03SJulian Elischer ng_unref_node(node_p node)
7774cf49a43SJulian Elischer {
77830400f03SJulian Elischer 	int v;
7796b795970SJulian Elischer 
7806b795970SJulian Elischer 	if (node == &ng_deadnode) {
7815951069aSJulian Elischer 		return (0);
7826b795970SJulian Elischer 	}
7836b795970SJulian Elischer 
784018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&node->nd_refs, -1);
785e8a49db2SJulian Elischer 
786018fe3d1SAlexander Motin 	if (v == 1) { /* we were the last */
787069154d5SJulian Elischer 
788cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
78930400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
79030400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
791cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
792069154d5SJulian Elischer 
7939ed346baSBosko Milekic 		mtx_lock(&ng_idhash_mtx);
79430400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
7959ed346baSBosko Milekic 		mtx_unlock(&ng_idhash_mtx);
796069154d5SJulian Elischer 
79712574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
798069154d5SJulian Elischer 		NG_FREE_NODE(node);
7994cf49a43SJulian Elischer 	}
800018fe3d1SAlexander Motin 	return (v - 1);
8014cf49a43SJulian Elischer }
8024cf49a43SJulian Elischer 
8034cf49a43SJulian Elischer /************************************************************************
804dc90cad9SJulian Elischer 			Node ID handling
805dc90cad9SJulian Elischer ************************************************************************/
806dc90cad9SJulian Elischer static node_p
807069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
808dc90cad9SJulian Elischer {
80930400f03SJulian Elischer 	node_p node;
8109ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
8110f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
81230400f03SJulian Elischer 	if(node)
81330400f03SJulian Elischer 		NG_NODE_REF(node);
8149ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
81530400f03SJulian Elischer 	return(node);
816dc90cad9SJulian Elischer }
817dc90cad9SJulian Elischer 
818dc90cad9SJulian Elischer ng_ID_t
819dc90cad9SJulian Elischer ng_node2ID(node_p node)
820dc90cad9SJulian Elischer {
82170de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
822dc90cad9SJulian Elischer }
823dc90cad9SJulian Elischer 
824dc90cad9SJulian Elischer /************************************************************************
8254cf49a43SJulian Elischer 			Node name handling
8264cf49a43SJulian Elischer ************************************************************************/
8274cf49a43SJulian Elischer 
8284cf49a43SJulian Elischer /*
8294cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
8304cf49a43SJulian Elischer  */
8314cf49a43SJulian Elischer int
8324cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8334cf49a43SJulian Elischer {
834cfea3f85SAlexander Motin 	int i, hash;
835069154d5SJulian Elischer 	node_p node2;
8364cf49a43SJulian Elischer 
8374cf49a43SJulian Elischer 	/* Check the name is valid */
83887e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8394cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8404cf49a43SJulian Elischer 			break;
8414cf49a43SJulian Elischer 	}
8424cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8436b795970SJulian Elischer 		TRAP_ERROR();
8444cf49a43SJulian Elischer 		return (EINVAL);
8454cf49a43SJulian Elischer 	}
846dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8476b795970SJulian Elischer 		TRAP_ERROR();
8484cf49a43SJulian Elischer 		return (EINVAL);
8494cf49a43SJulian Elischer 	}
8504cf49a43SJulian Elischer 
8514cf49a43SJulian Elischer 	/* Check the name isn't already being used */
852069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
85330400f03SJulian Elischer 		NG_NODE_UNREF(node2);
8546b795970SJulian Elischer 		TRAP_ERROR();
8554cf49a43SJulian Elischer 		return (EADDRINUSE);
8564cf49a43SJulian Elischer 	}
8574cf49a43SJulian Elischer 
858069154d5SJulian Elischer 	/* copy it */
85987e2c66aSHartmut Brandt 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8604cf49a43SJulian Elischer 
861cfea3f85SAlexander Motin 	/* Update name hash. */
862cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
863cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
864cfea3f85SAlexander Motin 	LIST_REMOVE(node, nd_nodes);
865603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
866cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
867cfea3f85SAlexander Motin 
8684cf49a43SJulian Elischer 	return (0);
8694cf49a43SJulian Elischer }
8704cf49a43SJulian Elischer 
8714cf49a43SJulian Elischer /*
8724cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8734cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8744cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8754cf49a43SJulian Elischer  *
8764cf49a43SJulian Elischer  * Returns the node if found, else NULL.
877069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
878e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
879e1e8f51bSRobert Watson  * there.
8804cf49a43SJulian Elischer  */
8814cf49a43SJulian Elischer node_p
882069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
8834cf49a43SJulian Elischer {
884dc90cad9SJulian Elischer 	node_p node;
885dc90cad9SJulian Elischer 	ng_ID_t temp;
886cfea3f85SAlexander Motin 	int	hash;
8874cf49a43SJulian Elischer 
8884cf49a43SJulian Elischer 	/* "." means "this node" */
889069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
89030400f03SJulian Elischer 		NG_NODE_REF(here);
891069154d5SJulian Elischer 		return(here);
892069154d5SJulian Elischer 	}
8934cf49a43SJulian Elischer 
8944cf49a43SJulian Elischer 	/* Check for name-by-ID */
895dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
896069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
8974cf49a43SJulian Elischer 	}
8984cf49a43SJulian Elischer 
8994cf49a43SJulian Elischer 	/* Find node by name */
900cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
901cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
902603724d3SBjoern A. Zeeb 	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) {
903cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
904cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
9054cf49a43SJulian Elischer 			break;
9064cf49a43SJulian Elischer 		}
90770de87f2SJulian Elischer 	}
908069154d5SJulian Elischer 	if (node)
90930400f03SJulian Elischer 		NG_NODE_REF(node);
910cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
9114cf49a43SJulian Elischer 	return (node);
9124cf49a43SJulian Elischer }
9134cf49a43SJulian Elischer 
9144cf49a43SJulian Elischer /*
9159d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
916dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9174cf49a43SJulian Elischer  */
918dc90cad9SJulian Elischer static ng_ID_t
9194cf49a43SJulian Elischer ng_decodeidname(const char *name)
9204cf49a43SJulian Elischer {
9212b70adcbSArchie Cobbs 	const int len = strlen(name);
92225792ef3SArchie Cobbs 	char *eptr;
9232b70adcbSArchie Cobbs 	u_long val;
9244cf49a43SJulian Elischer 
9252b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
92670de87f2SJulian Elischer 	if ((len < 3)
92770de87f2SJulian Elischer 	|| (name[0] != '[')
92870de87f2SJulian Elischer 	|| (name[len - 1] != ']')
92970de87f2SJulian Elischer 	|| (!isxdigit(name[1]))) {
93070de87f2SJulian Elischer 		return ((ng_ID_t)0);
93170de87f2SJulian Elischer 	}
9324cf49a43SJulian Elischer 
9332b70adcbSArchie Cobbs 	/* Decode number */
9342b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
93570de87f2SJulian Elischer 	if ((eptr - name != len - 1)
93670de87f2SJulian Elischer 	|| (val == ULONG_MAX)
93770de87f2SJulian Elischer 	|| (val == 0)) {
93812f035e0SJulian Elischer 		return ((ng_ID_t)0);
93970de87f2SJulian Elischer 	}
9402b70adcbSArchie Cobbs 	return (ng_ID_t)val;
9414cf49a43SJulian Elischer }
9424cf49a43SJulian Elischer 
9434cf49a43SJulian Elischer /*
9444cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9454cf49a43SJulian Elischer  * when shutting down and removing the node.
94664efc707SRobert Watson  * IF we allow name changing this may be more resurrected.
9474cf49a43SJulian Elischer  */
9484cf49a43SJulian Elischer void
9494cf49a43SJulian Elischer ng_unname(node_p node)
9504cf49a43SJulian Elischer {
9514cf49a43SJulian Elischer }
9524cf49a43SJulian Elischer 
9534cf49a43SJulian Elischer /************************************************************************
9544cf49a43SJulian Elischer 			Hook routines
9554cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
9563e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
9573e4084c8SJulian Elischer  they are connected to the 'dead' hook.
9584cf49a43SJulian Elischer ************************************************************************/
9594cf49a43SJulian Elischer 
9604cf49a43SJulian Elischer /*
9614cf49a43SJulian Elischer  * Remove a hook reference
9624cf49a43SJulian Elischer  */
96330400f03SJulian Elischer void
9644cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
9654cf49a43SJulian Elischer {
96630400f03SJulian Elischer 	int v;
9676b795970SJulian Elischer 
9686b795970SJulian Elischer 	if (hook == &ng_deadhook) {
9696b795970SJulian Elischer 		return;
9706b795970SJulian Elischer 	}
971018fe3d1SAlexander Motin 
972018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&hook->hk_refs, -1);
973e8a49db2SJulian Elischer 
97430400f03SJulian Elischer 	if (v == 1) { /* we were the last */
975f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
9766b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
977069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
978069154d5SJulian Elischer 	}
9794cf49a43SJulian Elischer }
9804cf49a43SJulian Elischer 
9814cf49a43SJulian Elischer /*
9824cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
9833e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
9844cf49a43SJulian Elischer  */
9854cf49a43SJulian Elischer static int
9864cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
9874cf49a43SJulian Elischer {
9884cf49a43SJulian Elischer 	hook_p hook;
9894cf49a43SJulian Elischer 	int error = 0;
9904cf49a43SJulian Elischer 
9914cf49a43SJulian Elischer 	/* Check that the given name is good */
9924cf49a43SJulian Elischer 	if (name == NULL) {
9936b795970SJulian Elischer 		TRAP_ERROR();
9944cf49a43SJulian Elischer 		return (EINVAL);
9954cf49a43SJulian Elischer 	}
996899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
9976b795970SJulian Elischer 		TRAP_ERROR();
9984cf49a43SJulian Elischer 		return (EEXIST);
9994cf49a43SJulian Elischer 	}
10004cf49a43SJulian Elischer 
10014cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
100230400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
10034cf49a43SJulian Elischer 	if (hook == NULL) {
10046b795970SJulian Elischer 		TRAP_ERROR();
10054cf49a43SJulian Elischer 		return (ENOMEM);
10064cf49a43SJulian Elischer 	}
10073e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
100830400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
10093e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
101030400f03SJulian Elischer 	hook->hk_node = node;
101130400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
10124cf49a43SJulian Elischer 
10133e4084c8SJulian Elischer 	/* Set hook name */
101487e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10153e4084c8SJulian Elischer 
10163e4084c8SJulian Elischer 	/*
10173e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10183e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10193e4084c8SJulian Elischer 	 */
1020954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1021954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
102230400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1023069154d5SJulian Elischer 			return (error);
1024069154d5SJulian Elischer 		}
1025954c4772SJulian Elischer 	}
10264cf49a43SJulian Elischer 	/*
10274cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10284cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10294cf49a43SJulian Elischer 	 */
103030400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
103130400f03SJulian Elischer 	node->nd_numhooks++;
10323e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
10334cf49a43SJulian Elischer 
10344cf49a43SJulian Elischer 	if (hookp)
10354cf49a43SJulian Elischer 		*hookp = hook;
10363e4084c8SJulian Elischer 	return (0);
10374cf49a43SJulian Elischer }
10384cf49a43SJulian Elischer 
10394cf49a43SJulian Elischer /*
1040899e9c4eSArchie Cobbs  * Find a hook
1041899e9c4eSArchie Cobbs  *
1042899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1043899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
10443e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1045899e9c4eSArchie Cobbs  */
1046899e9c4eSArchie Cobbs hook_p
1047899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1048899e9c4eSArchie Cobbs {
1049899e9c4eSArchie Cobbs 	hook_p hook;
1050899e9c4eSArchie Cobbs 
105130400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
105230400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
105330400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
105470de87f2SJulian Elischer 		if (NG_HOOK_IS_VALID(hook)
1055ce5e5f99SArchie Cobbs 		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
1056899e9c4eSArchie Cobbs 			return (hook);
1057899e9c4eSArchie Cobbs 	}
1058899e9c4eSArchie Cobbs 	return (NULL);
1059899e9c4eSArchie Cobbs }
1060899e9c4eSArchie Cobbs 
1061899e9c4eSArchie Cobbs /*
10624cf49a43SJulian Elischer  * Destroy a hook
10634cf49a43SJulian Elischer  *
10644cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
10654cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
10663e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
10673e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
10683e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
10693e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
10703e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
10713e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
10723e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
10733e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
10743e4084c8SJulian Elischer  * attached to the 'dead' hook.
10756b795970SJulian Elischer  *
10766b795970SJulian Elischer  * This routine is called at all stages of hook creation
10776b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
10784cf49a43SJulian Elischer  */
10794cf49a43SJulian Elischer void
10804cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
10814cf49a43SJulian Elischer {
1082ac5dd141SGleb Smirnoff 	hook_p peer;
1083ac5dd141SGleb Smirnoff 	node_p node;
10844cf49a43SJulian Elischer 
10856b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
10866b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
10876b795970SJulian Elischer 		return;
10886b795970SJulian Elischer 	}
1089ac5dd141SGleb Smirnoff 
1090ac5dd141SGleb Smirnoff 	/*
1091ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1092ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1093ac5dd141SGleb Smirnoff 	 */
1094ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1095ac5dd141SGleb Smirnoff 
1096ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1097ac5dd141SGleb Smirnoff 
1098ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1099ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1100ac5dd141SGleb Smirnoff 
11013e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
11023e4084c8SJulian Elischer 		/*
11033e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
11043e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
11053e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
11063e4084c8SJulian Elischer 		 */
11073e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
11083e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
11096b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
11106b795970SJulian Elischer 			/*
11116b795970SJulian Elischer 			 * If it's already divorced from a node,
11126b795970SJulian Elischer 			 * just free it.
11136b795970SJulian Elischer 			 */
1114ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11156b795970SJulian Elischer 		} else {
1116ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11176b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11186b795970SJulian Elischer 		}
111952fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
112052fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1121ac5dd141SGleb Smirnoff 	} else
1122ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1123ac5dd141SGleb Smirnoff 
1124ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11254cf49a43SJulian Elischer 
11264cf49a43SJulian Elischer 	/*
11274cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11284cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11294cf49a43SJulian Elischer 	 */
11306b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11316b795970SJulian Elischer 		return;
11326b795970SJulian Elischer 	}
113330400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
113430400f03SJulian Elischer 	node->nd_numhooks--;
113530400f03SJulian Elischer 	if (node->nd_type->disconnect) {
11364cf49a43SJulian Elischer 		/*
11376b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
113864efc707SRobert Watson 		 * trust its existence after this point. (except
11396b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
11406b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
11414cf49a43SJulian Elischer 		 */
114230400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
11434cf49a43SJulian Elischer 	}
11446b795970SJulian Elischer 
11456b795970SJulian Elischer 	/*
11466b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
11476b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
11486b795970SJulian Elischer 	 */
11496b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
11506b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
11516b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
11524cf49a43SJulian Elischer }
11534cf49a43SJulian Elischer 
11544cf49a43SJulian Elischer /*
11554cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
11564cf49a43SJulian Elischer  * is effectively bypassed.
11574cf49a43SJulian Elischer  */
11584cf49a43SJulian Elischer int
11594cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
11604cf49a43SJulian Elischer {
116130400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
11626b795970SJulian Elischer 		TRAP_ERROR();
11634cf49a43SJulian Elischer 		return (EINVAL);
116430400f03SJulian Elischer 	}
1165a7da736aSGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
116630400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
116730400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
11684cf49a43SJulian Elischer 
11693e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
11703e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
1171a7da736aSGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
11723e4084c8SJulian Elischer 
1173cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1174cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1175cf3254aaSGleb Smirnoff 
11764cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
11774cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
11784cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
11794cf49a43SJulian Elischer 	return (0);
11804cf49a43SJulian Elischer }
11814cf49a43SJulian Elischer 
11824cf49a43SJulian Elischer /*
11834cf49a43SJulian Elischer  * Install a new netgraph type
11844cf49a43SJulian Elischer  */
11854cf49a43SJulian Elischer int
11864cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
11874cf49a43SJulian Elischer {
11884cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
11894cf49a43SJulian Elischer 
11904cf49a43SJulian Elischer 	/* Check version and type name fields */
1191589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
1192589f6ed8SJulian Elischer 	|| (namelen == 0)
119387e2c66aSHartmut Brandt 	|| (namelen >= NG_TYPESIZ)) {
11946b795970SJulian Elischer 		TRAP_ERROR();
11958ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
11968ed370fdSJulian Elischer 			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
11978ed370fdSJulian Elischer 		}
11984cf49a43SJulian Elischer 		return (EINVAL);
11994cf49a43SJulian Elischer 	}
12004cf49a43SJulian Elischer 
12014cf49a43SJulian Elischer 	/* Check for name collision */
12024cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
12036b795970SJulian Elischer 		TRAP_ERROR();
12044cf49a43SJulian Elischer 		return (EEXIST);
12054cf49a43SJulian Elischer 	}
12064cf49a43SJulian Elischer 
1207069154d5SJulian Elischer 
1208069154d5SJulian Elischer 	/* Link in new type */
12099ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1210069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1211c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
12129ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12134cf49a43SJulian Elischer 	return (0);
12144cf49a43SJulian Elischer }
12154cf49a43SJulian Elischer 
12164cf49a43SJulian Elischer /*
1217c31b4a53SJulian Elischer  * unlink a netgraph type
1218c31b4a53SJulian Elischer  * If no examples exist
1219c31b4a53SJulian Elischer  */
1220c31b4a53SJulian Elischer int
1221c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1222c31b4a53SJulian Elischer {
1223c31b4a53SJulian Elischer 	/* Check for name collision */
1224c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1225c31b4a53SJulian Elischer 		TRAP_ERROR();
1226c31b4a53SJulian Elischer 		return (EBUSY);
1227c31b4a53SJulian Elischer 	}
1228c31b4a53SJulian Elischer 
1229c31b4a53SJulian Elischer 	/* Unlink type */
1230c31b4a53SJulian Elischer 	mtx_lock(&ng_typelist_mtx);
1231c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1232c31b4a53SJulian Elischer 	mtx_unlock(&ng_typelist_mtx);
1233c31b4a53SJulian Elischer 	return (0);
1234c31b4a53SJulian Elischer }
1235c31b4a53SJulian Elischer 
1236c31b4a53SJulian Elischer /*
12374cf49a43SJulian Elischer  * Look for a type of the name given
12384cf49a43SJulian Elischer  */
12394cf49a43SJulian Elischer struct ng_type *
12404cf49a43SJulian Elischer ng_findtype(const char *typename)
12414cf49a43SJulian Elischer {
12424cf49a43SJulian Elischer 	struct ng_type *type;
12434cf49a43SJulian Elischer 
12449ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1245069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
12464cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
12474cf49a43SJulian Elischer 			break;
12484cf49a43SJulian Elischer 	}
12499ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12504cf49a43SJulian Elischer 	return (type);
12514cf49a43SJulian Elischer }
12524cf49a43SJulian Elischer 
12534cf49a43SJulian Elischer /************************************************************************
12544cf49a43SJulian Elischer 			Composite routines
12554cf49a43SJulian Elischer ************************************************************************/
12564cf49a43SJulian Elischer /*
12576b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
12584cf49a43SJulian Elischer  */
1259e088dd4cSAlexander Motin static int
1260e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
12614cf49a43SJulian Elischer {
1262e088dd4cSAlexander Motin 	int	error = 0;
12634cf49a43SJulian Elischer 
12646b795970SJulian Elischer 	/*
12656b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12666b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12676b795970SJulian Elischer 	 * Our caller has a reference on the node.
12686b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12696b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12701acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
12711acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
12726b795970SJulian Elischer 	 */
12736b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
12746b795970SJulian Elischer 		/*
12756b795970SJulian Elischer 		 * The node must have been freed again since we last visited
12766b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
12776b795970SJulian Elischer 		 * We should just release our references and
12786b795970SJulian Elischer 		 * free anything we can think of.
12796b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
12806b795970SJulian Elischer 		 * that holds the references, just return.
12816b795970SJulian Elischer 		 */
1282e088dd4cSAlexander Motin 		ERROUT(ENOENT);
12836b795970SJulian Elischer 	}
12846b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1285e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
12866b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
12871acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1288e088dd4cSAlexander Motin 			ERROUT(error);
12894cf49a43SJulian Elischer 		}
12906b795970SJulian Elischer 	}
12916b795970SJulian Elischer 	/*
12926b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
12936b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
12946b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
12956b795970SJulian Elischer 	 */
12966b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1297e088dd4cSAlexander Motin done:
1298e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1299e088dd4cSAlexander Motin 	return (error);
13006b795970SJulian Elischer }
13016b795970SJulian Elischer 
1302e088dd4cSAlexander Motin static int
1303e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
13046b795970SJulian Elischer {
1305ac5dd141SGleb Smirnoff 	hook_p	peer;
1306e088dd4cSAlexander Motin 	int	error = 0;
13076b795970SJulian Elischer 
13086b795970SJulian Elischer 	/*
13096b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
13106b795970SJulian Elischer 	 * Our caller has a reference on the hook.
13116b795970SJulian Elischer 	 * Our caller has a reference on the node.
13126b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13136b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13146b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
13156b795970SJulian Elischer 	 * First check the hook name is unique.
13161acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13176b795970SJulian Elischer 	 */
13186b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13196b795970SJulian Elischer 		TRAP_ERROR();
13206b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
13211acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1322e088dd4cSAlexander Motin 		ERROUT(EEXIST);
13236b795970SJulian Elischer 	}
13246b795970SJulian Elischer 	/*
13256b795970SJulian Elischer 	 * Check if the node type code has something to say about it
13266b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
13276b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
13286b795970SJulian Elischer 	 * The peer hook will also be destroyed.
13296b795970SJulian Elischer 	 */
13306b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1331e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1332e088dd4cSAlexander Motin 		    hook->hk_name))) {
13336b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
13341acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1335e088dd4cSAlexander Motin 			ERROUT(error);
13366b795970SJulian Elischer 		}
13376b795970SJulian Elischer 	}
13386b795970SJulian Elischer 
13396b795970SJulian Elischer 	/*
13406b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
13416b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
13426b795970SJulian Elischer 	 */
13436b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
13446b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
13456b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
13466b795970SJulian Elischer 	node->nd_numhooks++;
13476b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
13486b795970SJulian Elischer 
13496b795970SJulian Elischer 	/*
135064efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
13515951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
13526b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
13536b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
13546b795970SJulian Elischer 	 * yet.
135564efc707SRobert Watson 	 * We can call the local one immediately as we have the
13566b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
13576b795970SJulian Elischer 	 */
13586b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1359e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13606b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13611acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1362e088dd4cSAlexander Motin 			ERROUT(error);
13636b795970SJulian Elischer 		}
13646b795970SJulian Elischer 	}
1365ac5dd141SGleb Smirnoff 
1366ac5dd141SGleb Smirnoff 	/*
1367ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1368ac5dd141SGleb Smirnoff 	 */
1369ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1370ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1371ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1372ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1373ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1374ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1375e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1376ac5dd141SGleb Smirnoff 	}
1377ac5dd141SGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
1378ac5dd141SGleb Smirnoff 
1379b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1380b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1381ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
13821acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1383e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
13841acb27c6SJulian Elischer 	}
13856b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1386e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1387e088dd4cSAlexander Motin done:
1388e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1389e088dd4cSAlexander Motin 	return (error);
13904cf49a43SJulian Elischer }
13914cf49a43SJulian Elischer 
13924cf49a43SJulian Elischer /*
13936b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
13946b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
13954cf49a43SJulian Elischer  */
13966b795970SJulian Elischer static int
1397e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1398e088dd4cSAlexander Motin     node_p node2, const char *name2)
13994cf49a43SJulian Elischer {
14004cf49a43SJulian Elischer 	int	error;
14014cf49a43SJulian Elischer 	hook_p	hook;
14024cf49a43SJulian Elischer 	hook_p	hook2;
14034cf49a43SJulian Elischer 
14041acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
14051acb27c6SJulian Elischer 		return(EEXIST);
14061acb27c6SJulian Elischer 	}
14073e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
14084cf49a43SJulian Elischer 		return (error);
14096b795970SJulian Elischer 	/* Allocate the other hook and link it up */
14106b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
141119724144SGleb Smirnoff 	if (hook2 == NULL) {
14126b795970SJulian Elischer 		TRAP_ERROR();
14136b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
14146b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
14156b795970SJulian Elischer 		return (ENOMEM);
14166b795970SJulian Elischer 	}
14176b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14186b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14196b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
14206b795970SJulian Elischer 	hook->hk_peer = hook2;
14216b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
14226b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14236b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
142487e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
14256b795970SJulian Elischer 
14266b795970SJulian Elischer 	/*
14276b795970SJulian Elischer 	 * Queue the function above.
14286b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
14296b795970SJulian Elischer 	 * the other node.
14306b795970SJulian Elischer 	 */
1431b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1432b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
14333fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
14343fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
14353fb87c24SAlexander Motin 	}
14366b795970SJulian Elischer 
14376b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
14386b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
14393fb87c24SAlexander Motin 	return (error);
14404cf49a43SJulian Elischer }
14416b795970SJulian Elischer 
14426b795970SJulian Elischer /*
14436b795970SJulian Elischer  * Make a peer and connect.
14446b795970SJulian Elischer  * We assume that the local node is locked.
14456b795970SJulian Elischer  * The new node probably doesn't need a lock until
14466b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
14476b795970SJulian Elischer  * but we should think about it a bit more.
14486b795970SJulian Elischer  *
14496b795970SJulian Elischer  * The problem may come if the other node also fires up
14506b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
14516b795970SJulian Elischer  * also it may already get a command msg via it's ID.
14526b795970SJulian Elischer  *
14536b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
14546b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
14556b795970SJulian Elischer  * make arg1 point to the node to remove).
14566b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
14576b795970SJulian Elischer  * an unconnected node?
14586b795970SJulian Elischer  */
14596b795970SJulian Elischer static int
14606b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
14616b795970SJulian Elischer {
14626b795970SJulian Elischer 	node_p	node2;
14634c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
14646b795970SJulian Elischer 	int	error;
14656b795970SJulian Elischer 
14666b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
14676b795970SJulian Elischer 		return (error);
14686b795970SJulian Elischer 	}
14696b795970SJulian Elischer 
14706b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
14711acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14726b795970SJulian Elischer 		return (error);
14736b795970SJulian Elischer 	}
14746b795970SJulian Elischer 
14756b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
14761acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14776b795970SJulian Elischer 		ng_destroy_hook(hook1);
14786b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
14796b795970SJulian Elischer 		return (error);
14806b795970SJulian Elischer 	}
14816b795970SJulian Elischer 
14826b795970SJulian Elischer 	/*
14836b795970SJulian Elischer 	 * Actually link the two hooks together.
14846b795970SJulian Elischer 	 */
14856b795970SJulian Elischer 	hook1->hk_peer = hook2;
14866b795970SJulian Elischer 	hook2->hk_peer = hook1;
14876b795970SJulian Elischer 
14886b795970SJulian Elischer 	/* Each hook is referenced by the other */
14896b795970SJulian Elischer 	NG_HOOK_REF(hook1);
14906b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14916b795970SJulian Elischer 
14926b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
14936b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
14946b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
14956b795970SJulian Elischer 	}
14966b795970SJulian Elischer 
14976b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
14986b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
14996b795970SJulian Elischer 
15006b795970SJulian Elischer 	}
15013e4084c8SJulian Elischer 
15023e4084c8SJulian Elischer 	/*
15033e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
15043e4084c8SJulian Elischer 	 */
15056b795970SJulian Elischer 	if (error) {
15066b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
15071acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15086b795970SJulian Elischer 	} else {
15096b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
15106b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
15116b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
15126b795970SJulian Elischer 	}
15136b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
15143e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
15153e4084c8SJulian Elischer 	return (error);
15164cf49a43SJulian Elischer }
15176b795970SJulian Elischer 
1518069154d5SJulian Elischer /************************************************************************
1519069154d5SJulian Elischer 		Utility routines to send self messages
1520069154d5SJulian Elischer ************************************************************************/
1521069154d5SJulian Elischer 
15221acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
152364efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1524069154d5SJulian Elischer int
152515cea89fSAlexander Motin ng_rmnode_self(node_p node)
1526069154d5SJulian Elischer {
15271acb27c6SJulian Elischer 	int		error;
15284cf49a43SJulian Elischer 
15291acb27c6SJulian Elischer 	if (node == &ng_deadnode)
15301acb27c6SJulian Elischer 		return (0);
1531be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1532be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
15331acb27c6SJulian Elischer 		return (0);
1534069154d5SJulian Elischer 
153515cea89fSAlexander Motin 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
15361acb27c6SJulian Elischer 	return (error);
1537069154d5SJulian Elischer }
1538069154d5SJulian Elischer 
15391acb27c6SJulian Elischer static void
15406b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
15416b795970SJulian Elischer {
15426b795970SJulian Elischer 	ng_destroy_hook(hook);
15431acb27c6SJulian Elischer 	return ;
15446b795970SJulian Elischer }
15456b795970SJulian Elischer 
1546954c4772SJulian Elischer int
1547954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1548954c4772SJulian Elischer {
15496b795970SJulian Elischer 	int		error;
1550954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1551954c4772SJulian Elischer 
15526b795970SJulian Elischer 	if (node == &ng_deadnode)
15536b795970SJulian Elischer 		return (0);
15546b795970SJulian Elischer 
15556b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
15566b795970SJulian Elischer 	return (error);
1557954c4772SJulian Elischer }
1558954c4772SJulian Elischer 
1559069154d5SJulian Elischer /***********************************************************************
15604cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
15614cf49a43SJulian Elischer  *
15624cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
15634cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
15644cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
15654cf49a43SJulian Elischer  *
15664cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
15674cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
15684cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
15694cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
15704cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
15714cf49a43SJulian Elischer  *
15724cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1573069154d5SJulian Elischer  ***********************************************************************/
15744cf49a43SJulian Elischer int
15754cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
15764cf49a43SJulian Elischer {
15774cf49a43SJulian Elischer 	char	*node, *path, *hook;
15784cf49a43SJulian Elischer 	int	k;
15794cf49a43SJulian Elischer 
15804cf49a43SJulian Elischer 	/*
15814cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
15824cf49a43SJulian Elischer 	 */
15834cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
15844cf49a43SJulian Elischer 	if (*path) {
15854cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
15864cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
15874cf49a43SJulian Elischer 
15884cf49a43SJulian Elischer 		/* Node name must not be empty */
15894cf49a43SJulian Elischer 		if (!*node)
15904cf49a43SJulian Elischer 			return -1;
15914cf49a43SJulian Elischer 
15924cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
15934cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
15944cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
15954cf49a43SJulian Elischer 				if (node[k] == '.')
15964cf49a43SJulian Elischer 					return -1;
15974cf49a43SJulian Elischer 		}
15984cf49a43SJulian Elischer 	} else {
15994cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
16004cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
16014cf49a43SJulian Elischer 	}
16024cf49a43SJulian Elischer 
16034cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
16044cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16054cf49a43SJulian Elischer 		if (path[k] == ':')
16064cf49a43SJulian Elischer 			return -1;
16074cf49a43SJulian Elischer 
16084cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
16094cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16104cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
16114cf49a43SJulian Elischer 			return -1;
16124cf49a43SJulian Elischer 
16134cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
16144cf49a43SJulian Elischer 	if (path[0] == '.')
16154cf49a43SJulian Elischer 		path++;
16164cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16174cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16184cf49a43SJulian Elischer 
16194cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
16204cf49a43SJulian Elischer 	if (*path) {
16214cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
16224cf49a43SJulian Elischer 			if (path[k] == '.') {
16234cf49a43SJulian Elischer 				hook = NULL;
16244cf49a43SJulian Elischer 				break;
16254cf49a43SJulian Elischer 			}
16264cf49a43SJulian Elischer 	} else
16274cf49a43SJulian Elischer 		path = hook = NULL;
16284cf49a43SJulian Elischer 
16294cf49a43SJulian Elischer 	/* Done */
16304cf49a43SJulian Elischer 	if (nodep)
16314cf49a43SJulian Elischer 		*nodep = node;
16324cf49a43SJulian Elischer 	if (pathp)
16334cf49a43SJulian Elischer 		*pathp = path;
16344cf49a43SJulian Elischer 	if (hookp)
16354cf49a43SJulian Elischer 		*hookp = hook;
16364cf49a43SJulian Elischer 	return (0);
16374cf49a43SJulian Elischer }
16384cf49a43SJulian Elischer 
16394cf49a43SJulian Elischer /*
16404cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1641069154d5SJulian Elischer  * return the destination node.
16424cf49a43SJulian Elischer  */
16434cf49a43SJulian Elischer int
1644069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1645069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
16464cf49a43SJulian Elischer {
164787e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
1648a7da736aSGleb Smirnoff 	char   *nodename, *path;
1649069154d5SJulian Elischer 	node_p  node, oldnode;
16504cf49a43SJulian Elischer 
16514cf49a43SJulian Elischer 	/* Initialize */
165230400f03SJulian Elischer 	if (destp == NULL) {
16536b795970SJulian Elischer 		TRAP_ERROR();
16544cf49a43SJulian Elischer 		return EINVAL;
165530400f03SJulian Elischer 	}
16564cf49a43SJulian Elischer 	*destp = NULL;
16574cf49a43SJulian Elischer 
16584cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
16594cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
16604cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
16614cf49a43SJulian Elischer 
16624cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
16634cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
16646b795970SJulian Elischer 		TRAP_ERROR();
16654cf49a43SJulian Elischer 		return EINVAL;
16664cf49a43SJulian Elischer 	}
16674cf49a43SJulian Elischer 
1668069154d5SJulian Elischer 	/*
1669069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1670069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1671069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1672069154d5SJulian Elischer 	 */
16734cf49a43SJulian Elischer 	if (nodename) {
1674069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
16754cf49a43SJulian Elischer 		if (node == NULL) {
16766b795970SJulian Elischer 			TRAP_ERROR();
16774cf49a43SJulian Elischer 			return (ENOENT);
16784cf49a43SJulian Elischer 		}
1679069154d5SJulian Elischer 	} else {
1680069154d5SJulian Elischer 		if (here == NULL) {
16816b795970SJulian Elischer 			TRAP_ERROR();
1682069154d5SJulian Elischer 			return (EINVAL);
1683069154d5SJulian Elischer 		}
16844cf49a43SJulian Elischer 		node = here;
168530400f03SJulian Elischer 		NG_NODE_REF(node);
1686069154d5SJulian Elischer 	}
16874cf49a43SJulian Elischer 
1688a7da736aSGleb Smirnoff 	if (path == NULL) {
1689a7da736aSGleb Smirnoff 		if (lasthook != NULL)
1690a7da736aSGleb Smirnoff 			*lasthook = NULL;
1691a7da736aSGleb Smirnoff 		*destp = node;
1692a7da736aSGleb Smirnoff 		return (0);
1693a7da736aSGleb Smirnoff 	}
1694a7da736aSGleb Smirnoff 
1695069154d5SJulian Elischer 	/*
1696069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1697a7da736aSGleb Smirnoff 	 *
1698a7da736aSGleb Smirnoff 	 * XXXGL: The path may demolish as we go the sequence, but if
1699a7da736aSGleb Smirnoff 	 * we hold the topology mutex at critical places, then, I hope,
1700a7da736aSGleb Smirnoff 	 * we would always have valid pointers in hand, although the
1701a7da736aSGleb Smirnoff 	 * path behind us may no longer exist.
1702069154d5SJulian Elischer 	 */
1703a7da736aSGleb Smirnoff 	for (;;) {
1704a7da736aSGleb Smirnoff 		hook_p hook;
17054cf49a43SJulian Elischer 		char *segment;
17064cf49a43SJulian Elischer 
17074cf49a43SJulian Elischer 		/*
17084cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
1709a7da736aSGleb Smirnoff 		 * found with a NUL; "path" points to the next segment (or the
17104cf49a43SJulian Elischer 		 * NUL at the end).
17114cf49a43SJulian Elischer 		 */
1712a7da736aSGleb Smirnoff 		for (segment = path; *path != '\0'; path++) {
1713a7da736aSGleb Smirnoff 			if (*path == '.') {
1714a7da736aSGleb Smirnoff 				*path++ = '\0';
17154cf49a43SJulian Elischer 				break;
17164cf49a43SJulian Elischer 			}
17174cf49a43SJulian Elischer 		}
17184cf49a43SJulian Elischer 
17194cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1720899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17214cf49a43SJulian Elischer 
1722a7da736aSGleb Smirnoff 		mtx_lock(&ng_topo_mtx);
17234cf49a43SJulian Elischer 		/* Can't get there from here... */
17244cf49a43SJulian Elischer 		if (hook == NULL
172530400f03SJulian Elischer 		    || NG_HOOK_PEER(hook) == NULL
172630400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(hook)
172730400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17286b795970SJulian Elischer 			TRAP_ERROR();
172930400f03SJulian Elischer 			NG_NODE_UNREF(node);
1730a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
17314cf49a43SJulian Elischer 			return (ENOENT);
17324cf49a43SJulian Elischer 		}
17334cf49a43SJulian Elischer 
1734069154d5SJulian Elischer 		/*
1735069154d5SJulian Elischer 		 * Hop on over to the next node
1736069154d5SJulian Elischer 		 * XXX
1737069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1738069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1739069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1740069154d5SJulian Elischer 		 */
1741069154d5SJulian Elischer 		oldnode = node;
174230400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
174330400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
174430400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
174530400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
174630400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1747a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
17486b795970SJulian Elischer 			TRAP_ERROR();
17494cf49a43SJulian Elischer 			return (ENXIO);
17504cf49a43SJulian Elischer 		}
17514cf49a43SJulian Elischer 
1752a7da736aSGleb Smirnoff 		if (*path == '\0') {
1753a7da736aSGleb Smirnoff 			if (lasthook != NULL) {
1754a7da736aSGleb Smirnoff 				if (hook != NULL) {
1755a7da736aSGleb Smirnoff 					*lasthook = NG_HOOK_PEER(hook);
1756a7da736aSGleb Smirnoff 					NG_HOOK_REF(*lasthook);
1757a7da736aSGleb Smirnoff 				} else
1758a7da736aSGleb Smirnoff 					*lasthook = NULL;
1759a7da736aSGleb Smirnoff 			}
1760a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
17614cf49a43SJulian Elischer 			*destp = node;
1762069154d5SJulian Elischer 			return (0);
1763069154d5SJulian Elischer 		}
1764a7da736aSGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1765a7da736aSGleb Smirnoff 	}
1766a7da736aSGleb Smirnoff }
1767069154d5SJulian Elischer 
1768069154d5SJulian Elischer /***************************************************************\
1769069154d5SJulian Elischer * Input queue handling.
1770069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1771069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
177264efc707SRobert Watson * Items which cannot be handled immediately are queued.
1773069154d5SJulian Elischer *
1774069154d5SJulian Elischer * read-write queue locking inline functions			*
1775069154d5SJulian Elischer \***************************************************************/
1776069154d5SJulian Elischer 
17779852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
17789852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
17799852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
17809852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
17819852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
17829852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1783069154d5SJulian Elischer 
1784069154d5SJulian Elischer /*
1785069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1786069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1787069154d5SJulian Elischer  * with them.
1788069154d5SJulian Elischer  *
1789b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1790069154d5SJulian Elischer  */
1791069154d5SJulian Elischer /*-
1792069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1793069154d5SJulian Elischer                        |
1794069154d5SJulian Elischer                        V
1795069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
17964be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
17974be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
17984be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1799069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18004be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
18014be59335SGleb Smirnoff                             V                                | |
18024be59335SGleb Smirnoff                   [active reader count]                      | |
1803069154d5SJulian Elischer                                                              | |
18044be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1805069154d5SJulian Elischer                                                                |
18064be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1807b57a7965SJulian Elischer 
18088f9ac44aSAlexander Motin Node queue has such semantics:
18098f9ac44aSAlexander Motin - All flags modifications are atomic.
18108f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18118f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18128f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18138f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18148f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18158f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18168f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18178f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18188f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1819069154d5SJulian Elischer */
18208f9ac44aSAlexander Motin 
18214be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18224be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18234be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18244be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18254be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1826b57a7965SJulian Elischer 
1827b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18284be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18294be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1830b57a7965SJulian Elischer 
18314be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1832b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1833b57a7965SJulian Elischer 
18344be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
18354be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
18364be59335SGleb Smirnoff 
18374be59335SGleb Smirnoff /* How to decide what the next queued item is. */
18389852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
18399852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
18404be59335SGleb Smirnoff 
18414be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
18424be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
18434be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
18444be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
18454be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1846b57a7965SJulian Elischer 
1847b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
18484be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
18494be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1850394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
18514be59335SGleb Smirnoff 
1852714fb865SGleb Smirnoff #define NGQRW_R 0
1853714fb865SGleb Smirnoff #define NGQRW_W 1
1854714fb865SGleb Smirnoff 
18559852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
18569852972bSAlexander Motin 
1857069154d5SJulian Elischer /*
1858069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1859069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1860069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1861069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1862069154d5SJulian Elischer  * on the queue.
1863069154d5SJulian Elischer  */
1864069154d5SJulian Elischer static __inline item_p
18659852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1866069154d5SJulian Elischer {
1867069154d5SJulian Elischer 	item_p item;
18689852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1869b57a7965SJulian Elischer 
18708f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1871d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
18728f9ac44aSAlexander Motin 
18738f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
18744be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
18752955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
18762955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
18779852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
18784be59335SGleb Smirnoff 		return (NULL);
18794be59335SGleb Smirnoff 	}
1880d58e678fSGleb Smirnoff 
18814be59335SGleb Smirnoff 	/*
18824be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
18834be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
18844be59335SGleb Smirnoff 	 * the current state of the node.
18854be59335SGleb Smirnoff 	 */
18864be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1887394cb30aSAlexander Motin 		while (1) {
1888394cb30aSAlexander Motin 			long t = ngq->q_flags;
1889394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
18908f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
18912955ee18SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
18922955ee18SGleb Smirnoff 				    "can't proceed; queue flags 0x%lx", __func__,
18939852972bSAlexander Motin 				    node->nd_ID, node, t);
18944be59335SGleb Smirnoff 				return (NULL);
18954be59335SGleb Smirnoff 			}
18969852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1897394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1898394cb30aSAlexander Motin 				break;
1899394cb30aSAlexander Motin 			cpu_spinwait();
1900394cb30aSAlexander Motin 		}
19018f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1902714fb865SGleb Smirnoff 		*rw = NGQRW_R;
19039852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1904394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19058f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1906714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1907069154d5SJulian Elischer 	} else {
19088f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
1909394cb30aSAlexander Motin 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer "
1910394cb30aSAlexander Motin 		    "can't proceed; queue flags 0x%lx", __func__,
19119852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19124be59335SGleb Smirnoff 		return (NULL);
19134cf49a43SJulian Elischer 	}
19144cf49a43SJulian Elischer 
19154cf49a43SJulian Elischer 	/*
1916069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1917069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19184cf49a43SJulian Elischer 	 */
19199852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19209852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
19219852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
19229852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
19232955ee18SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
19242955ee18SGleb Smirnoff 	    "queue flags 0x%lx", __func__,
19259852972bSAlexander Motin 	    node->nd_ID, node, item, *rw ? "WRITER" : "READER" ,
19263b33fbe7SGleb Smirnoff 	    ngq->q_flags);
1927069154d5SJulian Elischer 	return (item);
1928069154d5SJulian Elischer }
1929859a4d16SJulian Elischer 
1930859a4d16SJulian Elischer /*
19318f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
19328f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
1933859a4d16SJulian Elischer  */
1934069154d5SJulian Elischer static __inline void
19359852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
1936069154d5SJulian Elischer {
19379852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
19384be59335SGleb Smirnoff 	if (rw == NGQRW_W)
19394be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
19404be59335SGleb Smirnoff 	else
19414be59335SGleb Smirnoff 		NGI_SET_READER(item);
1942394cb30aSAlexander Motin 
1943394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
1944394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
19459852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
19469852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
1947394cb30aSAlexander Motin 
19482955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
19499852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
19504be59335SGleb Smirnoff 
19514be59335SGleb Smirnoff 	/*
19524be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
19534be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
19544be59335SGleb Smirnoff 	 */
19554be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
19569852972bSAlexander Motin 		ng_worklist_add(node);
1957394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
1958069154d5SJulian Elischer }
1959069154d5SJulian Elischer 
19608f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
1961069154d5SJulian Elischer static __inline item_p
19629852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
1963069154d5SJulian Elischer {
19649852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1965ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1966069154d5SJulian Elischer 
1967394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
1968394cb30aSAlexander Motin 	while (1) {
19699852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
1970394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
1971394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
19729852972bSAlexander Motin 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19739852972bSAlexander Motin 		    t, t + READER_INCREMENT)) {
1974069154d5SJulian Elischer 	    		/* Successfully grabbed node */
1975394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19769852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
1977069154d5SJulian Elischer 			return (item);
1978069154d5SJulian Elischer 		}
1979394cb30aSAlexander Motin 		cpu_spinwait();
1980394cb30aSAlexander Motin 	};
1981069154d5SJulian Elischer 
1982394cb30aSAlexander Motin 	/* Queue the request for later. */
19839852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
1984f912c0f7SGleb Smirnoff 
1985f912c0f7SGleb Smirnoff 	return (NULL);
1986069154d5SJulian Elischer }
1987069154d5SJulian Elischer 
19888f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
1989069154d5SJulian Elischer static __inline item_p
19909852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
1991069154d5SJulian Elischer {
19929852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1993ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1994ac5dd141SGleb Smirnoff 
1995394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
19969852972bSAlexander Motin 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19979852972bSAlexander Motin 	    0, WRITER_ACTIVE)) {
1998394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
19992955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20009852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
2001069154d5SJulian Elischer 		return (item);
2002069154d5SJulian Elischer 	}
2003069154d5SJulian Elischer 
2004394cb30aSAlexander Motin 	/* Queue the request for later. */
20059852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
2006f912c0f7SGleb Smirnoff 
2007f912c0f7SGleb Smirnoff 	return (NULL);
2008069154d5SJulian Elischer }
2009069154d5SJulian Elischer 
2010262dfd7fSJulian Elischer #if 0
2011262dfd7fSJulian Elischer static __inline item_p
20129852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
2013262dfd7fSJulian Elischer {
20149852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20159852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2016262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2017262dfd7fSJulian Elischer 
2018262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2019262dfd7fSJulian Elischer 
20209852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2021262dfd7fSJulian Elischer 
2022262dfd7fSJulian Elischer 	/*
2023262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2024262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2025262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2026262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2027262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2028262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2029262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2030262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2031262dfd7fSJulian Elischer 	 */
20329852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
20339852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
20349852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2035262dfd7fSJulian Elischer 
2036262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2037262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2038262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2039262dfd7fSJulian Elischer 
2040262dfd7fSJulian Elischer 		/*
2041262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
2042262dfd7fSJulian Elischer 		 * down grade back to READER and finish up
2043262dfd7fSJulian Elischer 	 	 */
20449852972bSAlexander Motin 		atomic_add_int(&ngq->q_flags,
2045262dfd7fSJulian Elischer 		    READER_INCREMENT - WRITER_ACTIVE);
2046262dfd7fSJulian Elischer 
2047262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2048262dfd7fSJulian Elischer 		return;
2049262dfd7fSJulian Elischer 	}
2050262dfd7fSJulian Elischer 	/*
2051262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2052262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2053262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2054262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2055262dfd7fSJulian Elischer 	 * set the correct flags.
2056262dfd7fSJulian Elischer 	 */
20579852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2058262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
20599852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2060262dfd7fSJulian Elischer 
2061262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
20629852972bSAlexander Motin 		    node->nd_ID, node);
2063262dfd7fSJulian Elischer 	};
20649852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
20659852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
20669852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2067262dfd7fSJulian Elischer 
2068262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
20699852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2070394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20719852972bSAlexander Motin 		ng_worklist_add(node);
20729852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2073262dfd7fSJulian Elischer 
2074262dfd7fSJulian Elischer 	return;
2075262dfd7fSJulian Elischer }
2076262dfd7fSJulian Elischer #endif
2077262dfd7fSJulian Elischer 
20788f9ac44aSAlexander Motin /* Release reader lock. */
2079069154d5SJulian Elischer static __inline void
20809852972bSAlexander Motin ng_leave_read(node_p node)
2081069154d5SJulian Elischer {
20829852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2083069154d5SJulian Elischer }
2084069154d5SJulian Elischer 
20858f9ac44aSAlexander Motin /* Release writer lock. */
2086069154d5SJulian Elischer static __inline void
20879852972bSAlexander Motin ng_leave_write(node_p node)
2088069154d5SJulian Elischer {
20899852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2090069154d5SJulian Elischer }
2091069154d5SJulian Elischer 
20928f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2093069154d5SJulian Elischer static void
20949852972bSAlexander Motin ng_flush_input_queue(node_p node)
2095069154d5SJulian Elischer {
20969852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2097069154d5SJulian Elischer 	item_p item;
2098069154d5SJulian Elischer 
20992c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
21009852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
21019852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
21029852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
21039852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
21042c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21054be59335SGleb Smirnoff 
21064be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
210710e87318SAlexander Motin 		if (item->apply != NULL) {
210810e87318SAlexander Motin 			if (item->depth == 1)
210910e87318SAlexander Motin 				item->apply->error = ENOENT;
211010e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
211110e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
211210e87318SAlexander Motin 				    item->apply->error);
211310e87318SAlexander Motin 			}
2114eb2405ddSGleb Smirnoff 		}
2115505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21162c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2117069154d5SJulian Elischer 	}
21182c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2119069154d5SJulian Elischer }
2120069154d5SJulian Elischer 
2121069154d5SJulian Elischer /***********************************************************************
2122069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2123069154d5SJulian Elischer ***********************************************************************/
2124069154d5SJulian Elischer 
2125069154d5SJulian Elischer /*
21261acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2127069154d5SJulian Elischer  * Common:
2128069154d5SJulian Elischer  *    reference to destination node.
2129069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2130e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2131069154d5SJulian Elischer  * Data:
2132069154d5SJulian Elischer  *    pointer to mbuf
2133069154d5SJulian Elischer  * Control_Message:
2134069154d5SJulian Elischer  *    pointer to msg.
2135069154d5SJulian Elischer  *    ID of original sender node. (return address)
21361acb27c6SJulian Elischer  * Function:
21371acb27c6SJulian Elischer  *    Function pointer
21381acb27c6SJulian Elischer  *    void * argument
21391acb27c6SJulian Elischer  *    integer argument
2140069154d5SJulian Elischer  *
2141069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2142069154d5SJulian Elischer  */
2143069154d5SJulian Elischer 
2144069154d5SJulian Elischer int
214542282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2146069154d5SJulian Elischer {
2147e088dd4cSAlexander Motin 	hook_p hook;
2148e088dd4cSAlexander Motin 	node_p node;
214942282202SGleb Smirnoff 	int queue, rw;
2150e088dd4cSAlexander Motin 	struct ng_queue *ngq;
215132b33288SGleb Smirnoff 	int error = 0;
2152069154d5SJulian Elischer 
2153f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2154f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2155e088dd4cSAlexander Motin 
2156e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2157e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2158e088dd4cSAlexander Motin #endif
2159e088dd4cSAlexander Motin 
2160f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2161e088dd4cSAlexander Motin 	if (item->apply)
2162e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2163e088dd4cSAlexander Motin 
2164e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2165f50597f5SAlexander Motin 	/* Node is never optional. */
2166f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2167e088dd4cSAlexander Motin 
2168e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2169f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2170f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2171f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
217210204449SJulian Elischer 		if (NGI_M(item) == NULL)
2173e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2174069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
21756f683eeeSGleb Smirnoff 	}
21766f683eeeSGleb Smirnoff 
2177069154d5SJulian Elischer 	/*
2178f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2179f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2180f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2181069154d5SJulian Elischer 	 */
2182db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2183f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2184f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2185069154d5SJulian Elischer 		rw = NGQRW_W;
2186f50597f5SAlexander Motin 	} else {
2187f50597f5SAlexander Motin 		rw = NGQRW_R;
2188f50597f5SAlexander Motin 	}
21896f683eeeSGleb Smirnoff 
219081a253a4SAlexander Motin 	/*
2191f089869fSMarko Zec 	 * If sender or receiver requests queued delivery, or call graph
2192f089869fSMarko Zec 	 * loops back from outbound to inbound path, or stack usage
219381a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
219481a253a4SAlexander Motin 	 */
219581a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
219681a253a4SAlexander Motin 		queue = 1;
2197f089869fSMarko Zec 	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2198f089869fSMarko Zec 	    curthread->td_ng_outbound) {
2199f089869fSMarko Zec 		queue = 1;
2200f50597f5SAlexander Motin 	} else {
2201f50597f5SAlexander Motin 		queue = 0;
220281a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2203d4529f98SAlexander Motin 		/*
2204942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2205f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2206d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2207f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2208f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2209d4529f98SAlexander Motin 		 */
2210f50597f5SAlexander Motin 		size_t	st, su, sl;
221181a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2212f50597f5SAlexander Motin 		sl = st - su;
2213f50597f5SAlexander Motin 		if ((sl * 4 < st) ||
2214f50597f5SAlexander Motin 		    ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) ||
221581a253a4SAlexander Motin 		      (hook && (hook->hk_flags & HK_HI_STACK))))) {
221681a253a4SAlexander Motin 			queue = 1;
221781a253a4SAlexander Motin 		}
221881a253a4SAlexander Motin #endif
2219f50597f5SAlexander Motin 	}
222081a253a4SAlexander Motin 
2221069154d5SJulian Elischer 	if (queue) {
222210e87318SAlexander Motin 		item->depth = 1;
2223394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
22249852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2225f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2226069154d5SJulian Elischer 	}
2227069154d5SJulian Elischer 
2228069154d5SJulian Elischer 	/*
2229069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2230069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2231069154d5SJulian Elischer 	 */
223232b33288SGleb Smirnoff  	if (rw == NGQRW_R)
22339852972bSAlexander Motin 		item = ng_acquire_read(node, item);
223432b33288SGleb Smirnoff 	else
22359852972bSAlexander Motin 		item = ng_acquire_write(node, item);
22364cf49a43SJulian Elischer 
2237f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2238f50597f5SAlexander Motin 	if (item == NULL)
2239f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2240069154d5SJulian Elischer 
22415951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
22425951069aSJulian Elischer 
224310e87318SAlexander Motin 	item->depth++;
224427757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2245069154d5SJulian Elischer 
2246394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
22479852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2248394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
22492c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2250394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
22519852972bSAlexander Motin 			ng_worklist_add(node);
22522c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2253394cb30aSAlexander Motin 	}
2254394cb30aSAlexander Motin 
2255394cb30aSAlexander Motin 	/*
2256394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2257394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2258394cb30aSAlexander Motin 	 */
2259394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2260069154d5SJulian Elischer 
22611acb27c6SJulian Elischer 	return (error);
2262e088dd4cSAlexander Motin 
2263e088dd4cSAlexander Motin done:
2264f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
226510e87318SAlexander Motin 	if (item->apply != NULL) {
226610e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
226710e87318SAlexander Motin 			item->apply->error = error;
226810e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
226910e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
227010e87318SAlexander Motin 			    item->apply->error);
227110e87318SAlexander Motin 		}
227210e87318SAlexander Motin 	}
2273e72a98f4SAlexander Motin 
2274e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2275e088dd4cSAlexander Motin 	return (error);
2276069154d5SJulian Elischer }
2277069154d5SJulian Elischer 
2278069154d5SJulian Elischer /*
2279069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2280069154d5SJulian Elischer  * It should contain all the information needed
2281069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2282e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
22834cf49a43SJulian Elischer  */
228427757487SGleb Smirnoff static int
2285714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2286069154d5SJulian Elischer {
2287069154d5SJulian Elischer 	hook_p  hook;
2288069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2289c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2290e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
229110e87318SAlexander Motin 	int	error = 0, depth;
2292069154d5SJulian Elischer 
2293f50597f5SAlexander Motin 	/* Node and item are never optional. */
2294f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2295f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2296f50597f5SAlexander Motin 
22971acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
229830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2299069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2300069154d5SJulian Elischer #endif
23018afe16d5SGleb Smirnoff 
23028afe16d5SGleb Smirnoff 	apply = item->apply;
230310e87318SAlexander Motin 	depth = item->depth;
23048afe16d5SGleb Smirnoff 
23056b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2306069154d5SJulian Elischer 	case NGQF_DATA:
2307069154d5SJulian Elischer 		/*
2308069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2309069154d5SJulian Elischer 		 */
2310f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2311f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2312f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2313a54a69d7SJulian Elischer 			error = EIO;
2314069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2315c4b5eea4SJulian Elischer 			break;
2316069154d5SJulian Elischer 		}
2317c4b5eea4SJulian Elischer 		/*
2318c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
2319c4b5eea4SJulian Elischer 		 * Give preference to the hook over-ride method
2320c4b5eea4SJulian Elischer 		 */
2321c4b5eea4SJulian Elischer 		if ((!(rcvdata = hook->hk_rcvdata))
2322c4b5eea4SJulian Elischer 		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2323c4b5eea4SJulian Elischer 			error = 0;
2324c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2325c4b5eea4SJulian Elischer 			break;
2326c4b5eea4SJulian Elischer 		}
2327a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2328069154d5SJulian Elischer 		break;
2329069154d5SJulian Elischer 	case NGQF_MESG:
2330e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2331069154d5SJulian Elischer 			/*
2332e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2333e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2334069154d5SJulian Elischer 			 * The message may not need it.
2335069154d5SJulian Elischer 			 */
233630400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2337069154d5SJulian Elischer 			hook = NULL;
2338069154d5SJulian Elischer 		}
2339069154d5SJulian Elischer 		/*
2340069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2341069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2342069154d5SJulian Elischer 		 */
234330400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
23446b795970SJulian Elischer 			TRAP_ERROR();
2345a54a69d7SJulian Elischer 			error = EINVAL;
2346069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2347e72a98f4SAlexander Motin 			break;
2348e72a98f4SAlexander Motin 		}
2349069154d5SJulian Elischer 		/*
2350069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2351069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2352069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2353e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2354069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2355069154d5SJulian Elischer 		 * reference a node or hook that has just been
2356069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2357069154d5SJulian Elischer 		 * is holding a reference, but..
2358069154d5SJulian Elischer 		 */
2359e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2360e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2361a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2362c4b5eea4SJulian Elischer 			break;
2363c4b5eea4SJulian Elischer 		}
2364e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2365e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
23666b795970SJulian Elischer 			TRAP_ERROR();
2367a54a69d7SJulian Elischer 			error = 0;
2368069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2369c4b5eea4SJulian Elischer 			break;
2370069154d5SJulian Elischer 		}
2371a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2372069154d5SJulian Elischer 		break;
23736b795970SJulian Elischer 	case NGQF_FN:
2374e088dd4cSAlexander Motin 	case NGQF_FN2:
2375e088dd4cSAlexander Motin 		/*
237674c9119dSAlexander Motin 		 * In the case of the shutdown message we allow it to hit
2377e088dd4cSAlexander Motin 		 * even if the node is invalid.
2378e088dd4cSAlexander Motin 		 */
237974c9119dSAlexander Motin 		if (NG_NODE_NOT_VALID(node) &&
238074c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmnode) {
2381e088dd4cSAlexander Motin 			TRAP_ERROR();
2382e088dd4cSAlexander Motin 			error = EINVAL;
2383e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2384e088dd4cSAlexander Motin 			break;
2385e088dd4cSAlexander Motin 		}
238674c9119dSAlexander Motin 		/* Same is about some internal functions and invalid hook. */
238774c9119dSAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook) &&
238874c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part2 &&
238974c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part3 &&
239074c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmhook_part2) {
239174c9119dSAlexander Motin 			TRAP_ERROR();
239274c9119dSAlexander Motin 			error = EINVAL;
239374c9119dSAlexander Motin 			NG_FREE_ITEM(item);
239474c9119dSAlexander Motin 			break;
239574c9119dSAlexander Motin 		}
239674c9119dSAlexander Motin 
2397b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2398b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2399b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2400b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2401b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2402e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2403e088dd4cSAlexander Motin 		break;
2404069154d5SJulian Elischer 	}
2405069154d5SJulian Elischer 	/*
2406069154d5SJulian Elischer 	 * We held references on some of the resources
2407069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2408069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2409069154d5SJulian Elischer 	 */
2410e72a98f4SAlexander Motin 	if (hook)
241130400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2412069154d5SJulian Elischer 
2413f50597f5SAlexander Motin  	if (rw == NGQRW_R)
24149852972bSAlexander Motin 		ng_leave_read(node);
2415f50597f5SAlexander Motin 	else
24169852972bSAlexander Motin 		ng_leave_write(node);
24178afe16d5SGleb Smirnoff 
24188afe16d5SGleb Smirnoff 	/* Apply callback. */
241910e87318SAlexander Motin 	if (apply != NULL) {
242010e87318SAlexander Motin 		if (depth == 1 && error != 0)
242110e87318SAlexander Motin 			apply->error = error;
242210e87318SAlexander Motin 		if (refcount_release(&apply->refs))
242310e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
242410e87318SAlexander Motin 	}
24258afe16d5SGleb Smirnoff 
242627757487SGleb Smirnoff 	return (error);
2427069154d5SJulian Elischer }
2428069154d5SJulian Elischer 
2429069154d5SJulian Elischer /***********************************************************************
2430069154d5SJulian Elischer  * Implement the 'generic' control messages
2431069154d5SJulian Elischer  ***********************************************************************/
2432069154d5SJulian Elischer static int
2433069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
24344cf49a43SJulian Elischer {
24354cf49a43SJulian Elischer 	int error = 0;
2436069154d5SJulian Elischer 	struct ng_mesg *msg;
2437069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
24384cf49a43SJulian Elischer 
2439069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
24404cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
24416b795970SJulian Elischer 		TRAP_ERROR();
2442069154d5SJulian Elischer 		error = EINVAL;
2443069154d5SJulian Elischer 		goto out;
24444cf49a43SJulian Elischer 	}
24454cf49a43SJulian Elischer 	switch (msg->header.cmd) {
24464cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
24471acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
24484cf49a43SJulian Elischer 		break;
24494cf49a43SJulian Elischer 	case NGM_MKPEER:
24504cf49a43SJulian Elischer 	    {
24514cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
24524cf49a43SJulian Elischer 
24534cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
24546b795970SJulian Elischer 			TRAP_ERROR();
2455069154d5SJulian Elischer 			error = EINVAL;
2456069154d5SJulian Elischer 			break;
24574cf49a43SJulian Elischer 		}
24584cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
24594cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
24604cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
24614cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
24624cf49a43SJulian Elischer 		break;
24634cf49a43SJulian Elischer 	    }
24644cf49a43SJulian Elischer 	case NGM_CONNECT:
24654cf49a43SJulian Elischer 	    {
24664cf49a43SJulian Elischer 		struct ngm_connect *const con =
24674cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
24684cf49a43SJulian Elischer 		node_p node2;
24694cf49a43SJulian Elischer 
24704cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
24716b795970SJulian Elischer 			TRAP_ERROR();
2472069154d5SJulian Elischer 			error = EINVAL;
2473069154d5SJulian Elischer 			break;
24744cf49a43SJulian Elischer 		}
24754cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
24764cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
24774cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2478069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2479069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
24804cf49a43SJulian Elischer 		if (error)
24814cf49a43SJulian Elischer 			break;
2482e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2483e088dd4cSAlexander Motin 		    node2, con->peerhook);
248430400f03SJulian Elischer 		NG_NODE_UNREF(node2);
24854cf49a43SJulian Elischer 		break;
24864cf49a43SJulian Elischer 	    }
24874cf49a43SJulian Elischer 	case NGM_NAME:
24884cf49a43SJulian Elischer 	    {
24894cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
24904cf49a43SJulian Elischer 
24914cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
24926b795970SJulian Elischer 			TRAP_ERROR();
2493069154d5SJulian Elischer 			error = EINVAL;
2494069154d5SJulian Elischer 			break;
24954cf49a43SJulian Elischer 		}
24964cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
24974cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
24984cf49a43SJulian Elischer 		break;
24994cf49a43SJulian Elischer 	    }
25004cf49a43SJulian Elischer 	case NGM_RMHOOK:
25014cf49a43SJulian Elischer 	    {
25024cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
25034cf49a43SJulian Elischer 		hook_p hook;
25044cf49a43SJulian Elischer 
25054cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
25066b795970SJulian Elischer 			TRAP_ERROR();
2507069154d5SJulian Elischer 			error = EINVAL;
2508069154d5SJulian Elischer 			break;
25094cf49a43SJulian Elischer 		}
25104cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2511899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
25124cf49a43SJulian Elischer 			ng_destroy_hook(hook);
25134cf49a43SJulian Elischer 		break;
25144cf49a43SJulian Elischer 	    }
25154cf49a43SJulian Elischer 	case NGM_NODEINFO:
25164cf49a43SJulian Elischer 	    {
25174cf49a43SJulian Elischer 		struct nodeinfo *ni;
25184cf49a43SJulian Elischer 
2519069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25204cf49a43SJulian Elischer 		if (resp == NULL) {
25214cf49a43SJulian Elischer 			error = ENOMEM;
25224cf49a43SJulian Elischer 			break;
25234cf49a43SJulian Elischer 		}
25244cf49a43SJulian Elischer 
25254cf49a43SJulian Elischer 		/* Fill in node info */
2526069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
252730400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
252887e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
252987e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2530dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
253130400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25324cf49a43SJulian Elischer 		break;
25334cf49a43SJulian Elischer 	    }
25344cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
25354cf49a43SJulian Elischer 	    {
253630400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
25374cf49a43SJulian Elischer 		struct hooklist *hl;
25384cf49a43SJulian Elischer 		struct nodeinfo *ni;
25394cf49a43SJulian Elischer 		hook_p hook;
25404cf49a43SJulian Elischer 
25414cf49a43SJulian Elischer 		/* Get response struct */
2542069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
25434cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2544069154d5SJulian Elischer 		if (resp == NULL) {
25454cf49a43SJulian Elischer 			error = ENOMEM;
25464cf49a43SJulian Elischer 			break;
25474cf49a43SJulian Elischer 		}
2548069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
25494cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
25504cf49a43SJulian Elischer 
25514cf49a43SJulian Elischer 		/* Fill in node info */
255230400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
255387e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
255487e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2555dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
25564cf49a43SJulian Elischer 
25574cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
25584cf49a43SJulian Elischer 		ni->hooks = 0;
255930400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
25604cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
25614cf49a43SJulian Elischer 
25624cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
25634cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
25646e551fb6SDavid E. O'Brien 				    __func__, "hooks");
25654cf49a43SJulian Elischer 				break;
25664cf49a43SJulian Elischer 			}
256730400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
25684cf49a43SJulian Elischer 				continue;
256987e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
257087e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
257130400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
257287e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
257387e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
257487e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
257587e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
257630400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
257730400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
25784cf49a43SJulian Elischer 			ni->hooks++;
25794cf49a43SJulian Elischer 		}
25804cf49a43SJulian Elischer 		break;
25814cf49a43SJulian Elischer 	    }
25824cf49a43SJulian Elischer 
25834cf49a43SJulian Elischer 	case NGM_LISTNAMES:
25844cf49a43SJulian Elischer 	case NGM_LISTNODES:
25854cf49a43SJulian Elischer 	    {
25864cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
25874cf49a43SJulian Elischer 		struct namelist *nl;
25884cf49a43SJulian Elischer 		node_p node;
2589cfea3f85SAlexander Motin 		int num = 0, i;
25904cf49a43SJulian Elischer 
2591cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
25924cf49a43SJulian Elischer 		/* Count number of nodes */
2593cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2594603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2595cfea3f85SAlexander Motin 				if (NG_NODE_IS_VALID(node) &&
2596cfea3f85SAlexander Motin 				    (unnamed || NG_NODE_HAS_NAME(node))) {
25974cf49a43SJulian Elischer 					num++;
25984cf49a43SJulian Elischer 				}
259970de87f2SJulian Elischer 			}
2600cfea3f85SAlexander Motin 		}
2601cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26024cf49a43SJulian Elischer 
26034cf49a43SJulian Elischer 		/* Get response struct */
2604069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
26054cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2606069154d5SJulian Elischer 		if (resp == NULL) {
26074cf49a43SJulian Elischer 			error = ENOMEM;
26084cf49a43SJulian Elischer 			break;
26094cf49a43SJulian Elischer 		}
2610069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
26114cf49a43SJulian Elischer 
26124cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
26134cf49a43SJulian Elischer 		nl->numnames = 0;
2614cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
2615cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2616603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2617cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2618cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
26194cf49a43SJulian Elischer 
2620b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2621b96baf0aSGleb Smirnoff 					continue;
2622b96baf0aSGleb Smirnoff 				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2623b96baf0aSGleb Smirnoff 					continue;
26244cf49a43SJulian Elischer 				if (nl->numnames >= num) {
2625cfea3f85SAlexander Motin 					log(LOG_ERR, "%s: number of nodes changed\n",
2626cfea3f85SAlexander Motin 					    __func__);
26274cf49a43SJulian Elischer 					break;
26284cf49a43SJulian Elischer 				}
262930400f03SJulian Elischer 				if (NG_NODE_HAS_NAME(node))
263087e2c66aSHartmut Brandt 					strcpy(np->name, NG_NODE_NAME(node));
263187e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2632dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
263330400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
26344cf49a43SJulian Elischer 				nl->numnames++;
26354cf49a43SJulian Elischer 			}
2636cfea3f85SAlexander Motin 		}
2637cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26384cf49a43SJulian Elischer 		break;
26394cf49a43SJulian Elischer 	    }
26404cf49a43SJulian Elischer 
26414cf49a43SJulian Elischer 	case NGM_LISTTYPES:
26424cf49a43SJulian Elischer 	    {
26434cf49a43SJulian Elischer 		struct typelist *tl;
26444cf49a43SJulian Elischer 		struct ng_type *type;
26454cf49a43SJulian Elischer 		int num = 0;
26464cf49a43SJulian Elischer 
26479ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
26484cf49a43SJulian Elischer 		/* Count number of types */
264970de87f2SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26504cf49a43SJulian Elischer 			num++;
265170de87f2SJulian Elischer 		}
26529ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26534cf49a43SJulian Elischer 
26544cf49a43SJulian Elischer 		/* Get response struct */
2655069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
26564cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2657069154d5SJulian Elischer 		if (resp == NULL) {
26584cf49a43SJulian Elischer 			error = ENOMEM;
26594cf49a43SJulian Elischer 			break;
26604cf49a43SJulian Elischer 		}
2661069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
26624cf49a43SJulian Elischer 
26634cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
26644cf49a43SJulian Elischer 		tl->numtypes = 0;
26659ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
2666069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26674cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
26684cf49a43SJulian Elischer 
26694cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
26704cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26716e551fb6SDavid E. O'Brien 				    __func__, "types");
26724cf49a43SJulian Elischer 				break;
26734cf49a43SJulian Elischer 			}
267487e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2675c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
26764cf49a43SJulian Elischer 			tl->numtypes++;
26774cf49a43SJulian Elischer 		}
26789ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26794cf49a43SJulian Elischer 		break;
26804cf49a43SJulian Elischer 	    }
26814cf49a43SJulian Elischer 
2682f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2683f8307e12SArchie Cobbs 	    {
26847133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2685f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2686f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2687069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2688f8307e12SArchie Cobbs 
2689f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2690f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
26914c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
26924c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
26934c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
26946b795970SJulian Elischer 			TRAP_ERROR();
2695f8307e12SArchie Cobbs 			error = EINVAL;
2696f8307e12SArchie Cobbs 			break;
2697f8307e12SArchie Cobbs 		}
2698f8307e12SArchie Cobbs 
2699f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2700069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2701069154d5SJulian Elischer 		if (resp == NULL) {
2702f8307e12SArchie Cobbs 			error = ENOMEM;
2703f8307e12SArchie Cobbs 			break;
2704f8307e12SArchie Cobbs 		}
2705069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2706f8307e12SArchie Cobbs 
2707f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2708f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2709f8307e12SArchie Cobbs 
2710f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
271130400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2712f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2713f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2714f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2715f8307e12SArchie Cobbs 				break;
2716f8307e12SArchie Cobbs 		}
2717f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2718f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2719f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2720f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2721f8307e12SArchie Cobbs 					break;
2722f8307e12SArchie Cobbs 			}
2723f8307e12SArchie Cobbs 			if (c->name == NULL) {
2724069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2725f8307e12SArchie Cobbs 				error = ENOSYS;
2726f8307e12SArchie Cobbs 				break;
2727f8307e12SArchie Cobbs 			}
2728f8307e12SArchie Cobbs 		}
2729f8307e12SArchie Cobbs 
2730f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2731f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2732f8307e12SArchie Cobbs 		    "%s", c->name);
2733f8307e12SArchie Cobbs 
2734f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2735f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2736f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
273770de87f2SJulian Elischer 		if (argstype == NULL) {
2738f8307e12SArchie Cobbs 			*ascii->data = '\0';
273970de87f2SJulian Elischer 		} else {
2740f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2741f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2742f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2743069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2744f8307e12SArchie Cobbs 				break;
2745f8307e12SArchie Cobbs 			}
2746f8307e12SArchie Cobbs 		}
2747f8307e12SArchie Cobbs 
2748f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2749f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2750f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2751069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2752f8307e12SArchie Cobbs 		break;
2753f8307e12SArchie Cobbs 	    }
2754f8307e12SArchie Cobbs 
2755f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2756f8307e12SArchie Cobbs 	    {
275798a5a343SMarko Zec 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2758f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2759f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2760069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
276152ec4a03SArchie Cobbs 		int off = 0;
2762f8307e12SArchie Cobbs 
2763f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2764f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
27654c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
27664c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
27674c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
27684c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
27696b795970SJulian Elischer 			TRAP_ERROR();
2770f8307e12SArchie Cobbs 			error = EINVAL;
2771f8307e12SArchie Cobbs 			break;
2772f8307e12SArchie Cobbs 		}
2773f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2774f8307e12SArchie Cobbs 
2775f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2776069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2777069154d5SJulian Elischer 		if (resp == NULL) {
2778f8307e12SArchie Cobbs 			error = ENOMEM;
2779f8307e12SArchie Cobbs 			break;
2780f8307e12SArchie Cobbs 		}
2781069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2782f8307e12SArchie Cobbs 
2783f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2784f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2785f8307e12SArchie Cobbs 
2786f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
278730400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2788f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2789f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2790f8307e12SArchie Cobbs 				break;
2791f8307e12SArchie Cobbs 		}
2792f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2793f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2794f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2795f8307e12SArchie Cobbs 					break;
2796f8307e12SArchie Cobbs 			}
2797f8307e12SArchie Cobbs 			if (c->name == NULL) {
2798069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2799f8307e12SArchie Cobbs 				error = ENOSYS;
2800f8307e12SArchie Cobbs 				break;
2801f8307e12SArchie Cobbs 			}
2802f8307e12SArchie Cobbs 		}
2803f8307e12SArchie Cobbs 
2804f8307e12SArchie Cobbs 		/* Convert command name to binary */
2805f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2806f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2807f8307e12SArchie Cobbs 
2808f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2809f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2810f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
281170de87f2SJulian Elischer 		if (argstype == NULL) {
2812f8307e12SArchie Cobbs 			bufSize = 0;
281370de87f2SJulian Elischer 		} else {
2814f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2815f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2816069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2817f8307e12SArchie Cobbs 				break;
2818f8307e12SArchie Cobbs 			}
2819f8307e12SArchie Cobbs 		}
2820f8307e12SArchie Cobbs 
2821f8307e12SArchie Cobbs 		/* Return the result */
2822f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2823069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2824f8307e12SArchie Cobbs 		break;
2825f8307e12SArchie Cobbs 	    }
2826f8307e12SArchie Cobbs 
28277095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
28284cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
28294cf49a43SJulian Elischer 		/*
28304cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
28314cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2832069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
28334cf49a43SJulian Elischer 		 * when control passes back to us.
28344cf49a43SJulian Elischer 		 */
283530400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2836069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
283730400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
28384cf49a43SJulian Elischer 		}
28394cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
28404cf49a43SJulian Elischer 	default:
28416b795970SJulian Elischer 		TRAP_ERROR();
28424cf49a43SJulian Elischer 		error = EINVAL;
28434cf49a43SJulian Elischer 	}
2844069154d5SJulian Elischer 	/*
2845069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2846069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2847069154d5SJulian Elischer 	 * Don't free it if it is so.
2848069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2849069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2850069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2851069154d5SJulian Elischer 	 */
2852069154d5SJulian Elischer out:
2853069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2854069154d5SJulian Elischer 	NG_FREE_MSG(msg);
28554cf49a43SJulian Elischer 	return (error);
28564cf49a43SJulian Elischer }
28574cf49a43SJulian Elischer 
28584cf49a43SJulian Elischer /************************************************************************
28598253c060SGleb Smirnoff 			Queue element get/free routines
28608253c060SGleb Smirnoff ************************************************************************/
28618253c060SGleb Smirnoff 
28628253c060SGleb Smirnoff uma_zone_t			ng_qzone;
28636aa6d011SAlexander Motin uma_zone_t			ng_qdzone;
2864f2fbb838SAlexander Motin static int			numthreads = 0; /* number of queue threads */
2865ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2866ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
28678253c060SGleb Smirnoff 
2868f2fbb838SAlexander Motin TUNABLE_INT("net.graph.threads", &numthreads);
2869f2fbb838SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2870f2fbb838SAlexander Motin     0, "Number of queue processing threads");
28718253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
28728253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
28736aa6d011SAlexander Motin     0, "Maximum number of non-data queue items to allocate");
2874ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
28756aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
28766aa6d011SAlexander Motin     0, "Maximum number of data queue items to allocate");
28778253c060SGleb Smirnoff 
28788253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
28798253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
28808253c060SGleb Smirnoff static int			allocated;	/* number of items malloc'd */
28818253c060SGleb Smirnoff #endif
28828253c060SGleb Smirnoff 
28838253c060SGleb Smirnoff /*
28848253c060SGleb Smirnoff  * Get a queue entry.
28858253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
28868253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
28878253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
28888253c060SGleb Smirnoff  * an interrupt.
28898253c060SGleb Smirnoff  */
28908253c060SGleb Smirnoff static __inline item_p
28916aa6d011SAlexander Motin ng_alloc_item(int type, int flags)
28928253c060SGleb Smirnoff {
28936aa6d011SAlexander Motin 	item_p item;
28948253c060SGleb Smirnoff 
28956aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
28966aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
289742282202SGleb Smirnoff 
28986aa6d011SAlexander Motin 	item = uma_zalloc((type == NGQF_DATA)?ng_qdzone:ng_qzone,
28996aa6d011SAlexander Motin 	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
29008253c060SGleb Smirnoff 
29018253c060SGleb Smirnoff 	if (item) {
29026aa6d011SAlexander Motin 		item->el_flags = type;
29036aa6d011SAlexander Motin #ifdef	NETGRAPH_DEBUG
29048253c060SGleb Smirnoff 		mtx_lock(&ngq_mtx);
29058253c060SGleb Smirnoff 		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
29068253c060SGleb Smirnoff 		allocated++;
29078253c060SGleb Smirnoff 		mtx_unlock(&ngq_mtx);
29088253c060SGleb Smirnoff #endif
29096aa6d011SAlexander Motin 	}
29108253c060SGleb Smirnoff 
29118253c060SGleb Smirnoff 	return (item);
29128253c060SGleb Smirnoff }
29138253c060SGleb Smirnoff 
29148253c060SGleb Smirnoff /*
29158253c060SGleb Smirnoff  * Release a queue entry
29168253c060SGleb Smirnoff  */
29178253c060SGleb Smirnoff void
29188253c060SGleb Smirnoff ng_free_item(item_p item)
29198253c060SGleb Smirnoff {
29208253c060SGleb Smirnoff 	/*
29218253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
29228253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
29238253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
29248253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
29258253c060SGleb Smirnoff 	 * or we release them again here.
29268253c060SGleb Smirnoff 	 */
29278253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
29288253c060SGleb Smirnoff 	case NGQF_DATA:
29298253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
29308253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
29318253c060SGleb Smirnoff 		break;
29328253c060SGleb Smirnoff 	case NGQF_MESG:
29338253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
29348253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
29358253c060SGleb Smirnoff 		break;
29368253c060SGleb Smirnoff 	case NGQF_FN:
2937e088dd4cSAlexander Motin 	case NGQF_FN2:
29388253c060SGleb Smirnoff 		/* nothing to free really, */
29398253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
29408253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
29418253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
29428253c060SGleb Smirnoff 		break;
29438253c060SGleb Smirnoff 	}
29448253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
29458253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
29468253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
29478253c060SGleb Smirnoff 
29488253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29498253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
29508253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
29518253c060SGleb Smirnoff 	allocated--;
29528253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
29538253c060SGleb Smirnoff #endif
29546aa6d011SAlexander Motin 	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA)?
29556aa6d011SAlexander Motin 	    ng_qdzone:ng_qzone, item);
29566aa6d011SAlexander Motin }
29576aa6d011SAlexander Motin 
29586aa6d011SAlexander Motin /*
29596aa6d011SAlexander Motin  * Change type of the queue entry.
29606aa6d011SAlexander Motin  * Possibly reallocates it from another UMA zone.
29616aa6d011SAlexander Motin  */
29626aa6d011SAlexander Motin static __inline item_p
29636aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags)
29646aa6d011SAlexander Motin {
29656aa6d011SAlexander Motin 	item_p item;
29666aa6d011SAlexander Motin 	int from, to;
29676aa6d011SAlexander Motin 
29686aa6d011SAlexander Motin 	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
29696aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29706aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
29716aa6d011SAlexander Motin 
29726aa6d011SAlexander Motin 	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
29736aa6d011SAlexander Motin 	to = (type == NGQF_DATA);
29746aa6d011SAlexander Motin 	if (from != to) {
29756aa6d011SAlexander Motin 		/* If reallocation is required do it and copy item. */
29766aa6d011SAlexander Motin 		if ((item = ng_alloc_item(type, flags)) == NULL) {
29776aa6d011SAlexander Motin 			ng_free_item(pitem);
29786aa6d011SAlexander Motin 			return (NULL);
29796aa6d011SAlexander Motin 		}
29806aa6d011SAlexander Motin 		*item = *pitem;
29816aa6d011SAlexander Motin 		ng_free_item(pitem);
29826aa6d011SAlexander Motin 	} else
29836aa6d011SAlexander Motin 		item = pitem;
29846aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
29856aa6d011SAlexander Motin 
29866aa6d011SAlexander Motin 	return (item);
29878253c060SGleb Smirnoff }
29888253c060SGleb Smirnoff 
29898253c060SGleb Smirnoff /************************************************************************
29904cf49a43SJulian Elischer 			Module routines
29914cf49a43SJulian Elischer ************************************************************************/
29924cf49a43SJulian Elischer 
29934cf49a43SJulian Elischer /*
29944cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
29954cf49a43SJulian Elischer  */
29964cf49a43SJulian Elischer int
29974cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
29984cf49a43SJulian Elischer {
29994cf49a43SJulian Elischer 	struct ng_type *const type = data;
30004cf49a43SJulian Elischer 	int s, error = 0;
30014cf49a43SJulian Elischer 
30024cf49a43SJulian Elischer 	switch (event) {
30034cf49a43SJulian Elischer 	case MOD_LOAD:
30044cf49a43SJulian Elischer 
30054cf49a43SJulian Elischer 		/* Register new netgraph node type */
30064cf49a43SJulian Elischer 		s = splnet();
30074cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
30084cf49a43SJulian Elischer 			splx(s);
30094cf49a43SJulian Elischer 			break;
30104cf49a43SJulian Elischer 		}
30114cf49a43SJulian Elischer 
30124cf49a43SJulian Elischer 		/* Call type specific code */
30134cf49a43SJulian Elischer 		if (type->mod_event != NULL)
3014069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
30159ed346baSBosko Milekic 				mtx_lock(&ng_typelist_mtx);
3016c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
30174cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
30189ed346baSBosko Milekic 				mtx_unlock(&ng_typelist_mtx);
3019069154d5SJulian Elischer 			}
30204cf49a43SJulian Elischer 		splx(s);
30214cf49a43SJulian Elischer 		break;
30224cf49a43SJulian Elischer 
30234cf49a43SJulian Elischer 	case MOD_UNLOAD:
30244cf49a43SJulian Elischer 		s = splnet();
3025c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
30264cf49a43SJulian Elischer 			error = EBUSY;
3027c73b94a2SJulian Elischer 		} else {
3028c73b94a2SJulian Elischer 			if (type->refs == 0) {
3029c73b94a2SJulian Elischer 				/* failed load, nothing to undo */
3030c73b94a2SJulian Elischer 				splx(s);
3031c73b94a2SJulian Elischer 				break;
3032c73b94a2SJulian Elischer 			}
30334cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
30344cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
30354cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
30364cf49a43SJulian Elischer 					splx(s);
30374cf49a43SJulian Elischer 					break;
30384cf49a43SJulian Elischer 				}
30394cf49a43SJulian Elischer 			}
30409ed346baSBosko Milekic 			mtx_lock(&ng_typelist_mtx);
30414cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
30429ed346baSBosko Milekic 			mtx_unlock(&ng_typelist_mtx);
30434cf49a43SJulian Elischer 		}
30444cf49a43SJulian Elischer 		splx(s);
30454cf49a43SJulian Elischer 		break;
30464cf49a43SJulian Elischer 
30474cf49a43SJulian Elischer 	default:
30484cf49a43SJulian Elischer 		if (type->mod_event != NULL)
30494cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
30504cf49a43SJulian Elischer 		else
30513e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
30524cf49a43SJulian Elischer 		break;
30534cf49a43SJulian Elischer 	}
30544cf49a43SJulian Elischer 	return (error);
30554cf49a43SJulian Elischer }
30564cf49a43SJulian Elischer 
3057eddfbb76SRobert Watson #ifdef VIMAGE
3058d0728d71SRobert Watson static void
3059d0728d71SRobert Watson vnet_netgraph_uninit(const void *unused __unused)
3060bc29160dSMarko Zec {
3061a3f93b72SMarko Zec 	node_p node = NULL, last_killed = NULL;
3062a3f93b72SMarko Zec 	int i;
3063bc29160dSMarko Zec 
3064a3f93b72SMarko Zec 	do {
3065a3f93b72SMarko Zec 		/* Find a node to kill */
3066a3f93b72SMarko Zec 		mtx_lock(&ng_namehash_mtx);
3067a3f93b72SMarko Zec 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
3068a3f93b72SMarko Zec 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
3069a3f93b72SMarko Zec 				if (node != &ng_deadnode) {
3070a3f93b72SMarko Zec 					NG_NODE_REF(node);
3071a3f93b72SMarko Zec 					break;
3072a3f93b72SMarko Zec 				}
3073a3f93b72SMarko Zec 			}
3074a3f93b72SMarko Zec 			if (node != NULL)
3075a3f93b72SMarko Zec 				break;
3076a3f93b72SMarko Zec 		}
3077a3f93b72SMarko Zec 		mtx_unlock(&ng_namehash_mtx);
3078a3f93b72SMarko Zec 
3079a3f93b72SMarko Zec 		/* Attempt to kill it only if it is a regular node */
3080a3f93b72SMarko Zec 		if (node != NULL) {
3081bc29160dSMarko Zec 			if (node == last_killed) {
3082bc29160dSMarko Zec 				/* This should never happen */
3083a3f93b72SMarko Zec 				printf("ng node %s needs"
3084a3f93b72SMarko Zec 				    "NGF_REALLY_DIE\n", node->nd_name);
3085a3f93b72SMarko Zec 				if (node->nd_flags & NGF_REALLY_DIE)
3086a3f93b72SMarko Zec 					panic("ng node %s won't die",
3087a3f93b72SMarko Zec 					    node->nd_name);
3088bc29160dSMarko Zec 				node->nd_flags |= NGF_REALLY_DIE;
3089bc29160dSMarko Zec 			}
3090bc29160dSMarko Zec 			ng_rmnode(node, NULL, NULL, 0);
3091a3f93b72SMarko Zec 			NG_NODE_UNREF(node);
3092bc29160dSMarko Zec 			last_killed = node;
3093bc29160dSMarko Zec 		}
3094a3f93b72SMarko Zec 	} while (node != NULL);
3095bc29160dSMarko Zec }
3096a3f93b72SMarko Zec VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
3097d0728d71SRobert Watson     vnet_netgraph_uninit, NULL);
3098bc29160dSMarko Zec #endif /* VIMAGE */
3099bc29160dSMarko Zec 
31004cf49a43SJulian Elischer /*
31014cf49a43SJulian Elischer  * Handle loading and unloading for this code.
31024cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
31034cf49a43SJulian Elischer  */
31044cf49a43SJulian Elischer static int
31054cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
31064cf49a43SJulian Elischer {
3107f2fbb838SAlexander Motin 	struct proc *p;
3108f2fbb838SAlexander Motin 	struct thread *td;
3109f2fbb838SAlexander Motin 	int i, error = 0;
31104cf49a43SJulian Elischer 
31114cf49a43SJulian Elischer 	switch (event) {
31124cf49a43SJulian Elischer 	case MOD_LOAD:
31131489164fSGleb Smirnoff 		/* Initialize everything. */
31142c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3115efd8e7c9SDon Lewis 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3116efd8e7c9SDon Lewis 		    MTX_DEF);
3117efd8e7c9SDon Lewis 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3118efd8e7c9SDon Lewis 		    MTX_DEF);
3119cfea3f85SAlexander Motin 		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3120cfea3f85SAlexander Motin 		    MTX_DEF);
3121ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3122ac5dd141SGleb Smirnoff 		    MTX_DEF);
31231489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3124cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3125cfea3f85SAlexander Motin 		    MTX_DEF);
31261489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3127efd8e7c9SDon Lewis 		    MTX_DEF);
31281489164fSGleb Smirnoff #endif
31291489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
31301489164fSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
31311489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
31326aa6d011SAlexander Motin 		ng_qdzone = uma_zcreate("NetGraph data items", sizeof(struct ng_item),
31336aa6d011SAlexander Motin 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
31346aa6d011SAlexander Motin 		uma_zone_set_max(ng_qdzone, maxdata);
3135f2fbb838SAlexander Motin 		/* Autoconfigure number of threads. */
3136f2fbb838SAlexander Motin 		if (numthreads <= 0)
3137f2fbb838SAlexander Motin 			numthreads = mp_ncpus;
3138f2fbb838SAlexander Motin 		/* Create threads. */
3139f2fbb838SAlexander Motin     		p = NULL; /* start with no process */
3140f2fbb838SAlexander Motin 		for (i = 0; i < numthreads; i++) {
3141f2fbb838SAlexander Motin 			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3142f2fbb838SAlexander Motin 			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3143f2fbb838SAlexander Motin 				numthreads = i;
3144f2fbb838SAlexander Motin 				break;
3145f2fbb838SAlexander Motin 			}
3146f2fbb838SAlexander Motin 		}
31474cf49a43SJulian Elischer 		break;
31484cf49a43SJulian Elischer 	case MOD_UNLOAD:
314964efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
31504cf49a43SJulian Elischer 		error = EBUSY;
31514cf49a43SJulian Elischer 		break;
31524cf49a43SJulian Elischer 	default:
31534cf49a43SJulian Elischer 		error = EOPNOTSUPP;
31544cf49a43SJulian Elischer 		break;
31554cf49a43SJulian Elischer 	}
31564cf49a43SJulian Elischer 	return (error);
31574cf49a43SJulian Elischer }
31584cf49a43SJulian Elischer 
31594cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
31604cf49a43SJulian Elischer 	"netgraph",
31614cf49a43SJulian Elischer 	ngb_mod_event,
31624cf49a43SJulian Elischer 	(NULL)
31634cf49a43SJulian Elischer };
3164aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
3165bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3166bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3167bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
31684cf49a43SJulian Elischer 
316930400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
317030400f03SJulian Elischer void
317130400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
317230400f03SJulian Elischer {
317330400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
317430400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
317530400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
317630400f03SJulian Elischer 		hook->lastfile, hook->lastline);
317730400f03SJulian Elischer 	if (line) {
317830400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
317930400f03SJulian Elischer 	}
318030400f03SJulian Elischer }
318130400f03SJulian Elischer 
318230400f03SJulian Elischer void
318330400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
318430400f03SJulian Elischer {
318530400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
31866b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
318730400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
318830400f03SJulian Elischer 		node->nd_refs, node->nd_name);
318930400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
319030400f03SJulian Elischer 		node->lastfile, node->lastline);
319130400f03SJulian Elischer 	if (line) {
319230400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
319330400f03SJulian Elischer 	}
319430400f03SJulian Elischer }
319530400f03SJulian Elischer 
3196069154d5SJulian Elischer void
3197069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3198069154d5SJulian Elischer {
3199069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3200069154d5SJulian Elischer 		item->lastfile, item->lastline);
32016b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
32026b795970SJulian Elischer 	case NGQF_DATA:
3203069154d5SJulian Elischer 		printf(" - [data]\n");
32046b795970SJulian Elischer 		break;
32056b795970SJulian Elischer 	case NGQF_MESG:
32066b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
32076b795970SJulian Elischer 		break;
32086b795970SJulian Elischer 	case NGQF_FN:
3209857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3210857304e6SRuslan Ermilov 			_NGI_FN(item),
3211857304e6SRuslan Ermilov 			_NGI_NODE(item),
3212857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3213857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3214857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3215857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3216857304e6SRuslan Ermilov 		break;
3217e088dd4cSAlexander Motin 	case NGQF_FN2:
3218eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3219857304e6SRuslan Ermilov 			_NGI_FN2(item),
32206064e568SGleb Smirnoff 			_NGI_NODE(item),
32216064e568SGleb Smirnoff 			_NGI_HOOK(item),
32226b795970SJulian Elischer 			item->body.fn.fn_arg1,
32236b795970SJulian Elischer 			item->body.fn.fn_arg2,
32246b795970SJulian Elischer 			item->body.fn.fn_arg2);
32256b795970SJulian Elischer 		break;
3226069154d5SJulian Elischer 	}
322730400f03SJulian Elischer 	if (line) {
3228069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
32296064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
323030400f03SJulian Elischer 			printf("node %p ([%x])\n",
32316064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3232069154d5SJulian Elischer 		}
323330400f03SJulian Elischer 	}
323430400f03SJulian Elischer }
323530400f03SJulian Elischer 
323630400f03SJulian Elischer static void
323730400f03SJulian Elischer ng_dumpitems(void)
323830400f03SJulian Elischer {
323930400f03SJulian Elischer 	item_p item;
324030400f03SJulian Elischer 	int i = 1;
324130400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
324230400f03SJulian Elischer 		printf("[%d] ", i++);
324330400f03SJulian Elischer 		dumpitem(item, NULL, 0);
324430400f03SJulian Elischer 	}
324530400f03SJulian Elischer }
324630400f03SJulian Elischer 
324730400f03SJulian Elischer static void
324830400f03SJulian Elischer ng_dumpnodes(void)
324930400f03SJulian Elischer {
325030400f03SJulian Elischer 	node_p node;
325130400f03SJulian Elischer 	int i = 1;
325253f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
325330400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
325430400f03SJulian Elischer 		printf("[%d] ", i++);
325530400f03SJulian Elischer 		dumpnode(node, NULL, 0);
325630400f03SJulian Elischer 	}
325753f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
325830400f03SJulian Elischer }
325930400f03SJulian Elischer 
326030400f03SJulian Elischer static void
326130400f03SJulian Elischer ng_dumphooks(void)
326230400f03SJulian Elischer {
326330400f03SJulian Elischer 	hook_p hook;
326430400f03SJulian Elischer 	int i = 1;
326553f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
326630400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
326730400f03SJulian Elischer 		printf("[%d] ", i++);
326830400f03SJulian Elischer 		dumphook(hook, NULL, 0);
326930400f03SJulian Elischer 	}
327053f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
327130400f03SJulian Elischer }
3272069154d5SJulian Elischer 
3273069154d5SJulian Elischer static int
3274069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3275069154d5SJulian Elischer {
3276069154d5SJulian Elischer 	int error;
3277069154d5SJulian Elischer 	int val;
3278069154d5SJulian Elischer 	int i;
3279069154d5SJulian Elischer 
3280069154d5SJulian Elischer 	val = allocated;
3281069154d5SJulian Elischer 	i = 1;
3282041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
32836b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
32846b795970SJulian Elischer 		return (error);
32856b795970SJulian Elischer 	if (val == 42) {
328630400f03SJulian Elischer 		ng_dumpitems();
328730400f03SJulian Elischer 		ng_dumpnodes();
328830400f03SJulian Elischer 		ng_dumphooks();
3289069154d5SJulian Elischer 	}
32906b795970SJulian Elischer 	return (0);
3291069154d5SJulian Elischer }
3292069154d5SJulian Elischer 
32936b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
32946b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
329530400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3296069154d5SJulian Elischer 
3297069154d5SJulian Elischer 
3298069154d5SJulian Elischer /***********************************************************************
3299069154d5SJulian Elischer * Worklist routines
3300069154d5SJulian Elischer **********************************************************************/
3301069154d5SJulian Elischer /*
3302069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3303f2fbb838SAlexander Motin  * try get an item to process off it. Remove the node from the list.
3304069154d5SJulian Elischer  */
3305069154d5SJulian Elischer static void
3306f2fbb838SAlexander Motin ngthread(void *arg)
3307069154d5SJulian Elischer {
3308069154d5SJulian Elischer 	for (;;) {
3309394cb30aSAlexander Motin 		node_p  node;
3310394cb30aSAlexander Motin 
3311394cb30aSAlexander Motin 		/* Get node from the worklist. */
33122c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
3313f2fbb838SAlexander Motin 		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3314f2fbb838SAlexander Motin 			NG_WORKLIST_SLEEP();
33159852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
33162c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3317bc29160dSMarko Zec 		CURVNET_SET(node->nd_vnet);
33182955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
33192955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3320069154d5SJulian Elischer 		/*
3321069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3322069154d5SJulian Elischer 		 * that the list had on it.
3323069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3324069154d5SJulian Elischer 		 * let you have another item off the queue.
3325069154d5SJulian Elischer 		 * All this time, keep the reference
3326069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3327069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3328069154d5SJulian Elischer 		 */
3329069154d5SJulian Elischer 		for (;;) {
3330394cb30aSAlexander Motin 			item_p item;
3331714fb865SGleb Smirnoff 			int rw;
3332714fb865SGleb Smirnoff 
33332c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
33349852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3335069154d5SJulian Elischer 			if (item == NULL) {
33369852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
33372c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3338069154d5SJulian Elischer 				break; /* go look for another node */
3339069154d5SJulian Elischer 			} else {
33402c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
33415951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3342714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
33435951069aSJulian Elischer 				NG_NODE_UNREF(node);
3344069154d5SJulian Elischer 			}
3345069154d5SJulian Elischer 		}
3346a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3347bc29160dSMarko Zec 		CURVNET_RESTORE();
3348069154d5SJulian Elischer 	}
3349069154d5SJulian Elischer }
3350069154d5SJulian Elischer 
335133338e73SJulian Elischer /*
335233338e73SJulian Elischer  * XXX
335333338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
335433338e73SJulian Elischer  * to be outside the mutex zone
335533338e73SJulian Elischer  */
3356069154d5SJulian Elischer static void
3357394cb30aSAlexander Motin ng_worklist_add(node_p node)
3358069154d5SJulian Elischer {
3359f912c0f7SGleb Smirnoff 
33605bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3361f912c0f7SGleb Smirnoff 
33629852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3363069154d5SJulian Elischer 		/*
3364069154d5SJulian Elischer 		 * If we are not already on the work queue,
3365069154d5SJulian Elischer 		 * then put us on.
3366069154d5SJulian Elischer 		 */
33679852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3368394cb30aSAlexander Motin 		NG_NODE_REF(node); /* XXX fafe in mutex? */
33692c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
33709852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
33712c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
33722955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
33732955ee18SGleb Smirnoff 		    node->nd_ID, node);
3374f2fbb838SAlexander Motin 		NG_WORKLIST_WAKEUP();
3375394cb30aSAlexander Motin 	} else {
33762955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
33772955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3378394cb30aSAlexander Motin 	}
3379069154d5SJulian Elischer }
3380069154d5SJulian Elischer 
3381069154d5SJulian Elischer 
3382069154d5SJulian Elischer /***********************************************************************
3383069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3384069154d5SJulian Elischer ***********************************************************************/
3385069154d5SJulian Elischer 
338630400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
338730400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
33884cf49a43SJulian Elischer 	do {								\
33891acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3390069154d5SJulian Elischer 			printf("item already has node");		\
33913de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
33921acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3393069154d5SJulian Elischer 		}							\
33941acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3395069154d5SJulian Elischer 			printf("item already has hook");		\
33963de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
33971acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3398069154d5SJulian Elischer 		}							\
3399069154d5SJulian Elischer 	} while (0)
3400069154d5SJulian Elischer #else
340130400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3402069154d5SJulian Elischer #endif
3403069154d5SJulian Elischer 
3404069154d5SJulian Elischer /*
34058ed370fdSJulian Elischer  * Put mbuf into the item.
3406069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3407069154d5SJulian Elischer  * (or equivalent)
3408069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3409069154d5SJulian Elischer  * remote node might go away in this timescale.
3410069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3411069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
34124be59335SGleb Smirnoff  * here to be able to do this.
3413069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3414069154d5SJulian Elischer  *
3415069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3416069154d5SJulian Elischer  */
3417069154d5SJulian Elischer item_p
341842282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3419069154d5SJulian Elischer {
3420069154d5SJulian Elischer 	item_p item;
3421069154d5SJulian Elischer 
34226aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3423069154d5SJulian Elischer 		NG_FREE_M(m);
3424069154d5SJulian Elischer 		return (NULL);
3425069154d5SJulian Elischer 	}
342630400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34276aa6d011SAlexander Motin 	item->el_flags |= NGQF_READER;
3428069154d5SJulian Elischer 	NGI_M(item) = m;
3429069154d5SJulian Elischer 	return (item);
3430069154d5SJulian Elischer }
3431069154d5SJulian Elischer 
3432069154d5SJulian Elischer /*
3433069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3434069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3435069154d5SJulian Elischer  * to work out what some of the fields should be.
3436069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3437069154d5SJulian Elischer  * (or equivalent)
3438069154d5SJulian Elischer  */
3439069154d5SJulian Elischer item_p
344042282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3441069154d5SJulian Elischer {
3442069154d5SJulian Elischer 	item_p item;
3443069154d5SJulian Elischer 
34446aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3445069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3446069154d5SJulian Elischer 		return (NULL);
3447069154d5SJulian Elischer 	}
344830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34496f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
34506f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
34516aa6d011SAlexander Motin 		item->el_flags |= NGQF_READER;
34526f683eeeSGleb Smirnoff 	else
34536aa6d011SAlexander Motin 		item->el_flags |= NGQF_WRITER;
3454069154d5SJulian Elischer 	/*
3455069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3456069154d5SJulian Elischer 	 */
3457069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3458facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3459069154d5SJulian Elischer 	return (item);
3460069154d5SJulian Elischer }
3461069154d5SJulian Elischer 
3462069154d5SJulian Elischer 
3463069154d5SJulian Elischer 
34641acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
34656b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
34666b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3467069154d5SJulian Elischer 			if (retaddr) {					\
3468069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
34694cf49a43SJulian Elischer 			} else {					\
3470069154d5SJulian Elischer 				/*					\
3471069154d5SJulian Elischer 				 * The old return address should be ok.	\
3472069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3473069154d5SJulian Elischer 				 * here.				\
3474069154d5SJulian Elischer 				 */					\
3475069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3476069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3477069154d5SJulian Elischer 						= ng_node2ID(here);	\
3478069154d5SJulian Elischer 				}					\
3479069154d5SJulian Elischer 			}						\
34804cf49a43SJulian Elischer 		}							\
34814cf49a43SJulian Elischer 	} while (0)
34824cf49a43SJulian Elischer 
34834cf49a43SJulian Elischer int
3484069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
34854cf49a43SJulian Elischer {
34861acb27c6SJulian Elischer 	hook_p peer;
34871acb27c6SJulian Elischer 	node_p peernode;
348830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3489069154d5SJulian Elischer 	/*
3490069154d5SJulian Elischer 	 * Quick sanity check..
349130400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
349230400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
349330400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3494069154d5SJulian Elischer 	 */
3495a7da736aSGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
3496a04e9846SAlexander Motin 	if ((hook == NULL) ||
3497a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(hook) ||
3498a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3499a04e9846SAlexander Motin 	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3500069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35016b795970SJulian Elischer 		TRAP_ERROR();
3502a7da736aSGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
3503e08d3e3cSJulian Elischer 		return (ENETDOWN);
35044cf49a43SJulian Elischer 	}
35054cf49a43SJulian Elischer 
35064cf49a43SJulian Elischer 	/*
3507069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
35084cf49a43SJulian Elischer 	 */
35091acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
35101acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
3511a04e9846SAlexander Motin 	NGI_SET_HOOK(item, peer);
35121acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
35138b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3514a7da736aSGleb Smirnoff 
3515a7da736aSGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
3516a7da736aSGleb Smirnoff 
3517069154d5SJulian Elischer 	return (0);
3518069154d5SJulian Elischer }
3519069154d5SJulian Elischer 
35204cf49a43SJulian Elischer int
3521069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
35224cf49a43SJulian Elischer {
35234cf49a43SJulian Elischer 	node_p	dest = NULL;
3524069154d5SJulian Elischer 	hook_p	hook = NULL;
35254cf49a43SJulian Elischer 	int	error;
3526069154d5SJulian Elischer 
352730400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3528069154d5SJulian Elischer 	/*
3529069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3530069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3531069154d5SJulian Elischer 	 */
3532069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3533069154d5SJulian Elischer 	if (error) {
3534069154d5SJulian Elischer 		NG_FREE_ITEM(item);
353530400f03SJulian Elischer 		return (error);
3536069154d5SJulian Elischer 	}
35371acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
3538a7da736aSGleb Smirnoff 	if (hook)
35391acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
3540a7da736aSGleb Smirnoff 
35411acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3542069154d5SJulian Elischer 	return (0);
3543069154d5SJulian Elischer }
3544069154d5SJulian Elischer 
3545069154d5SJulian Elischer int
3546069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3547069154d5SJulian Elischer {
3548069154d5SJulian Elischer 	node_p dest;
3549069154d5SJulian Elischer 
355030400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3551069154d5SJulian Elischer 	/*
3552069154d5SJulian Elischer 	 * Find the target node.
3553069154d5SJulian Elischer 	 */
3554069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3555069154d5SJulian Elischer 	if (dest == NULL) {
3556069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35576b795970SJulian Elischer 		TRAP_ERROR();
3558069154d5SJulian Elischer 		return(EINVAL);
3559069154d5SJulian Elischer 	}
3560069154d5SJulian Elischer 	/* Fill out the contents */
35611acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
35621acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
35631acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3564069154d5SJulian Elischer 	return (0);
3565069154d5SJulian Elischer }
3566069154d5SJulian Elischer 
3567069154d5SJulian Elischer /*
3568069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3569069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3570069154d5SJulian Elischer  * Useful for removing that hook :-)
3571069154d5SJulian Elischer  */
3572069154d5SJulian Elischer item_p
3573069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3574069154d5SJulian Elischer {
3575069154d5SJulian Elischer 	item_p item;
35764cf49a43SJulian Elischer 
3577859a4d16SJulian Elischer 	/*
3578859a4d16SJulian Elischer 	 * Find the target node.
3579859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3580859a4d16SJulian Elischer 	 * to the address.
3581859a4d16SJulian Elischer 	 */
35826aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3583069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3584069154d5SJulian Elischer 		return (NULL);
35854cf49a43SJulian Elischer 	}
35864cf49a43SJulian Elischer 
35874cf49a43SJulian Elischer 	/* Fill out the contents */
35886aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
358930400f03SJulian Elischer 	NG_NODE_REF(here);
35901acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
35911acb27c6SJulian Elischer 	if (hook) {
359230400f03SJulian Elischer 		NG_HOOK_REF(hook);
35931acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35941acb27c6SJulian Elischer 	}
3595069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3596069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3597069154d5SJulian Elischer 	return (item);
35984cf49a43SJulian Elischer }
35994cf49a43SJulian Elischer 
3600e088dd4cSAlexander Motin /*
3601e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3602e088dd4cSAlexander Motin  */
3603e088dd4cSAlexander Motin 
360442282202SGleb Smirnoff int
3605b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3606b332b91fSGleb Smirnoff {
3607b332b91fSGleb Smirnoff 
3608b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3609b332b91fSGleb Smirnoff }
3610b332b91fSGleb Smirnoff 
3611b332b91fSGleb Smirnoff int
3612aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
361342282202SGleb Smirnoff 	int flags)
36146b795970SJulian Elischer {
36156b795970SJulian Elischer 	item_p item;
36166b795970SJulian Elischer 
36176aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
36186b795970SJulian Elischer 		return (ENOMEM);
36196b795970SJulian Elischer 	}
36206aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3621a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
36221acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
36231acb27c6SJulian Elischer 	if (hook) {
36246b795970SJulian Elischer 		NG_HOOK_REF(hook);
36251acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36266b795970SJulian Elischer 	}
36276b795970SJulian Elischer 	NGI_FN(item) = fn;
36286b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
36296b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
363042282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
36316b795970SJulian Elischer }
36326b795970SJulian Elischer 
36334cf49a43SJulian Elischer /*
3634b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3635b332b91fSGleb Smirnoff  *
3636b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3637b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3638b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3639b332b91fSGleb Smirnoff  * be used.
3640e088dd4cSAlexander Motin  */
3641e088dd4cSAlexander Motin int
3642b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3643b332b91fSGleb Smirnoff 	int arg2, int flags)
3644e088dd4cSAlexander Motin {
3645e088dd4cSAlexander Motin 	item_p item;
3646e088dd4cSAlexander Motin 
3647b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3648b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3649e088dd4cSAlexander Motin 
3650e088dd4cSAlexander Motin 	/*
3651b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3652b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3653e088dd4cSAlexander Motin 	 */
3654b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
36556aa6d011SAlexander Motin 		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3656e088dd4cSAlexander Motin 			return (ENOMEM);
36576aa6d011SAlexander Motin 		if (pitem != NULL)
36586aa6d011SAlexander Motin 			item->apply = pitem->apply;
3659ed75521fSAlexander Motin 	} else {
36606aa6d011SAlexander Motin 		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
36616aa6d011SAlexander Motin 			return (ENOMEM);
3662ed75521fSAlexander Motin 	}
3663b332b91fSGleb Smirnoff 
36646aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3665e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3666e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3667e088dd4cSAlexander Motin 	if (hook) {
3668e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3669e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3670e088dd4cSAlexander Motin 	}
3671e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3672e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3673e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3674e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3675e088dd4cSAlexander Motin }
3676e088dd4cSAlexander Motin 
3677e088dd4cSAlexander Motin /*
3678d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3679d2ca21a9SJulian Elischer  */
3680d2ca21a9SJulian Elischer static void
36811fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3682d2ca21a9SJulian Elischer {
3683d2ca21a9SJulian Elischer 	item_p item = arg;
3684d2ca21a9SJulian Elischer 
3685bc29160dSMarko Zec 	CURVNET_SET(NGI_NODE(item)->nd_vnet);
3686d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3687bc29160dSMarko Zec 	CURVNET_RESTORE();
3688d2ca21a9SJulian Elischer }
3689d2ca21a9SJulian Elischer 
3690d2ca21a9SJulian Elischer 
369130bef41bSGleb Smirnoff int
3692f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3693d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3694d2ca21a9SJulian Elischer {
36951bf8e0faSGleb Smirnoff 	item_p item, oitem;
3696d2ca21a9SJulian Elischer 
36976aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
369830bef41bSGleb Smirnoff 		return (ENOMEM);
369930bef41bSGleb Smirnoff 
37006aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3701d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3702d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3703d2ca21a9SJulian Elischer 	if (hook) {
3704d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3705d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3706d2ca21a9SJulian Elischer 	}
3707d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3708d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3709d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
37101bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
37111bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
37121bf8e0faSGleb Smirnoff 	    oitem != NULL)
37131bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
371430bef41bSGleb Smirnoff 	return (0);
3715d2ca21a9SJulian Elischer }
3716d2ca21a9SJulian Elischer 
3717d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3718d2ca21a9SJulian Elischer int
3719f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3720d2ca21a9SJulian Elischer {
3721d2ca21a9SJulian Elischer 	item_p item;
372230bef41bSGleb Smirnoff 	int rval;
3723d2ca21a9SJulian Elischer 
372403b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
372503b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
372603b25f5dSGleb Smirnoff 
37273eadb26dSGleb Smirnoff 	rval = callout_stop(c);
372830bef41bSGleb Smirnoff 	item = c->c_arg;
372930bef41bSGleb Smirnoff 	/* Do an extra check */
37301fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
373130bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3732d2ca21a9SJulian Elischer 		/*
3733d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3734d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3735d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3736d2ca21a9SJulian Elischer 		 */
3737d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3738d2ca21a9SJulian Elischer 	}
37391bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
374030bef41bSGleb Smirnoff 
374130bef41bSGleb Smirnoff 	return (rval);
3742d2ca21a9SJulian Elischer }
3743d2ca21a9SJulian Elischer 
3744d2ca21a9SJulian Elischer /*
3745069154d5SJulian Elischer  * Set the address, if none given, give the node here.
37464cf49a43SJulian Elischer  */
3747069154d5SJulian Elischer void
3748069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
37494cf49a43SJulian Elischer {
3750069154d5SJulian Elischer 	if (retaddr) {
3751069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3752069154d5SJulian Elischer 	} else {
3753069154d5SJulian Elischer 		/*
3754069154d5SJulian Elischer 		 * The old return address should be ok.
3755069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3756069154d5SJulian Elischer 		 */
3757069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3758069154d5SJulian Elischer 	}
3759069154d5SJulian Elischer }
3760069154d5SJulian Elischer 
3761069154d5SJulian Elischer #define TESTING
3762069154d5SJulian Elischer #ifdef TESTING
3763069154d5SJulian Elischer /* just test all the macros */
3764069154d5SJulian Elischer void
3765069154d5SJulian Elischer ng_macro_test(item_p item);
3766069154d5SJulian Elischer void
3767069154d5SJulian Elischer ng_macro_test(item_p item)
3768069154d5SJulian Elischer {
3769069154d5SJulian Elischer 	node_p node = NULL;
3770069154d5SJulian Elischer 	hook_p hook = NULL;
37714cf49a43SJulian Elischer 	struct mbuf *m;
37724cf49a43SJulian Elischer 	struct ng_mesg *msg;
3773069154d5SJulian Elischer 	ng_ID_t retaddr;
3774069154d5SJulian Elischer 	int	error;
37754cf49a43SJulian Elischer 
3776069154d5SJulian Elischer 	NGI_GET_M(item, m);
3777069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3778069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
37798ed370fdSJulian Elischer 	NG_SEND_DATA(error, hook, m, NULL);
3780069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3781069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
378230400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3783069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3784069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3785069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3786069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
37874cf49a43SJulian Elischer }
3788069154d5SJulian Elischer #endif /* TESTING */
37894cf49a43SJulian Elischer 
3790