xref: /freebsd/sys/netgraph/ng_base.c (revision eddfbb763ded6b5f6777335142be9a0edab628bb)
14cf49a43SJulian Elischer /*
24cf49a43SJulian Elischer  * ng_base.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
64cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
74cf49a43SJulian Elischer  * All rights reserved.
84cf49a43SJulian Elischer  *
94cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
104cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
114cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
124cf49a43SJulian Elischer  * provided, however, that:
134cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
144cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
154cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
164cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
174cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
184cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
194cf49a43SJulian Elischer  *
204cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
214cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
224cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
234cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
244cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
254cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
264cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
274cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
284cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
294cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
304cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
314cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
324cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
334cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
344cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
354cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
364cf49a43SJulian Elischer  * OF SUCH DAMAGE.
374cf49a43SJulian Elischer  *
38cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
39cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
404cf49a43SJulian Elischer  *
414cf49a43SJulian Elischer  * $FreeBSD$
424cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
434cf49a43SJulian Elischer  */
444cf49a43SJulian Elischer 
454cf49a43SJulian Elischer /*
464cf49a43SJulian Elischer  * This file implements the base netgraph code.
474cf49a43SJulian Elischer  */
484cf49a43SJulian Elischer 
494cf49a43SJulian Elischer #include <sys/param.h>
508e853a2cSGleb Smirnoff #include <sys/systm.h>
5177a58296SGleb Smirnoff #include <sys/ctype.h>
524cf49a43SJulian Elischer #include <sys/errno.h>
53f33ca0c9SMarcel Moolenaar #include <sys/kdb.h>
544cf49a43SJulian Elischer #include <sys/kernel.h>
553b33fbe7SGleb Smirnoff #include <sys/ktr.h>
56104a9b7eSAlexander Kabaev #include <sys/limits.h>
574cf49a43SJulian Elischer #include <sys/malloc.h>
584cf49a43SJulian Elischer #include <sys/mbuf.h>
5977a58296SGleb Smirnoff #include <sys/queue.h>
60bfa7e882SJulian Elischer #include <sys/sysctl.h>
6177a58296SGleb Smirnoff #include <sys/syslog.h>
62e088dd4cSAlexander Motin #include <sys/refcount.h>
6381a253a4SAlexander Motin #include <sys/proc.h>
64603724d3SBjoern A. Zeeb #include <sys/vimage.h>
65f2fbb838SAlexander Motin #include <sys/unistd.h>
66f2fbb838SAlexander Motin #include <sys/kthread.h>
67f2fbb838SAlexander Motin #include <sys/smp.h>
68394cb30aSAlexander Motin #include <machine/cpu.h>
694cf49a43SJulian Elischer 
704cf49a43SJulian Elischer #include <net/netisr.h>
71eddfbb76SRobert Watson #include <net/vnet.h>
724cf49a43SJulian Elischer 
734cf49a43SJulian Elischer #include <netgraph/ng_message.h>
744cf49a43SJulian Elischer #include <netgraph/netgraph.h>
75f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
764cf49a43SJulian Elischer 
779d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION);
7899ff8176SPeter Wemm 
79ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */
80ac5dd141SGleb Smirnoff static struct mtx	ng_topo_mtx;
81ac5dd141SGleb Smirnoff 
82bc29160dSMarko Zec #ifdef VIMAGE
83bc29160dSMarko Zec static vnet_detach_fn vnet_netgraph_idetach;
84bc29160dSMarko Zec #endif
85aef8f344SMarko Zec 
8630400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
87cfea3f85SAlexander Motin static struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
881489164fSGleb Smirnoff static struct mtx	ngq_mtx;	/* protects the queue item list */
8930400f03SJulian Elischer 
9030400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
9130400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
9230400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
9330400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
9430400f03SJulian Elischer 
9530400f03SJulian Elischer static void ng_dumpitems(void);
9630400f03SJulian Elischer static void ng_dumpnodes(void);
9730400f03SJulian Elischer static void ng_dumphooks(void);
9830400f03SJulian Elischer 
9930400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
100954c4772SJulian Elischer /*
101954c4772SJulian Elischer  * DEAD versions of the structures.
102954c4772SJulian Elischer  * In order to avoid races, it is sometimes neccesary to point
103954c4772SJulian Elischer  * at SOMETHING even though theoretically, the current entity is
104954c4772SJulian Elischer  * INVALID. Use these to avoid these races.
105954c4772SJulian Elischer  */
106954c4772SJulian Elischer struct ng_type ng_deadtype = {
107954c4772SJulian Elischer 	NG_ABI_VERSION,
108954c4772SJulian Elischer 	"dead",
109954c4772SJulian Elischer 	NULL,	/* modevent */
110954c4772SJulian Elischer 	NULL,	/* constructor */
111954c4772SJulian Elischer 	NULL,	/* rcvmsg */
112954c4772SJulian Elischer 	NULL,	/* shutdown */
113954c4772SJulian Elischer 	NULL,	/* newhook */
114954c4772SJulian Elischer 	NULL,	/* findhook */
115954c4772SJulian Elischer 	NULL,	/* connect */
116954c4772SJulian Elischer 	NULL,	/* rcvdata */
117954c4772SJulian Elischer 	NULL,	/* disconnect */
118954c4772SJulian Elischer 	NULL, 	/* cmdlist */
119954c4772SJulian Elischer };
12030400f03SJulian Elischer 
121954c4772SJulian Elischer struct ng_node ng_deadnode = {
122954c4772SJulian Elischer 	"dead",
123954c4772SJulian Elischer 	&ng_deadtype,
124be4252b3SJulian Elischer 	NGF_INVALID,
125954c4772SJulian Elischer 	0,	/* numhooks */
126954c4772SJulian Elischer 	NULL,	/* private */
127954c4772SJulian Elischer 	0,	/* ID */
128954c4772SJulian Elischer 	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
129954c4772SJulian Elischer 	{},	/* all_nodes list entry */
130954c4772SJulian Elischer 	{},	/* id hashtable list entry */
131954c4772SJulian Elischer 	{	0,
1329852972bSAlexander Motin 		0,
133954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
1349852972bSAlexander Motin 		{}, /* workqueue entry */
1359852972bSAlexander Motin 		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
136954c4772SJulian Elischer 	},
1379852972bSAlexander Motin 	1,	/* refs */
138a40b7874SMarko Zec 	NULL,	/* vnet */
139954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
140954c4772SJulian Elischer 	ND_MAGIC,
141954c4772SJulian Elischer 	__FILE__,
142954c4772SJulian Elischer 	__LINE__,
143954c4772SJulian Elischer 	{NULL}
144954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
145954c4772SJulian Elischer };
146954c4772SJulian Elischer 
147954c4772SJulian Elischer struct ng_hook ng_deadhook = {
148954c4772SJulian Elischer 	"dead",
149954c4772SJulian Elischer 	NULL,		/* private */
150954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
151e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
152954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
153954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
154954c4772SJulian Elischer 	{},		/* hooks list */
155c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
156c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1579852972bSAlexander Motin 	1,		/* refs always >= 1 */
158954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
159954c4772SJulian Elischer 	HK_MAGIC,
160954c4772SJulian Elischer 	__FILE__,
161954c4772SJulian Elischer 	__LINE__,
162954c4772SJulian Elischer 	{NULL}
163954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
164954c4772SJulian Elischer };
165954c4772SJulian Elischer 
166954c4772SJulian Elischer /*
167954c4772SJulian Elischer  * END DEAD STRUCTURES
168954c4772SJulian Elischer  */
169069154d5SJulian Elischer /* List nodes with unallocated work */
1709852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
171b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1724cf49a43SJulian Elischer 
1734cf49a43SJulian Elischer /* List of installed types */
174069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
175069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
1764cf49a43SJulian Elischer 
177069154d5SJulian Elischer /* Hash related definitions */
1780f150d04SJulian Elischer /* XXX Don't need to initialise them because it's a LIST */
179eddfbb76SRobert Watson static VNET_DEFINE(LIST_HEAD(, ng_node), ng_ID_hash[NG_ID_HASH_SIZE]);
180eddfbb76SRobert Watson #define	V_ng_ID_hash			VNET_GET(ng_ID_hash)
181eddfbb76SRobert Watson 
182069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
1830f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
1840f150d04SJulian Elischer #define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
1850f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
1860f150d04SJulian Elischer 	do { 								\
18753f9c5e9SRobert Watson 		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
188603724d3SBjoern A. Zeeb 		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
1890f150d04SJulian Elischer 						nd_idnodes) {		\
1900f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
1910f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
1920f150d04SJulian Elischer 				break;					\
1930f150d04SJulian Elischer 			}						\
1940f150d04SJulian Elischer 		}							\
1950f150d04SJulian Elischer 	} while (0)
196069154d5SJulian Elischer 
197eddfbb76SRobert Watson static VNET_DEFINE(LIST_HEAD(, ng_node), ng_name_hash[NG_NAME_HASH_SIZE]);
198eddfbb76SRobert Watson #define	V_ng_name_hash			VNET_GET(ng_name_hash)
199eddfbb76SRobert Watson 
200cfea3f85SAlexander Motin static struct mtx	ng_namehash_mtx;
201cfea3f85SAlexander Motin #define NG_NAMEHASH(NAME, HASH)				\
202cfea3f85SAlexander Motin 	do {						\
203cfea3f85SAlexander Motin 		u_char	h = 0;				\
204cfea3f85SAlexander Motin 		const u_char	*c;			\
205cfea3f85SAlexander Motin 		for (c = (const u_char*)(NAME); *c; c++)\
206cfea3f85SAlexander Motin 			h += *c;			\
207cfea3f85SAlexander Motin 		(HASH) = h % (NG_NAME_HASH_SIZE);	\
208cfea3f85SAlexander Motin 	} while (0)
209cfea3f85SAlexander Motin 
210dc90cad9SJulian Elischer 
2114cf49a43SJulian Elischer /* Internal functions */
2124cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
213069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
214dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2154cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
216394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
217f2fbb838SAlexander Motin static void	ngthread(void *);
21827757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2199852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
220069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
221e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
222e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
223e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
224e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
2256b795970SJulian Elischer static int	ng_mkpeer(node_p node, const char *name,
2266b795970SJulian Elischer 						const char *name2, char *type);
227069154d5SJulian Elischer 
2284c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */
229069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
230069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
231069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
232069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
233069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
2341acb27c6SJulian Elischer void	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
23530400f03SJulian Elischer void	ng_unname(node_p node);
236069154d5SJulian Elischer 
2374cf49a43SJulian Elischer 
2384cf49a43SJulian Elischer /* Our own netgraph malloc type */
2394cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
240069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
241069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
242069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
243069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
244069154d5SJulian Elischer 
245069154d5SJulian Elischer /* Should not be visible outside this file */
24630400f03SJulian Elischer 
24730400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
2481ede983cSDag-Erling Smørgrav 	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
24930400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
2501ede983cSDag-Erling Smørgrav 	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
25130400f03SJulian Elischer 
2522c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2534abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2542c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2554abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2562c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2574abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2582c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2594abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2602c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2614abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2622c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2634abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
264f2fbb838SAlexander Motin #define	NG_WORKLIST_SLEEP()			\
265f2fbb838SAlexander Motin 	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
266f2fbb838SAlexander Motin #define	NG_WORKLIST_WAKEUP()			\
267f2fbb838SAlexander Motin 	wakeup_one(&ng_worklist)
2682c8dda8dSWojciech A. Koszek 
26930400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
27030400f03SJulian Elischer /*
27130400f03SJulian Elischer  * In debug mode:
27230400f03SJulian Elischer  * In an attempt to help track reference count screwups
27330400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
27430400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
27530400f03SJulian Elischer  * after they have been freed.
27630400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
27730400f03SJulian Elischer  */
27830400f03SJulian Elischer static __inline hook_p
27930400f03SJulian Elischer ng_alloc_hook(void)
28030400f03SJulian Elischer {
28130400f03SJulian Elischer 	hook_p hook;
28230400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2839ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
28430400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
28530400f03SJulian Elischer 	if (hook) {
28630400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
28730400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
28830400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
28930400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
2909ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
29130400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
29230400f03SJulian Elischer 	} else {
2939ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
29430400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
29530400f03SJulian Elischer 		if (hook) {
29630400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
2979ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
29830400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
2999ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
30030400f03SJulian Elischer 		}
30130400f03SJulian Elischer 	}
30230400f03SJulian Elischer 	return (hook);
30330400f03SJulian Elischer }
30430400f03SJulian Elischer 
30530400f03SJulian Elischer static __inline node_p
30630400f03SJulian Elischer ng_alloc_node(void)
30730400f03SJulian Elischer {
30830400f03SJulian Elischer 	node_p node;
30930400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
3109ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
31130400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
31230400f03SJulian Elischer 	if (node) {
31330400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
31430400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
31530400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
31630400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3179ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
31830400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
31930400f03SJulian Elischer 	} else {
3209ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
32130400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
32230400f03SJulian Elischer 		if (node) {
32330400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3249ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
32530400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3269ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
32730400f03SJulian Elischer 		}
32830400f03SJulian Elischer 	}
32930400f03SJulian Elischer 	return (node);
33030400f03SJulian Elischer }
33130400f03SJulian Elischer 
33230400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
33330400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
33430400f03SJulian Elischer 
33530400f03SJulian Elischer 
33630400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
33730400f03SJulian Elischer 	do {								\
3389ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
33930400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
34030400f03SJulian Elischer 		hook->hk_magic = 0;					\
3419ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
34230400f03SJulian Elischer 	} while (0)
34330400f03SJulian Elischer 
34430400f03SJulian Elischer #define NG_FREE_NODE(node)						\
34530400f03SJulian Elischer 	do {								\
3469ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
34730400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
34830400f03SJulian Elischer 		node->nd_magic = 0;					\
3499ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
35030400f03SJulian Elischer 	} while (0)
35130400f03SJulian Elischer 
35230400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
35330400f03SJulian Elischer 
35430400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
35530400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
35630400f03SJulian Elischer 
3571ede983cSDag-Erling Smørgrav #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
3581ede983cSDag-Erling Smørgrav #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
35930400f03SJulian Elischer 
36030400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
36130400f03SJulian Elischer 
362f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3634cf49a43SJulian Elischer #ifndef TRAP_ERROR
3646b795970SJulian Elischer #define TRAP_ERROR()
3654cf49a43SJulian Elischer #endif
3664cf49a43SJulian Elischer 
367eddfbb76SRobert Watson static VNET_DEFINE(ng_ID_t, nextID) = 1;
368eddfbb76SRobert Watson #define	V_nextID			VNET_GET(nextID)
369dc90cad9SJulian Elischer 
370b2da83c2SArchie Cobbs #ifdef INVARIANTS
371b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
372b2da83c2SArchie Cobbs 		struct mbuf *n;						\
373b2da83c2SArchie Cobbs 		int total;						\
374b2da83c2SArchie Cobbs 									\
375fe584538SDag-Erling Smørgrav 		M_ASSERTPKTHDR(m);					\
376b32cfb32SGleb Smirnoff 		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
377b2da83c2SArchie Cobbs 			total += n->m_len;				\
378b32cfb32SGleb Smirnoff 			if (n->m_nextpkt != NULL)			\
379b32cfb32SGleb Smirnoff 				panic("%s: m_nextpkt", __func__);	\
380b32cfb32SGleb Smirnoff 		}							\
381ba5b359aSGleb Smirnoff 									\
382b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
383b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
3846e551fb6SDavid E. O'Brien 			    __func__, (m)->m_pkthdr.len, total);	\
385b2da83c2SArchie Cobbs 		}							\
386b2da83c2SArchie Cobbs 	} while (0)
387b2da83c2SArchie Cobbs #else
388b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
389b2da83c2SArchie Cobbs #endif
390b2da83c2SArchie Cobbs 
391e088dd4cSAlexander Motin #define ERROUT(x)	do { error = (x); goto done; } while (0)
392dc90cad9SJulian Elischer 
3934cf49a43SJulian Elischer /************************************************************************
394f8307e12SArchie Cobbs 	Parse type definitions for generic messages
395f8307e12SArchie Cobbs ************************************************************************/
396f8307e12SArchie Cobbs 
397f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
398f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
399f0184ff8SArchie Cobbs static const struct ng_parse_struct_field				\
400f0184ff8SArchie Cobbs 	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
401f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
402f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
403f0184ff8SArchie Cobbs 	&ng_ ## lo ## _type_fields					\
404f8307e12SArchie Cobbs }
405f8307e12SArchie Cobbs 
406f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
407f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
408f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
409f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
410f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
411f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
412f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
413f8307e12SArchie Cobbs 
414f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
415d7d97eb0SJeroen Ruigrok van der Werven    value immediately preceding the array -- as with struct namelist
416f8307e12SArchie Cobbs    and struct typelist. */
417f8307e12SArchie Cobbs static int
418f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
419f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
420f8307e12SArchie Cobbs {
421f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
422f8307e12SArchie Cobbs }
423f8307e12SArchie Cobbs 
424f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
425f8307e12SArchie Cobbs static int
426f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
427f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
428f8307e12SArchie Cobbs {
429f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
430f8307e12SArchie Cobbs 
431f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
432f8307e12SArchie Cobbs }
433f8307e12SArchie Cobbs 
434f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
435f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
436f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
437f8307e12SArchie Cobbs 	&ng_generic_list_getLength
438f8307e12SArchie Cobbs };
439f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
440f8307e12SArchie Cobbs 	&ng_parse_array_type,
441f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
442f8307e12SArchie Cobbs };
443f8307e12SArchie Cobbs 
444f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
445f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
446f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
447f8307e12SArchie Cobbs 	&ng_generic_list_getLength
448f8307e12SArchie Cobbs };
449f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
450f8307e12SArchie Cobbs 	&ng_parse_array_type,
451f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
452f8307e12SArchie Cobbs };
453f8307e12SArchie Cobbs 
454f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
455f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
456f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
457f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
458f8307e12SArchie Cobbs };
459f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
460f8307e12SArchie Cobbs 	&ng_parse_array_type,
461f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
462f8307e12SArchie Cobbs };
463f8307e12SArchie Cobbs 
464f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
465f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
466f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
467f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
468f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
469f8307e12SArchie Cobbs 
470f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
471f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
472f8307e12SArchie Cobbs 	{
473f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
474f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
475f8307e12SArchie Cobbs 	  "shutdown",
476f8307e12SArchie Cobbs 	  NULL,
477f8307e12SArchie Cobbs 	  NULL
478f8307e12SArchie Cobbs 	},
479f8307e12SArchie Cobbs 	{
480f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
481f8307e12SArchie Cobbs 	  NGM_MKPEER,
482f8307e12SArchie Cobbs 	  "mkpeer",
483f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
484f8307e12SArchie Cobbs 	  NULL
485f8307e12SArchie Cobbs 	},
486f8307e12SArchie Cobbs 	{
487f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
488f8307e12SArchie Cobbs 	  NGM_CONNECT,
489f8307e12SArchie Cobbs 	  "connect",
490f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
491f8307e12SArchie Cobbs 	  NULL
492f8307e12SArchie Cobbs 	},
493f8307e12SArchie Cobbs 	{
494f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
495f8307e12SArchie Cobbs 	  NGM_NAME,
496f8307e12SArchie Cobbs 	  "name",
497f8307e12SArchie Cobbs 	  &ng_generic_name_type,
498f8307e12SArchie Cobbs 	  NULL
499f8307e12SArchie Cobbs 	},
500f8307e12SArchie Cobbs 	{
501f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
502f8307e12SArchie Cobbs 	  NGM_RMHOOK,
503f8307e12SArchie Cobbs 	  "rmhook",
504f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
505f8307e12SArchie Cobbs 	  NULL
506f8307e12SArchie Cobbs 	},
507f8307e12SArchie Cobbs 	{
508f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
509f8307e12SArchie Cobbs 	  NGM_NODEINFO,
510f8307e12SArchie Cobbs 	  "nodeinfo",
511f8307e12SArchie Cobbs 	  NULL,
512f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
513f8307e12SArchie Cobbs 	},
514f8307e12SArchie Cobbs 	{
515f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
516f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
517f8307e12SArchie Cobbs 	  "listhooks",
518f8307e12SArchie Cobbs 	  NULL,
519f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
520f8307e12SArchie Cobbs 	},
521f8307e12SArchie Cobbs 	{
522f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
523f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
524f8307e12SArchie Cobbs 	  "listnames",
525f8307e12SArchie Cobbs 	  NULL,
526f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
527f8307e12SArchie Cobbs 	},
528f8307e12SArchie Cobbs 	{
529f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
530f8307e12SArchie Cobbs 	  NGM_LISTNODES,
531f8307e12SArchie Cobbs 	  "listnodes",
532f8307e12SArchie Cobbs 	  NULL,
533f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
534f8307e12SArchie Cobbs 	},
535f8307e12SArchie Cobbs 	{
536f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
537f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
538f8307e12SArchie Cobbs 	  "listtypes",
539f8307e12SArchie Cobbs 	  NULL,
540f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
541f8307e12SArchie Cobbs 	},
542f8307e12SArchie Cobbs 	{
543f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
5447095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
5457095e097SPoul-Henning Kamp 	  "textconfig",
5467095e097SPoul-Henning Kamp 	  NULL,
5477095e097SPoul-Henning Kamp 	  &ng_parse_string_type
5487095e097SPoul-Henning Kamp 	},
5497095e097SPoul-Henning Kamp 	{
5507095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
551f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
552f8307e12SArchie Cobbs 	  "textstatus",
553f8307e12SArchie Cobbs 	  NULL,
554f8307e12SArchie Cobbs 	  &ng_parse_string_type
555f8307e12SArchie Cobbs 	},
556f8307e12SArchie Cobbs 	{
557f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
558f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
559f8307e12SArchie Cobbs 	  "ascii2binary",
560f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
561f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
562f8307e12SArchie Cobbs 	},
563f8307e12SArchie Cobbs 	{
564f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
565f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
566f8307e12SArchie Cobbs 	  "binary2ascii",
567f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
568f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
569f8307e12SArchie Cobbs 	},
570f8307e12SArchie Cobbs 	{ 0 }
571f8307e12SArchie Cobbs };
572f8307e12SArchie Cobbs 
573f8307e12SArchie Cobbs /************************************************************************
5744cf49a43SJulian Elischer 			Node routines
5754cf49a43SJulian Elischer ************************************************************************/
5764cf49a43SJulian Elischer 
5774cf49a43SJulian Elischer /*
5784cf49a43SJulian Elischer  * Instantiate a node of the requested type
5794cf49a43SJulian Elischer  */
5804cf49a43SJulian Elischer int
5814cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
5824cf49a43SJulian Elischer {
5834cf49a43SJulian Elischer 	struct ng_type *type;
584069154d5SJulian Elischer 	int	error;
5854cf49a43SJulian Elischer 
5864cf49a43SJulian Elischer 	/* Check that the type makes sense */
5874cf49a43SJulian Elischer 	if (typename == NULL) {
5886b795970SJulian Elischer 		TRAP_ERROR();
5894cf49a43SJulian Elischer 		return (EINVAL);
5904cf49a43SJulian Elischer 	}
5914cf49a43SJulian Elischer 
5927610f574SGleb Smirnoff 	/* Locate the node type. If we fail we return. Do not try to load
5937610f574SGleb Smirnoff 	 * module.
5947610f574SGleb Smirnoff 	 */
5954cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL)
5964cf49a43SJulian Elischer 		return (ENXIO);
5974cf49a43SJulian Elischer 
598069154d5SJulian Elischer 	/*
599069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
600069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
601069154d5SJulian Elischer 	 */
602069154d5SJulian Elischer 	if (type->constructor != NULL) {
603069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
604069154d5SJulian Elischer 			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
60530400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
606069154d5SJulian Elischer 			}
607069154d5SJulian Elischer 		}
608069154d5SJulian Elischer 	} else {
609069154d5SJulian Elischer 		/*
610069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
61164efc707SRobert Watson 		 * to be made. It must be brought into existence by
612954c4772SJulian Elischer 		 * some external agency. The external agency should
613069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
614069154d5SJulian Elischer 		 * netgraph part initialised.
615069154d5SJulian Elischer 		 */
6166b795970SJulian Elischer 		TRAP_ERROR();
617069154d5SJulian Elischer 		error = EINVAL;
618069154d5SJulian Elischer 	}
619069154d5SJulian Elischer 	return (error);
6204cf49a43SJulian Elischer }
6214cf49a43SJulian Elischer 
6224cf49a43SJulian Elischer /*
623069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
624069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
6254cf49a43SJulian Elischer  * The returned node has a reference count of 1.
6264cf49a43SJulian Elischer  */
6274cf49a43SJulian Elischer int
6284cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
6294cf49a43SJulian Elischer {
6304cf49a43SJulian Elischer 	node_p node;
6314cf49a43SJulian Elischer 
6324cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6334cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6346b795970SJulian Elischer 		TRAP_ERROR();
6354cf49a43SJulian Elischer 		return (EINVAL);
6364cf49a43SJulian Elischer 	}
6374cf49a43SJulian Elischer 
6384cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
63930400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6404cf49a43SJulian Elischer 	if (node == NULL) {
6416b795970SJulian Elischer 		TRAP_ERROR();
6424cf49a43SJulian Elischer 		return (ENOMEM);
6434cf49a43SJulian Elischer 	}
64430400f03SJulian Elischer 	node->nd_type = type;
645bc29160dSMarko Zec #ifdef VIMAGE
646bc29160dSMarko Zec 	node->nd_vnet = curvnet;
647bc29160dSMarko Zec #endif
64830400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6494cf49a43SJulian Elischer 	type->refs++;
6504cf49a43SJulian Elischer 
6512c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6529852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
65330400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6544cf49a43SJulian Elischer 
6554cf49a43SJulian Elischer 	/* Initialize hook list for new node */
65630400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6574cf49a43SJulian Elischer 
658cfea3f85SAlexander Motin 	/* Link us into the name hash. */
659cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
660603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[0], node, nd_nodes);
661cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
662069154d5SJulian Elischer 
663dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
6649ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
66530400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
666069154d5SJulian Elischer 		node_p node2 = NULL;
667ac957cd2SJulian Elischer 		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6680f150d04SJulian Elischer 
66930400f03SJulian Elischer 		/* Is there a problem with the new number? */
6700f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6710f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
67230400f03SJulian Elischer 			break;
673069154d5SJulian Elischer 		}
67430400f03SJulian Elischer 	}
675603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
67630400f03SJulian Elischer 							node, nd_idnodes);
6779ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
678dc90cad9SJulian Elischer 
6794cf49a43SJulian Elischer 	/* Done */
6804cf49a43SJulian Elischer 	*nodepp = node;
6814cf49a43SJulian Elischer 	return (0);
6824cf49a43SJulian Elischer }
6834cf49a43SJulian Elischer 
6844cf49a43SJulian Elischer /*
6854cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
68664efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6874cf49a43SJulian Elischer  * no type-specific method.
6884cf49a43SJulian Elischer  *
68964efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6903e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6913e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
692069154d5SJulian Elischer  *
693069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
69464efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
6953e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
6963e4084c8SJulian Elischer  * in which case we allow the node to survive.
6973e4084c8SJulian Elischer  *
69864efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
6993e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
7003e4084c8SJulian Elischer  * are rebooting.... etc.
7014cf49a43SJulian Elischer  */
7024cf49a43SJulian Elischer void
7031acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
7044cf49a43SJulian Elischer {
7053e4084c8SJulian Elischer 	hook_p hook;
7063e4084c8SJulian Elischer 
7074cf49a43SJulian Elischer 	/* Check if it's already shutting down */
708be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
7094cf49a43SJulian Elischer 		return;
7104cf49a43SJulian Elischer 
7111acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
7121acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
7131acb27c6SJulian Elischer 		return;
7141acb27c6SJulian Elischer 	}
7151acb27c6SJulian Elischer 
7164cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
71730400f03SJulian Elischer 	NG_NODE_REF(node);
7184cf49a43SJulian Elischer 
71930400f03SJulian Elischer 	/*
72030400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
72130400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
722be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
72330400f03SJulian Elischer 	 * creation
72430400f03SJulian Elischer 	 */
725be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7264cf49a43SJulian Elischer 
727991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
728991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
729991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
730991fc65aSJulian Elischer 
7313e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7323e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7333e4084c8SJulian Elischer 		ng_destroy_hook(hook);
73430400f03SJulian Elischer 
735069154d5SJulian Elischer 	/*
736069154d5SJulian Elischer 	 * Drain the input queue forceably.
73730400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
73830400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
73930400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
740b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
741069154d5SJulian Elischer 	 */
7429852972bSAlexander Motin 	ng_flush_input_queue(node);
743069154d5SJulian Elischer 
744069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
74530400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
74630400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
74730400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
74830400f03SJulian Elischer 			/*
74930400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
75030400f03SJulian Elischer 			 * that it doesn't want to die.
75130400f03SJulian Elischer 			 * Presumably it is a persistant node.
7521acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7531acb27c6SJulian Elischer 			 *  e.g. hardware going away,
754be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
75530400f03SJulian Elischer 			 */
756be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7571acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
75830400f03SJulian Elischer 			return;
7594cf49a43SJulian Elischer 		}
7601acb27c6SJulian Elischer 	} else {				/* do the default thing */
7611acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7621acb27c6SJulian Elischer 	}
7634cf49a43SJulian Elischer 
76430400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
76530400f03SJulian Elischer 
76630400f03SJulian Elischer 	/*
76730400f03SJulian Elischer 	 * Remove extra reference, possibly the last
76830400f03SJulian Elischer 	 * Possible other holders of references may include
76930400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
77030400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
77130400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
77230400f03SJulian Elischer 	 */
77330400f03SJulian Elischer 	NG_NODE_UNREF(node);
7744cf49a43SJulian Elischer }
7754cf49a43SJulian Elischer 
7765951069aSJulian Elischer /*
7775951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7785951069aSJulian Elischer  * deadnode always acts as it it were the last.
7795951069aSJulian Elischer  */
7805951069aSJulian Elischer int
78130400f03SJulian Elischer ng_unref_node(node_p node)
7824cf49a43SJulian Elischer {
78330400f03SJulian Elischer 	int v;
7846b795970SJulian Elischer 
7856b795970SJulian Elischer 	if (node == &ng_deadnode) {
7865951069aSJulian Elischer 		return (0);
7876b795970SJulian Elischer 	}
7886b795970SJulian Elischer 
789018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&node->nd_refs, -1);
790e8a49db2SJulian Elischer 
791018fe3d1SAlexander Motin 	if (v == 1) { /* we were the last */
792069154d5SJulian Elischer 
793cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
79430400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
79530400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
796cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
797069154d5SJulian Elischer 
7989ed346baSBosko Milekic 		mtx_lock(&ng_idhash_mtx);
79930400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
8009ed346baSBosko Milekic 		mtx_unlock(&ng_idhash_mtx);
801069154d5SJulian Elischer 
80212574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
803069154d5SJulian Elischer 		NG_FREE_NODE(node);
8044cf49a43SJulian Elischer 	}
805018fe3d1SAlexander Motin 	return (v - 1);
8064cf49a43SJulian Elischer }
8074cf49a43SJulian Elischer 
8084cf49a43SJulian Elischer /************************************************************************
809dc90cad9SJulian Elischer 			Node ID handling
810dc90cad9SJulian Elischer ************************************************************************/
811dc90cad9SJulian Elischer static node_p
812069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
813dc90cad9SJulian Elischer {
81430400f03SJulian Elischer 	node_p node;
8159ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
8160f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
81730400f03SJulian Elischer 	if(node)
81830400f03SJulian Elischer 		NG_NODE_REF(node);
8199ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
82030400f03SJulian Elischer 	return(node);
821dc90cad9SJulian Elischer }
822dc90cad9SJulian Elischer 
823dc90cad9SJulian Elischer ng_ID_t
824dc90cad9SJulian Elischer ng_node2ID(node_p node)
825dc90cad9SJulian Elischer {
82670de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
827dc90cad9SJulian Elischer }
828dc90cad9SJulian Elischer 
829dc90cad9SJulian Elischer /************************************************************************
8304cf49a43SJulian Elischer 			Node name handling
8314cf49a43SJulian Elischer ************************************************************************/
8324cf49a43SJulian Elischer 
8334cf49a43SJulian Elischer /*
8344cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
8354cf49a43SJulian Elischer  */
8364cf49a43SJulian Elischer int
8374cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8384cf49a43SJulian Elischer {
839cfea3f85SAlexander Motin 	int i, hash;
840069154d5SJulian Elischer 	node_p node2;
8414cf49a43SJulian Elischer 
8424cf49a43SJulian Elischer 	/* Check the name is valid */
84387e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8444cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8454cf49a43SJulian Elischer 			break;
8464cf49a43SJulian Elischer 	}
8474cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8486b795970SJulian Elischer 		TRAP_ERROR();
8494cf49a43SJulian Elischer 		return (EINVAL);
8504cf49a43SJulian Elischer 	}
851dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8526b795970SJulian Elischer 		TRAP_ERROR();
8534cf49a43SJulian Elischer 		return (EINVAL);
8544cf49a43SJulian Elischer 	}
8554cf49a43SJulian Elischer 
8564cf49a43SJulian Elischer 	/* Check the name isn't already being used */
857069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
85830400f03SJulian Elischer 		NG_NODE_UNREF(node2);
8596b795970SJulian Elischer 		TRAP_ERROR();
8604cf49a43SJulian Elischer 		return (EADDRINUSE);
8614cf49a43SJulian Elischer 	}
8624cf49a43SJulian Elischer 
863069154d5SJulian Elischer 	/* copy it */
86487e2c66aSHartmut Brandt 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8654cf49a43SJulian Elischer 
866cfea3f85SAlexander Motin 	/* Update name hash. */
867cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
868cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
869cfea3f85SAlexander Motin 	LIST_REMOVE(node, nd_nodes);
870603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
871cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
872cfea3f85SAlexander Motin 
8734cf49a43SJulian Elischer 	return (0);
8744cf49a43SJulian Elischer }
8754cf49a43SJulian Elischer 
8764cf49a43SJulian Elischer /*
8774cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8784cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8794cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8804cf49a43SJulian Elischer  *
8814cf49a43SJulian Elischer  * Returns the node if found, else NULL.
882069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
883e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
884e1e8f51bSRobert Watson  * there.
8854cf49a43SJulian Elischer  */
8864cf49a43SJulian Elischer node_p
887069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
8884cf49a43SJulian Elischer {
889dc90cad9SJulian Elischer 	node_p node;
890dc90cad9SJulian Elischer 	ng_ID_t temp;
891cfea3f85SAlexander Motin 	int	hash;
8924cf49a43SJulian Elischer 
8934cf49a43SJulian Elischer 	/* "." means "this node" */
894069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
89530400f03SJulian Elischer 		NG_NODE_REF(here);
896069154d5SJulian Elischer 		return(here);
897069154d5SJulian Elischer 	}
8984cf49a43SJulian Elischer 
8994cf49a43SJulian Elischer 	/* Check for name-by-ID */
900dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
901069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
9024cf49a43SJulian Elischer 	}
9034cf49a43SJulian Elischer 
9044cf49a43SJulian Elischer 	/* Find node by name */
905cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
906cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
907603724d3SBjoern A. Zeeb 	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) {
908cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
909cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
9104cf49a43SJulian Elischer 			break;
9114cf49a43SJulian Elischer 		}
91270de87f2SJulian Elischer 	}
913069154d5SJulian Elischer 	if (node)
91430400f03SJulian Elischer 		NG_NODE_REF(node);
915cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
9164cf49a43SJulian Elischer 	return (node);
9174cf49a43SJulian Elischer }
9184cf49a43SJulian Elischer 
9194cf49a43SJulian Elischer /*
9209d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
921dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9224cf49a43SJulian Elischer  */
923dc90cad9SJulian Elischer static ng_ID_t
9244cf49a43SJulian Elischer ng_decodeidname(const char *name)
9254cf49a43SJulian Elischer {
9262b70adcbSArchie Cobbs 	const int len = strlen(name);
92725792ef3SArchie Cobbs 	char *eptr;
9282b70adcbSArchie Cobbs 	u_long val;
9294cf49a43SJulian Elischer 
9302b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
93170de87f2SJulian Elischer 	if ((len < 3)
93270de87f2SJulian Elischer 	|| (name[0] != '[')
93370de87f2SJulian Elischer 	|| (name[len - 1] != ']')
93470de87f2SJulian Elischer 	|| (!isxdigit(name[1]))) {
93570de87f2SJulian Elischer 		return ((ng_ID_t)0);
93670de87f2SJulian Elischer 	}
9374cf49a43SJulian Elischer 
9382b70adcbSArchie Cobbs 	/* Decode number */
9392b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
94070de87f2SJulian Elischer 	if ((eptr - name != len - 1)
94170de87f2SJulian Elischer 	|| (val == ULONG_MAX)
94270de87f2SJulian Elischer 	|| (val == 0)) {
94312f035e0SJulian Elischer 		return ((ng_ID_t)0);
94470de87f2SJulian Elischer 	}
9452b70adcbSArchie Cobbs 	return (ng_ID_t)val;
9464cf49a43SJulian Elischer }
9474cf49a43SJulian Elischer 
9484cf49a43SJulian Elischer /*
9494cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9504cf49a43SJulian Elischer  * when shutting down and removing the node.
95164efc707SRobert Watson  * IF we allow name changing this may be more resurrected.
9524cf49a43SJulian Elischer  */
9534cf49a43SJulian Elischer void
9544cf49a43SJulian Elischer ng_unname(node_p node)
9554cf49a43SJulian Elischer {
9564cf49a43SJulian Elischer }
9574cf49a43SJulian Elischer 
9584cf49a43SJulian Elischer /************************************************************************
9594cf49a43SJulian Elischer 			Hook routines
9604cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
9613e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
9623e4084c8SJulian Elischer  they are connected to the 'dead' hook.
9634cf49a43SJulian Elischer ************************************************************************/
9644cf49a43SJulian Elischer 
9654cf49a43SJulian Elischer /*
9664cf49a43SJulian Elischer  * Remove a hook reference
9674cf49a43SJulian Elischer  */
96830400f03SJulian Elischer void
9694cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
9704cf49a43SJulian Elischer {
97130400f03SJulian Elischer 	int v;
9726b795970SJulian Elischer 
9736b795970SJulian Elischer 	if (hook == &ng_deadhook) {
9746b795970SJulian Elischer 		return;
9756b795970SJulian Elischer 	}
976018fe3d1SAlexander Motin 
977018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&hook->hk_refs, -1);
978e8a49db2SJulian Elischer 
97930400f03SJulian Elischer 	if (v == 1) { /* we were the last */
980f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
9816b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
982069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
983069154d5SJulian Elischer 	}
9844cf49a43SJulian Elischer }
9854cf49a43SJulian Elischer 
9864cf49a43SJulian Elischer /*
9874cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
9883e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
9894cf49a43SJulian Elischer  */
9904cf49a43SJulian Elischer static int
9914cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
9924cf49a43SJulian Elischer {
9934cf49a43SJulian Elischer 	hook_p hook;
9944cf49a43SJulian Elischer 	int error = 0;
9954cf49a43SJulian Elischer 
9964cf49a43SJulian Elischer 	/* Check that the given name is good */
9974cf49a43SJulian Elischer 	if (name == NULL) {
9986b795970SJulian Elischer 		TRAP_ERROR();
9994cf49a43SJulian Elischer 		return (EINVAL);
10004cf49a43SJulian Elischer 	}
1001899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
10026b795970SJulian Elischer 		TRAP_ERROR();
10034cf49a43SJulian Elischer 		return (EEXIST);
10044cf49a43SJulian Elischer 	}
10054cf49a43SJulian Elischer 
10064cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
100730400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
10084cf49a43SJulian Elischer 	if (hook == NULL) {
10096b795970SJulian Elischer 		TRAP_ERROR();
10104cf49a43SJulian Elischer 		return (ENOMEM);
10114cf49a43SJulian Elischer 	}
10123e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
101330400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
10143e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
101530400f03SJulian Elischer 	hook->hk_node = node;
101630400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
10174cf49a43SJulian Elischer 
10183e4084c8SJulian Elischer 	/* Set hook name */
101987e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10203e4084c8SJulian Elischer 
10213e4084c8SJulian Elischer 	/*
10223e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10233e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10243e4084c8SJulian Elischer 	 */
1025954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1026954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
102730400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1028069154d5SJulian Elischer 			return (error);
1029069154d5SJulian Elischer 		}
1030954c4772SJulian Elischer 	}
10314cf49a43SJulian Elischer 	/*
10324cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10334cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10344cf49a43SJulian Elischer 	 */
103530400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
103630400f03SJulian Elischer 	node->nd_numhooks++;
10373e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
10384cf49a43SJulian Elischer 
10394cf49a43SJulian Elischer 	if (hookp)
10404cf49a43SJulian Elischer 		*hookp = hook;
10413e4084c8SJulian Elischer 	return (0);
10424cf49a43SJulian Elischer }
10434cf49a43SJulian Elischer 
10444cf49a43SJulian Elischer /*
1045899e9c4eSArchie Cobbs  * Find a hook
1046899e9c4eSArchie Cobbs  *
1047899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1048899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
10493e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1050899e9c4eSArchie Cobbs  */
1051899e9c4eSArchie Cobbs hook_p
1052899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1053899e9c4eSArchie Cobbs {
1054899e9c4eSArchie Cobbs 	hook_p hook;
1055899e9c4eSArchie Cobbs 
105630400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
105730400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
105830400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
105970de87f2SJulian Elischer 		if (NG_HOOK_IS_VALID(hook)
1060ce5e5f99SArchie Cobbs 		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
1061899e9c4eSArchie Cobbs 			return (hook);
1062899e9c4eSArchie Cobbs 	}
1063899e9c4eSArchie Cobbs 	return (NULL);
1064899e9c4eSArchie Cobbs }
1065899e9c4eSArchie Cobbs 
1066899e9c4eSArchie Cobbs /*
10674cf49a43SJulian Elischer  * Destroy a hook
10684cf49a43SJulian Elischer  *
10694cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
10704cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
10713e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
10723e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
10733e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
10743e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
10753e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
10763e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
10773e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
10783e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
10793e4084c8SJulian Elischer  * attached to the 'dead' hook.
10806b795970SJulian Elischer  *
10816b795970SJulian Elischer  * This routine is called at all stages of hook creation
10826b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
10834cf49a43SJulian Elischer  */
10844cf49a43SJulian Elischer void
10854cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
10864cf49a43SJulian Elischer {
1087ac5dd141SGleb Smirnoff 	hook_p peer;
1088ac5dd141SGleb Smirnoff 	node_p node;
10894cf49a43SJulian Elischer 
10906b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
10916b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
10926b795970SJulian Elischer 		return;
10936b795970SJulian Elischer 	}
1094ac5dd141SGleb Smirnoff 
1095ac5dd141SGleb Smirnoff 	/*
1096ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1097ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1098ac5dd141SGleb Smirnoff 	 */
1099ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1100ac5dd141SGleb Smirnoff 
1101ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1102ac5dd141SGleb Smirnoff 
1103ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1104ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1105ac5dd141SGleb Smirnoff 
11063e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
11073e4084c8SJulian Elischer 		/*
11083e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
11093e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
11103e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
11113e4084c8SJulian Elischer 		 */
11123e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
11133e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
11146b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
11156b795970SJulian Elischer 			/*
11166b795970SJulian Elischer 			 * If it's already divorced from a node,
11176b795970SJulian Elischer 			 * just free it.
11186b795970SJulian Elischer 			 */
1119ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11206b795970SJulian Elischer 		} else {
1121ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11226b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11236b795970SJulian Elischer 		}
112452fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
112552fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1126ac5dd141SGleb Smirnoff 	} else
1127ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1128ac5dd141SGleb Smirnoff 
1129ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11304cf49a43SJulian Elischer 
11314cf49a43SJulian Elischer 	/*
11324cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11334cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11344cf49a43SJulian Elischer 	 */
11356b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11366b795970SJulian Elischer 		return;
11376b795970SJulian Elischer 	}
113830400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
113930400f03SJulian Elischer 	node->nd_numhooks--;
114030400f03SJulian Elischer 	if (node->nd_type->disconnect) {
11414cf49a43SJulian Elischer 		/*
11426b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
114364efc707SRobert Watson 		 * trust its existence after this point. (except
11446b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
11456b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
11464cf49a43SJulian Elischer 		 */
114730400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
11484cf49a43SJulian Elischer 	}
11496b795970SJulian Elischer 
11506b795970SJulian Elischer 	/*
11516b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
11526b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
11536b795970SJulian Elischer 	 */
11546b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
11556b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
11566b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
11574cf49a43SJulian Elischer }
11584cf49a43SJulian Elischer 
11594cf49a43SJulian Elischer /*
11604cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
11614cf49a43SJulian Elischer  * is effectively bypassed.
11624cf49a43SJulian Elischer  */
11634cf49a43SJulian Elischer int
11644cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
11654cf49a43SJulian Elischer {
116630400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
11676b795970SJulian Elischer 		TRAP_ERROR();
11684cf49a43SJulian Elischer 		return (EINVAL);
116930400f03SJulian Elischer 	}
117030400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
117130400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
11724cf49a43SJulian Elischer 
11733e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
11743e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
11753e4084c8SJulian Elischer 
1176cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1177cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1178cf3254aaSGleb Smirnoff 
11794cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
11804cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
11814cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
11824cf49a43SJulian Elischer 	return (0);
11834cf49a43SJulian Elischer }
11844cf49a43SJulian Elischer 
11854cf49a43SJulian Elischer /*
11864cf49a43SJulian Elischer  * Install a new netgraph type
11874cf49a43SJulian Elischer  */
11884cf49a43SJulian Elischer int
11894cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
11904cf49a43SJulian Elischer {
11914cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
11924cf49a43SJulian Elischer 
11934cf49a43SJulian Elischer 	/* Check version and type name fields */
1194589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
1195589f6ed8SJulian Elischer 	|| (namelen == 0)
119687e2c66aSHartmut Brandt 	|| (namelen >= NG_TYPESIZ)) {
11976b795970SJulian Elischer 		TRAP_ERROR();
11988ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
11998ed370fdSJulian Elischer 			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
12008ed370fdSJulian Elischer 		}
12014cf49a43SJulian Elischer 		return (EINVAL);
12024cf49a43SJulian Elischer 	}
12034cf49a43SJulian Elischer 
12044cf49a43SJulian Elischer 	/* Check for name collision */
12054cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
12066b795970SJulian Elischer 		TRAP_ERROR();
12074cf49a43SJulian Elischer 		return (EEXIST);
12084cf49a43SJulian Elischer 	}
12094cf49a43SJulian Elischer 
1210069154d5SJulian Elischer 
1211069154d5SJulian Elischer 	/* Link in new type */
12129ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1213069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1214c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
12159ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12164cf49a43SJulian Elischer 	return (0);
12174cf49a43SJulian Elischer }
12184cf49a43SJulian Elischer 
12194cf49a43SJulian Elischer /*
1220c31b4a53SJulian Elischer  * unlink a netgraph type
1221c31b4a53SJulian Elischer  * If no examples exist
1222c31b4a53SJulian Elischer  */
1223c31b4a53SJulian Elischer int
1224c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1225c31b4a53SJulian Elischer {
1226c31b4a53SJulian Elischer 	/* Check for name collision */
1227c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1228c31b4a53SJulian Elischer 		TRAP_ERROR();
1229c31b4a53SJulian Elischer 		return (EBUSY);
1230c31b4a53SJulian Elischer 	}
1231c31b4a53SJulian Elischer 
1232c31b4a53SJulian Elischer 	/* Unlink type */
1233c31b4a53SJulian Elischer 	mtx_lock(&ng_typelist_mtx);
1234c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1235c31b4a53SJulian Elischer 	mtx_unlock(&ng_typelist_mtx);
1236c31b4a53SJulian Elischer 	return (0);
1237c31b4a53SJulian Elischer }
1238c31b4a53SJulian Elischer 
1239c31b4a53SJulian Elischer /*
12404cf49a43SJulian Elischer  * Look for a type of the name given
12414cf49a43SJulian Elischer  */
12424cf49a43SJulian Elischer struct ng_type *
12434cf49a43SJulian Elischer ng_findtype(const char *typename)
12444cf49a43SJulian Elischer {
12454cf49a43SJulian Elischer 	struct ng_type *type;
12464cf49a43SJulian Elischer 
12479ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1248069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
12494cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
12504cf49a43SJulian Elischer 			break;
12514cf49a43SJulian Elischer 	}
12529ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12534cf49a43SJulian Elischer 	return (type);
12544cf49a43SJulian Elischer }
12554cf49a43SJulian Elischer 
12564cf49a43SJulian Elischer /************************************************************************
12574cf49a43SJulian Elischer 			Composite routines
12584cf49a43SJulian Elischer ************************************************************************/
12594cf49a43SJulian Elischer /*
12606b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
12614cf49a43SJulian Elischer  */
1262e088dd4cSAlexander Motin static int
1263e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
12644cf49a43SJulian Elischer {
1265e088dd4cSAlexander Motin 	int	error = 0;
12664cf49a43SJulian Elischer 
12676b795970SJulian Elischer 	/*
12686b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12696b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12706b795970SJulian Elischer 	 * Our caller has a reference on the node.
12716b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12726b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12731acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
12741acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
12756b795970SJulian Elischer 	 */
12766b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
12776b795970SJulian Elischer 		/*
12786b795970SJulian Elischer 		 * The node must have been freed again since we last visited
12796b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
12806b795970SJulian Elischer 		 * We should just release our references and
12816b795970SJulian Elischer 		 * free anything we can think of.
12826b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
12836b795970SJulian Elischer 		 * that holds the references, just return.
12846b795970SJulian Elischer 		 */
1285e088dd4cSAlexander Motin 		ERROUT(ENOENT);
12866b795970SJulian Elischer 	}
12876b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1288e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
12896b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
12901acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1291e088dd4cSAlexander Motin 			ERROUT(error);
12924cf49a43SJulian Elischer 		}
12936b795970SJulian Elischer 	}
12946b795970SJulian Elischer 	/*
12956b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
12966b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
12976b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
12986b795970SJulian Elischer 	 */
12996b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1300e088dd4cSAlexander Motin done:
1301e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1302e088dd4cSAlexander Motin 	return (error);
13036b795970SJulian Elischer }
13046b795970SJulian Elischer 
1305e088dd4cSAlexander Motin static int
1306e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
13076b795970SJulian Elischer {
1308ac5dd141SGleb Smirnoff 	hook_p	peer;
1309e088dd4cSAlexander Motin 	int	error = 0;
13106b795970SJulian Elischer 
13116b795970SJulian Elischer 	/*
13126b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
13136b795970SJulian Elischer 	 * Our caller has a reference on the hook.
13146b795970SJulian Elischer 	 * Our caller has a reference on the node.
13156b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13166b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13176b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
13186b795970SJulian Elischer 	 * First check the hook name is unique.
13191acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13206b795970SJulian Elischer 	 */
13216b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13226b795970SJulian Elischer 		TRAP_ERROR();
13236b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
13241acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1325e088dd4cSAlexander Motin 		ERROUT(EEXIST);
13266b795970SJulian Elischer 	}
13276b795970SJulian Elischer 	/*
13286b795970SJulian Elischer 	 * Check if the node type code has something to say about it
13296b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
13306b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
13316b795970SJulian Elischer 	 * The peer hook will also be destroyed.
13326b795970SJulian Elischer 	 */
13336b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1334e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1335e088dd4cSAlexander Motin 		    hook->hk_name))) {
13366b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
13371acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1338e088dd4cSAlexander Motin 			ERROUT(error);
13396b795970SJulian Elischer 		}
13406b795970SJulian Elischer 	}
13416b795970SJulian Elischer 
13426b795970SJulian Elischer 	/*
13436b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
13446b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
13456b795970SJulian Elischer 	 */
13466b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
13476b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
13486b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
13496b795970SJulian Elischer 	node->nd_numhooks++;
13506b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
13516b795970SJulian Elischer 
13526b795970SJulian Elischer 	/*
135364efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
13545951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
13556b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
13566b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
13576b795970SJulian Elischer 	 * yet.
135864efc707SRobert Watson 	 * We can call the local one immediately as we have the
13596b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
13606b795970SJulian Elischer 	 */
13616b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1362e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13636b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13641acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1365e088dd4cSAlexander Motin 			ERROUT(error);
13666b795970SJulian Elischer 		}
13676b795970SJulian Elischer 	}
1368ac5dd141SGleb Smirnoff 
1369ac5dd141SGleb Smirnoff 	/*
1370ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1371ac5dd141SGleb Smirnoff 	 */
1372ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1373ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1374ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1375ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1376ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1377ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1378e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1379ac5dd141SGleb Smirnoff 	}
1380ac5dd141SGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
1381ac5dd141SGleb Smirnoff 
1382b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1383b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1384ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
13851acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1386e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
13871acb27c6SJulian Elischer 	}
13886b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1389e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1390e088dd4cSAlexander Motin done:
1391e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1392e088dd4cSAlexander Motin 	return (error);
13934cf49a43SJulian Elischer }
13944cf49a43SJulian Elischer 
13954cf49a43SJulian Elischer /*
13966b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
13976b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
13984cf49a43SJulian Elischer  */
13996b795970SJulian Elischer static int
1400e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1401e088dd4cSAlexander Motin     node_p node2, const char *name2)
14024cf49a43SJulian Elischer {
14034cf49a43SJulian Elischer 	int	error;
14044cf49a43SJulian Elischer 	hook_p	hook;
14054cf49a43SJulian Elischer 	hook_p	hook2;
14064cf49a43SJulian Elischer 
14071acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
14081acb27c6SJulian Elischer 		return(EEXIST);
14091acb27c6SJulian Elischer 	}
14103e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
14114cf49a43SJulian Elischer 		return (error);
14126b795970SJulian Elischer 	/* Allocate the other hook and link it up */
14136b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
141419724144SGleb Smirnoff 	if (hook2 == NULL) {
14156b795970SJulian Elischer 		TRAP_ERROR();
14166b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
14176b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
14186b795970SJulian Elischer 		return (ENOMEM);
14196b795970SJulian Elischer 	}
14206b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14216b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14226b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
14236b795970SJulian Elischer 	hook->hk_peer = hook2;
14246b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
14256b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14266b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
142787e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
14286b795970SJulian Elischer 
14296b795970SJulian Elischer 	/*
14306b795970SJulian Elischer 	 * Queue the function above.
14316b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
14326b795970SJulian Elischer 	 * the other node.
14336b795970SJulian Elischer 	 */
1434b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1435b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
14363fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
14373fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
14383fb87c24SAlexander Motin 	}
14396b795970SJulian Elischer 
14406b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
14416b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
14423fb87c24SAlexander Motin 	return (error);
14434cf49a43SJulian Elischer }
14446b795970SJulian Elischer 
14456b795970SJulian Elischer /*
14466b795970SJulian Elischer  * Make a peer and connect.
14476b795970SJulian Elischer  * We assume that the local node is locked.
14486b795970SJulian Elischer  * The new node probably doesn't need a lock until
14496b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
14506b795970SJulian Elischer  * but we should think about it a bit more.
14516b795970SJulian Elischer  *
14526b795970SJulian Elischer  * The problem may come if the other node also fires up
14536b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
14546b795970SJulian Elischer  * also it may already get a command msg via it's ID.
14556b795970SJulian Elischer  *
14566b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
14576b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
14586b795970SJulian Elischer  * make arg1 point to the node to remove).
14596b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
14606b795970SJulian Elischer  * an unconnected node?
14616b795970SJulian Elischer  */
14626b795970SJulian Elischer static int
14636b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
14646b795970SJulian Elischer {
14656b795970SJulian Elischer 	node_p	node2;
14664c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
14676b795970SJulian Elischer 	int	error;
14686b795970SJulian Elischer 
14696b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
14706b795970SJulian Elischer 		return (error);
14716b795970SJulian Elischer 	}
14726b795970SJulian Elischer 
14736b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
14741acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14756b795970SJulian Elischer 		return (error);
14766b795970SJulian Elischer 	}
14776b795970SJulian Elischer 
14786b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
14791acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14806b795970SJulian Elischer 		ng_destroy_hook(hook1);
14816b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
14826b795970SJulian Elischer 		return (error);
14836b795970SJulian Elischer 	}
14846b795970SJulian Elischer 
14856b795970SJulian Elischer 	/*
14866b795970SJulian Elischer 	 * Actually link the two hooks together.
14876b795970SJulian Elischer 	 */
14886b795970SJulian Elischer 	hook1->hk_peer = hook2;
14896b795970SJulian Elischer 	hook2->hk_peer = hook1;
14906b795970SJulian Elischer 
14916b795970SJulian Elischer 	/* Each hook is referenced by the other */
14926b795970SJulian Elischer 	NG_HOOK_REF(hook1);
14936b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14946b795970SJulian Elischer 
14956b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
14966b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
14976b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
14986b795970SJulian Elischer 	}
14996b795970SJulian Elischer 
15006b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
15016b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
15026b795970SJulian Elischer 
15036b795970SJulian Elischer 	}
15043e4084c8SJulian Elischer 
15053e4084c8SJulian Elischer 	/*
15063e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
15073e4084c8SJulian Elischer 	 */
15086b795970SJulian Elischer 	if (error) {
15096b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
15101acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15116b795970SJulian Elischer 	} else {
15126b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
15136b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
15146b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
15156b795970SJulian Elischer 	}
15166b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
15173e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
15183e4084c8SJulian Elischer 	return (error);
15194cf49a43SJulian Elischer }
15206b795970SJulian Elischer 
1521069154d5SJulian Elischer /************************************************************************
1522069154d5SJulian Elischer 		Utility routines to send self messages
1523069154d5SJulian Elischer ************************************************************************/
1524069154d5SJulian Elischer 
15251acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
152664efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1527069154d5SJulian Elischer int
152815cea89fSAlexander Motin ng_rmnode_self(node_p node)
1529069154d5SJulian Elischer {
15301acb27c6SJulian Elischer 	int		error;
15314cf49a43SJulian Elischer 
15321acb27c6SJulian Elischer 	if (node == &ng_deadnode)
15331acb27c6SJulian Elischer 		return (0);
1534be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1535be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
15361acb27c6SJulian Elischer 		return (0);
1537069154d5SJulian Elischer 
153815cea89fSAlexander Motin 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
15391acb27c6SJulian Elischer 	return (error);
1540069154d5SJulian Elischer }
1541069154d5SJulian Elischer 
15421acb27c6SJulian Elischer static void
15436b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
15446b795970SJulian Elischer {
15456b795970SJulian Elischer 	ng_destroy_hook(hook);
15461acb27c6SJulian Elischer 	return ;
15476b795970SJulian Elischer }
15486b795970SJulian Elischer 
1549954c4772SJulian Elischer int
1550954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1551954c4772SJulian Elischer {
15526b795970SJulian Elischer 	int		error;
1553954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1554954c4772SJulian Elischer 
15556b795970SJulian Elischer 	if (node == &ng_deadnode)
15566b795970SJulian Elischer 		return (0);
15576b795970SJulian Elischer 
15586b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
15596b795970SJulian Elischer 	return (error);
1560954c4772SJulian Elischer }
1561954c4772SJulian Elischer 
1562069154d5SJulian Elischer /***********************************************************************
15634cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
15644cf49a43SJulian Elischer  *
15654cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
15664cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
15674cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
15684cf49a43SJulian Elischer  *
15694cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
15704cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
15714cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
15724cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
15734cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
15744cf49a43SJulian Elischer  *
15754cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1576069154d5SJulian Elischer  ***********************************************************************/
15774cf49a43SJulian Elischer int
15784cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
15794cf49a43SJulian Elischer {
15804cf49a43SJulian Elischer 	char	*node, *path, *hook;
15814cf49a43SJulian Elischer 	int	k;
15824cf49a43SJulian Elischer 
15834cf49a43SJulian Elischer 	/*
15844cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
15854cf49a43SJulian Elischer 	 */
15864cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
15874cf49a43SJulian Elischer 	if (*path) {
15884cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
15894cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
15904cf49a43SJulian Elischer 
15914cf49a43SJulian Elischer 		/* Node name must not be empty */
15924cf49a43SJulian Elischer 		if (!*node)
15934cf49a43SJulian Elischer 			return -1;
15944cf49a43SJulian Elischer 
15954cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
15964cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
15974cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
15984cf49a43SJulian Elischer 				if (node[k] == '.')
15994cf49a43SJulian Elischer 					return -1;
16004cf49a43SJulian Elischer 		}
16014cf49a43SJulian Elischer 	} else {
16024cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
16034cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
16044cf49a43SJulian Elischer 	}
16054cf49a43SJulian Elischer 
16064cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
16074cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16084cf49a43SJulian Elischer 		if (path[k] == ':')
16094cf49a43SJulian Elischer 			return -1;
16104cf49a43SJulian Elischer 
16114cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
16124cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16134cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
16144cf49a43SJulian Elischer 			return -1;
16154cf49a43SJulian Elischer 
16164cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
16174cf49a43SJulian Elischer 	if (path[0] == '.')
16184cf49a43SJulian Elischer 		path++;
16194cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16204cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16214cf49a43SJulian Elischer 
16224cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
16234cf49a43SJulian Elischer 	if (*path) {
16244cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
16254cf49a43SJulian Elischer 			if (path[k] == '.') {
16264cf49a43SJulian Elischer 				hook = NULL;
16274cf49a43SJulian Elischer 				break;
16284cf49a43SJulian Elischer 			}
16294cf49a43SJulian Elischer 	} else
16304cf49a43SJulian Elischer 		path = hook = NULL;
16314cf49a43SJulian Elischer 
16324cf49a43SJulian Elischer 	/* Done */
16334cf49a43SJulian Elischer 	if (nodep)
16344cf49a43SJulian Elischer 		*nodep = node;
16354cf49a43SJulian Elischer 	if (pathp)
16364cf49a43SJulian Elischer 		*pathp = path;
16374cf49a43SJulian Elischer 	if (hookp)
16384cf49a43SJulian Elischer 		*hookp = hook;
16394cf49a43SJulian Elischer 	return (0);
16404cf49a43SJulian Elischer }
16414cf49a43SJulian Elischer 
16424cf49a43SJulian Elischer /*
16434cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1644069154d5SJulian Elischer  * return the destination node.
16454cf49a43SJulian Elischer  */
16464cf49a43SJulian Elischer int
1647069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1648069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
16494cf49a43SJulian Elischer {
165087e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
16514cf49a43SJulian Elischer 	char   *nodename, *path, pbuf[2];
1652069154d5SJulian Elischer 	node_p  node, oldnode;
16534cf49a43SJulian Elischer 	char   *cp;
1654a4ec03cfSJulian Elischer 	hook_p hook = NULL;
16554cf49a43SJulian Elischer 
16564cf49a43SJulian Elischer 	/* Initialize */
165730400f03SJulian Elischer 	if (destp == NULL) {
16586b795970SJulian Elischer 		TRAP_ERROR();
16594cf49a43SJulian Elischer 		return EINVAL;
166030400f03SJulian Elischer 	}
16614cf49a43SJulian Elischer 	*destp = NULL;
16624cf49a43SJulian Elischer 
16634cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
16644cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
16654cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
16664cf49a43SJulian Elischer 
16674cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
16684cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
16696b795970SJulian Elischer 		TRAP_ERROR();
16704cf49a43SJulian Elischer 		return EINVAL;
16714cf49a43SJulian Elischer 	}
16724cf49a43SJulian Elischer 	if (path == NULL) {
16734cf49a43SJulian Elischer 		pbuf[0] = '.';	/* Needs to be writable */
16744cf49a43SJulian Elischer 		pbuf[1] = '\0';
16754cf49a43SJulian Elischer 		path = pbuf;
16764cf49a43SJulian Elischer 	}
16774cf49a43SJulian Elischer 
1678069154d5SJulian Elischer 	/*
1679069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1680069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1681069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1682069154d5SJulian Elischer 	 */
16834cf49a43SJulian Elischer 	if (nodename) {
1684069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
16854cf49a43SJulian Elischer 		if (node == NULL) {
16866b795970SJulian Elischer 			TRAP_ERROR();
16874cf49a43SJulian Elischer 			return (ENOENT);
16884cf49a43SJulian Elischer 		}
1689069154d5SJulian Elischer 	} else {
1690069154d5SJulian Elischer 		if (here == NULL) {
16916b795970SJulian Elischer 			TRAP_ERROR();
1692069154d5SJulian Elischer 			return (EINVAL);
1693069154d5SJulian Elischer 		}
16944cf49a43SJulian Elischer 		node = here;
169530400f03SJulian Elischer 		NG_NODE_REF(node);
1696069154d5SJulian Elischer 	}
16974cf49a43SJulian Elischer 
1698069154d5SJulian Elischer 	/*
1699069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1700069154d5SJulian Elischer 	 * XXX
1701069154d5SJulian Elischer 	 * We actually cannot guarantee that the sequence
1702069154d5SJulian Elischer 	 * is not being demolished as we crawl along it
1703069154d5SJulian Elischer 	 * without extra-ordinary locking etc.
1704069154d5SJulian Elischer 	 * So this is a bit dodgy to say the least.
1705069154d5SJulian Elischer 	 * We can probably hold up some things by holding
1706069154d5SJulian Elischer 	 * the nodelist mutex for the time of this
1707069154d5SJulian Elischer 	 * crawl if we wanted.. At least that way we wouldn't have to
170864efc707SRobert Watson 	 * worry about the nodes disappearing, but the hooks would still
1709069154d5SJulian Elischer 	 * be a problem.
1710069154d5SJulian Elischer 	 */
17114cf49a43SJulian Elischer 	for (cp = path; node != NULL && *cp != '\0'; ) {
17124cf49a43SJulian Elischer 		char *segment;
17134cf49a43SJulian Elischer 
17144cf49a43SJulian Elischer 		/*
17154cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
17164cf49a43SJulian Elischer 		 * found with a NUL; "cp" points to the next segment (or the
17174cf49a43SJulian Elischer 		 * NUL at the end).
17184cf49a43SJulian Elischer 		 */
17194cf49a43SJulian Elischer 		for (segment = cp; *cp != '\0'; cp++) {
17204cf49a43SJulian Elischer 			if (*cp == '.') {
17214cf49a43SJulian Elischer 				*cp++ = '\0';
17224cf49a43SJulian Elischer 				break;
17234cf49a43SJulian Elischer 			}
17244cf49a43SJulian Elischer 		}
17254cf49a43SJulian Elischer 
17264cf49a43SJulian Elischer 		/* Empty segment */
17274cf49a43SJulian Elischer 		if (*segment == '\0')
17284cf49a43SJulian Elischer 			continue;
17294cf49a43SJulian Elischer 
17304cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1731899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17324cf49a43SJulian Elischer 
17334cf49a43SJulian Elischer 		/* Can't get there from here... */
17344cf49a43SJulian Elischer 		if (hook == NULL
173530400f03SJulian Elischer 		    || NG_HOOK_PEER(hook) == NULL
173630400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(hook)
173730400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17386b795970SJulian Elischer 			TRAP_ERROR();
173930400f03SJulian Elischer 			NG_NODE_UNREF(node);
174030400f03SJulian Elischer #if 0
174130400f03SJulian Elischer 			printf("hooknotvalid %s %s %d %d %d %d ",
174230400f03SJulian Elischer 					path,
174330400f03SJulian Elischer 					segment,
174430400f03SJulian Elischer 					hook == NULL,
174530400f03SJulian Elischer 					NG_HOOK_PEER(hook) == NULL,
174630400f03SJulian Elischer 					NG_HOOK_NOT_VALID(hook),
174730400f03SJulian Elischer 					NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
174830400f03SJulian Elischer #endif
17494cf49a43SJulian Elischer 			return (ENOENT);
17504cf49a43SJulian Elischer 		}
17514cf49a43SJulian Elischer 
1752069154d5SJulian Elischer 		/*
1753069154d5SJulian Elischer 		 * Hop on over to the next node
1754069154d5SJulian Elischer 		 * XXX
1755069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1756069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1757069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1758069154d5SJulian Elischer 		 */
1759069154d5SJulian Elischer 		oldnode = node;
176030400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
176130400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
176230400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
176330400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
176430400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1765069154d5SJulian Elischer 			node = NULL;
1766069154d5SJulian Elischer 		}
17674cf49a43SJulian Elischer 	}
17684cf49a43SJulian Elischer 
17694cf49a43SJulian Elischer 	/* If node somehow missing, fail here (probably this is not needed) */
17704cf49a43SJulian Elischer 	if (node == NULL) {
17716b795970SJulian Elischer 		TRAP_ERROR();
17724cf49a43SJulian Elischer 		return (ENXIO);
17734cf49a43SJulian Elischer 	}
17744cf49a43SJulian Elischer 
17754cf49a43SJulian Elischer 	/* Done */
17764cf49a43SJulian Elischer 	*destp = node;
17771bdebe4dSArchie Cobbs 	if (lasthook != NULL)
177830400f03SJulian Elischer 		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
1779069154d5SJulian Elischer 	return (0);
1780069154d5SJulian Elischer }
1781069154d5SJulian Elischer 
1782069154d5SJulian Elischer /***************************************************************\
1783069154d5SJulian Elischer * Input queue handling.
1784069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1785069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
178664efc707SRobert Watson * Items which cannot be handled immediately are queued.
1787069154d5SJulian Elischer *
1788069154d5SJulian Elischer * read-write queue locking inline functions			*
1789069154d5SJulian Elischer \***************************************************************/
1790069154d5SJulian Elischer 
17919852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
17929852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
17939852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
17949852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
17959852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
17969852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1797069154d5SJulian Elischer 
1798069154d5SJulian Elischer /*
1799069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1800069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1801069154d5SJulian Elischer  * with them.
1802069154d5SJulian Elischer  *
1803b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1804069154d5SJulian Elischer  */
1805069154d5SJulian Elischer /*-
1806069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1807069154d5SJulian Elischer                        |
1808069154d5SJulian Elischer                        V
1809069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18104be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
18114be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
18124be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1813069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18144be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
18154be59335SGleb Smirnoff                             V                                | |
18164be59335SGleb Smirnoff                   [active reader count]                      | |
1817069154d5SJulian Elischer                                                              | |
18184be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1819069154d5SJulian Elischer                                                                |
18204be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1821b57a7965SJulian Elischer 
18228f9ac44aSAlexander Motin Node queue has such semantics:
18238f9ac44aSAlexander Motin - All flags modifications are atomic.
18248f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18258f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18268f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18278f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18288f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18298f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18308f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18318f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18328f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1833069154d5SJulian Elischer */
18348f9ac44aSAlexander Motin 
18354be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18364be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18374be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18384be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18394be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1840b57a7965SJulian Elischer 
1841b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18424be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18434be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1844b57a7965SJulian Elischer 
18454be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1846b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1847b57a7965SJulian Elischer 
18484be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
18494be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
18504be59335SGleb Smirnoff 
18514be59335SGleb Smirnoff /* How to decide what the next queued item is. */
18529852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
18539852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
18544be59335SGleb Smirnoff 
18554be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
18564be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
18574be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
18584be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
18594be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1860b57a7965SJulian Elischer 
1861b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
18624be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
18634be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1864394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
18654be59335SGleb Smirnoff 
1866714fb865SGleb Smirnoff #define NGQRW_R 0
1867714fb865SGleb Smirnoff #define NGQRW_W 1
1868714fb865SGleb Smirnoff 
18699852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
18709852972bSAlexander Motin 
1871069154d5SJulian Elischer /*
1872069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1873069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1874069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1875069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1876069154d5SJulian Elischer  * on the queue.
1877069154d5SJulian Elischer  */
1878069154d5SJulian Elischer static __inline item_p
18799852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1880069154d5SJulian Elischer {
1881069154d5SJulian Elischer 	item_p item;
18829852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1883b57a7965SJulian Elischer 
18848f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1885d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
18868f9ac44aSAlexander Motin 
18878f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
18884be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
18892955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
18902955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
18919852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
18924be59335SGleb Smirnoff 		return (NULL);
18934be59335SGleb Smirnoff 	}
1894d58e678fSGleb Smirnoff 
18954be59335SGleb Smirnoff 	/*
18964be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
18974be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
18984be59335SGleb Smirnoff 	 * the current state of the node.
18994be59335SGleb Smirnoff 	 */
19004be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1901394cb30aSAlexander Motin 		while (1) {
1902394cb30aSAlexander Motin 			long t = ngq->q_flags;
1903394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
19048f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
19052955ee18SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
19062955ee18SGleb Smirnoff 				    "can't proceed; queue flags 0x%lx", __func__,
19079852972bSAlexander Motin 				    node->nd_ID, node, t);
19084be59335SGleb Smirnoff 				return (NULL);
19094be59335SGleb Smirnoff 			}
19109852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1911394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1912394cb30aSAlexander Motin 				break;
1913394cb30aSAlexander Motin 			cpu_spinwait();
1914394cb30aSAlexander Motin 		}
19158f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1916714fb865SGleb Smirnoff 		*rw = NGQRW_R;
19179852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1918394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19198f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1920714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1921069154d5SJulian Elischer 	} else {
19228f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
1923394cb30aSAlexander Motin 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer "
1924394cb30aSAlexander Motin 		    "can't proceed; queue flags 0x%lx", __func__,
19259852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19264be59335SGleb Smirnoff 		return (NULL);
19274cf49a43SJulian Elischer 	}
19284cf49a43SJulian Elischer 
19294cf49a43SJulian Elischer 	/*
1930069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1931069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19324cf49a43SJulian Elischer 	 */
19339852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19349852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
19359852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
19369852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
19372955ee18SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
19382955ee18SGleb Smirnoff 	    "queue flags 0x%lx", __func__,
19399852972bSAlexander Motin 	    node->nd_ID, node, item, *rw ? "WRITER" : "READER" ,
19403b33fbe7SGleb Smirnoff 	    ngq->q_flags);
1941069154d5SJulian Elischer 	return (item);
1942069154d5SJulian Elischer }
1943859a4d16SJulian Elischer 
1944859a4d16SJulian Elischer /*
19458f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
19468f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
1947859a4d16SJulian Elischer  */
1948069154d5SJulian Elischer static __inline void
19499852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
1950069154d5SJulian Elischer {
19519852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
19524be59335SGleb Smirnoff 	if (rw == NGQRW_W)
19534be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
19544be59335SGleb Smirnoff 	else
19554be59335SGleb Smirnoff 		NGI_SET_READER(item);
1956394cb30aSAlexander Motin 
1957394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
1958394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
19599852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
19609852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
1961394cb30aSAlexander Motin 
19622955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
19639852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
19644be59335SGleb Smirnoff 
19654be59335SGleb Smirnoff 	/*
19664be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
19674be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
19684be59335SGleb Smirnoff 	 */
19694be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
19709852972bSAlexander Motin 		ng_worklist_add(node);
1971394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
1972069154d5SJulian Elischer }
1973069154d5SJulian Elischer 
19748f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
1975069154d5SJulian Elischer static __inline item_p
19769852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
1977069154d5SJulian Elischer {
19789852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1979ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1980069154d5SJulian Elischer 
1981394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
1982394cb30aSAlexander Motin 	while (1) {
19839852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
1984394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
1985394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
19869852972bSAlexander Motin 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19879852972bSAlexander Motin 		    t, t + READER_INCREMENT)) {
1988069154d5SJulian Elischer 	    		/* Successfully grabbed node */
1989394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
19909852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
1991069154d5SJulian Elischer 			return (item);
1992069154d5SJulian Elischer 		}
1993394cb30aSAlexander Motin 		cpu_spinwait();
1994394cb30aSAlexander Motin 	};
1995069154d5SJulian Elischer 
1996394cb30aSAlexander Motin 	/* Queue the request for later. */
19979852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
1998f912c0f7SGleb Smirnoff 
1999f912c0f7SGleb Smirnoff 	return (NULL);
2000069154d5SJulian Elischer }
2001069154d5SJulian Elischer 
20028f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
2003069154d5SJulian Elischer static __inline item_p
20049852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
2005069154d5SJulian Elischer {
20069852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2007ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
2008ac5dd141SGleb Smirnoff 
2009394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
20109852972bSAlexander Motin 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
20119852972bSAlexander Motin 	    0, WRITER_ACTIVE)) {
2012394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
20132955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20149852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
2015069154d5SJulian Elischer 		return (item);
2016069154d5SJulian Elischer 	}
2017069154d5SJulian Elischer 
2018394cb30aSAlexander Motin 	/* Queue the request for later. */
20199852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
2020f912c0f7SGleb Smirnoff 
2021f912c0f7SGleb Smirnoff 	return (NULL);
2022069154d5SJulian Elischer }
2023069154d5SJulian Elischer 
2024262dfd7fSJulian Elischer #if 0
2025262dfd7fSJulian Elischer static __inline item_p
20269852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
2027262dfd7fSJulian Elischer {
20289852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20299852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2030262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2031262dfd7fSJulian Elischer 
2032262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2033262dfd7fSJulian Elischer 
20349852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2035262dfd7fSJulian Elischer 
2036262dfd7fSJulian Elischer 	/*
2037262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2038262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2039262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2040262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2041262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2042262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2043262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2044262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2045262dfd7fSJulian Elischer 	 */
20469852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
20479852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
20489852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2049262dfd7fSJulian Elischer 
2050262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2051262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2052262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2053262dfd7fSJulian Elischer 
2054262dfd7fSJulian Elischer 		/*
2055262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
2056262dfd7fSJulian Elischer 		 * down grade back to READER and finish up
2057262dfd7fSJulian Elischer 	 	 */
20589852972bSAlexander Motin 		atomic_add_int(&ngq->q_flags,
2059262dfd7fSJulian Elischer 		    READER_INCREMENT - WRITER_ACTIVE);
2060262dfd7fSJulian Elischer 
2061262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2062262dfd7fSJulian Elischer 		return;
2063262dfd7fSJulian Elischer 	}
2064262dfd7fSJulian Elischer 	/*
2065262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2066262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2067262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2068262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2069262dfd7fSJulian Elischer 	 * set the correct flags.
2070262dfd7fSJulian Elischer 	 */
20719852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2072262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
20739852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2074262dfd7fSJulian Elischer 
2075262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
20769852972bSAlexander Motin 		    node->nd_ID, node);
2077262dfd7fSJulian Elischer 	};
20789852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
20799852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
20809852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2081262dfd7fSJulian Elischer 
2082262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
20839852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2084394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20859852972bSAlexander Motin 		ng_worklist_add(node);
20869852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2087262dfd7fSJulian Elischer 
2088262dfd7fSJulian Elischer 	return;
2089262dfd7fSJulian Elischer }
2090262dfd7fSJulian Elischer #endif
2091262dfd7fSJulian Elischer 
20928f9ac44aSAlexander Motin /* Release reader lock. */
2093069154d5SJulian Elischer static __inline void
20949852972bSAlexander Motin ng_leave_read(node_p node)
2095069154d5SJulian Elischer {
20969852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2097069154d5SJulian Elischer }
2098069154d5SJulian Elischer 
20998f9ac44aSAlexander Motin /* Release writer lock. */
2100069154d5SJulian Elischer static __inline void
21019852972bSAlexander Motin ng_leave_write(node_p node)
2102069154d5SJulian Elischer {
21039852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2104069154d5SJulian Elischer }
2105069154d5SJulian Elischer 
21068f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2107069154d5SJulian Elischer static void
21089852972bSAlexander Motin ng_flush_input_queue(node_p node)
2109069154d5SJulian Elischer {
21109852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2111069154d5SJulian Elischer 	item_p item;
2112069154d5SJulian Elischer 
21132c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
21149852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
21159852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
21169852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
21179852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
21182c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21194be59335SGleb Smirnoff 
21204be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
212110e87318SAlexander Motin 		if (item->apply != NULL) {
212210e87318SAlexander Motin 			if (item->depth == 1)
212310e87318SAlexander Motin 				item->apply->error = ENOENT;
212410e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
212510e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
212610e87318SAlexander Motin 				    item->apply->error);
212710e87318SAlexander Motin 			}
2128eb2405ddSGleb Smirnoff 		}
2129505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21302c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2131069154d5SJulian Elischer 	}
21322c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2133069154d5SJulian Elischer }
2134069154d5SJulian Elischer 
2135069154d5SJulian Elischer /***********************************************************************
2136069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2137069154d5SJulian Elischer ***********************************************************************/
2138069154d5SJulian Elischer 
2139069154d5SJulian Elischer /*
21401acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2141069154d5SJulian Elischer  * Common:
2142069154d5SJulian Elischer  *    reference to destination node.
2143069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2144e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2145069154d5SJulian Elischer  * Data:
2146069154d5SJulian Elischer  *    pointer to mbuf
2147069154d5SJulian Elischer  * Control_Message:
2148069154d5SJulian Elischer  *    pointer to msg.
2149069154d5SJulian Elischer  *    ID of original sender node. (return address)
21501acb27c6SJulian Elischer  * Function:
21511acb27c6SJulian Elischer  *    Function pointer
21521acb27c6SJulian Elischer  *    void * argument
21531acb27c6SJulian Elischer  *    integer argument
2154069154d5SJulian Elischer  *
2155069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2156069154d5SJulian Elischer  */
2157069154d5SJulian Elischer 
2158069154d5SJulian Elischer int
215942282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2160069154d5SJulian Elischer {
2161e088dd4cSAlexander Motin 	hook_p hook;
2162e088dd4cSAlexander Motin 	node_p node;
216342282202SGleb Smirnoff 	int queue, rw;
2164e088dd4cSAlexander Motin 	struct ng_queue *ngq;
216532b33288SGleb Smirnoff 	int error = 0;
2166069154d5SJulian Elischer 
2167f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2168f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2169e088dd4cSAlexander Motin 
2170e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2171e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2172e088dd4cSAlexander Motin #endif
2173e088dd4cSAlexander Motin 
2174f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2175e088dd4cSAlexander Motin 	if (item->apply)
2176e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2177e088dd4cSAlexander Motin 
2178e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2179f50597f5SAlexander Motin 	/* Node is never optional. */
2180f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2181e088dd4cSAlexander Motin 
2182e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2183f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2184f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2185f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
218610204449SJulian Elischer 		if (NGI_M(item) == NULL)
2187e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2188069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
21896f683eeeSGleb Smirnoff 	}
21906f683eeeSGleb Smirnoff 
2191069154d5SJulian Elischer 	/*
2192f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2193f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2194f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2195069154d5SJulian Elischer 	 */
2196db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2197f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2198f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2199069154d5SJulian Elischer 		rw = NGQRW_W;
2200f50597f5SAlexander Motin 	} else {
2201f50597f5SAlexander Motin 		rw = NGQRW_R;
2202f50597f5SAlexander Motin 	}
22036f683eeeSGleb Smirnoff 
220481a253a4SAlexander Motin 	/*
2205f089869fSMarko Zec 	 * If sender or receiver requests queued delivery, or call graph
2206f089869fSMarko Zec 	 * loops back from outbound to inbound path, or stack usage
220781a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
220881a253a4SAlexander Motin 	 */
220981a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
221081a253a4SAlexander Motin 		queue = 1;
2211f089869fSMarko Zec 	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2212f089869fSMarko Zec 	    curthread->td_ng_outbound) {
2213f089869fSMarko Zec 		queue = 1;
2214f50597f5SAlexander Motin 	} else {
2215f50597f5SAlexander Motin 		queue = 0;
221681a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2217d4529f98SAlexander Motin 		/*
2218942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2219f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2220d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2221f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2222f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2223d4529f98SAlexander Motin 		 */
2224f50597f5SAlexander Motin 		size_t	st, su, sl;
222581a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2226f50597f5SAlexander Motin 		sl = st - su;
2227f50597f5SAlexander Motin 		if ((sl * 4 < st) ||
2228f50597f5SAlexander Motin 		    ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) ||
222981a253a4SAlexander Motin 		      (hook && (hook->hk_flags & HK_HI_STACK))))) {
223081a253a4SAlexander Motin 			queue = 1;
223181a253a4SAlexander Motin 		}
223281a253a4SAlexander Motin #endif
2233f50597f5SAlexander Motin 	}
223481a253a4SAlexander Motin 
2235069154d5SJulian Elischer 	if (queue) {
223610e87318SAlexander Motin 		item->depth = 1;
2237394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
22389852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2239f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2240069154d5SJulian Elischer 	}
2241069154d5SJulian Elischer 
2242069154d5SJulian Elischer 	/*
2243069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2244069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2245069154d5SJulian Elischer 	 */
224632b33288SGleb Smirnoff  	if (rw == NGQRW_R)
22479852972bSAlexander Motin 		item = ng_acquire_read(node, item);
224832b33288SGleb Smirnoff 	else
22499852972bSAlexander Motin 		item = ng_acquire_write(node, item);
22504cf49a43SJulian Elischer 
2251f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2252f50597f5SAlexander Motin 	if (item == NULL)
2253f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2254069154d5SJulian Elischer 
22555951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
22565951069aSJulian Elischer 
225710e87318SAlexander Motin 	item->depth++;
225827757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2259069154d5SJulian Elischer 
2260394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
22619852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2262394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
22632c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2264394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
22659852972bSAlexander Motin 			ng_worklist_add(node);
22662c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2267394cb30aSAlexander Motin 	}
2268394cb30aSAlexander Motin 
2269394cb30aSAlexander Motin 	/*
2270394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2271394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2272394cb30aSAlexander Motin 	 */
2273394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2274069154d5SJulian Elischer 
22751acb27c6SJulian Elischer 	return (error);
2276e088dd4cSAlexander Motin 
2277e088dd4cSAlexander Motin done:
2278f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
227910e87318SAlexander Motin 	if (item->apply != NULL) {
228010e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
228110e87318SAlexander Motin 			item->apply->error = error;
228210e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
228310e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
228410e87318SAlexander Motin 			    item->apply->error);
228510e87318SAlexander Motin 		}
228610e87318SAlexander Motin 	}
2287e72a98f4SAlexander Motin 
2288e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2289e088dd4cSAlexander Motin 	return (error);
2290069154d5SJulian Elischer }
2291069154d5SJulian Elischer 
2292069154d5SJulian Elischer /*
2293069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2294069154d5SJulian Elischer  * It should contain all the information needed
2295069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2296e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
22974cf49a43SJulian Elischer  */
229827757487SGleb Smirnoff static int
2299714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2300069154d5SJulian Elischer {
2301069154d5SJulian Elischer 	hook_p  hook;
2302069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2303c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2304e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
230510e87318SAlexander Motin 	int	error = 0, depth;
2306069154d5SJulian Elischer 
2307f50597f5SAlexander Motin 	/* Node and item are never optional. */
2308f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2309f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2310f50597f5SAlexander Motin 
23111acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
231230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2313069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2314069154d5SJulian Elischer #endif
23158afe16d5SGleb Smirnoff 
23168afe16d5SGleb Smirnoff 	apply = item->apply;
231710e87318SAlexander Motin 	depth = item->depth;
23188afe16d5SGleb Smirnoff 
23196b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2320069154d5SJulian Elischer 	case NGQF_DATA:
2321069154d5SJulian Elischer 		/*
2322069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2323069154d5SJulian Elischer 		 */
2324f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2325f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2326f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2327a54a69d7SJulian Elischer 			error = EIO;
2328069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2329c4b5eea4SJulian Elischer 			break;
2330069154d5SJulian Elischer 		}
2331c4b5eea4SJulian Elischer 		/*
2332c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
2333c4b5eea4SJulian Elischer 		 * Give preference to the hook over-ride method
2334c4b5eea4SJulian Elischer 		 */
2335c4b5eea4SJulian Elischer 		if ((!(rcvdata = hook->hk_rcvdata))
2336c4b5eea4SJulian Elischer 		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2337c4b5eea4SJulian Elischer 			error = 0;
2338c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2339c4b5eea4SJulian Elischer 			break;
2340c4b5eea4SJulian Elischer 		}
2341a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2342069154d5SJulian Elischer 		break;
2343069154d5SJulian Elischer 	case NGQF_MESG:
2344e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2345069154d5SJulian Elischer 			/*
2346e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2347e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2348069154d5SJulian Elischer 			 * The message may not need it.
2349069154d5SJulian Elischer 			 */
235030400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2351069154d5SJulian Elischer 			hook = NULL;
2352069154d5SJulian Elischer 		}
2353069154d5SJulian Elischer 		/*
2354069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2355069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2356069154d5SJulian Elischer 		 */
235730400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
23586b795970SJulian Elischer 			TRAP_ERROR();
2359a54a69d7SJulian Elischer 			error = EINVAL;
2360069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2361e72a98f4SAlexander Motin 			break;
2362e72a98f4SAlexander Motin 		}
2363069154d5SJulian Elischer 		/*
2364069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2365069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2366069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2367e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2368069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2369069154d5SJulian Elischer 		 * reference a node or hook that has just been
2370069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2371069154d5SJulian Elischer 		 * is holding a reference, but..
2372069154d5SJulian Elischer 		 */
2373e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2374e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2375a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2376c4b5eea4SJulian Elischer 			break;
2377c4b5eea4SJulian Elischer 		}
2378e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2379e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
23806b795970SJulian Elischer 			TRAP_ERROR();
2381a54a69d7SJulian Elischer 			error = 0;
2382069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2383c4b5eea4SJulian Elischer 			break;
2384069154d5SJulian Elischer 		}
2385a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2386069154d5SJulian Elischer 		break;
23876b795970SJulian Elischer 	case NGQF_FN:
2388e088dd4cSAlexander Motin 	case NGQF_FN2:
2389e088dd4cSAlexander Motin 		/*
239074c9119dSAlexander Motin 		 * In the case of the shutdown message we allow it to hit
2391e088dd4cSAlexander Motin 		 * even if the node is invalid.
2392e088dd4cSAlexander Motin 		 */
239374c9119dSAlexander Motin 		if (NG_NODE_NOT_VALID(node) &&
239474c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmnode) {
2395e088dd4cSAlexander Motin 			TRAP_ERROR();
2396e088dd4cSAlexander Motin 			error = EINVAL;
2397e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2398e088dd4cSAlexander Motin 			break;
2399e088dd4cSAlexander Motin 		}
240074c9119dSAlexander Motin 		/* Same is about some internal functions and invalid hook. */
240174c9119dSAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook) &&
240274c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part2 &&
240374c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part3 &&
240474c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmhook_part2) {
240574c9119dSAlexander Motin 			TRAP_ERROR();
240674c9119dSAlexander Motin 			error = EINVAL;
240774c9119dSAlexander Motin 			NG_FREE_ITEM(item);
240874c9119dSAlexander Motin 			break;
240974c9119dSAlexander Motin 		}
241074c9119dSAlexander Motin 
2411b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2412b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2413b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2414b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2415b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2416e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2417e088dd4cSAlexander Motin 		break;
2418069154d5SJulian Elischer 	}
2419069154d5SJulian Elischer 	/*
2420069154d5SJulian Elischer 	 * We held references on some of the resources
2421069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2422069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2423069154d5SJulian Elischer 	 */
2424e72a98f4SAlexander Motin 	if (hook)
242530400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2426069154d5SJulian Elischer 
2427f50597f5SAlexander Motin  	if (rw == NGQRW_R)
24289852972bSAlexander Motin 		ng_leave_read(node);
2429f50597f5SAlexander Motin 	else
24309852972bSAlexander Motin 		ng_leave_write(node);
24318afe16d5SGleb Smirnoff 
24328afe16d5SGleb Smirnoff 	/* Apply callback. */
243310e87318SAlexander Motin 	if (apply != NULL) {
243410e87318SAlexander Motin 		if (depth == 1 && error != 0)
243510e87318SAlexander Motin 			apply->error = error;
243610e87318SAlexander Motin 		if (refcount_release(&apply->refs))
243710e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
243810e87318SAlexander Motin 	}
24398afe16d5SGleb Smirnoff 
244027757487SGleb Smirnoff 	return (error);
2441069154d5SJulian Elischer }
2442069154d5SJulian Elischer 
2443069154d5SJulian Elischer /***********************************************************************
2444069154d5SJulian Elischer  * Implement the 'generic' control messages
2445069154d5SJulian Elischer  ***********************************************************************/
2446069154d5SJulian Elischer static int
2447069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
24484cf49a43SJulian Elischer {
24494cf49a43SJulian Elischer 	int error = 0;
2450069154d5SJulian Elischer 	struct ng_mesg *msg;
2451069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
24524cf49a43SJulian Elischer 
2453069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
24544cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
24556b795970SJulian Elischer 		TRAP_ERROR();
2456069154d5SJulian Elischer 		error = EINVAL;
2457069154d5SJulian Elischer 		goto out;
24584cf49a43SJulian Elischer 	}
24594cf49a43SJulian Elischer 	switch (msg->header.cmd) {
24604cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
24611acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
24624cf49a43SJulian Elischer 		break;
24634cf49a43SJulian Elischer 	case NGM_MKPEER:
24644cf49a43SJulian Elischer 	    {
24654cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
24664cf49a43SJulian Elischer 
24674cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
24686b795970SJulian Elischer 			TRAP_ERROR();
2469069154d5SJulian Elischer 			error = EINVAL;
2470069154d5SJulian Elischer 			break;
24714cf49a43SJulian Elischer 		}
24724cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
24734cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
24744cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
24754cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
24764cf49a43SJulian Elischer 		break;
24774cf49a43SJulian Elischer 	    }
24784cf49a43SJulian Elischer 	case NGM_CONNECT:
24794cf49a43SJulian Elischer 	    {
24804cf49a43SJulian Elischer 		struct ngm_connect *const con =
24814cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
24824cf49a43SJulian Elischer 		node_p node2;
24834cf49a43SJulian Elischer 
24844cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
24856b795970SJulian Elischer 			TRAP_ERROR();
2486069154d5SJulian Elischer 			error = EINVAL;
2487069154d5SJulian Elischer 			break;
24884cf49a43SJulian Elischer 		}
24894cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
24904cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
24914cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2492069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2493069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
24944cf49a43SJulian Elischer 		if (error)
24954cf49a43SJulian Elischer 			break;
2496e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2497e088dd4cSAlexander Motin 		    node2, con->peerhook);
249830400f03SJulian Elischer 		NG_NODE_UNREF(node2);
24994cf49a43SJulian Elischer 		break;
25004cf49a43SJulian Elischer 	    }
25014cf49a43SJulian Elischer 	case NGM_NAME:
25024cf49a43SJulian Elischer 	    {
25034cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
25044cf49a43SJulian Elischer 
25054cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
25066b795970SJulian Elischer 			TRAP_ERROR();
2507069154d5SJulian Elischer 			error = EINVAL;
2508069154d5SJulian Elischer 			break;
25094cf49a43SJulian Elischer 		}
25104cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
25114cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
25124cf49a43SJulian Elischer 		break;
25134cf49a43SJulian Elischer 	    }
25144cf49a43SJulian Elischer 	case NGM_RMHOOK:
25154cf49a43SJulian Elischer 	    {
25164cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
25174cf49a43SJulian Elischer 		hook_p hook;
25184cf49a43SJulian Elischer 
25194cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
25206b795970SJulian Elischer 			TRAP_ERROR();
2521069154d5SJulian Elischer 			error = EINVAL;
2522069154d5SJulian Elischer 			break;
25234cf49a43SJulian Elischer 		}
25244cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2525899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
25264cf49a43SJulian Elischer 			ng_destroy_hook(hook);
25274cf49a43SJulian Elischer 		break;
25284cf49a43SJulian Elischer 	    }
25294cf49a43SJulian Elischer 	case NGM_NODEINFO:
25304cf49a43SJulian Elischer 	    {
25314cf49a43SJulian Elischer 		struct nodeinfo *ni;
25324cf49a43SJulian Elischer 
2533069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25344cf49a43SJulian Elischer 		if (resp == NULL) {
25354cf49a43SJulian Elischer 			error = ENOMEM;
25364cf49a43SJulian Elischer 			break;
25374cf49a43SJulian Elischer 		}
25384cf49a43SJulian Elischer 
25394cf49a43SJulian Elischer 		/* Fill in node info */
2540069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
254130400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
254287e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
254387e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2544dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
254530400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25464cf49a43SJulian Elischer 		break;
25474cf49a43SJulian Elischer 	    }
25484cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
25494cf49a43SJulian Elischer 	    {
255030400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
25514cf49a43SJulian Elischer 		struct hooklist *hl;
25524cf49a43SJulian Elischer 		struct nodeinfo *ni;
25534cf49a43SJulian Elischer 		hook_p hook;
25544cf49a43SJulian Elischer 
25554cf49a43SJulian Elischer 		/* Get response struct */
2556069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
25574cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2558069154d5SJulian Elischer 		if (resp == NULL) {
25594cf49a43SJulian Elischer 			error = ENOMEM;
25604cf49a43SJulian Elischer 			break;
25614cf49a43SJulian Elischer 		}
2562069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
25634cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
25644cf49a43SJulian Elischer 
25654cf49a43SJulian Elischer 		/* Fill in node info */
256630400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
256787e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
256887e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2569dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
25704cf49a43SJulian Elischer 
25714cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
25724cf49a43SJulian Elischer 		ni->hooks = 0;
257330400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
25744cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
25754cf49a43SJulian Elischer 
25764cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
25774cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
25786e551fb6SDavid E. O'Brien 				    __func__, "hooks");
25794cf49a43SJulian Elischer 				break;
25804cf49a43SJulian Elischer 			}
258130400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
25824cf49a43SJulian Elischer 				continue;
258387e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
258487e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
258530400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
258687e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
258787e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
258887e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
258987e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
259030400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
259130400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
25924cf49a43SJulian Elischer 			ni->hooks++;
25934cf49a43SJulian Elischer 		}
25944cf49a43SJulian Elischer 		break;
25954cf49a43SJulian Elischer 	    }
25964cf49a43SJulian Elischer 
25974cf49a43SJulian Elischer 	case NGM_LISTNAMES:
25984cf49a43SJulian Elischer 	case NGM_LISTNODES:
25994cf49a43SJulian Elischer 	    {
26004cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
26014cf49a43SJulian Elischer 		struct namelist *nl;
26024cf49a43SJulian Elischer 		node_p node;
2603cfea3f85SAlexander Motin 		int num = 0, i;
26044cf49a43SJulian Elischer 
2605cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
26064cf49a43SJulian Elischer 		/* Count number of nodes */
2607cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2608603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2609cfea3f85SAlexander Motin 				if (NG_NODE_IS_VALID(node) &&
2610cfea3f85SAlexander Motin 				    (unnamed || NG_NODE_HAS_NAME(node))) {
26114cf49a43SJulian Elischer 					num++;
26124cf49a43SJulian Elischer 				}
261370de87f2SJulian Elischer 			}
2614cfea3f85SAlexander Motin 		}
2615cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26164cf49a43SJulian Elischer 
26174cf49a43SJulian Elischer 		/* Get response struct */
2618069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
26194cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2620069154d5SJulian Elischer 		if (resp == NULL) {
26214cf49a43SJulian Elischer 			error = ENOMEM;
26224cf49a43SJulian Elischer 			break;
26234cf49a43SJulian Elischer 		}
2624069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
26254cf49a43SJulian Elischer 
26264cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
26274cf49a43SJulian Elischer 		nl->numnames = 0;
2628cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
2629cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2630603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2631cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2632cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
26334cf49a43SJulian Elischer 
2634b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2635b96baf0aSGleb Smirnoff 					continue;
2636b96baf0aSGleb Smirnoff 				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2637b96baf0aSGleb Smirnoff 					continue;
26384cf49a43SJulian Elischer 				if (nl->numnames >= num) {
2639cfea3f85SAlexander Motin 					log(LOG_ERR, "%s: number of nodes changed\n",
2640cfea3f85SAlexander Motin 					    __func__);
26414cf49a43SJulian Elischer 					break;
26424cf49a43SJulian Elischer 				}
264330400f03SJulian Elischer 				if (NG_NODE_HAS_NAME(node))
264487e2c66aSHartmut Brandt 					strcpy(np->name, NG_NODE_NAME(node));
264587e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2646dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
264730400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
26484cf49a43SJulian Elischer 				nl->numnames++;
26494cf49a43SJulian Elischer 			}
2650cfea3f85SAlexander Motin 		}
2651cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26524cf49a43SJulian Elischer 		break;
26534cf49a43SJulian Elischer 	    }
26544cf49a43SJulian Elischer 
26554cf49a43SJulian Elischer 	case NGM_LISTTYPES:
26564cf49a43SJulian Elischer 	    {
26574cf49a43SJulian Elischer 		struct typelist *tl;
26584cf49a43SJulian Elischer 		struct ng_type *type;
26594cf49a43SJulian Elischer 		int num = 0;
26604cf49a43SJulian Elischer 
26619ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
26624cf49a43SJulian Elischer 		/* Count number of types */
266370de87f2SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26644cf49a43SJulian Elischer 			num++;
266570de87f2SJulian Elischer 		}
26669ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26674cf49a43SJulian Elischer 
26684cf49a43SJulian Elischer 		/* Get response struct */
2669069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
26704cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2671069154d5SJulian Elischer 		if (resp == NULL) {
26724cf49a43SJulian Elischer 			error = ENOMEM;
26734cf49a43SJulian Elischer 			break;
26744cf49a43SJulian Elischer 		}
2675069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
26764cf49a43SJulian Elischer 
26774cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
26784cf49a43SJulian Elischer 		tl->numtypes = 0;
26799ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
2680069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26814cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
26824cf49a43SJulian Elischer 
26834cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
26844cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26856e551fb6SDavid E. O'Brien 				    __func__, "types");
26864cf49a43SJulian Elischer 				break;
26874cf49a43SJulian Elischer 			}
268887e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2689c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
26904cf49a43SJulian Elischer 			tl->numtypes++;
26914cf49a43SJulian Elischer 		}
26929ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26934cf49a43SJulian Elischer 		break;
26944cf49a43SJulian Elischer 	    }
26954cf49a43SJulian Elischer 
2696f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2697f8307e12SArchie Cobbs 	    {
26987133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2699f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2700f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2701069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2702f8307e12SArchie Cobbs 
2703f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2704f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
27054c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
27064c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
27074c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
27086b795970SJulian Elischer 			TRAP_ERROR();
2709f8307e12SArchie Cobbs 			error = EINVAL;
2710f8307e12SArchie Cobbs 			break;
2711f8307e12SArchie Cobbs 		}
2712f8307e12SArchie Cobbs 
2713f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2714069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2715069154d5SJulian Elischer 		if (resp == NULL) {
2716f8307e12SArchie Cobbs 			error = ENOMEM;
2717f8307e12SArchie Cobbs 			break;
2718f8307e12SArchie Cobbs 		}
2719069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2720f8307e12SArchie Cobbs 
2721f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2722f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2723f8307e12SArchie Cobbs 
2724f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
272530400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2726f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2727f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2728f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2729f8307e12SArchie Cobbs 				break;
2730f8307e12SArchie Cobbs 		}
2731f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2732f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2733f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2734f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2735f8307e12SArchie Cobbs 					break;
2736f8307e12SArchie Cobbs 			}
2737f8307e12SArchie Cobbs 			if (c->name == NULL) {
2738069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2739f8307e12SArchie Cobbs 				error = ENOSYS;
2740f8307e12SArchie Cobbs 				break;
2741f8307e12SArchie Cobbs 			}
2742f8307e12SArchie Cobbs 		}
2743f8307e12SArchie Cobbs 
2744f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2745f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2746f8307e12SArchie Cobbs 		    "%s", c->name);
2747f8307e12SArchie Cobbs 
2748f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2749f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2750f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
275170de87f2SJulian Elischer 		if (argstype == NULL) {
2752f8307e12SArchie Cobbs 			*ascii->data = '\0';
275370de87f2SJulian Elischer 		} else {
2754f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2755f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2756f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2757069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2758f8307e12SArchie Cobbs 				break;
2759f8307e12SArchie Cobbs 			}
2760f8307e12SArchie Cobbs 		}
2761f8307e12SArchie Cobbs 
2762f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2763f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2764f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2765069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2766f8307e12SArchie Cobbs 		break;
2767f8307e12SArchie Cobbs 	    }
2768f8307e12SArchie Cobbs 
2769f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2770f8307e12SArchie Cobbs 	    {
2771f8307e12SArchie Cobbs 		int bufSize = 2000;	/* XXX hard coded constant */
2772f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2773f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2774069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
277552ec4a03SArchie Cobbs 		int off = 0;
2776f8307e12SArchie Cobbs 
2777f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2778f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
27794c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
27804c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
27814c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
27824c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
27836b795970SJulian Elischer 			TRAP_ERROR();
2784f8307e12SArchie Cobbs 			error = EINVAL;
2785f8307e12SArchie Cobbs 			break;
2786f8307e12SArchie Cobbs 		}
2787f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2788f8307e12SArchie Cobbs 
2789f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2790069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2791069154d5SJulian Elischer 		if (resp == NULL) {
2792f8307e12SArchie Cobbs 			error = ENOMEM;
2793f8307e12SArchie Cobbs 			break;
2794f8307e12SArchie Cobbs 		}
2795069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2796f8307e12SArchie Cobbs 
2797f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2798f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2799f8307e12SArchie Cobbs 
2800f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
280130400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2802f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2803f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2804f8307e12SArchie Cobbs 				break;
2805f8307e12SArchie Cobbs 		}
2806f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2807f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2808f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2809f8307e12SArchie Cobbs 					break;
2810f8307e12SArchie Cobbs 			}
2811f8307e12SArchie Cobbs 			if (c->name == NULL) {
2812069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2813f8307e12SArchie Cobbs 				error = ENOSYS;
2814f8307e12SArchie Cobbs 				break;
2815f8307e12SArchie Cobbs 			}
2816f8307e12SArchie Cobbs 		}
2817f8307e12SArchie Cobbs 
2818f8307e12SArchie Cobbs 		/* Convert command name to binary */
2819f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2820f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2821f8307e12SArchie Cobbs 
2822f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2823f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2824f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
282570de87f2SJulian Elischer 		if (argstype == NULL) {
2826f8307e12SArchie Cobbs 			bufSize = 0;
282770de87f2SJulian Elischer 		} else {
2828f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2829f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2830069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2831f8307e12SArchie Cobbs 				break;
2832f8307e12SArchie Cobbs 			}
2833f8307e12SArchie Cobbs 		}
2834f8307e12SArchie Cobbs 
2835f8307e12SArchie Cobbs 		/* Return the result */
2836f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2837069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2838f8307e12SArchie Cobbs 		break;
2839f8307e12SArchie Cobbs 	    }
2840f8307e12SArchie Cobbs 
28417095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
28424cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
28434cf49a43SJulian Elischer 		/*
28444cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
28454cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2846069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
28474cf49a43SJulian Elischer 		 * when control passes back to us.
28484cf49a43SJulian Elischer 		 */
284930400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2850069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
285130400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
28524cf49a43SJulian Elischer 		}
28534cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
28544cf49a43SJulian Elischer 	default:
28556b795970SJulian Elischer 		TRAP_ERROR();
28564cf49a43SJulian Elischer 		error = EINVAL;
28574cf49a43SJulian Elischer 	}
2858069154d5SJulian Elischer 	/*
2859069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2860069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2861069154d5SJulian Elischer 	 * Don't free it if it is so.
2862069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2863069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2864069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2865069154d5SJulian Elischer 	 */
2866069154d5SJulian Elischer out:
2867069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2868069154d5SJulian Elischer 	NG_FREE_MSG(msg);
28694cf49a43SJulian Elischer 	return (error);
28704cf49a43SJulian Elischer }
28714cf49a43SJulian Elischer 
28724cf49a43SJulian Elischer /************************************************************************
28738253c060SGleb Smirnoff 			Queue element get/free routines
28748253c060SGleb Smirnoff ************************************************************************/
28758253c060SGleb Smirnoff 
28768253c060SGleb Smirnoff uma_zone_t			ng_qzone;
28776aa6d011SAlexander Motin uma_zone_t			ng_qdzone;
2878f2fbb838SAlexander Motin static int			numthreads = 0; /* number of queue threads */
2879ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2880ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
28818253c060SGleb Smirnoff 
2882f2fbb838SAlexander Motin TUNABLE_INT("net.graph.threads", &numthreads);
2883f2fbb838SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2884f2fbb838SAlexander Motin     0, "Number of queue processing threads");
28858253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
28868253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
28876aa6d011SAlexander Motin     0, "Maximum number of non-data queue items to allocate");
2888ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
28896aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
28906aa6d011SAlexander Motin     0, "Maximum number of data queue items to allocate");
28918253c060SGleb Smirnoff 
28928253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
28938253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
28948253c060SGleb Smirnoff static int			allocated;	/* number of items malloc'd */
28958253c060SGleb Smirnoff #endif
28968253c060SGleb Smirnoff 
28978253c060SGleb Smirnoff /*
28988253c060SGleb Smirnoff  * Get a queue entry.
28998253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
29008253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
29018253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
29028253c060SGleb Smirnoff  * an interrupt.
29038253c060SGleb Smirnoff  */
29048253c060SGleb Smirnoff static __inline item_p
29056aa6d011SAlexander Motin ng_alloc_item(int type, int flags)
29068253c060SGleb Smirnoff {
29076aa6d011SAlexander Motin 	item_p item;
29088253c060SGleb Smirnoff 
29096aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29106aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
291142282202SGleb Smirnoff 
29126aa6d011SAlexander Motin 	item = uma_zalloc((type == NGQF_DATA)?ng_qdzone:ng_qzone,
29136aa6d011SAlexander Motin 	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
29148253c060SGleb Smirnoff 
29158253c060SGleb Smirnoff 	if (item) {
29166aa6d011SAlexander Motin 		item->el_flags = type;
29176aa6d011SAlexander Motin #ifdef	NETGRAPH_DEBUG
29188253c060SGleb Smirnoff 		mtx_lock(&ngq_mtx);
29198253c060SGleb Smirnoff 		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
29208253c060SGleb Smirnoff 		allocated++;
29218253c060SGleb Smirnoff 		mtx_unlock(&ngq_mtx);
29228253c060SGleb Smirnoff #endif
29236aa6d011SAlexander Motin 	}
29248253c060SGleb Smirnoff 
29258253c060SGleb Smirnoff 	return (item);
29268253c060SGleb Smirnoff }
29278253c060SGleb Smirnoff 
29288253c060SGleb Smirnoff /*
29298253c060SGleb Smirnoff  * Release a queue entry
29308253c060SGleb Smirnoff  */
29318253c060SGleb Smirnoff void
29328253c060SGleb Smirnoff ng_free_item(item_p item)
29338253c060SGleb Smirnoff {
29348253c060SGleb Smirnoff 	/*
29358253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
29368253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
29378253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
29388253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
29398253c060SGleb Smirnoff 	 * or we release them again here.
29408253c060SGleb Smirnoff 	 */
29418253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
29428253c060SGleb Smirnoff 	case NGQF_DATA:
29438253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
29448253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
29458253c060SGleb Smirnoff 		break;
29468253c060SGleb Smirnoff 	case NGQF_MESG:
29478253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
29488253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
29498253c060SGleb Smirnoff 		break;
29508253c060SGleb Smirnoff 	case NGQF_FN:
2951e088dd4cSAlexander Motin 	case NGQF_FN2:
29528253c060SGleb Smirnoff 		/* nothing to free really, */
29538253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
29548253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
29558253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
29568253c060SGleb Smirnoff 		break;
29578253c060SGleb Smirnoff 	}
29588253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
29598253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
29608253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
29618253c060SGleb Smirnoff 
29628253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29638253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
29648253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
29658253c060SGleb Smirnoff 	allocated--;
29668253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
29678253c060SGleb Smirnoff #endif
29686aa6d011SAlexander Motin 	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA)?
29696aa6d011SAlexander Motin 	    ng_qdzone:ng_qzone, item);
29706aa6d011SAlexander Motin }
29716aa6d011SAlexander Motin 
29726aa6d011SAlexander Motin /*
29736aa6d011SAlexander Motin  * Change type of the queue entry.
29746aa6d011SAlexander Motin  * Possibly reallocates it from another UMA zone.
29756aa6d011SAlexander Motin  */
29766aa6d011SAlexander Motin static __inline item_p
29776aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags)
29786aa6d011SAlexander Motin {
29796aa6d011SAlexander Motin 	item_p item;
29806aa6d011SAlexander Motin 	int from, to;
29816aa6d011SAlexander Motin 
29826aa6d011SAlexander Motin 	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
29836aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29846aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
29856aa6d011SAlexander Motin 
29866aa6d011SAlexander Motin 	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
29876aa6d011SAlexander Motin 	to = (type == NGQF_DATA);
29886aa6d011SAlexander Motin 	if (from != to) {
29896aa6d011SAlexander Motin 		/* If reallocation is required do it and copy item. */
29906aa6d011SAlexander Motin 		if ((item = ng_alloc_item(type, flags)) == NULL) {
29916aa6d011SAlexander Motin 			ng_free_item(pitem);
29926aa6d011SAlexander Motin 			return (NULL);
29936aa6d011SAlexander Motin 		}
29946aa6d011SAlexander Motin 		*item = *pitem;
29956aa6d011SAlexander Motin 		ng_free_item(pitem);
29966aa6d011SAlexander Motin 	} else
29976aa6d011SAlexander Motin 		item = pitem;
29986aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
29996aa6d011SAlexander Motin 
30006aa6d011SAlexander Motin 	return (item);
30018253c060SGleb Smirnoff }
30028253c060SGleb Smirnoff 
30038253c060SGleb Smirnoff /************************************************************************
30044cf49a43SJulian Elischer 			Module routines
30054cf49a43SJulian Elischer ************************************************************************/
30064cf49a43SJulian Elischer 
30074cf49a43SJulian Elischer /*
30084cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
30094cf49a43SJulian Elischer  */
30104cf49a43SJulian Elischer int
30114cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
30124cf49a43SJulian Elischer {
30134cf49a43SJulian Elischer 	struct ng_type *const type = data;
30144cf49a43SJulian Elischer 	int s, error = 0;
30154cf49a43SJulian Elischer 
30164cf49a43SJulian Elischer 	switch (event) {
30174cf49a43SJulian Elischer 	case MOD_LOAD:
30184cf49a43SJulian Elischer 
30194cf49a43SJulian Elischer 		/* Register new netgraph node type */
30204cf49a43SJulian Elischer 		s = splnet();
30214cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
30224cf49a43SJulian Elischer 			splx(s);
30234cf49a43SJulian Elischer 			break;
30244cf49a43SJulian Elischer 		}
30254cf49a43SJulian Elischer 
30264cf49a43SJulian Elischer 		/* Call type specific code */
30274cf49a43SJulian Elischer 		if (type->mod_event != NULL)
3028069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
30299ed346baSBosko Milekic 				mtx_lock(&ng_typelist_mtx);
3030c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
30314cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
30329ed346baSBosko Milekic 				mtx_unlock(&ng_typelist_mtx);
3033069154d5SJulian Elischer 			}
30344cf49a43SJulian Elischer 		splx(s);
30354cf49a43SJulian Elischer 		break;
30364cf49a43SJulian Elischer 
30374cf49a43SJulian Elischer 	case MOD_UNLOAD:
30384cf49a43SJulian Elischer 		s = splnet();
3039c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
30404cf49a43SJulian Elischer 			error = EBUSY;
3041c73b94a2SJulian Elischer 		} else {
3042c73b94a2SJulian Elischer 			if (type->refs == 0) {
3043c73b94a2SJulian Elischer 				/* failed load, nothing to undo */
3044c73b94a2SJulian Elischer 				splx(s);
3045c73b94a2SJulian Elischer 				break;
3046c73b94a2SJulian Elischer 			}
30474cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
30484cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
30494cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
30504cf49a43SJulian Elischer 					splx(s);
30514cf49a43SJulian Elischer 					break;
30524cf49a43SJulian Elischer 				}
30534cf49a43SJulian Elischer 			}
30549ed346baSBosko Milekic 			mtx_lock(&ng_typelist_mtx);
30554cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
30569ed346baSBosko Milekic 			mtx_unlock(&ng_typelist_mtx);
30574cf49a43SJulian Elischer 		}
30584cf49a43SJulian Elischer 		splx(s);
30594cf49a43SJulian Elischer 		break;
30604cf49a43SJulian Elischer 
30614cf49a43SJulian Elischer 	default:
30624cf49a43SJulian Elischer 		if (type->mod_event != NULL)
30634cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
30644cf49a43SJulian Elischer 		else
30653e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
30664cf49a43SJulian Elischer 		break;
30674cf49a43SJulian Elischer 	}
30684cf49a43SJulian Elischer 	return (error);
30694cf49a43SJulian Elischer }
30704cf49a43SJulian Elischer 
3071eddfbb76SRobert Watson #ifdef VIMAGE
3072aef8f344SMarko Zec static const vnet_modinfo_t vnet_netgraph_modinfo = {
3073aef8f344SMarko Zec 	.vmi_id		= VNET_MOD_NETGRAPH,
3074aef8f344SMarko Zec 	.vmi_name	= "netgraph",
3075bc29160dSMarko Zec 	.vmi_dependson	= VNET_MOD_LOIF,
3076bc29160dSMarko Zec 	.vmi_idetach	= vnet_netgraph_idetach
3077aef8f344SMarko Zec };
3078aef8f344SMarko Zec #endif
3079aef8f344SMarko Zec 
3080bc29160dSMarko Zec #ifdef VIMAGE
3081bc29160dSMarko Zec static int
3082bc29160dSMarko Zec vnet_netgraph_idetach(const void *unused __unused)
3083bc29160dSMarko Zec {
3084eddfbb76SRobert Watson #if 0
3085bc29160dSMarko Zec 	node_p node, last_killed = NULL;
3086bc29160dSMarko Zec 
3087eddfbb76SRobert Watson 	/* XXXRW: utterly bogus. */
3088eddfbb76SRobert Watson 	while ((node = LIST_FIRST(&V_ng_allnodes)) != NULL) {
3089bc29160dSMarko Zec 		if (node == last_killed) {
3090bc29160dSMarko Zec 			/* This should never happen */
3091bc29160dSMarko Zec 			node->nd_flags |= NGF_REALLY_DIE;
3092bc29160dSMarko Zec 			printf("netgraph node %s needs NGF_REALLY_DIE\n",
3093bc29160dSMarko Zec 			    node->nd_name);
3094bc29160dSMarko Zec 			ng_rmnode(node, NULL, NULL, 0);
3095bc29160dSMarko Zec 			/* This must never happen */
3096eddfbb76SRobert Watson 			if (node == LIST_FIRST(&V_ng_allnodes))
3097bc29160dSMarko Zec 				panic("netgraph node %s won't die",
3098bc29160dSMarko Zec 				    node->nd_name);
3099bc29160dSMarko Zec 		}
3100bc29160dSMarko Zec 		ng_rmnode(node, NULL, NULL, 0);
3101bc29160dSMarko Zec 		last_killed = node;
3102bc29160dSMarko Zec 	}
3103eddfbb76SRobert Watson #endif
3104bc29160dSMarko Zec 
3105bc29160dSMarko Zec 	return (0);
3106bc29160dSMarko Zec }
3107bc29160dSMarko Zec #endif /* VIMAGE */
3108bc29160dSMarko Zec 
31094cf49a43SJulian Elischer /*
31104cf49a43SJulian Elischer  * Handle loading and unloading for this code.
31114cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
31124cf49a43SJulian Elischer  */
31134cf49a43SJulian Elischer static int
31144cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
31154cf49a43SJulian Elischer {
3116f2fbb838SAlexander Motin 	struct proc *p;
3117f2fbb838SAlexander Motin 	struct thread *td;
3118f2fbb838SAlexander Motin 	int i, error = 0;
31194cf49a43SJulian Elischer 
31204cf49a43SJulian Elischer 	switch (event) {
31214cf49a43SJulian Elischer 	case MOD_LOAD:
31221489164fSGleb Smirnoff 		/* Initialize everything. */
3123eddfbb76SRobert Watson #ifdef VIMAGE
3124aef8f344SMarko Zec 		vnet_mod_register(&vnet_netgraph_modinfo);
3125aef8f344SMarko Zec #endif
31262c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3127efd8e7c9SDon Lewis 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3128efd8e7c9SDon Lewis 		    MTX_DEF);
3129efd8e7c9SDon Lewis 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3130efd8e7c9SDon Lewis 		    MTX_DEF);
3131cfea3f85SAlexander Motin 		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3132cfea3f85SAlexander Motin 		    MTX_DEF);
3133ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3134ac5dd141SGleb Smirnoff 		    MTX_DEF);
31351489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3136cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3137cfea3f85SAlexander Motin 		    MTX_DEF);
31381489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3139efd8e7c9SDon Lewis 		    MTX_DEF);
31401489164fSGleb Smirnoff #endif
31411489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
31421489164fSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
31431489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
31446aa6d011SAlexander Motin 		ng_qdzone = uma_zcreate("NetGraph data items", sizeof(struct ng_item),
31456aa6d011SAlexander Motin 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
31466aa6d011SAlexander Motin 		uma_zone_set_max(ng_qdzone, maxdata);
3147f2fbb838SAlexander Motin 		/* Autoconfigure number of threads. */
3148f2fbb838SAlexander Motin 		if (numthreads <= 0)
3149f2fbb838SAlexander Motin 			numthreads = mp_ncpus;
3150f2fbb838SAlexander Motin 		/* Create threads. */
3151f2fbb838SAlexander Motin     		p = NULL; /* start with no process */
3152f2fbb838SAlexander Motin 		for (i = 0; i < numthreads; i++) {
3153f2fbb838SAlexander Motin 			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3154f2fbb838SAlexander Motin 			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3155f2fbb838SAlexander Motin 				numthreads = i;
3156f2fbb838SAlexander Motin 				break;
3157f2fbb838SAlexander Motin 			}
3158f2fbb838SAlexander Motin 		}
31594cf49a43SJulian Elischer 		break;
31604cf49a43SJulian Elischer 	case MOD_UNLOAD:
316164efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
31624cf49a43SJulian Elischer 		error = EBUSY;
31634cf49a43SJulian Elischer 		break;
31644cf49a43SJulian Elischer 	default:
31654cf49a43SJulian Elischer 		error = EOPNOTSUPP;
31664cf49a43SJulian Elischer 		break;
31674cf49a43SJulian Elischer 	}
31684cf49a43SJulian Elischer 	return (error);
31694cf49a43SJulian Elischer }
31704cf49a43SJulian Elischer 
31714cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
31724cf49a43SJulian Elischer 	"netgraph",
31734cf49a43SJulian Elischer 	ngb_mod_event,
31744cf49a43SJulian Elischer 	(NULL)
31754cf49a43SJulian Elischer };
3176aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
3177bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3178bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3179bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
31804cf49a43SJulian Elischer 
318130400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
318230400f03SJulian Elischer void
318330400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
318430400f03SJulian Elischer {
318530400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
318630400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
318730400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
318830400f03SJulian Elischer 		hook->lastfile, hook->lastline);
318930400f03SJulian Elischer 	if (line) {
319030400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
319130400f03SJulian Elischer 	}
319230400f03SJulian Elischer }
319330400f03SJulian Elischer 
319430400f03SJulian Elischer void
319530400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
319630400f03SJulian Elischer {
319730400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
31986b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
319930400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
320030400f03SJulian Elischer 		node->nd_refs, node->nd_name);
320130400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
320230400f03SJulian Elischer 		node->lastfile, node->lastline);
320330400f03SJulian Elischer 	if (line) {
320430400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
320530400f03SJulian Elischer 	}
320630400f03SJulian Elischer }
320730400f03SJulian Elischer 
3208069154d5SJulian Elischer void
3209069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3210069154d5SJulian Elischer {
3211069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3212069154d5SJulian Elischer 		item->lastfile, item->lastline);
32136b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
32146b795970SJulian Elischer 	case NGQF_DATA:
3215069154d5SJulian Elischer 		printf(" - [data]\n");
32166b795970SJulian Elischer 		break;
32176b795970SJulian Elischer 	case NGQF_MESG:
32186b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
32196b795970SJulian Elischer 		break;
32206b795970SJulian Elischer 	case NGQF_FN:
3221857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3222857304e6SRuslan Ermilov 			_NGI_FN(item),
3223857304e6SRuslan Ermilov 			_NGI_NODE(item),
3224857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3225857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3226857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3227857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3228857304e6SRuslan Ermilov 		break;
3229e088dd4cSAlexander Motin 	case NGQF_FN2:
3230eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3231857304e6SRuslan Ermilov 			_NGI_FN2(item),
32326064e568SGleb Smirnoff 			_NGI_NODE(item),
32336064e568SGleb Smirnoff 			_NGI_HOOK(item),
32346b795970SJulian Elischer 			item->body.fn.fn_arg1,
32356b795970SJulian Elischer 			item->body.fn.fn_arg2,
32366b795970SJulian Elischer 			item->body.fn.fn_arg2);
32376b795970SJulian Elischer 		break;
3238069154d5SJulian Elischer 	}
323930400f03SJulian Elischer 	if (line) {
3240069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
32416064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
324230400f03SJulian Elischer 			printf("node %p ([%x])\n",
32436064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3244069154d5SJulian Elischer 		}
324530400f03SJulian Elischer 	}
324630400f03SJulian Elischer }
324730400f03SJulian Elischer 
324830400f03SJulian Elischer static void
324930400f03SJulian Elischer ng_dumpitems(void)
325030400f03SJulian Elischer {
325130400f03SJulian Elischer 	item_p item;
325230400f03SJulian Elischer 	int i = 1;
325330400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
325430400f03SJulian Elischer 		printf("[%d] ", i++);
325530400f03SJulian Elischer 		dumpitem(item, NULL, 0);
325630400f03SJulian Elischer 	}
325730400f03SJulian Elischer }
325830400f03SJulian Elischer 
325930400f03SJulian Elischer static void
326030400f03SJulian Elischer ng_dumpnodes(void)
326130400f03SJulian Elischer {
326230400f03SJulian Elischer 	node_p node;
326330400f03SJulian Elischer 	int i = 1;
326453f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
326530400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
326630400f03SJulian Elischer 		printf("[%d] ", i++);
326730400f03SJulian Elischer 		dumpnode(node, NULL, 0);
326830400f03SJulian Elischer 	}
326953f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
327030400f03SJulian Elischer }
327130400f03SJulian Elischer 
327230400f03SJulian Elischer static void
327330400f03SJulian Elischer ng_dumphooks(void)
327430400f03SJulian Elischer {
327530400f03SJulian Elischer 	hook_p hook;
327630400f03SJulian Elischer 	int i = 1;
327753f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
327830400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
327930400f03SJulian Elischer 		printf("[%d] ", i++);
328030400f03SJulian Elischer 		dumphook(hook, NULL, 0);
328130400f03SJulian Elischer 	}
328253f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
328330400f03SJulian Elischer }
3284069154d5SJulian Elischer 
3285069154d5SJulian Elischer static int
3286069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3287069154d5SJulian Elischer {
3288069154d5SJulian Elischer 	int error;
3289069154d5SJulian Elischer 	int val;
3290069154d5SJulian Elischer 	int i;
3291069154d5SJulian Elischer 
3292069154d5SJulian Elischer 	val = allocated;
3293069154d5SJulian Elischer 	i = 1;
3294041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
32956b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
32966b795970SJulian Elischer 		return (error);
32976b795970SJulian Elischer 	if (val == 42) {
329830400f03SJulian Elischer 		ng_dumpitems();
329930400f03SJulian Elischer 		ng_dumpnodes();
330030400f03SJulian Elischer 		ng_dumphooks();
3301069154d5SJulian Elischer 	}
33026b795970SJulian Elischer 	return (0);
3303069154d5SJulian Elischer }
3304069154d5SJulian Elischer 
33056b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
33066b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
330730400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3308069154d5SJulian Elischer 
3309069154d5SJulian Elischer 
3310069154d5SJulian Elischer /***********************************************************************
3311069154d5SJulian Elischer * Worklist routines
3312069154d5SJulian Elischer **********************************************************************/
3313069154d5SJulian Elischer /*
3314069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3315f2fbb838SAlexander Motin  * try get an item to process off it. Remove the node from the list.
3316069154d5SJulian Elischer  */
3317069154d5SJulian Elischer static void
3318f2fbb838SAlexander Motin ngthread(void *arg)
3319069154d5SJulian Elischer {
3320069154d5SJulian Elischer 	for (;;) {
3321394cb30aSAlexander Motin 		node_p  node;
3322394cb30aSAlexander Motin 
3323394cb30aSAlexander Motin 		/* Get node from the worklist. */
33242c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
3325f2fbb838SAlexander Motin 		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3326f2fbb838SAlexander Motin 			NG_WORKLIST_SLEEP();
33279852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
33282c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3329bc29160dSMarko Zec 		CURVNET_SET(node->nd_vnet);
33302955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
33312955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3332069154d5SJulian Elischer 		/*
3333069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3334069154d5SJulian Elischer 		 * that the list had on it.
3335069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3336069154d5SJulian Elischer 		 * let you have another item off the queue.
3337069154d5SJulian Elischer 		 * All this time, keep the reference
3338069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3339069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3340069154d5SJulian Elischer 		 */
3341069154d5SJulian Elischer 		for (;;) {
3342394cb30aSAlexander Motin 			item_p item;
3343714fb865SGleb Smirnoff 			int rw;
3344714fb865SGleb Smirnoff 
33452c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
33469852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3347069154d5SJulian Elischer 			if (item == NULL) {
33489852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
33492c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3350069154d5SJulian Elischer 				break; /* go look for another node */
3351069154d5SJulian Elischer 			} else {
33522c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
33535951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3354714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
33555951069aSJulian Elischer 				NG_NODE_UNREF(node);
3356069154d5SJulian Elischer 			}
3357069154d5SJulian Elischer 		}
3358a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3359bc29160dSMarko Zec 		CURVNET_RESTORE();
3360069154d5SJulian Elischer 	}
3361069154d5SJulian Elischer }
3362069154d5SJulian Elischer 
336333338e73SJulian Elischer /*
336433338e73SJulian Elischer  * XXX
336533338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
336633338e73SJulian Elischer  * to be outside the mutex zone
336733338e73SJulian Elischer  */
3368069154d5SJulian Elischer static void
3369394cb30aSAlexander Motin ng_worklist_add(node_p node)
3370069154d5SJulian Elischer {
3371f912c0f7SGleb Smirnoff 
33725bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3373f912c0f7SGleb Smirnoff 
33749852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3375069154d5SJulian Elischer 		/*
3376069154d5SJulian Elischer 		 * If we are not already on the work queue,
3377069154d5SJulian Elischer 		 * then put us on.
3378069154d5SJulian Elischer 		 */
33799852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3380394cb30aSAlexander Motin 		NG_NODE_REF(node); /* XXX fafe in mutex? */
33812c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
33829852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
33832c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
33842955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
33852955ee18SGleb Smirnoff 		    node->nd_ID, node);
3386f2fbb838SAlexander Motin 		NG_WORKLIST_WAKEUP();
3387394cb30aSAlexander Motin 	} else {
33882955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
33892955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3390394cb30aSAlexander Motin 	}
3391069154d5SJulian Elischer }
3392069154d5SJulian Elischer 
3393069154d5SJulian Elischer 
3394069154d5SJulian Elischer /***********************************************************************
3395069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3396069154d5SJulian Elischer ***********************************************************************/
3397069154d5SJulian Elischer 
339830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
339930400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
34004cf49a43SJulian Elischer 	do {								\
34011acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3402069154d5SJulian Elischer 			printf("item already has node");		\
34033de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
34041acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3405069154d5SJulian Elischer 		}							\
34061acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3407069154d5SJulian Elischer 			printf("item already has hook");		\
34083de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
34091acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3410069154d5SJulian Elischer 		}							\
3411069154d5SJulian Elischer 	} while (0)
3412069154d5SJulian Elischer #else
341330400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3414069154d5SJulian Elischer #endif
3415069154d5SJulian Elischer 
3416069154d5SJulian Elischer /*
34178ed370fdSJulian Elischer  * Put mbuf into the item.
3418069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3419069154d5SJulian Elischer  * (or equivalent)
3420069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3421069154d5SJulian Elischer  * remote node might go away in this timescale.
3422069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3423069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
34244be59335SGleb Smirnoff  * here to be able to do this.
3425069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3426069154d5SJulian Elischer  *
3427069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3428069154d5SJulian Elischer  */
3429069154d5SJulian Elischer item_p
343042282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3431069154d5SJulian Elischer {
3432069154d5SJulian Elischer 	item_p item;
3433069154d5SJulian Elischer 
34346aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3435069154d5SJulian Elischer 		NG_FREE_M(m);
3436069154d5SJulian Elischer 		return (NULL);
3437069154d5SJulian Elischer 	}
343830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34396aa6d011SAlexander Motin 	item->el_flags |= NGQF_READER;
3440069154d5SJulian Elischer 	NGI_M(item) = m;
3441069154d5SJulian Elischer 	return (item);
3442069154d5SJulian Elischer }
3443069154d5SJulian Elischer 
3444069154d5SJulian Elischer /*
3445069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3446069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3447069154d5SJulian Elischer  * to work out what some of the fields should be.
3448069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3449069154d5SJulian Elischer  * (or equivalent)
3450069154d5SJulian Elischer  */
3451069154d5SJulian Elischer item_p
345242282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3453069154d5SJulian Elischer {
3454069154d5SJulian Elischer 	item_p item;
3455069154d5SJulian Elischer 
34566aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3457069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3458069154d5SJulian Elischer 		return (NULL);
3459069154d5SJulian Elischer 	}
346030400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34616f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
34626f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
34636aa6d011SAlexander Motin 		item->el_flags |= NGQF_READER;
34646f683eeeSGleb Smirnoff 	else
34656aa6d011SAlexander Motin 		item->el_flags |= NGQF_WRITER;
3466069154d5SJulian Elischer 	/*
3467069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3468069154d5SJulian Elischer 	 */
3469069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3470facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3471069154d5SJulian Elischer 	return (item);
3472069154d5SJulian Elischer }
3473069154d5SJulian Elischer 
3474069154d5SJulian Elischer 
3475069154d5SJulian Elischer 
34761acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
34776b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
34786b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3479069154d5SJulian Elischer 			if (retaddr) {					\
3480069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
34814cf49a43SJulian Elischer 			} else {					\
3482069154d5SJulian Elischer 				/*					\
3483069154d5SJulian Elischer 				 * The old return address should be ok.	\
3484069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3485069154d5SJulian Elischer 				 * here.				\
3486069154d5SJulian Elischer 				 */					\
3487069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3488069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3489069154d5SJulian Elischer 						= ng_node2ID(here);	\
3490069154d5SJulian Elischer 				}					\
3491069154d5SJulian Elischer 			}						\
34924cf49a43SJulian Elischer 		}							\
34934cf49a43SJulian Elischer 	} while (0)
34944cf49a43SJulian Elischer 
34954cf49a43SJulian Elischer int
3496069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
34974cf49a43SJulian Elischer {
34981acb27c6SJulian Elischer 	hook_p peer;
34991acb27c6SJulian Elischer 	node_p peernode;
350030400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3501069154d5SJulian Elischer 	/*
3502069154d5SJulian Elischer 	 * Quick sanity check..
350330400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
350430400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
350530400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3506069154d5SJulian Elischer 	 */
3507a04e9846SAlexander Motin 	if ((hook == NULL) ||
3508a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(hook) ||
3509a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3510a04e9846SAlexander Motin 	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3511069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35126b795970SJulian Elischer 		TRAP_ERROR();
3513e08d3e3cSJulian Elischer 		return (ENETDOWN);
35144cf49a43SJulian Elischer 	}
35154cf49a43SJulian Elischer 
35164cf49a43SJulian Elischer 	/*
3517069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
35184cf49a43SJulian Elischer 	 */
35191acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
35201acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
3521a04e9846SAlexander Motin 	NGI_SET_HOOK(item, peer);
35221acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
35238b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3524069154d5SJulian Elischer 	return (0);
3525069154d5SJulian Elischer }
3526069154d5SJulian Elischer 
35274cf49a43SJulian Elischer int
3528069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
35294cf49a43SJulian Elischer {
35304cf49a43SJulian Elischer 	node_p	dest = NULL;
3531069154d5SJulian Elischer 	hook_p	hook = NULL;
35324cf49a43SJulian Elischer 	int	error;
3533069154d5SJulian Elischer 
353430400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3535069154d5SJulian Elischer 	/*
3536069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3537069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3538069154d5SJulian Elischer 	 */
3539069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3540069154d5SJulian Elischer 	if (error) {
3541069154d5SJulian Elischer 		NG_FREE_ITEM(item);
354230400f03SJulian Elischer 		return (error);
3543069154d5SJulian Elischer 	}
35441acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
35451acb27c6SJulian Elischer 	if ( hook) {
354630400f03SJulian Elischer 		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
35471acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35481acb27c6SJulian Elischer 	}
35491acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3550069154d5SJulian Elischer 	return (0);
3551069154d5SJulian Elischer }
3552069154d5SJulian Elischer 
3553069154d5SJulian Elischer int
3554069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3555069154d5SJulian Elischer {
3556069154d5SJulian Elischer 	node_p dest;
3557069154d5SJulian Elischer 
355830400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3559069154d5SJulian Elischer 	/*
3560069154d5SJulian Elischer 	 * Find the target node.
3561069154d5SJulian Elischer 	 */
3562069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3563069154d5SJulian Elischer 	if (dest == NULL) {
3564069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35656b795970SJulian Elischer 		TRAP_ERROR();
3566069154d5SJulian Elischer 		return(EINVAL);
3567069154d5SJulian Elischer 	}
3568069154d5SJulian Elischer 	/* Fill out the contents */
35691acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
35701acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
35711acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3572069154d5SJulian Elischer 	return (0);
3573069154d5SJulian Elischer }
3574069154d5SJulian Elischer 
3575069154d5SJulian Elischer /*
3576069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3577069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3578069154d5SJulian Elischer  * Useful for removing that hook :-)
3579069154d5SJulian Elischer  */
3580069154d5SJulian Elischer item_p
3581069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3582069154d5SJulian Elischer {
3583069154d5SJulian Elischer 	item_p item;
35844cf49a43SJulian Elischer 
3585859a4d16SJulian Elischer 	/*
3586859a4d16SJulian Elischer 	 * Find the target node.
3587859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3588859a4d16SJulian Elischer 	 * to the address.
3589859a4d16SJulian Elischer 	 */
35906aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3591069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3592069154d5SJulian Elischer 		return (NULL);
35934cf49a43SJulian Elischer 	}
35944cf49a43SJulian Elischer 
35954cf49a43SJulian Elischer 	/* Fill out the contents */
35966aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
359730400f03SJulian Elischer 	NG_NODE_REF(here);
35981acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
35991acb27c6SJulian Elischer 	if (hook) {
360030400f03SJulian Elischer 		NG_HOOK_REF(hook);
36011acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36021acb27c6SJulian Elischer 	}
3603069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3604069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3605069154d5SJulian Elischer 	return (item);
36064cf49a43SJulian Elischer }
36074cf49a43SJulian Elischer 
3608e088dd4cSAlexander Motin /*
3609e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3610e088dd4cSAlexander Motin  */
3611e088dd4cSAlexander Motin 
361242282202SGleb Smirnoff int
3613b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3614b332b91fSGleb Smirnoff {
3615b332b91fSGleb Smirnoff 
3616b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3617b332b91fSGleb Smirnoff }
3618b332b91fSGleb Smirnoff 
3619b332b91fSGleb Smirnoff int
3620aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
362142282202SGleb Smirnoff 	int flags)
36226b795970SJulian Elischer {
36236b795970SJulian Elischer 	item_p item;
36246b795970SJulian Elischer 
36256aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
36266b795970SJulian Elischer 		return (ENOMEM);
36276b795970SJulian Elischer 	}
36286aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3629a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
36301acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
36311acb27c6SJulian Elischer 	if (hook) {
36326b795970SJulian Elischer 		NG_HOOK_REF(hook);
36331acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36346b795970SJulian Elischer 	}
36356b795970SJulian Elischer 	NGI_FN(item) = fn;
36366b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
36376b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
363842282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
36396b795970SJulian Elischer }
36406b795970SJulian Elischer 
36414cf49a43SJulian Elischer /*
3642b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3643b332b91fSGleb Smirnoff  *
3644b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3645b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3646b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3647b332b91fSGleb Smirnoff  * be used.
3648e088dd4cSAlexander Motin  */
3649e088dd4cSAlexander Motin int
3650b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3651b332b91fSGleb Smirnoff 	int arg2, int flags)
3652e088dd4cSAlexander Motin {
3653e088dd4cSAlexander Motin 	item_p item;
3654e088dd4cSAlexander Motin 
3655b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3656b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3657e088dd4cSAlexander Motin 
3658e088dd4cSAlexander Motin 	/*
3659b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3660b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3661e088dd4cSAlexander Motin 	 */
3662b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
36636aa6d011SAlexander Motin 		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3664e088dd4cSAlexander Motin 			return (ENOMEM);
36656aa6d011SAlexander Motin 		if (pitem != NULL)
36666aa6d011SAlexander Motin 			item->apply = pitem->apply;
3667ed75521fSAlexander Motin 	} else {
36686aa6d011SAlexander Motin 		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
36696aa6d011SAlexander Motin 			return (ENOMEM);
3670ed75521fSAlexander Motin 	}
3671b332b91fSGleb Smirnoff 
36726aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3673e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3674e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3675e088dd4cSAlexander Motin 	if (hook) {
3676e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3677e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3678e088dd4cSAlexander Motin 	}
3679e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3680e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3681e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3682e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3683e088dd4cSAlexander Motin }
3684e088dd4cSAlexander Motin 
3685e088dd4cSAlexander Motin /*
3686d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3687d2ca21a9SJulian Elischer  */
3688d2ca21a9SJulian Elischer static void
36891fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3690d2ca21a9SJulian Elischer {
3691d2ca21a9SJulian Elischer 	item_p item = arg;
3692d2ca21a9SJulian Elischer 
3693bc29160dSMarko Zec 	CURVNET_SET(NGI_NODE(item)->nd_vnet);
3694d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3695bc29160dSMarko Zec 	CURVNET_RESTORE();
3696d2ca21a9SJulian Elischer }
3697d2ca21a9SJulian Elischer 
3698d2ca21a9SJulian Elischer 
369930bef41bSGleb Smirnoff int
3700f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3701d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3702d2ca21a9SJulian Elischer {
37031bf8e0faSGleb Smirnoff 	item_p item, oitem;
3704d2ca21a9SJulian Elischer 
37056aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
370630bef41bSGleb Smirnoff 		return (ENOMEM);
370730bef41bSGleb Smirnoff 
37086aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3709d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3710d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3711d2ca21a9SJulian Elischer 	if (hook) {
3712d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3713d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3714d2ca21a9SJulian Elischer 	}
3715d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3716d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3717d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
37181bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
37191bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
37201bf8e0faSGleb Smirnoff 	    oitem != NULL)
37211bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
372230bef41bSGleb Smirnoff 	return (0);
3723d2ca21a9SJulian Elischer }
3724d2ca21a9SJulian Elischer 
3725d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3726d2ca21a9SJulian Elischer int
3727f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3728d2ca21a9SJulian Elischer {
3729d2ca21a9SJulian Elischer 	item_p item;
373030bef41bSGleb Smirnoff 	int rval;
3731d2ca21a9SJulian Elischer 
373203b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
373303b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
373403b25f5dSGleb Smirnoff 
37353eadb26dSGleb Smirnoff 	rval = callout_stop(c);
373630bef41bSGleb Smirnoff 	item = c->c_arg;
373730bef41bSGleb Smirnoff 	/* Do an extra check */
37381fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
373930bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3740d2ca21a9SJulian Elischer 		/*
3741d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3742d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3743d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3744d2ca21a9SJulian Elischer 		 */
3745d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3746d2ca21a9SJulian Elischer 	}
37471bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
374830bef41bSGleb Smirnoff 
374930bef41bSGleb Smirnoff 	return (rval);
3750d2ca21a9SJulian Elischer }
3751d2ca21a9SJulian Elischer 
3752d2ca21a9SJulian Elischer /*
3753069154d5SJulian Elischer  * Set the address, if none given, give the node here.
37544cf49a43SJulian Elischer  */
3755069154d5SJulian Elischer void
3756069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
37574cf49a43SJulian Elischer {
3758069154d5SJulian Elischer 	if (retaddr) {
3759069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3760069154d5SJulian Elischer 	} else {
3761069154d5SJulian Elischer 		/*
3762069154d5SJulian Elischer 		 * The old return address should be ok.
3763069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3764069154d5SJulian Elischer 		 */
3765069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3766069154d5SJulian Elischer 	}
3767069154d5SJulian Elischer }
3768069154d5SJulian Elischer 
3769069154d5SJulian Elischer #define TESTING
3770069154d5SJulian Elischer #ifdef TESTING
3771069154d5SJulian Elischer /* just test all the macros */
3772069154d5SJulian Elischer void
3773069154d5SJulian Elischer ng_macro_test(item_p item);
3774069154d5SJulian Elischer void
3775069154d5SJulian Elischer ng_macro_test(item_p item)
3776069154d5SJulian Elischer {
3777069154d5SJulian Elischer 	node_p node = NULL;
3778069154d5SJulian Elischer 	hook_p hook = NULL;
37794cf49a43SJulian Elischer 	struct mbuf *m;
37804cf49a43SJulian Elischer 	struct ng_mesg *msg;
3781069154d5SJulian Elischer 	ng_ID_t retaddr;
3782069154d5SJulian Elischer 	int	error;
37834cf49a43SJulian Elischer 
3784069154d5SJulian Elischer 	NGI_GET_M(item, m);
3785069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3786069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
37878ed370fdSJulian Elischer 	NG_SEND_DATA(error, hook, m, NULL);
3788069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3789069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
379030400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3791069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3792069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3793069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3794069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
37954cf49a43SJulian Elischer }
3796069154d5SJulian Elischer #endif /* TESTING */
37974cf49a43SJulian Elischer 
3798