xref: /freebsd/sys/netgraph/ng_base.c (revision bc29160df3d0b3a65d9b85a63a4589f1f9652e6b)
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>
714cf49a43SJulian Elischer 
724cf49a43SJulian Elischer #include <netgraph/ng_message.h>
734cf49a43SJulian Elischer #include <netgraph/netgraph.h>
74f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
754cf49a43SJulian Elischer 
769d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION);
7799ff8176SPeter Wemm 
78385195c0SMarko Zec #ifndef VIMAGE
79385195c0SMarko Zec #ifndef VIMAGE_GLOBALS
80385195c0SMarko Zec struct vnet_netgraph vnet_netgraph_0;
81385195c0SMarko Zec #endif
82385195c0SMarko Zec #endif
83385195c0SMarko Zec 
84ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */
85ac5dd141SGleb Smirnoff static struct mtx	ng_topo_mtx;
86ac5dd141SGleb Smirnoff 
87aef8f344SMarko Zec static vnet_attach_fn vnet_netgraph_iattach;
88bc29160dSMarko Zec #ifdef VIMAGE
89bc29160dSMarko Zec static vnet_detach_fn vnet_netgraph_idetach;
90bc29160dSMarko Zec #endif
91aef8f344SMarko Zec 
9230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
93cfea3f85SAlexander Motin static struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
941489164fSGleb Smirnoff static struct mtx	ngq_mtx;	/* protects the queue item list */
9530400f03SJulian Elischer 
9630400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
9730400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
9830400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
9930400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
10030400f03SJulian Elischer 
10130400f03SJulian Elischer static void ng_dumpitems(void);
10230400f03SJulian Elischer static void ng_dumpnodes(void);
10330400f03SJulian Elischer static void ng_dumphooks(void);
10430400f03SJulian Elischer 
10530400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
106954c4772SJulian Elischer /*
107954c4772SJulian Elischer  * DEAD versions of the structures.
108954c4772SJulian Elischer  * In order to avoid races, it is sometimes neccesary to point
109954c4772SJulian Elischer  * at SOMETHING even though theoretically, the current entity is
110954c4772SJulian Elischer  * INVALID. Use these to avoid these races.
111954c4772SJulian Elischer  */
112954c4772SJulian Elischer struct ng_type ng_deadtype = {
113954c4772SJulian Elischer 	NG_ABI_VERSION,
114954c4772SJulian Elischer 	"dead",
115954c4772SJulian Elischer 	NULL,	/* modevent */
116954c4772SJulian Elischer 	NULL,	/* constructor */
117954c4772SJulian Elischer 	NULL,	/* rcvmsg */
118954c4772SJulian Elischer 	NULL,	/* shutdown */
119954c4772SJulian Elischer 	NULL,	/* newhook */
120954c4772SJulian Elischer 	NULL,	/* findhook */
121954c4772SJulian Elischer 	NULL,	/* connect */
122954c4772SJulian Elischer 	NULL,	/* rcvdata */
123954c4772SJulian Elischer 	NULL,	/* disconnect */
124954c4772SJulian Elischer 	NULL, 	/* cmdlist */
125954c4772SJulian Elischer };
12630400f03SJulian Elischer 
127954c4772SJulian Elischer struct ng_node ng_deadnode = {
128954c4772SJulian Elischer 	"dead",
129954c4772SJulian Elischer 	&ng_deadtype,
130be4252b3SJulian Elischer 	NGF_INVALID,
131954c4772SJulian Elischer 	0,	/* numhooks */
132954c4772SJulian Elischer 	NULL,	/* private */
133954c4772SJulian Elischer 	0,	/* ID */
134954c4772SJulian Elischer 	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
135954c4772SJulian Elischer 	{},	/* all_nodes list entry */
136954c4772SJulian Elischer 	{},	/* id hashtable list entry */
137954c4772SJulian Elischer 	{	0,
1389852972bSAlexander Motin 		0,
139954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
1409852972bSAlexander Motin 		{}, /* workqueue entry */
1419852972bSAlexander Motin 		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
142954c4772SJulian Elischer 	},
1439852972bSAlexander Motin 	1,	/* refs */
144a40b7874SMarko Zec 	NULL,	/* vnet */
145954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
146954c4772SJulian Elischer 	ND_MAGIC,
147954c4772SJulian Elischer 	__FILE__,
148954c4772SJulian Elischer 	__LINE__,
149954c4772SJulian Elischer 	{NULL}
150954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
151954c4772SJulian Elischer };
152954c4772SJulian Elischer 
153954c4772SJulian Elischer struct ng_hook ng_deadhook = {
154954c4772SJulian Elischer 	"dead",
155954c4772SJulian Elischer 	NULL,		/* private */
156954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
157e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
158954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
159954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
160954c4772SJulian Elischer 	{},		/* hooks list */
161c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
162c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1639852972bSAlexander Motin 	1,		/* refs always >= 1 */
164954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
165954c4772SJulian Elischer 	HK_MAGIC,
166954c4772SJulian Elischer 	__FILE__,
167954c4772SJulian Elischer 	__LINE__,
168954c4772SJulian Elischer 	{NULL}
169954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
170954c4772SJulian Elischer };
171954c4772SJulian Elischer 
172954c4772SJulian Elischer /*
173954c4772SJulian Elischer  * END DEAD STRUCTURES
174954c4772SJulian Elischer  */
175069154d5SJulian Elischer /* List nodes with unallocated work */
1769852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
177b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1784cf49a43SJulian Elischer 
1794cf49a43SJulian Elischer /* List of installed types */
180069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
181069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
1824cf49a43SJulian Elischer 
183069154d5SJulian Elischer /* Hash related definitions */
1840f150d04SJulian Elischer /* XXX Don't need to initialise them because it's a LIST */
185385195c0SMarko Zec #ifdef VIMAGE_GLOBALS
1860f150d04SJulian Elischer static LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
187385195c0SMarko Zec #endif
188069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
1890f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
1900f150d04SJulian Elischer #define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
1910f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
1920f150d04SJulian Elischer 	do { 								\
19353f9c5e9SRobert Watson 		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
194603724d3SBjoern A. Zeeb 		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
1950f150d04SJulian Elischer 						nd_idnodes) {		\
1960f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
1970f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
1980f150d04SJulian Elischer 				break;					\
1990f150d04SJulian Elischer 			}						\
2000f150d04SJulian Elischer 		}							\
2010f150d04SJulian Elischer 	} while (0)
202069154d5SJulian Elischer 
203385195c0SMarko Zec #ifdef VIMAGE_GLOBALS
204cfea3f85SAlexander Motin static LIST_HEAD(, ng_node) ng_name_hash[NG_NAME_HASH_SIZE];
205385195c0SMarko Zec #endif
206cfea3f85SAlexander Motin static struct mtx	ng_namehash_mtx;
207cfea3f85SAlexander Motin #define NG_NAMEHASH(NAME, HASH)				\
208cfea3f85SAlexander Motin 	do {						\
209cfea3f85SAlexander Motin 		u_char	h = 0;				\
210cfea3f85SAlexander Motin 		const u_char	*c;			\
211cfea3f85SAlexander Motin 		for (c = (const u_char*)(NAME); *c; c++)\
212cfea3f85SAlexander Motin 			h += *c;			\
213cfea3f85SAlexander Motin 		(HASH) = h % (NG_NAME_HASH_SIZE);	\
214cfea3f85SAlexander Motin 	} while (0)
215cfea3f85SAlexander Motin 
216dc90cad9SJulian Elischer 
2174cf49a43SJulian Elischer /* Internal functions */
2184cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
219069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
220dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2214cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
222394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
223f2fbb838SAlexander Motin static void	ngthread(void *);
22427757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2259852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
226069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
227e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
228e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
229e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
230e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
2316b795970SJulian Elischer static int	ng_mkpeer(node_p node, const char *name,
2326b795970SJulian Elischer 						const char *name2, char *type);
233069154d5SJulian Elischer 
2344c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */
235069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
236069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
237069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
238069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
239069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
2401acb27c6SJulian Elischer void	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
24130400f03SJulian Elischer void	ng_unname(node_p node);
242069154d5SJulian Elischer 
2434cf49a43SJulian Elischer 
2444cf49a43SJulian Elischer /* Our own netgraph malloc type */
2454cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
246069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
247069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
248069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
249069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
250069154d5SJulian Elischer 
251069154d5SJulian Elischer /* Should not be visible outside this file */
25230400f03SJulian Elischer 
25330400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
2541ede983cSDag-Erling Smørgrav 	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
25530400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
2561ede983cSDag-Erling Smørgrav 	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
25730400f03SJulian Elischer 
2582c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2594abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2602c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2614abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2622c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2634abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2642c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2654abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2662c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2674abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2682c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2694abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
270f2fbb838SAlexander Motin #define	NG_WORKLIST_SLEEP()			\
271f2fbb838SAlexander Motin 	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
272f2fbb838SAlexander Motin #define	NG_WORKLIST_WAKEUP()			\
273f2fbb838SAlexander Motin 	wakeup_one(&ng_worklist)
2742c8dda8dSWojciech A. Koszek 
27530400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
27630400f03SJulian Elischer /*
27730400f03SJulian Elischer  * In debug mode:
27830400f03SJulian Elischer  * In an attempt to help track reference count screwups
27930400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
28030400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
28130400f03SJulian Elischer  * after they have been freed.
28230400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
28330400f03SJulian Elischer  */
28430400f03SJulian Elischer static __inline hook_p
28530400f03SJulian Elischer ng_alloc_hook(void)
28630400f03SJulian Elischer {
28730400f03SJulian Elischer 	hook_p hook;
28830400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2899ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
29030400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
29130400f03SJulian Elischer 	if (hook) {
29230400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
29330400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
29430400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
29530400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
2969ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
29730400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
29830400f03SJulian Elischer 	} else {
2999ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30030400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
30130400f03SJulian Elischer 		if (hook) {
30230400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
3039ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
30430400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
3059ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
30630400f03SJulian Elischer 		}
30730400f03SJulian Elischer 	}
30830400f03SJulian Elischer 	return (hook);
30930400f03SJulian Elischer }
31030400f03SJulian Elischer 
31130400f03SJulian Elischer static __inline node_p
31230400f03SJulian Elischer ng_alloc_node(void)
31330400f03SJulian Elischer {
31430400f03SJulian Elischer 	node_p node;
31530400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
3169ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
31730400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
31830400f03SJulian Elischer 	if (node) {
31930400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
32030400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
32130400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
32230400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3239ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
32430400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
32530400f03SJulian Elischer 	} else {
3269ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
32730400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
32830400f03SJulian Elischer 		if (node) {
32930400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3309ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
33130400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3329ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
33330400f03SJulian Elischer 		}
33430400f03SJulian Elischer 	}
33530400f03SJulian Elischer 	return (node);
33630400f03SJulian Elischer }
33730400f03SJulian Elischer 
33830400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
33930400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
34030400f03SJulian Elischer 
34130400f03SJulian Elischer 
34230400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
34330400f03SJulian Elischer 	do {								\
3449ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
34530400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
34630400f03SJulian Elischer 		hook->hk_magic = 0;					\
3479ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
34830400f03SJulian Elischer 	} while (0)
34930400f03SJulian Elischer 
35030400f03SJulian Elischer #define NG_FREE_NODE(node)						\
35130400f03SJulian Elischer 	do {								\
3529ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);			\
35330400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
35430400f03SJulian Elischer 		node->nd_magic = 0;					\
3559ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);			\
35630400f03SJulian Elischer 	} while (0)
35730400f03SJulian Elischer 
35830400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
35930400f03SJulian Elischer 
36030400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
36130400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
36230400f03SJulian Elischer 
3631ede983cSDag-Erling Smørgrav #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
3641ede983cSDag-Erling Smørgrav #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
36530400f03SJulian Elischer 
36630400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
36730400f03SJulian Elischer 
368f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3694cf49a43SJulian Elischer #ifndef TRAP_ERROR
3706b795970SJulian Elischer #define TRAP_ERROR()
3714cf49a43SJulian Elischer #endif
3724cf49a43SJulian Elischer 
373385195c0SMarko Zec #ifdef VIMAGE_GLOBALS
374385195c0SMarko Zec static	ng_ID_t nextID;
375385195c0SMarko Zec #endif
376dc90cad9SJulian Elischer 
377b2da83c2SArchie Cobbs #ifdef INVARIANTS
378b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
379b2da83c2SArchie Cobbs 		struct mbuf *n;						\
380b2da83c2SArchie Cobbs 		int total;						\
381b2da83c2SArchie Cobbs 									\
382fe584538SDag-Erling Smørgrav 		M_ASSERTPKTHDR(m);					\
383b32cfb32SGleb Smirnoff 		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
384b2da83c2SArchie Cobbs 			total += n->m_len;				\
385b32cfb32SGleb Smirnoff 			if (n->m_nextpkt != NULL)			\
386b32cfb32SGleb Smirnoff 				panic("%s: m_nextpkt", __func__);	\
387b32cfb32SGleb Smirnoff 		}							\
388ba5b359aSGleb Smirnoff 									\
389b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
390b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
3916e551fb6SDavid E. O'Brien 			    __func__, (m)->m_pkthdr.len, total);	\
392b2da83c2SArchie Cobbs 		}							\
393b2da83c2SArchie Cobbs 	} while (0)
394b2da83c2SArchie Cobbs #else
395b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
396b2da83c2SArchie Cobbs #endif
397b2da83c2SArchie Cobbs 
398e088dd4cSAlexander Motin #define ERROUT(x)	do { error = (x); goto done; } while (0)
399dc90cad9SJulian Elischer 
4004cf49a43SJulian Elischer /************************************************************************
401f8307e12SArchie Cobbs 	Parse type definitions for generic messages
402f8307e12SArchie Cobbs ************************************************************************/
403f8307e12SArchie Cobbs 
404f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
405f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
406f0184ff8SArchie Cobbs static const struct ng_parse_struct_field				\
407f0184ff8SArchie Cobbs 	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
408f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
409f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
410f0184ff8SArchie Cobbs 	&ng_ ## lo ## _type_fields					\
411f8307e12SArchie Cobbs }
412f8307e12SArchie Cobbs 
413f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
414f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
415f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
416f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
417f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
418f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
419f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
420f8307e12SArchie Cobbs 
421f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
422d7d97eb0SJeroen Ruigrok van der Werven    value immediately preceding the array -- as with struct namelist
423f8307e12SArchie Cobbs    and struct typelist. */
424f8307e12SArchie Cobbs static int
425f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
426f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
427f8307e12SArchie Cobbs {
428f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
429f8307e12SArchie Cobbs }
430f8307e12SArchie Cobbs 
431f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
432f8307e12SArchie Cobbs static int
433f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
434f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
435f8307e12SArchie Cobbs {
436f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
437f8307e12SArchie Cobbs 
438f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
439f8307e12SArchie Cobbs }
440f8307e12SArchie Cobbs 
441f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
442f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
443f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
444f8307e12SArchie Cobbs 	&ng_generic_list_getLength
445f8307e12SArchie Cobbs };
446f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
447f8307e12SArchie Cobbs 	&ng_parse_array_type,
448f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
449f8307e12SArchie Cobbs };
450f8307e12SArchie Cobbs 
451f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
452f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
453f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
454f8307e12SArchie Cobbs 	&ng_generic_list_getLength
455f8307e12SArchie Cobbs };
456f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
457f8307e12SArchie Cobbs 	&ng_parse_array_type,
458f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
459f8307e12SArchie Cobbs };
460f8307e12SArchie Cobbs 
461f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
462f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
463f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
464f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
465f8307e12SArchie Cobbs };
466f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
467f8307e12SArchie Cobbs 	&ng_parse_array_type,
468f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
469f8307e12SArchie Cobbs };
470f8307e12SArchie Cobbs 
471f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
472f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
473f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
474f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
475f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
476f8307e12SArchie Cobbs 
477f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
478f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
479f8307e12SArchie Cobbs 	{
480f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
481f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
482f8307e12SArchie Cobbs 	  "shutdown",
483f8307e12SArchie Cobbs 	  NULL,
484f8307e12SArchie Cobbs 	  NULL
485f8307e12SArchie Cobbs 	},
486f8307e12SArchie Cobbs 	{
487f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
488f8307e12SArchie Cobbs 	  NGM_MKPEER,
489f8307e12SArchie Cobbs 	  "mkpeer",
490f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
491f8307e12SArchie Cobbs 	  NULL
492f8307e12SArchie Cobbs 	},
493f8307e12SArchie Cobbs 	{
494f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
495f8307e12SArchie Cobbs 	  NGM_CONNECT,
496f8307e12SArchie Cobbs 	  "connect",
497f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
498f8307e12SArchie Cobbs 	  NULL
499f8307e12SArchie Cobbs 	},
500f8307e12SArchie Cobbs 	{
501f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
502f8307e12SArchie Cobbs 	  NGM_NAME,
503f8307e12SArchie Cobbs 	  "name",
504f8307e12SArchie Cobbs 	  &ng_generic_name_type,
505f8307e12SArchie Cobbs 	  NULL
506f8307e12SArchie Cobbs 	},
507f8307e12SArchie Cobbs 	{
508f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
509f8307e12SArchie Cobbs 	  NGM_RMHOOK,
510f8307e12SArchie Cobbs 	  "rmhook",
511f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
512f8307e12SArchie Cobbs 	  NULL
513f8307e12SArchie Cobbs 	},
514f8307e12SArchie Cobbs 	{
515f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
516f8307e12SArchie Cobbs 	  NGM_NODEINFO,
517f8307e12SArchie Cobbs 	  "nodeinfo",
518f8307e12SArchie Cobbs 	  NULL,
519f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
520f8307e12SArchie Cobbs 	},
521f8307e12SArchie Cobbs 	{
522f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
523f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
524f8307e12SArchie Cobbs 	  "listhooks",
525f8307e12SArchie Cobbs 	  NULL,
526f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
527f8307e12SArchie Cobbs 	},
528f8307e12SArchie Cobbs 	{
529f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
530f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
531f8307e12SArchie Cobbs 	  "listnames",
532f8307e12SArchie Cobbs 	  NULL,
533f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
534f8307e12SArchie Cobbs 	},
535f8307e12SArchie Cobbs 	{
536f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
537f8307e12SArchie Cobbs 	  NGM_LISTNODES,
538f8307e12SArchie Cobbs 	  "listnodes",
539f8307e12SArchie Cobbs 	  NULL,
540f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
541f8307e12SArchie Cobbs 	},
542f8307e12SArchie Cobbs 	{
543f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
544f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
545f8307e12SArchie Cobbs 	  "listtypes",
546f8307e12SArchie Cobbs 	  NULL,
547f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
548f8307e12SArchie Cobbs 	},
549f8307e12SArchie Cobbs 	{
550f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
5517095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
5527095e097SPoul-Henning Kamp 	  "textconfig",
5537095e097SPoul-Henning Kamp 	  NULL,
5547095e097SPoul-Henning Kamp 	  &ng_parse_string_type
5557095e097SPoul-Henning Kamp 	},
5567095e097SPoul-Henning Kamp 	{
5577095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
558f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
559f8307e12SArchie Cobbs 	  "textstatus",
560f8307e12SArchie Cobbs 	  NULL,
561f8307e12SArchie Cobbs 	  &ng_parse_string_type
562f8307e12SArchie Cobbs 	},
563f8307e12SArchie Cobbs 	{
564f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
565f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
566f8307e12SArchie Cobbs 	  "ascii2binary",
567f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
568f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
569f8307e12SArchie Cobbs 	},
570f8307e12SArchie Cobbs 	{
571f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
572f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
573f8307e12SArchie Cobbs 	  "binary2ascii",
574f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
575f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
576f8307e12SArchie Cobbs 	},
577f8307e12SArchie Cobbs 	{ 0 }
578f8307e12SArchie Cobbs };
579f8307e12SArchie Cobbs 
580f8307e12SArchie Cobbs /************************************************************************
5814cf49a43SJulian Elischer 			Node routines
5824cf49a43SJulian Elischer ************************************************************************/
5834cf49a43SJulian Elischer 
5844cf49a43SJulian Elischer /*
5854cf49a43SJulian Elischer  * Instantiate a node of the requested type
5864cf49a43SJulian Elischer  */
5874cf49a43SJulian Elischer int
5884cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
5894cf49a43SJulian Elischer {
5904cf49a43SJulian Elischer 	struct ng_type *type;
591069154d5SJulian Elischer 	int	error;
5924cf49a43SJulian Elischer 
5934cf49a43SJulian Elischer 	/* Check that the type makes sense */
5944cf49a43SJulian Elischer 	if (typename == NULL) {
5956b795970SJulian Elischer 		TRAP_ERROR();
5964cf49a43SJulian Elischer 		return (EINVAL);
5974cf49a43SJulian Elischer 	}
5984cf49a43SJulian Elischer 
5997610f574SGleb Smirnoff 	/* Locate the node type. If we fail we return. Do not try to load
6007610f574SGleb Smirnoff 	 * module.
6017610f574SGleb Smirnoff 	 */
6024cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL)
6034cf49a43SJulian Elischer 		return (ENXIO);
6044cf49a43SJulian Elischer 
605069154d5SJulian Elischer 	/*
606069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
607069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
608069154d5SJulian Elischer 	 */
609069154d5SJulian Elischer 	if (type->constructor != NULL) {
610069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
611069154d5SJulian Elischer 			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
61230400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
613069154d5SJulian Elischer 			}
614069154d5SJulian Elischer 		}
615069154d5SJulian Elischer 	} else {
616069154d5SJulian Elischer 		/*
617069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
61864efc707SRobert Watson 		 * to be made. It must be brought into existence by
619954c4772SJulian Elischer 		 * some external agency. The external agency should
620069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
621069154d5SJulian Elischer 		 * netgraph part initialised.
622069154d5SJulian Elischer 		 */
6236b795970SJulian Elischer 		TRAP_ERROR();
624069154d5SJulian Elischer 		error = EINVAL;
625069154d5SJulian Elischer 	}
626069154d5SJulian Elischer 	return (error);
6274cf49a43SJulian Elischer }
6284cf49a43SJulian Elischer 
6294cf49a43SJulian Elischer /*
630069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
631069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
6324cf49a43SJulian Elischer  * The returned node has a reference count of 1.
6334cf49a43SJulian Elischer  */
6344cf49a43SJulian Elischer int
6354cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
6364cf49a43SJulian Elischer {
6378b615593SMarko Zec 	INIT_VNET_NETGRAPH(curvnet);
6384cf49a43SJulian Elischer 	node_p node;
6394cf49a43SJulian Elischer 
6404cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6414cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6426b795970SJulian Elischer 		TRAP_ERROR();
6434cf49a43SJulian Elischer 		return (EINVAL);
6444cf49a43SJulian Elischer 	}
6454cf49a43SJulian Elischer 
6464cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
64730400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6484cf49a43SJulian Elischer 	if (node == NULL) {
6496b795970SJulian Elischer 		TRAP_ERROR();
6504cf49a43SJulian Elischer 		return (ENOMEM);
6514cf49a43SJulian Elischer 	}
65230400f03SJulian Elischer 	node->nd_type = type;
653bc29160dSMarko Zec #ifdef VIMAGE
654bc29160dSMarko Zec 	node->nd_vnet = curvnet;
655bc29160dSMarko Zec #endif
65630400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6574cf49a43SJulian Elischer 	type->refs++;
6584cf49a43SJulian Elischer 
6592c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6609852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
66130400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6624cf49a43SJulian Elischer 
6634cf49a43SJulian Elischer 	/* Initialize hook list for new node */
66430400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6654cf49a43SJulian Elischer 
666cfea3f85SAlexander Motin 	/* Link us into the name hash. */
667cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
668603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[0], node, nd_nodes);
669cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
670069154d5SJulian Elischer 
671dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
6729ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
67330400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
674069154d5SJulian Elischer 		node_p node2 = NULL;
675ac957cd2SJulian Elischer 		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6760f150d04SJulian Elischer 
67730400f03SJulian Elischer 		/* Is there a problem with the new number? */
6780f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6790f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
68030400f03SJulian Elischer 			break;
681069154d5SJulian Elischer 		}
68230400f03SJulian Elischer 	}
683603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
68430400f03SJulian Elischer 							node, nd_idnodes);
6859ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
686dc90cad9SJulian Elischer 
6874cf49a43SJulian Elischer 	/* Done */
6884cf49a43SJulian Elischer 	*nodepp = node;
6894cf49a43SJulian Elischer 	return (0);
6904cf49a43SJulian Elischer }
6914cf49a43SJulian Elischer 
6924cf49a43SJulian Elischer /*
6934cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
69464efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6954cf49a43SJulian Elischer  * no type-specific method.
6964cf49a43SJulian Elischer  *
69764efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6983e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6993e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
700069154d5SJulian Elischer  *
701069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
70264efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
7033e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
7043e4084c8SJulian Elischer  * in which case we allow the node to survive.
7053e4084c8SJulian Elischer  *
70664efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
7073e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
7083e4084c8SJulian Elischer  * are rebooting.... etc.
7094cf49a43SJulian Elischer  */
7104cf49a43SJulian Elischer void
7111acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
7124cf49a43SJulian Elischer {
7133e4084c8SJulian Elischer 	hook_p hook;
7143e4084c8SJulian Elischer 
7154cf49a43SJulian Elischer 	/* Check if it's already shutting down */
716be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
7174cf49a43SJulian Elischer 		return;
7184cf49a43SJulian Elischer 
7191acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
7201acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
7211acb27c6SJulian Elischer 		return;
7221acb27c6SJulian Elischer 	}
7231acb27c6SJulian Elischer 
7244cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
72530400f03SJulian Elischer 	NG_NODE_REF(node);
7264cf49a43SJulian Elischer 
72730400f03SJulian Elischer 	/*
72830400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
72930400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
730be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
73130400f03SJulian Elischer 	 * creation
73230400f03SJulian Elischer 	 */
733be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7344cf49a43SJulian Elischer 
735991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
736991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
737991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
738991fc65aSJulian Elischer 
7393e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7403e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7413e4084c8SJulian Elischer 		ng_destroy_hook(hook);
74230400f03SJulian Elischer 
743069154d5SJulian Elischer 	/*
744069154d5SJulian Elischer 	 * Drain the input queue forceably.
74530400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
74630400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
74730400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
748b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
749069154d5SJulian Elischer 	 */
7509852972bSAlexander Motin 	ng_flush_input_queue(node);
751069154d5SJulian Elischer 
752069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
75330400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
75430400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
75530400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
75630400f03SJulian Elischer 			/*
75730400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
75830400f03SJulian Elischer 			 * that it doesn't want to die.
75930400f03SJulian Elischer 			 * Presumably it is a persistant node.
7601acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7611acb27c6SJulian Elischer 			 *  e.g. hardware going away,
762be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
76330400f03SJulian Elischer 			 */
764be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7651acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
76630400f03SJulian Elischer 			return;
7674cf49a43SJulian Elischer 		}
7681acb27c6SJulian Elischer 	} else {				/* do the default thing */
7691acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7701acb27c6SJulian Elischer 	}
7714cf49a43SJulian Elischer 
77230400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
77330400f03SJulian Elischer 
77430400f03SJulian Elischer 	/*
77530400f03SJulian Elischer 	 * Remove extra reference, possibly the last
77630400f03SJulian Elischer 	 * Possible other holders of references may include
77730400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
77830400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
77930400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
78030400f03SJulian Elischer 	 */
78130400f03SJulian Elischer 	NG_NODE_UNREF(node);
7824cf49a43SJulian Elischer }
7834cf49a43SJulian Elischer 
7845951069aSJulian Elischer /*
7855951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7865951069aSJulian Elischer  * deadnode always acts as it it were the last.
7875951069aSJulian Elischer  */
7885951069aSJulian Elischer int
78930400f03SJulian Elischer ng_unref_node(node_p node)
7904cf49a43SJulian Elischer {
79130400f03SJulian Elischer 	int v;
7926b795970SJulian Elischer 
7936b795970SJulian Elischer 	if (node == &ng_deadnode) {
7945951069aSJulian Elischer 		return (0);
7956b795970SJulian Elischer 	}
7966b795970SJulian Elischer 
797018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&node->nd_refs, -1);
798e8a49db2SJulian Elischer 
799018fe3d1SAlexander Motin 	if (v == 1) { /* we were the last */
800069154d5SJulian Elischer 
801cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
80230400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
80330400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
804cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
805069154d5SJulian Elischer 
8069ed346baSBosko Milekic 		mtx_lock(&ng_idhash_mtx);
80730400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
8089ed346baSBosko Milekic 		mtx_unlock(&ng_idhash_mtx);
809069154d5SJulian Elischer 
81012574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
811069154d5SJulian Elischer 		NG_FREE_NODE(node);
8124cf49a43SJulian Elischer 	}
813018fe3d1SAlexander Motin 	return (v - 1);
8144cf49a43SJulian Elischer }
8154cf49a43SJulian Elischer 
8164cf49a43SJulian Elischer /************************************************************************
817dc90cad9SJulian Elischer 			Node ID handling
818dc90cad9SJulian Elischer ************************************************************************/
819dc90cad9SJulian Elischer static node_p
820069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
821dc90cad9SJulian Elischer {
8228b615593SMarko Zec 	INIT_VNET_NETGRAPH(curvnet);
82330400f03SJulian Elischer 	node_p node;
8249ed346baSBosko Milekic 	mtx_lock(&ng_idhash_mtx);
8250f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
82630400f03SJulian Elischer 	if(node)
82730400f03SJulian Elischer 		NG_NODE_REF(node);
8289ed346baSBosko Milekic 	mtx_unlock(&ng_idhash_mtx);
82930400f03SJulian Elischer 	return(node);
830dc90cad9SJulian Elischer }
831dc90cad9SJulian Elischer 
832dc90cad9SJulian Elischer ng_ID_t
833dc90cad9SJulian Elischer ng_node2ID(node_p node)
834dc90cad9SJulian Elischer {
83570de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
836dc90cad9SJulian Elischer }
837dc90cad9SJulian Elischer 
838dc90cad9SJulian Elischer /************************************************************************
8394cf49a43SJulian Elischer 			Node name handling
8404cf49a43SJulian Elischer ************************************************************************/
8414cf49a43SJulian Elischer 
8424cf49a43SJulian Elischer /*
8434cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
8444cf49a43SJulian Elischer  */
8454cf49a43SJulian Elischer int
8464cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8474cf49a43SJulian Elischer {
8488b615593SMarko Zec 	INIT_VNET_NETGRAPH(curvnet);
849cfea3f85SAlexander Motin 	int i, hash;
850069154d5SJulian Elischer 	node_p node2;
8514cf49a43SJulian Elischer 
8524cf49a43SJulian Elischer 	/* Check the name is valid */
85387e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8544cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8554cf49a43SJulian Elischer 			break;
8564cf49a43SJulian Elischer 	}
8574cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8586b795970SJulian Elischer 		TRAP_ERROR();
8594cf49a43SJulian Elischer 		return (EINVAL);
8604cf49a43SJulian Elischer 	}
861dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8626b795970SJulian Elischer 		TRAP_ERROR();
8634cf49a43SJulian Elischer 		return (EINVAL);
8644cf49a43SJulian Elischer 	}
8654cf49a43SJulian Elischer 
8664cf49a43SJulian Elischer 	/* Check the name isn't already being used */
867069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
86830400f03SJulian Elischer 		NG_NODE_UNREF(node2);
8696b795970SJulian Elischer 		TRAP_ERROR();
8704cf49a43SJulian Elischer 		return (EADDRINUSE);
8714cf49a43SJulian Elischer 	}
8724cf49a43SJulian Elischer 
873069154d5SJulian Elischer 	/* copy it */
87487e2c66aSHartmut Brandt 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
8754cf49a43SJulian Elischer 
876cfea3f85SAlexander Motin 	/* Update name hash. */
877cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
878cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
879cfea3f85SAlexander Motin 	LIST_REMOVE(node, nd_nodes);
880603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
881cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
882cfea3f85SAlexander Motin 
8834cf49a43SJulian Elischer 	return (0);
8844cf49a43SJulian Elischer }
8854cf49a43SJulian Elischer 
8864cf49a43SJulian Elischer /*
8874cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8884cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8894cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8904cf49a43SJulian Elischer  *
8914cf49a43SJulian Elischer  * Returns the node if found, else NULL.
892069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
893e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
894e1e8f51bSRobert Watson  * there.
8954cf49a43SJulian Elischer  */
8964cf49a43SJulian Elischer node_p
897069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
8984cf49a43SJulian Elischer {
8998b615593SMarko Zec 	INIT_VNET_NETGRAPH(curvnet);
900dc90cad9SJulian Elischer 	node_p node;
901dc90cad9SJulian Elischer 	ng_ID_t temp;
902cfea3f85SAlexander Motin 	int	hash;
9034cf49a43SJulian Elischer 
9044cf49a43SJulian Elischer 	/* "." means "this node" */
905069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
90630400f03SJulian Elischer 		NG_NODE_REF(here);
907069154d5SJulian Elischer 		return(here);
908069154d5SJulian Elischer 	}
9094cf49a43SJulian Elischer 
9104cf49a43SJulian Elischer 	/* Check for name-by-ID */
911dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
912069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
9134cf49a43SJulian Elischer 	}
9144cf49a43SJulian Elischer 
9154cf49a43SJulian Elischer 	/* Find node by name */
916cfea3f85SAlexander Motin 	NG_NAMEHASH(name, hash);
917cfea3f85SAlexander Motin 	mtx_lock(&ng_namehash_mtx);
918603724d3SBjoern A. Zeeb 	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) {
919cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
920cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
9214cf49a43SJulian Elischer 			break;
9224cf49a43SJulian Elischer 		}
92370de87f2SJulian Elischer 	}
924069154d5SJulian Elischer 	if (node)
92530400f03SJulian Elischer 		NG_NODE_REF(node);
926cfea3f85SAlexander Motin 	mtx_unlock(&ng_namehash_mtx);
9274cf49a43SJulian Elischer 	return (node);
9284cf49a43SJulian Elischer }
9294cf49a43SJulian Elischer 
9304cf49a43SJulian Elischer /*
9319d5abbddSJens Schweikhardt  * Decode an ID name, eg. "[f03034de]". Returns 0 if the
932dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
9334cf49a43SJulian Elischer  */
934dc90cad9SJulian Elischer static ng_ID_t
9354cf49a43SJulian Elischer ng_decodeidname(const char *name)
9364cf49a43SJulian Elischer {
9372b70adcbSArchie Cobbs 	const int len = strlen(name);
93825792ef3SArchie Cobbs 	char *eptr;
9392b70adcbSArchie Cobbs 	u_long val;
9404cf49a43SJulian Elischer 
9412b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
94270de87f2SJulian Elischer 	if ((len < 3)
94370de87f2SJulian Elischer 	|| (name[0] != '[')
94470de87f2SJulian Elischer 	|| (name[len - 1] != ']')
94570de87f2SJulian Elischer 	|| (!isxdigit(name[1]))) {
94670de87f2SJulian Elischer 		return ((ng_ID_t)0);
94770de87f2SJulian Elischer 	}
9484cf49a43SJulian Elischer 
9492b70adcbSArchie Cobbs 	/* Decode number */
9502b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
95170de87f2SJulian Elischer 	if ((eptr - name != len - 1)
95270de87f2SJulian Elischer 	|| (val == ULONG_MAX)
95370de87f2SJulian Elischer 	|| (val == 0)) {
95412f035e0SJulian Elischer 		return ((ng_ID_t)0);
95570de87f2SJulian Elischer 	}
9562b70adcbSArchie Cobbs 	return (ng_ID_t)val;
9574cf49a43SJulian Elischer }
9584cf49a43SJulian Elischer 
9594cf49a43SJulian Elischer /*
9604cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9614cf49a43SJulian Elischer  * when shutting down and removing the node.
96264efc707SRobert Watson  * IF we allow name changing this may be more resurrected.
9634cf49a43SJulian Elischer  */
9644cf49a43SJulian Elischer void
9654cf49a43SJulian Elischer ng_unname(node_p node)
9664cf49a43SJulian Elischer {
9674cf49a43SJulian Elischer }
9684cf49a43SJulian Elischer 
9694cf49a43SJulian Elischer /************************************************************************
9704cf49a43SJulian Elischer 			Hook routines
9714cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
9723e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
9733e4084c8SJulian Elischer  they are connected to the 'dead' hook.
9744cf49a43SJulian Elischer ************************************************************************/
9754cf49a43SJulian Elischer 
9764cf49a43SJulian Elischer /*
9774cf49a43SJulian Elischer  * Remove a hook reference
9784cf49a43SJulian Elischer  */
97930400f03SJulian Elischer void
9804cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
9814cf49a43SJulian Elischer {
98230400f03SJulian Elischer 	int v;
9836b795970SJulian Elischer 
9846b795970SJulian Elischer 	if (hook == &ng_deadhook) {
9856b795970SJulian Elischer 		return;
9866b795970SJulian Elischer 	}
987018fe3d1SAlexander Motin 
988018fe3d1SAlexander Motin 	v = atomic_fetchadd_int(&hook->hk_refs, -1);
989e8a49db2SJulian Elischer 
99030400f03SJulian Elischer 	if (v == 1) { /* we were the last */
991f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
9926b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
993069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
994069154d5SJulian Elischer 	}
9954cf49a43SJulian Elischer }
9964cf49a43SJulian Elischer 
9974cf49a43SJulian Elischer /*
9984cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
9993e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
10004cf49a43SJulian Elischer  */
10014cf49a43SJulian Elischer static int
10024cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
10034cf49a43SJulian Elischer {
10044cf49a43SJulian Elischer 	hook_p hook;
10054cf49a43SJulian Elischer 	int error = 0;
10064cf49a43SJulian Elischer 
10074cf49a43SJulian Elischer 	/* Check that the given name is good */
10084cf49a43SJulian Elischer 	if (name == NULL) {
10096b795970SJulian Elischer 		TRAP_ERROR();
10104cf49a43SJulian Elischer 		return (EINVAL);
10114cf49a43SJulian Elischer 	}
1012899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
10136b795970SJulian Elischer 		TRAP_ERROR();
10144cf49a43SJulian Elischer 		return (EEXIST);
10154cf49a43SJulian Elischer 	}
10164cf49a43SJulian Elischer 
10174cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
101830400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
10194cf49a43SJulian Elischer 	if (hook == NULL) {
10206b795970SJulian Elischer 		TRAP_ERROR();
10214cf49a43SJulian Elischer 		return (ENOMEM);
10224cf49a43SJulian Elischer 	}
10233e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
102430400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
10253e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
102630400f03SJulian Elischer 	hook->hk_node = node;
102730400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
10284cf49a43SJulian Elischer 
10293e4084c8SJulian Elischer 	/* Set hook name */
103087e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10313e4084c8SJulian Elischer 
10323e4084c8SJulian Elischer 	/*
10333e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10343e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10353e4084c8SJulian Elischer 	 */
1036954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1037954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
103830400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1039069154d5SJulian Elischer 			return (error);
1040069154d5SJulian Elischer 		}
1041954c4772SJulian Elischer 	}
10424cf49a43SJulian Elischer 	/*
10434cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10444cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10454cf49a43SJulian Elischer 	 */
104630400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
104730400f03SJulian Elischer 	node->nd_numhooks++;
10483e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
10494cf49a43SJulian Elischer 
10504cf49a43SJulian Elischer 	if (hookp)
10514cf49a43SJulian Elischer 		*hookp = hook;
10523e4084c8SJulian Elischer 	return (0);
10534cf49a43SJulian Elischer }
10544cf49a43SJulian Elischer 
10554cf49a43SJulian Elischer /*
1056899e9c4eSArchie Cobbs  * Find a hook
1057899e9c4eSArchie Cobbs  *
1058899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1059899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
10603e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1061899e9c4eSArchie Cobbs  */
1062899e9c4eSArchie Cobbs hook_p
1063899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1064899e9c4eSArchie Cobbs {
1065899e9c4eSArchie Cobbs 	hook_p hook;
1066899e9c4eSArchie Cobbs 
106730400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
106830400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
106930400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
107070de87f2SJulian Elischer 		if (NG_HOOK_IS_VALID(hook)
1071ce5e5f99SArchie Cobbs 		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
1072899e9c4eSArchie Cobbs 			return (hook);
1073899e9c4eSArchie Cobbs 	}
1074899e9c4eSArchie Cobbs 	return (NULL);
1075899e9c4eSArchie Cobbs }
1076899e9c4eSArchie Cobbs 
1077899e9c4eSArchie Cobbs /*
10784cf49a43SJulian Elischer  * Destroy a hook
10794cf49a43SJulian Elischer  *
10804cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
10814cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
10823e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
10833e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
10843e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
10853e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
10863e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
10873e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
10883e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
10893e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
10903e4084c8SJulian Elischer  * attached to the 'dead' hook.
10916b795970SJulian Elischer  *
10926b795970SJulian Elischer  * This routine is called at all stages of hook creation
10936b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
10944cf49a43SJulian Elischer  */
10954cf49a43SJulian Elischer void
10964cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
10974cf49a43SJulian Elischer {
1098ac5dd141SGleb Smirnoff 	hook_p peer;
1099ac5dd141SGleb Smirnoff 	node_p node;
11004cf49a43SJulian Elischer 
11016b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
11026b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
11036b795970SJulian Elischer 		return;
11046b795970SJulian Elischer 	}
1105ac5dd141SGleb Smirnoff 
1106ac5dd141SGleb Smirnoff 	/*
1107ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1108ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1109ac5dd141SGleb Smirnoff 	 */
1110ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1111ac5dd141SGleb Smirnoff 
1112ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1113ac5dd141SGleb Smirnoff 
1114ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1115ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1116ac5dd141SGleb Smirnoff 
11173e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
11183e4084c8SJulian Elischer 		/*
11193e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
11203e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
11213e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
11223e4084c8SJulian Elischer 		 */
11233e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
11243e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
11256b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
11266b795970SJulian Elischer 			/*
11276b795970SJulian Elischer 			 * If it's already divorced from a node,
11286b795970SJulian Elischer 			 * just free it.
11296b795970SJulian Elischer 			 */
1130ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11316b795970SJulian Elischer 		} else {
1132ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11336b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11346b795970SJulian Elischer 		}
113552fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
113652fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1137ac5dd141SGleb Smirnoff 	} else
1138ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1139ac5dd141SGleb Smirnoff 
1140ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11414cf49a43SJulian Elischer 
11424cf49a43SJulian Elischer 	/*
11434cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11444cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11454cf49a43SJulian Elischer 	 */
11466b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11476b795970SJulian Elischer 		return;
11486b795970SJulian Elischer 	}
114930400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
115030400f03SJulian Elischer 	node->nd_numhooks--;
115130400f03SJulian Elischer 	if (node->nd_type->disconnect) {
11524cf49a43SJulian Elischer 		/*
11536b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
115464efc707SRobert Watson 		 * trust its existence after this point. (except
11556b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
11566b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
11574cf49a43SJulian Elischer 		 */
115830400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
11594cf49a43SJulian Elischer 	}
11606b795970SJulian Elischer 
11616b795970SJulian Elischer 	/*
11626b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
11636b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
11646b795970SJulian Elischer 	 */
11656b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
11666b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
11676b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
11684cf49a43SJulian Elischer }
11694cf49a43SJulian Elischer 
11704cf49a43SJulian Elischer /*
11714cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
11724cf49a43SJulian Elischer  * is effectively bypassed.
11734cf49a43SJulian Elischer  */
11744cf49a43SJulian Elischer int
11754cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
11764cf49a43SJulian Elischer {
117730400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
11786b795970SJulian Elischer 		TRAP_ERROR();
11794cf49a43SJulian Elischer 		return (EINVAL);
118030400f03SJulian Elischer 	}
118130400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
118230400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
11834cf49a43SJulian Elischer 
11843e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
11853e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
11863e4084c8SJulian Elischer 
1187cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1188cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1189cf3254aaSGleb Smirnoff 
11904cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
11914cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
11924cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
11934cf49a43SJulian Elischer 	return (0);
11944cf49a43SJulian Elischer }
11954cf49a43SJulian Elischer 
11964cf49a43SJulian Elischer /*
11974cf49a43SJulian Elischer  * Install a new netgraph type
11984cf49a43SJulian Elischer  */
11994cf49a43SJulian Elischer int
12004cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
12014cf49a43SJulian Elischer {
12024cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
12034cf49a43SJulian Elischer 
12044cf49a43SJulian Elischer 	/* Check version and type name fields */
1205589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
1206589f6ed8SJulian Elischer 	|| (namelen == 0)
120787e2c66aSHartmut Brandt 	|| (namelen >= NG_TYPESIZ)) {
12086b795970SJulian Elischer 		TRAP_ERROR();
12098ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
12108ed370fdSJulian Elischer 			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
12118ed370fdSJulian Elischer 		}
12124cf49a43SJulian Elischer 		return (EINVAL);
12134cf49a43SJulian Elischer 	}
12144cf49a43SJulian Elischer 
12154cf49a43SJulian Elischer 	/* Check for name collision */
12164cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
12176b795970SJulian Elischer 		TRAP_ERROR();
12184cf49a43SJulian Elischer 		return (EEXIST);
12194cf49a43SJulian Elischer 	}
12204cf49a43SJulian Elischer 
1221069154d5SJulian Elischer 
1222069154d5SJulian Elischer 	/* Link in new type */
12239ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1224069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1225c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
12269ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12274cf49a43SJulian Elischer 	return (0);
12284cf49a43SJulian Elischer }
12294cf49a43SJulian Elischer 
12304cf49a43SJulian Elischer /*
1231c31b4a53SJulian Elischer  * unlink a netgraph type
1232c31b4a53SJulian Elischer  * If no examples exist
1233c31b4a53SJulian Elischer  */
1234c31b4a53SJulian Elischer int
1235c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1236c31b4a53SJulian Elischer {
1237c31b4a53SJulian Elischer 	/* Check for name collision */
1238c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1239c31b4a53SJulian Elischer 		TRAP_ERROR();
1240c31b4a53SJulian Elischer 		return (EBUSY);
1241c31b4a53SJulian Elischer 	}
1242c31b4a53SJulian Elischer 
1243c31b4a53SJulian Elischer 	/* Unlink type */
1244c31b4a53SJulian Elischer 	mtx_lock(&ng_typelist_mtx);
1245c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1246c31b4a53SJulian Elischer 	mtx_unlock(&ng_typelist_mtx);
1247c31b4a53SJulian Elischer 	return (0);
1248c31b4a53SJulian Elischer }
1249c31b4a53SJulian Elischer 
1250c31b4a53SJulian Elischer /*
12514cf49a43SJulian Elischer  * Look for a type of the name given
12524cf49a43SJulian Elischer  */
12534cf49a43SJulian Elischer struct ng_type *
12544cf49a43SJulian Elischer ng_findtype(const char *typename)
12554cf49a43SJulian Elischer {
12564cf49a43SJulian Elischer 	struct ng_type *type;
12574cf49a43SJulian Elischer 
12589ed346baSBosko Milekic 	mtx_lock(&ng_typelist_mtx);
1259069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
12604cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
12614cf49a43SJulian Elischer 			break;
12624cf49a43SJulian Elischer 	}
12639ed346baSBosko Milekic 	mtx_unlock(&ng_typelist_mtx);
12644cf49a43SJulian Elischer 	return (type);
12654cf49a43SJulian Elischer }
12664cf49a43SJulian Elischer 
12674cf49a43SJulian Elischer /************************************************************************
12684cf49a43SJulian Elischer 			Composite routines
12694cf49a43SJulian Elischer ************************************************************************/
12704cf49a43SJulian Elischer /*
12716b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
12724cf49a43SJulian Elischer  */
1273e088dd4cSAlexander Motin static int
1274e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
12754cf49a43SJulian Elischer {
1276e088dd4cSAlexander Motin 	int	error = 0;
12774cf49a43SJulian Elischer 
12786b795970SJulian Elischer 	/*
12796b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
12806b795970SJulian Elischer 	 * Our caller has a reference on the hook.
12816b795970SJulian Elischer 	 * Our caller has a reference on the node.
12826b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
12836b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
12841acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
12851acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
12866b795970SJulian Elischer 	 */
12876b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
12886b795970SJulian Elischer 		/*
12896b795970SJulian Elischer 		 * The node must have been freed again since we last visited
12906b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
12916b795970SJulian Elischer 		 * We should just release our references and
12926b795970SJulian Elischer 		 * free anything we can think of.
12936b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
12946b795970SJulian Elischer 		 * that holds the references, just return.
12956b795970SJulian Elischer 		 */
1296e088dd4cSAlexander Motin 		ERROUT(ENOENT);
12976b795970SJulian Elischer 	}
12986b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1299e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13006b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13011acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1302e088dd4cSAlexander Motin 			ERROUT(error);
13034cf49a43SJulian Elischer 		}
13046b795970SJulian Elischer 	}
13056b795970SJulian Elischer 	/*
13066b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
13076b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
13086b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
13096b795970SJulian Elischer 	 */
13106b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1311e088dd4cSAlexander Motin done:
1312e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1313e088dd4cSAlexander Motin 	return (error);
13146b795970SJulian Elischer }
13156b795970SJulian Elischer 
1316e088dd4cSAlexander Motin static int
1317e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
13186b795970SJulian Elischer {
1319ac5dd141SGleb Smirnoff 	hook_p	peer;
1320e088dd4cSAlexander Motin 	int	error = 0;
13216b795970SJulian Elischer 
13226b795970SJulian Elischer 	/*
13236b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
13246b795970SJulian Elischer 	 * Our caller has a reference on the hook.
13256b795970SJulian Elischer 	 * Our caller has a reference on the node.
13266b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13276b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13286b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
13296b795970SJulian Elischer 	 * First check the hook name is unique.
13301acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13316b795970SJulian Elischer 	 */
13326b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13336b795970SJulian Elischer 		TRAP_ERROR();
13346b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
13351acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1336e088dd4cSAlexander Motin 		ERROUT(EEXIST);
13376b795970SJulian Elischer 	}
13386b795970SJulian Elischer 	/*
13396b795970SJulian Elischer 	 * Check if the node type code has something to say about it
13406b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
13416b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
13426b795970SJulian Elischer 	 * The peer hook will also be destroyed.
13436b795970SJulian Elischer 	 */
13446b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1345e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1346e088dd4cSAlexander Motin 		    hook->hk_name))) {
13476b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
13481acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1349e088dd4cSAlexander Motin 			ERROUT(error);
13506b795970SJulian Elischer 		}
13516b795970SJulian Elischer 	}
13526b795970SJulian Elischer 
13536b795970SJulian Elischer 	/*
13546b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
13556b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
13566b795970SJulian Elischer 	 */
13576b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
13586b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
13596b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
13606b795970SJulian Elischer 	node->nd_numhooks++;
13616b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
13626b795970SJulian Elischer 
13636b795970SJulian Elischer 	/*
136464efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
13655951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
13666b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
13676b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
13686b795970SJulian Elischer 	 * yet.
136964efc707SRobert Watson 	 * We can call the local one immediately as we have the
13706b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
13716b795970SJulian Elischer 	 */
13726b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1373e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13746b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13751acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1376e088dd4cSAlexander Motin 			ERROUT(error);
13776b795970SJulian Elischer 		}
13786b795970SJulian Elischer 	}
1379ac5dd141SGleb Smirnoff 
1380ac5dd141SGleb Smirnoff 	/*
1381ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1382ac5dd141SGleb Smirnoff 	 */
1383ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1384ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1385ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1386ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1387ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1388ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1389e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1390ac5dd141SGleb Smirnoff 	}
1391ac5dd141SGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
1392ac5dd141SGleb Smirnoff 
1393b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1394b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1395ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
13961acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1397e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
13981acb27c6SJulian Elischer 	}
13996b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1400e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1401e088dd4cSAlexander Motin done:
1402e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1403e088dd4cSAlexander Motin 	return (error);
14044cf49a43SJulian Elischer }
14054cf49a43SJulian Elischer 
14064cf49a43SJulian Elischer /*
14076b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
14086b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
14094cf49a43SJulian Elischer  */
14106b795970SJulian Elischer static int
1411e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1412e088dd4cSAlexander Motin     node_p node2, const char *name2)
14134cf49a43SJulian Elischer {
14144cf49a43SJulian Elischer 	int	error;
14154cf49a43SJulian Elischer 	hook_p	hook;
14164cf49a43SJulian Elischer 	hook_p	hook2;
14174cf49a43SJulian Elischer 
14181acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
14191acb27c6SJulian Elischer 		return(EEXIST);
14201acb27c6SJulian Elischer 	}
14213e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
14224cf49a43SJulian Elischer 		return (error);
14236b795970SJulian Elischer 	/* Allocate the other hook and link it up */
14246b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
142519724144SGleb Smirnoff 	if (hook2 == NULL) {
14266b795970SJulian Elischer 		TRAP_ERROR();
14276b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
14286b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
14296b795970SJulian Elischer 		return (ENOMEM);
14306b795970SJulian Elischer 	}
14316b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14326b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14336b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
14346b795970SJulian Elischer 	hook->hk_peer = hook2;
14356b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
14366b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14376b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
143887e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
14396b795970SJulian Elischer 
14406b795970SJulian Elischer 	/*
14416b795970SJulian Elischer 	 * Queue the function above.
14426b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
14436b795970SJulian Elischer 	 * the other node.
14446b795970SJulian Elischer 	 */
1445b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1446b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
14473fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
14483fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
14493fb87c24SAlexander Motin 	}
14506b795970SJulian Elischer 
14516b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
14526b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
14533fb87c24SAlexander Motin 	return (error);
14544cf49a43SJulian Elischer }
14556b795970SJulian Elischer 
14566b795970SJulian Elischer /*
14576b795970SJulian Elischer  * Make a peer and connect.
14586b795970SJulian Elischer  * We assume that the local node is locked.
14596b795970SJulian Elischer  * The new node probably doesn't need a lock until
14606b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
14616b795970SJulian Elischer  * but we should think about it a bit more.
14626b795970SJulian Elischer  *
14636b795970SJulian Elischer  * The problem may come if the other node also fires up
14646b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
14656b795970SJulian Elischer  * also it may already get a command msg via it's ID.
14666b795970SJulian Elischer  *
14676b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
14686b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
14696b795970SJulian Elischer  * make arg1 point to the node to remove).
14706b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
14716b795970SJulian Elischer  * an unconnected node?
14726b795970SJulian Elischer  */
14736b795970SJulian Elischer static int
14746b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
14756b795970SJulian Elischer {
14766b795970SJulian Elischer 	node_p	node2;
14774c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
14786b795970SJulian Elischer 	int	error;
14796b795970SJulian Elischer 
14806b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
14816b795970SJulian Elischer 		return (error);
14826b795970SJulian Elischer 	}
14836b795970SJulian Elischer 
14846b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
14851acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14866b795970SJulian Elischer 		return (error);
14876b795970SJulian Elischer 	}
14886b795970SJulian Elischer 
14896b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
14901acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
14916b795970SJulian Elischer 		ng_destroy_hook(hook1);
14926b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
14936b795970SJulian Elischer 		return (error);
14946b795970SJulian Elischer 	}
14956b795970SJulian Elischer 
14966b795970SJulian Elischer 	/*
14976b795970SJulian Elischer 	 * Actually link the two hooks together.
14986b795970SJulian Elischer 	 */
14996b795970SJulian Elischer 	hook1->hk_peer = hook2;
15006b795970SJulian Elischer 	hook2->hk_peer = hook1;
15016b795970SJulian Elischer 
15026b795970SJulian Elischer 	/* Each hook is referenced by the other */
15036b795970SJulian Elischer 	NG_HOOK_REF(hook1);
15046b795970SJulian Elischer 	NG_HOOK_REF(hook2);
15056b795970SJulian Elischer 
15066b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
15076b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
15086b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
15096b795970SJulian Elischer 	}
15106b795970SJulian Elischer 
15116b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
15126b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
15136b795970SJulian Elischer 
15146b795970SJulian Elischer 	}
15153e4084c8SJulian Elischer 
15163e4084c8SJulian Elischer 	/*
15173e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
15183e4084c8SJulian Elischer 	 */
15196b795970SJulian Elischer 	if (error) {
15206b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
15211acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15226b795970SJulian Elischer 	} else {
15236b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
15246b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
15256b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
15266b795970SJulian Elischer 	}
15276b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
15283e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
15293e4084c8SJulian Elischer 	return (error);
15304cf49a43SJulian Elischer }
15316b795970SJulian Elischer 
1532069154d5SJulian Elischer /************************************************************************
1533069154d5SJulian Elischer 		Utility routines to send self messages
1534069154d5SJulian Elischer ************************************************************************/
1535069154d5SJulian Elischer 
15361acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
153764efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1538069154d5SJulian Elischer int
153915cea89fSAlexander Motin ng_rmnode_self(node_p node)
1540069154d5SJulian Elischer {
15411acb27c6SJulian Elischer 	int		error;
15424cf49a43SJulian Elischer 
15431acb27c6SJulian Elischer 	if (node == &ng_deadnode)
15441acb27c6SJulian Elischer 		return (0);
1545be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1546be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
15471acb27c6SJulian Elischer 		return (0);
1548069154d5SJulian Elischer 
154915cea89fSAlexander Motin 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
15501acb27c6SJulian Elischer 	return (error);
1551069154d5SJulian Elischer }
1552069154d5SJulian Elischer 
15531acb27c6SJulian Elischer static void
15546b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
15556b795970SJulian Elischer {
15566b795970SJulian Elischer 	ng_destroy_hook(hook);
15571acb27c6SJulian Elischer 	return ;
15586b795970SJulian Elischer }
15596b795970SJulian Elischer 
1560954c4772SJulian Elischer int
1561954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1562954c4772SJulian Elischer {
15636b795970SJulian Elischer 	int		error;
1564954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1565954c4772SJulian Elischer 
15666b795970SJulian Elischer 	if (node == &ng_deadnode)
15676b795970SJulian Elischer 		return (0);
15686b795970SJulian Elischer 
15696b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
15706b795970SJulian Elischer 	return (error);
1571954c4772SJulian Elischer }
1572954c4772SJulian Elischer 
1573069154d5SJulian Elischer /***********************************************************************
15744cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
15754cf49a43SJulian Elischer  *
15764cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
15774cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
15784cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
15794cf49a43SJulian Elischer  *
15804cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
15814cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
15824cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
15834cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
15844cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
15854cf49a43SJulian Elischer  *
15864cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1587069154d5SJulian Elischer  ***********************************************************************/
15884cf49a43SJulian Elischer int
15894cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
15904cf49a43SJulian Elischer {
15914cf49a43SJulian Elischer 	char	*node, *path, *hook;
15924cf49a43SJulian Elischer 	int	k;
15934cf49a43SJulian Elischer 
15944cf49a43SJulian Elischer 	/*
15954cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
15964cf49a43SJulian Elischer 	 */
15974cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
15984cf49a43SJulian Elischer 	if (*path) {
15994cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
16004cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
16014cf49a43SJulian Elischer 
16024cf49a43SJulian Elischer 		/* Node name must not be empty */
16034cf49a43SJulian Elischer 		if (!*node)
16044cf49a43SJulian Elischer 			return -1;
16054cf49a43SJulian Elischer 
16064cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
16074cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
16084cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
16094cf49a43SJulian Elischer 				if (node[k] == '.')
16104cf49a43SJulian Elischer 					return -1;
16114cf49a43SJulian Elischer 		}
16124cf49a43SJulian Elischer 	} else {
16134cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
16144cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
16154cf49a43SJulian Elischer 	}
16164cf49a43SJulian Elischer 
16174cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
16184cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16194cf49a43SJulian Elischer 		if (path[k] == ':')
16204cf49a43SJulian Elischer 			return -1;
16214cf49a43SJulian Elischer 
16224cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
16234cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16244cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
16254cf49a43SJulian Elischer 			return -1;
16264cf49a43SJulian Elischer 
16274cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
16284cf49a43SJulian Elischer 	if (path[0] == '.')
16294cf49a43SJulian Elischer 		path++;
16304cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16314cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16324cf49a43SJulian Elischer 
16334cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
16344cf49a43SJulian Elischer 	if (*path) {
16354cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
16364cf49a43SJulian Elischer 			if (path[k] == '.') {
16374cf49a43SJulian Elischer 				hook = NULL;
16384cf49a43SJulian Elischer 				break;
16394cf49a43SJulian Elischer 			}
16404cf49a43SJulian Elischer 	} else
16414cf49a43SJulian Elischer 		path = hook = NULL;
16424cf49a43SJulian Elischer 
16434cf49a43SJulian Elischer 	/* Done */
16444cf49a43SJulian Elischer 	if (nodep)
16454cf49a43SJulian Elischer 		*nodep = node;
16464cf49a43SJulian Elischer 	if (pathp)
16474cf49a43SJulian Elischer 		*pathp = path;
16484cf49a43SJulian Elischer 	if (hookp)
16494cf49a43SJulian Elischer 		*hookp = hook;
16504cf49a43SJulian Elischer 	return (0);
16514cf49a43SJulian Elischer }
16524cf49a43SJulian Elischer 
16534cf49a43SJulian Elischer /*
16544cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1655069154d5SJulian Elischer  * return the destination node.
16564cf49a43SJulian Elischer  */
16574cf49a43SJulian Elischer int
1658069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1659069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
16604cf49a43SJulian Elischer {
166187e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
16624cf49a43SJulian Elischer 	char   *nodename, *path, pbuf[2];
1663069154d5SJulian Elischer 	node_p  node, oldnode;
16644cf49a43SJulian Elischer 	char   *cp;
1665a4ec03cfSJulian Elischer 	hook_p hook = NULL;
16664cf49a43SJulian Elischer 
16674cf49a43SJulian Elischer 	/* Initialize */
166830400f03SJulian Elischer 	if (destp == NULL) {
16696b795970SJulian Elischer 		TRAP_ERROR();
16704cf49a43SJulian Elischer 		return EINVAL;
167130400f03SJulian Elischer 	}
16724cf49a43SJulian Elischer 	*destp = NULL;
16734cf49a43SJulian Elischer 
16744cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
16754cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
16764cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
16774cf49a43SJulian Elischer 
16784cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
16794cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
16806b795970SJulian Elischer 		TRAP_ERROR();
16814cf49a43SJulian Elischer 		return EINVAL;
16824cf49a43SJulian Elischer 	}
16834cf49a43SJulian Elischer 	if (path == NULL) {
16844cf49a43SJulian Elischer 		pbuf[0] = '.';	/* Needs to be writable */
16854cf49a43SJulian Elischer 		pbuf[1] = '\0';
16864cf49a43SJulian Elischer 		path = pbuf;
16874cf49a43SJulian Elischer 	}
16884cf49a43SJulian Elischer 
1689069154d5SJulian Elischer 	/*
1690069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1691069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1692069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1693069154d5SJulian Elischer 	 */
16944cf49a43SJulian Elischer 	if (nodename) {
1695069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
16964cf49a43SJulian Elischer 		if (node == NULL) {
16976b795970SJulian Elischer 			TRAP_ERROR();
16984cf49a43SJulian Elischer 			return (ENOENT);
16994cf49a43SJulian Elischer 		}
1700069154d5SJulian Elischer 	} else {
1701069154d5SJulian Elischer 		if (here == NULL) {
17026b795970SJulian Elischer 			TRAP_ERROR();
1703069154d5SJulian Elischer 			return (EINVAL);
1704069154d5SJulian Elischer 		}
17054cf49a43SJulian Elischer 		node = here;
170630400f03SJulian Elischer 		NG_NODE_REF(node);
1707069154d5SJulian Elischer 	}
17084cf49a43SJulian Elischer 
1709069154d5SJulian Elischer 	/*
1710069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1711069154d5SJulian Elischer 	 * XXX
1712069154d5SJulian Elischer 	 * We actually cannot guarantee that the sequence
1713069154d5SJulian Elischer 	 * is not being demolished as we crawl along it
1714069154d5SJulian Elischer 	 * without extra-ordinary locking etc.
1715069154d5SJulian Elischer 	 * So this is a bit dodgy to say the least.
1716069154d5SJulian Elischer 	 * We can probably hold up some things by holding
1717069154d5SJulian Elischer 	 * the nodelist mutex for the time of this
1718069154d5SJulian Elischer 	 * crawl if we wanted.. At least that way we wouldn't have to
171964efc707SRobert Watson 	 * worry about the nodes disappearing, but the hooks would still
1720069154d5SJulian Elischer 	 * be a problem.
1721069154d5SJulian Elischer 	 */
17224cf49a43SJulian Elischer 	for (cp = path; node != NULL && *cp != '\0'; ) {
17234cf49a43SJulian Elischer 		char *segment;
17244cf49a43SJulian Elischer 
17254cf49a43SJulian Elischer 		/*
17264cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
17274cf49a43SJulian Elischer 		 * found with a NUL; "cp" points to the next segment (or the
17284cf49a43SJulian Elischer 		 * NUL at the end).
17294cf49a43SJulian Elischer 		 */
17304cf49a43SJulian Elischer 		for (segment = cp; *cp != '\0'; cp++) {
17314cf49a43SJulian Elischer 			if (*cp == '.') {
17324cf49a43SJulian Elischer 				*cp++ = '\0';
17334cf49a43SJulian Elischer 				break;
17344cf49a43SJulian Elischer 			}
17354cf49a43SJulian Elischer 		}
17364cf49a43SJulian Elischer 
17374cf49a43SJulian Elischer 		/* Empty segment */
17384cf49a43SJulian Elischer 		if (*segment == '\0')
17394cf49a43SJulian Elischer 			continue;
17404cf49a43SJulian Elischer 
17414cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1742899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17434cf49a43SJulian Elischer 
17444cf49a43SJulian Elischer 		/* Can't get there from here... */
17454cf49a43SJulian Elischer 		if (hook == NULL
174630400f03SJulian Elischer 		    || NG_HOOK_PEER(hook) == NULL
174730400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(hook)
174830400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17496b795970SJulian Elischer 			TRAP_ERROR();
175030400f03SJulian Elischer 			NG_NODE_UNREF(node);
175130400f03SJulian Elischer #if 0
175230400f03SJulian Elischer 			printf("hooknotvalid %s %s %d %d %d %d ",
175330400f03SJulian Elischer 					path,
175430400f03SJulian Elischer 					segment,
175530400f03SJulian Elischer 					hook == NULL,
175630400f03SJulian Elischer 					NG_HOOK_PEER(hook) == NULL,
175730400f03SJulian Elischer 					NG_HOOK_NOT_VALID(hook),
175830400f03SJulian Elischer 					NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
175930400f03SJulian Elischer #endif
17604cf49a43SJulian Elischer 			return (ENOENT);
17614cf49a43SJulian Elischer 		}
17624cf49a43SJulian Elischer 
1763069154d5SJulian Elischer 		/*
1764069154d5SJulian Elischer 		 * Hop on over to the next node
1765069154d5SJulian Elischer 		 * XXX
1766069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1767069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1768069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1769069154d5SJulian Elischer 		 */
1770069154d5SJulian Elischer 		oldnode = node;
177130400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
177230400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
177330400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
177430400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
177530400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1776069154d5SJulian Elischer 			node = NULL;
1777069154d5SJulian Elischer 		}
17784cf49a43SJulian Elischer 	}
17794cf49a43SJulian Elischer 
17804cf49a43SJulian Elischer 	/* If node somehow missing, fail here (probably this is not needed) */
17814cf49a43SJulian Elischer 	if (node == NULL) {
17826b795970SJulian Elischer 		TRAP_ERROR();
17834cf49a43SJulian Elischer 		return (ENXIO);
17844cf49a43SJulian Elischer 	}
17854cf49a43SJulian Elischer 
17864cf49a43SJulian Elischer 	/* Done */
17874cf49a43SJulian Elischer 	*destp = node;
17881bdebe4dSArchie Cobbs 	if (lasthook != NULL)
178930400f03SJulian Elischer 		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
1790069154d5SJulian Elischer 	return (0);
1791069154d5SJulian Elischer }
1792069154d5SJulian Elischer 
1793069154d5SJulian Elischer /***************************************************************\
1794069154d5SJulian Elischer * Input queue handling.
1795069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1796069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
179764efc707SRobert Watson * Items which cannot be handled immediately are queued.
1798069154d5SJulian Elischer *
1799069154d5SJulian Elischer * read-write queue locking inline functions			*
1800069154d5SJulian Elischer \***************************************************************/
1801069154d5SJulian Elischer 
18029852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
18039852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
18049852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
18059852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
18069852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
18079852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1808069154d5SJulian Elischer 
1809069154d5SJulian Elischer /*
1810069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1811069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1812069154d5SJulian Elischer  * with them.
1813069154d5SJulian Elischer  *
1814b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1815069154d5SJulian Elischer  */
1816069154d5SJulian Elischer /*-
1817069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1818069154d5SJulian Elischer                        |
1819069154d5SJulian Elischer                        V
1820069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18214be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
18224be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
18234be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1824069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18254be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
18264be59335SGleb Smirnoff                             V                                | |
18274be59335SGleb Smirnoff                   [active reader count]                      | |
1828069154d5SJulian Elischer                                                              | |
18294be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1830069154d5SJulian Elischer                                                                |
18314be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1832b57a7965SJulian Elischer 
18338f9ac44aSAlexander Motin Node queue has such semantics:
18348f9ac44aSAlexander Motin - All flags modifications are atomic.
18358f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18368f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18378f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18388f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18398f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18408f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18418f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18428f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18438f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1844069154d5SJulian Elischer */
18458f9ac44aSAlexander Motin 
18464be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18474be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18484be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18494be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18504be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1851b57a7965SJulian Elischer 
1852b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18534be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18544be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1855b57a7965SJulian Elischer 
18564be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1857b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1858b57a7965SJulian Elischer 
18594be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
18604be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
18614be59335SGleb Smirnoff 
18624be59335SGleb Smirnoff /* How to decide what the next queued item is. */
18639852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
18649852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
18654be59335SGleb Smirnoff 
18664be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
18674be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
18684be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
18694be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
18704be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1871b57a7965SJulian Elischer 
1872b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
18734be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
18744be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1875394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
18764be59335SGleb Smirnoff 
1877714fb865SGleb Smirnoff #define NGQRW_R 0
1878714fb865SGleb Smirnoff #define NGQRW_W 1
1879714fb865SGleb Smirnoff 
18809852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
18819852972bSAlexander Motin 
1882069154d5SJulian Elischer /*
1883069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1884069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1885069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1886069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1887069154d5SJulian Elischer  * on the queue.
1888069154d5SJulian Elischer  */
1889069154d5SJulian Elischer static __inline item_p
18909852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1891069154d5SJulian Elischer {
1892069154d5SJulian Elischer 	item_p item;
18939852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1894b57a7965SJulian Elischer 
18958f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1896d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
18978f9ac44aSAlexander Motin 
18988f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
18994be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
19002955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
19012955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
19029852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19034be59335SGleb Smirnoff 		return (NULL);
19044be59335SGleb Smirnoff 	}
1905d58e678fSGleb Smirnoff 
19064be59335SGleb Smirnoff 	/*
19074be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
19084be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
19094be59335SGleb Smirnoff 	 * the current state of the node.
19104be59335SGleb Smirnoff 	 */
19114be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1912394cb30aSAlexander Motin 		while (1) {
1913394cb30aSAlexander Motin 			long t = ngq->q_flags;
1914394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
19158f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
19162955ee18SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
19172955ee18SGleb Smirnoff 				    "can't proceed; queue flags 0x%lx", __func__,
19189852972bSAlexander Motin 				    node->nd_ID, node, t);
19194be59335SGleb Smirnoff 				return (NULL);
19204be59335SGleb Smirnoff 			}
19219852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1922394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1923394cb30aSAlexander Motin 				break;
1924394cb30aSAlexander Motin 			cpu_spinwait();
1925394cb30aSAlexander Motin 		}
19268f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1927714fb865SGleb Smirnoff 		*rw = NGQRW_R;
19289852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1929394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19308f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1931714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1932069154d5SJulian Elischer 	} else {
19338f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
1934394cb30aSAlexander Motin 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer "
1935394cb30aSAlexander Motin 		    "can't proceed; queue flags 0x%lx", __func__,
19369852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19374be59335SGleb Smirnoff 		return (NULL);
19384cf49a43SJulian Elischer 	}
19394cf49a43SJulian Elischer 
19404cf49a43SJulian Elischer 	/*
1941069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1942069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19434cf49a43SJulian Elischer 	 */
19449852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19459852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
19469852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
19479852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
19482955ee18SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
19492955ee18SGleb Smirnoff 	    "queue flags 0x%lx", __func__,
19509852972bSAlexander Motin 	    node->nd_ID, node, item, *rw ? "WRITER" : "READER" ,
19513b33fbe7SGleb Smirnoff 	    ngq->q_flags);
1952069154d5SJulian Elischer 	return (item);
1953069154d5SJulian Elischer }
1954859a4d16SJulian Elischer 
1955859a4d16SJulian Elischer /*
19568f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
19578f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
1958859a4d16SJulian Elischer  */
1959069154d5SJulian Elischer static __inline void
19609852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
1961069154d5SJulian Elischer {
19629852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
19634be59335SGleb Smirnoff 	if (rw == NGQRW_W)
19644be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
19654be59335SGleb Smirnoff 	else
19664be59335SGleb Smirnoff 		NGI_SET_READER(item);
1967394cb30aSAlexander Motin 
1968394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
1969394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
19709852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
19719852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
1972394cb30aSAlexander Motin 
19732955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
19749852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
19754be59335SGleb Smirnoff 
19764be59335SGleb Smirnoff 	/*
19774be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
19784be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
19794be59335SGleb Smirnoff 	 */
19804be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
19819852972bSAlexander Motin 		ng_worklist_add(node);
1982394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
1983069154d5SJulian Elischer }
1984069154d5SJulian Elischer 
19858f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
1986069154d5SJulian Elischer static __inline item_p
19879852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
1988069154d5SJulian Elischer {
19899852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
1990ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
1991069154d5SJulian Elischer 
1992394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
1993394cb30aSAlexander Motin 	while (1) {
19949852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
1995394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
1996394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
19979852972bSAlexander Motin 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
19989852972bSAlexander Motin 		    t, t + READER_INCREMENT)) {
1999069154d5SJulian Elischer 	    		/* Successfully grabbed node */
2000394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20019852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
2002069154d5SJulian Elischer 			return (item);
2003069154d5SJulian Elischer 		}
2004394cb30aSAlexander Motin 		cpu_spinwait();
2005394cb30aSAlexander Motin 	};
2006069154d5SJulian Elischer 
2007394cb30aSAlexander Motin 	/* Queue the request for later. */
20089852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
2009f912c0f7SGleb Smirnoff 
2010f912c0f7SGleb Smirnoff 	return (NULL);
2011069154d5SJulian Elischer }
2012069154d5SJulian Elischer 
20138f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
2014069154d5SJulian Elischer static __inline item_p
20159852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
2016069154d5SJulian Elischer {
20179852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2018ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
2019ac5dd141SGleb Smirnoff 
2020394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
20219852972bSAlexander Motin 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags,
20229852972bSAlexander Motin 	    0, WRITER_ACTIVE)) {
2023394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
20242955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20259852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
2026069154d5SJulian Elischer 		return (item);
2027069154d5SJulian Elischer 	}
2028069154d5SJulian Elischer 
2029394cb30aSAlexander Motin 	/* Queue the request for later. */
20309852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
2031f912c0f7SGleb Smirnoff 
2032f912c0f7SGleb Smirnoff 	return (NULL);
2033069154d5SJulian Elischer }
2034069154d5SJulian Elischer 
2035262dfd7fSJulian Elischer #if 0
2036262dfd7fSJulian Elischer static __inline item_p
20379852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
2038262dfd7fSJulian Elischer {
20399852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20409852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2041262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2042262dfd7fSJulian Elischer 
2043262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2044262dfd7fSJulian Elischer 
20459852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2046262dfd7fSJulian Elischer 
2047262dfd7fSJulian Elischer 	/*
2048262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2049262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2050262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2051262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2052262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2053262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2054262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2055262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2056262dfd7fSJulian Elischer 	 */
20579852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
20589852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
20599852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2060262dfd7fSJulian Elischer 
2061262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2062262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2063262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2064262dfd7fSJulian Elischer 
2065262dfd7fSJulian Elischer 		/*
2066262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
2067262dfd7fSJulian Elischer 		 * down grade back to READER and finish up
2068262dfd7fSJulian Elischer 	 	 */
20699852972bSAlexander Motin 		atomic_add_int(&ngq->q_flags,
2070262dfd7fSJulian Elischer 		    READER_INCREMENT - WRITER_ACTIVE);
2071262dfd7fSJulian Elischer 
2072262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2073262dfd7fSJulian Elischer 		return;
2074262dfd7fSJulian Elischer 	}
2075262dfd7fSJulian Elischer 	/*
2076262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2077262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2078262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2079262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2080262dfd7fSJulian Elischer 	 * set the correct flags.
2081262dfd7fSJulian Elischer 	 */
20829852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2083262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
20849852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2085262dfd7fSJulian Elischer 
2086262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
20879852972bSAlexander Motin 		    node->nd_ID, node);
2088262dfd7fSJulian Elischer 	};
20899852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
20909852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
20919852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2092262dfd7fSJulian Elischer 
2093262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
20949852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2095394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20969852972bSAlexander Motin 		ng_worklist_add(node);
20979852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2098262dfd7fSJulian Elischer 
2099262dfd7fSJulian Elischer 	return;
2100262dfd7fSJulian Elischer }
2101262dfd7fSJulian Elischer #endif
2102262dfd7fSJulian Elischer 
21038f9ac44aSAlexander Motin /* Release reader lock. */
2104069154d5SJulian Elischer static __inline void
21059852972bSAlexander Motin ng_leave_read(node_p node)
2106069154d5SJulian Elischer {
21079852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2108069154d5SJulian Elischer }
2109069154d5SJulian Elischer 
21108f9ac44aSAlexander Motin /* Release writer lock. */
2111069154d5SJulian Elischer static __inline void
21129852972bSAlexander Motin ng_leave_write(node_p node)
2113069154d5SJulian Elischer {
21149852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2115069154d5SJulian Elischer }
2116069154d5SJulian Elischer 
21178f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2118069154d5SJulian Elischer static void
21199852972bSAlexander Motin ng_flush_input_queue(node_p node)
2120069154d5SJulian Elischer {
21219852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2122069154d5SJulian Elischer 	item_p item;
2123069154d5SJulian Elischer 
21242c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
21259852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
21269852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
21279852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
21289852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
21292c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21304be59335SGleb Smirnoff 
21314be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
213210e87318SAlexander Motin 		if (item->apply != NULL) {
213310e87318SAlexander Motin 			if (item->depth == 1)
213410e87318SAlexander Motin 				item->apply->error = ENOENT;
213510e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
213610e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
213710e87318SAlexander Motin 				    item->apply->error);
213810e87318SAlexander Motin 			}
2139eb2405ddSGleb Smirnoff 		}
2140505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21412c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2142069154d5SJulian Elischer 	}
21432c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2144069154d5SJulian Elischer }
2145069154d5SJulian Elischer 
2146069154d5SJulian Elischer /***********************************************************************
2147069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2148069154d5SJulian Elischer ***********************************************************************/
2149069154d5SJulian Elischer 
2150069154d5SJulian Elischer /*
21511acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2152069154d5SJulian Elischer  * Common:
2153069154d5SJulian Elischer  *    reference to destination node.
2154069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2155e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2156069154d5SJulian Elischer  * Data:
2157069154d5SJulian Elischer  *    pointer to mbuf
2158069154d5SJulian Elischer  * Control_Message:
2159069154d5SJulian Elischer  *    pointer to msg.
2160069154d5SJulian Elischer  *    ID of original sender node. (return address)
21611acb27c6SJulian Elischer  * Function:
21621acb27c6SJulian Elischer  *    Function pointer
21631acb27c6SJulian Elischer  *    void * argument
21641acb27c6SJulian Elischer  *    integer argument
2165069154d5SJulian Elischer  *
2166069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2167069154d5SJulian Elischer  */
2168069154d5SJulian Elischer 
2169069154d5SJulian Elischer int
217042282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2171069154d5SJulian Elischer {
2172e088dd4cSAlexander Motin 	hook_p hook;
2173e088dd4cSAlexander Motin 	node_p node;
217442282202SGleb Smirnoff 	int queue, rw;
2175e088dd4cSAlexander Motin 	struct ng_queue *ngq;
217632b33288SGleb Smirnoff 	int error = 0;
2177069154d5SJulian Elischer 
2178f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2179f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2180e088dd4cSAlexander Motin 
2181e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2182e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2183e088dd4cSAlexander Motin #endif
2184e088dd4cSAlexander Motin 
2185f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2186e088dd4cSAlexander Motin 	if (item->apply)
2187e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2188e088dd4cSAlexander Motin 
2189e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2190f50597f5SAlexander Motin 	/* Node is never optional. */
2191f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2192e088dd4cSAlexander Motin 
2193e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2194f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2195f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2196f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
219710204449SJulian Elischer 		if (NGI_M(item) == NULL)
2198e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2199069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
22006f683eeeSGleb Smirnoff 	}
22016f683eeeSGleb Smirnoff 
2202069154d5SJulian Elischer 	/*
2203f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2204f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2205f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2206069154d5SJulian Elischer 	 */
2207db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2208f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2209f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2210069154d5SJulian Elischer 		rw = NGQRW_W;
2211f50597f5SAlexander Motin 	} else {
2212f50597f5SAlexander Motin 		rw = NGQRW_R;
2213f50597f5SAlexander Motin 	}
22146f683eeeSGleb Smirnoff 
221581a253a4SAlexander Motin 	/*
221681a253a4SAlexander Motin 	 * If sender or receiver requests queued delivery or stack usage
221781a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
221881a253a4SAlexander Motin 	 */
221981a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
222081a253a4SAlexander Motin 		queue = 1;
2221f50597f5SAlexander Motin 	} else {
2222f50597f5SAlexander Motin 		queue = 0;
222381a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2224d4529f98SAlexander Motin 		/*
2225942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2226f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2227d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2228f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2229f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2230d4529f98SAlexander Motin 		 */
2231f50597f5SAlexander Motin 		size_t	st, su, sl;
223281a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2233f50597f5SAlexander Motin 		sl = st - su;
2234f50597f5SAlexander Motin 		if ((sl * 4 < st) ||
2235f50597f5SAlexander Motin 		    ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) ||
223681a253a4SAlexander Motin 		      (hook && (hook->hk_flags & HK_HI_STACK))))) {
223781a253a4SAlexander Motin 			queue = 1;
223881a253a4SAlexander Motin 		}
223981a253a4SAlexander Motin #endif
2240f50597f5SAlexander Motin 	}
224181a253a4SAlexander Motin 
2242069154d5SJulian Elischer 	if (queue) {
224310e87318SAlexander Motin 		item->depth = 1;
2244394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
22459852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2246f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2247069154d5SJulian Elischer 	}
2248069154d5SJulian Elischer 
2249069154d5SJulian Elischer 	/*
2250069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2251069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2252069154d5SJulian Elischer 	 */
225332b33288SGleb Smirnoff  	if (rw == NGQRW_R)
22549852972bSAlexander Motin 		item = ng_acquire_read(node, item);
225532b33288SGleb Smirnoff 	else
22569852972bSAlexander Motin 		item = ng_acquire_write(node, item);
22574cf49a43SJulian Elischer 
2258f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2259f50597f5SAlexander Motin 	if (item == NULL)
2260f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2261069154d5SJulian Elischer 
22625951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
22635951069aSJulian Elischer 
226410e87318SAlexander Motin 	item->depth++;
226527757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2266069154d5SJulian Elischer 
2267394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
22689852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2269394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
22702c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2271394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
22729852972bSAlexander Motin 			ng_worklist_add(node);
22732c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2274394cb30aSAlexander Motin 	}
2275394cb30aSAlexander Motin 
2276394cb30aSAlexander Motin 	/*
2277394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2278394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2279394cb30aSAlexander Motin 	 */
2280394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2281069154d5SJulian Elischer 
22821acb27c6SJulian Elischer 	return (error);
2283e088dd4cSAlexander Motin 
2284e088dd4cSAlexander Motin done:
2285f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
228610e87318SAlexander Motin 	if (item->apply != NULL) {
228710e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
228810e87318SAlexander Motin 			item->apply->error = error;
228910e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
229010e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
229110e87318SAlexander Motin 			    item->apply->error);
229210e87318SAlexander Motin 		}
229310e87318SAlexander Motin 	}
2294e72a98f4SAlexander Motin 
2295e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2296e088dd4cSAlexander Motin 	return (error);
2297069154d5SJulian Elischer }
2298069154d5SJulian Elischer 
2299069154d5SJulian Elischer /*
2300069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2301069154d5SJulian Elischer  * It should contain all the information needed
2302069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2303e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
23044cf49a43SJulian Elischer  */
230527757487SGleb Smirnoff static int
2306714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2307069154d5SJulian Elischer {
2308069154d5SJulian Elischer 	hook_p  hook;
2309069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2310c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2311e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
231210e87318SAlexander Motin 	int	error = 0, depth;
2313069154d5SJulian Elischer 
2314f50597f5SAlexander Motin 	/* Node and item are never optional. */
2315f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2316f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2317f50597f5SAlexander Motin 
23181acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
231930400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2320069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2321069154d5SJulian Elischer #endif
23228afe16d5SGleb Smirnoff 
23238afe16d5SGleb Smirnoff 	apply = item->apply;
232410e87318SAlexander Motin 	depth = item->depth;
23258afe16d5SGleb Smirnoff 
23266b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2327069154d5SJulian Elischer 	case NGQF_DATA:
2328069154d5SJulian Elischer 		/*
2329069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2330069154d5SJulian Elischer 		 */
2331f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2332f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2333f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2334a54a69d7SJulian Elischer 			error = EIO;
2335069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2336c4b5eea4SJulian Elischer 			break;
2337069154d5SJulian Elischer 		}
2338c4b5eea4SJulian Elischer 		/*
2339c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
2340c4b5eea4SJulian Elischer 		 * Give preference to the hook over-ride method
2341c4b5eea4SJulian Elischer 		 */
2342c4b5eea4SJulian Elischer 		if ((!(rcvdata = hook->hk_rcvdata))
2343c4b5eea4SJulian Elischer 		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2344c4b5eea4SJulian Elischer 			error = 0;
2345c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2346c4b5eea4SJulian Elischer 			break;
2347c4b5eea4SJulian Elischer 		}
2348a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2349069154d5SJulian Elischer 		break;
2350069154d5SJulian Elischer 	case NGQF_MESG:
2351e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2352069154d5SJulian Elischer 			/*
2353e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2354e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2355069154d5SJulian Elischer 			 * The message may not need it.
2356069154d5SJulian Elischer 			 */
235730400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2358069154d5SJulian Elischer 			hook = NULL;
2359069154d5SJulian Elischer 		}
2360069154d5SJulian Elischer 		/*
2361069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2362069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2363069154d5SJulian Elischer 		 */
236430400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
23656b795970SJulian Elischer 			TRAP_ERROR();
2366a54a69d7SJulian Elischer 			error = EINVAL;
2367069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2368e72a98f4SAlexander Motin 			break;
2369e72a98f4SAlexander Motin 		}
2370069154d5SJulian Elischer 		/*
2371069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2372069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2373069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2374e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2375069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2376069154d5SJulian Elischer 		 * reference a node or hook that has just been
2377069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2378069154d5SJulian Elischer 		 * is holding a reference, but..
2379069154d5SJulian Elischer 		 */
2380e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2381e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2382a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2383c4b5eea4SJulian Elischer 			break;
2384c4b5eea4SJulian Elischer 		}
2385e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2386e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
23876b795970SJulian Elischer 			TRAP_ERROR();
2388a54a69d7SJulian Elischer 			error = 0;
2389069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2390c4b5eea4SJulian Elischer 			break;
2391069154d5SJulian Elischer 		}
2392a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2393069154d5SJulian Elischer 		break;
23946b795970SJulian Elischer 	case NGQF_FN:
2395e088dd4cSAlexander Motin 	case NGQF_FN2:
2396e088dd4cSAlexander Motin 		/*
239774c9119dSAlexander Motin 		 * In the case of the shutdown message we allow it to hit
2398e088dd4cSAlexander Motin 		 * even if the node is invalid.
2399e088dd4cSAlexander Motin 		 */
240074c9119dSAlexander Motin 		if (NG_NODE_NOT_VALID(node) &&
240174c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmnode) {
2402e088dd4cSAlexander Motin 			TRAP_ERROR();
2403e088dd4cSAlexander Motin 			error = EINVAL;
2404e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2405e088dd4cSAlexander Motin 			break;
2406e088dd4cSAlexander Motin 		}
240774c9119dSAlexander Motin 		/* Same is about some internal functions and invalid hook. */
240874c9119dSAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook) &&
240974c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part2 &&
241074c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part3 &&
241174c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmhook_part2) {
241274c9119dSAlexander Motin 			TRAP_ERROR();
241374c9119dSAlexander Motin 			error = EINVAL;
241474c9119dSAlexander Motin 			NG_FREE_ITEM(item);
241574c9119dSAlexander Motin 			break;
241674c9119dSAlexander Motin 		}
241774c9119dSAlexander Motin 
2418b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2419b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2420b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2421b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2422b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2423e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2424e088dd4cSAlexander Motin 		break;
2425069154d5SJulian Elischer 	}
2426069154d5SJulian Elischer 	/*
2427069154d5SJulian Elischer 	 * We held references on some of the resources
2428069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2429069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2430069154d5SJulian Elischer 	 */
2431e72a98f4SAlexander Motin 	if (hook)
243230400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2433069154d5SJulian Elischer 
2434f50597f5SAlexander Motin  	if (rw == NGQRW_R)
24359852972bSAlexander Motin 		ng_leave_read(node);
2436f50597f5SAlexander Motin 	else
24379852972bSAlexander Motin 		ng_leave_write(node);
24388afe16d5SGleb Smirnoff 
24398afe16d5SGleb Smirnoff 	/* Apply callback. */
244010e87318SAlexander Motin 	if (apply != NULL) {
244110e87318SAlexander Motin 		if (depth == 1 && error != 0)
244210e87318SAlexander Motin 			apply->error = error;
244310e87318SAlexander Motin 		if (refcount_release(&apply->refs))
244410e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
244510e87318SAlexander Motin 	}
24468afe16d5SGleb Smirnoff 
244727757487SGleb Smirnoff 	return (error);
2448069154d5SJulian Elischer }
2449069154d5SJulian Elischer 
2450069154d5SJulian Elischer /***********************************************************************
2451069154d5SJulian Elischer  * Implement the 'generic' control messages
2452069154d5SJulian Elischer  ***********************************************************************/
2453069154d5SJulian Elischer static int
2454069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
24554cf49a43SJulian Elischer {
24568b615593SMarko Zec 	INIT_VNET_NETGRAPH(curvnet);
24574cf49a43SJulian Elischer 	int error = 0;
2458069154d5SJulian Elischer 	struct ng_mesg *msg;
2459069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
24604cf49a43SJulian Elischer 
2461069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
24624cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
24636b795970SJulian Elischer 		TRAP_ERROR();
2464069154d5SJulian Elischer 		error = EINVAL;
2465069154d5SJulian Elischer 		goto out;
24664cf49a43SJulian Elischer 	}
24674cf49a43SJulian Elischer 	switch (msg->header.cmd) {
24684cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
24691acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
24704cf49a43SJulian Elischer 		break;
24714cf49a43SJulian Elischer 	case NGM_MKPEER:
24724cf49a43SJulian Elischer 	    {
24734cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
24744cf49a43SJulian Elischer 
24754cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
24766b795970SJulian Elischer 			TRAP_ERROR();
2477069154d5SJulian Elischer 			error = EINVAL;
2478069154d5SJulian Elischer 			break;
24794cf49a43SJulian Elischer 		}
24804cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
24814cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
24824cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
24834cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
24844cf49a43SJulian Elischer 		break;
24854cf49a43SJulian Elischer 	    }
24864cf49a43SJulian Elischer 	case NGM_CONNECT:
24874cf49a43SJulian Elischer 	    {
24884cf49a43SJulian Elischer 		struct ngm_connect *const con =
24894cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
24904cf49a43SJulian Elischer 		node_p node2;
24914cf49a43SJulian Elischer 
24924cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
24936b795970SJulian Elischer 			TRAP_ERROR();
2494069154d5SJulian Elischer 			error = EINVAL;
2495069154d5SJulian Elischer 			break;
24964cf49a43SJulian Elischer 		}
24974cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
24984cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
24994cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2500069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2501069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
25024cf49a43SJulian Elischer 		if (error)
25034cf49a43SJulian Elischer 			break;
2504e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2505e088dd4cSAlexander Motin 		    node2, con->peerhook);
250630400f03SJulian Elischer 		NG_NODE_UNREF(node2);
25074cf49a43SJulian Elischer 		break;
25084cf49a43SJulian Elischer 	    }
25094cf49a43SJulian Elischer 	case NGM_NAME:
25104cf49a43SJulian Elischer 	    {
25114cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
25124cf49a43SJulian Elischer 
25134cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
25146b795970SJulian Elischer 			TRAP_ERROR();
2515069154d5SJulian Elischer 			error = EINVAL;
2516069154d5SJulian Elischer 			break;
25174cf49a43SJulian Elischer 		}
25184cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
25194cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
25204cf49a43SJulian Elischer 		break;
25214cf49a43SJulian Elischer 	    }
25224cf49a43SJulian Elischer 	case NGM_RMHOOK:
25234cf49a43SJulian Elischer 	    {
25244cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
25254cf49a43SJulian Elischer 		hook_p hook;
25264cf49a43SJulian Elischer 
25274cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
25286b795970SJulian Elischer 			TRAP_ERROR();
2529069154d5SJulian Elischer 			error = EINVAL;
2530069154d5SJulian Elischer 			break;
25314cf49a43SJulian Elischer 		}
25324cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2533899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
25344cf49a43SJulian Elischer 			ng_destroy_hook(hook);
25354cf49a43SJulian Elischer 		break;
25364cf49a43SJulian Elischer 	    }
25374cf49a43SJulian Elischer 	case NGM_NODEINFO:
25384cf49a43SJulian Elischer 	    {
25394cf49a43SJulian Elischer 		struct nodeinfo *ni;
25404cf49a43SJulian Elischer 
2541069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25424cf49a43SJulian Elischer 		if (resp == NULL) {
25434cf49a43SJulian Elischer 			error = ENOMEM;
25444cf49a43SJulian Elischer 			break;
25454cf49a43SJulian Elischer 		}
25464cf49a43SJulian Elischer 
25474cf49a43SJulian Elischer 		/* Fill in node info */
2548069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
254930400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
255087e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
255187e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2552dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
255330400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25544cf49a43SJulian Elischer 		break;
25554cf49a43SJulian Elischer 	    }
25564cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
25574cf49a43SJulian Elischer 	    {
255830400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
25594cf49a43SJulian Elischer 		struct hooklist *hl;
25604cf49a43SJulian Elischer 		struct nodeinfo *ni;
25614cf49a43SJulian Elischer 		hook_p hook;
25624cf49a43SJulian Elischer 
25634cf49a43SJulian Elischer 		/* Get response struct */
2564069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
25654cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2566069154d5SJulian Elischer 		if (resp == NULL) {
25674cf49a43SJulian Elischer 			error = ENOMEM;
25684cf49a43SJulian Elischer 			break;
25694cf49a43SJulian Elischer 		}
2570069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
25714cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
25724cf49a43SJulian Elischer 
25734cf49a43SJulian Elischer 		/* Fill in node info */
257430400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
257587e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
257687e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2577dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
25784cf49a43SJulian Elischer 
25794cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
25804cf49a43SJulian Elischer 		ni->hooks = 0;
258130400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
25824cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
25834cf49a43SJulian Elischer 
25844cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
25854cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
25866e551fb6SDavid E. O'Brien 				    __func__, "hooks");
25874cf49a43SJulian Elischer 				break;
25884cf49a43SJulian Elischer 			}
258930400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
25904cf49a43SJulian Elischer 				continue;
259187e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
259287e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
259330400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
259487e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
259587e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
259687e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
259787e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
259830400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
259930400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
26004cf49a43SJulian Elischer 			ni->hooks++;
26014cf49a43SJulian Elischer 		}
26024cf49a43SJulian Elischer 		break;
26034cf49a43SJulian Elischer 	    }
26044cf49a43SJulian Elischer 
26054cf49a43SJulian Elischer 	case NGM_LISTNAMES:
26064cf49a43SJulian Elischer 	case NGM_LISTNODES:
26074cf49a43SJulian Elischer 	    {
26084cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
26094cf49a43SJulian Elischer 		struct namelist *nl;
26104cf49a43SJulian Elischer 		node_p node;
2611cfea3f85SAlexander Motin 		int num = 0, i;
26124cf49a43SJulian Elischer 
2613cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
26144cf49a43SJulian Elischer 		/* Count number of nodes */
2615cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2616603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2617cfea3f85SAlexander Motin 				if (NG_NODE_IS_VALID(node) &&
2618cfea3f85SAlexander Motin 				    (unnamed || NG_NODE_HAS_NAME(node))) {
26194cf49a43SJulian Elischer 					num++;
26204cf49a43SJulian Elischer 				}
262170de87f2SJulian Elischer 			}
2622cfea3f85SAlexander Motin 		}
2623cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26244cf49a43SJulian Elischer 
26254cf49a43SJulian Elischer 		/* Get response struct */
2626069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
26274cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2628069154d5SJulian Elischer 		if (resp == NULL) {
26294cf49a43SJulian Elischer 			error = ENOMEM;
26304cf49a43SJulian Elischer 			break;
26314cf49a43SJulian Elischer 		}
2632069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
26334cf49a43SJulian Elischer 
26344cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
26354cf49a43SJulian Elischer 		nl->numnames = 0;
2636cfea3f85SAlexander Motin 		mtx_lock(&ng_namehash_mtx);
2637cfea3f85SAlexander Motin 		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2638603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2639cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2640cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
26414cf49a43SJulian Elischer 
2642b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2643b96baf0aSGleb Smirnoff 					continue;
2644b96baf0aSGleb Smirnoff 				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2645b96baf0aSGleb Smirnoff 					continue;
26464cf49a43SJulian Elischer 				if (nl->numnames >= num) {
2647cfea3f85SAlexander Motin 					log(LOG_ERR, "%s: number of nodes changed\n",
2648cfea3f85SAlexander Motin 					    __func__);
26494cf49a43SJulian Elischer 					break;
26504cf49a43SJulian Elischer 				}
265130400f03SJulian Elischer 				if (NG_NODE_HAS_NAME(node))
265287e2c66aSHartmut Brandt 					strcpy(np->name, NG_NODE_NAME(node));
265387e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2654dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
265530400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
26564cf49a43SJulian Elischer 				nl->numnames++;
26574cf49a43SJulian Elischer 			}
2658cfea3f85SAlexander Motin 		}
2659cfea3f85SAlexander Motin 		mtx_unlock(&ng_namehash_mtx);
26604cf49a43SJulian Elischer 		break;
26614cf49a43SJulian Elischer 	    }
26624cf49a43SJulian Elischer 
26634cf49a43SJulian Elischer 	case NGM_LISTTYPES:
26644cf49a43SJulian Elischer 	    {
26654cf49a43SJulian Elischer 		struct typelist *tl;
26664cf49a43SJulian Elischer 		struct ng_type *type;
26674cf49a43SJulian Elischer 		int num = 0;
26684cf49a43SJulian Elischer 
26699ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
26704cf49a43SJulian Elischer 		/* Count number of types */
267170de87f2SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26724cf49a43SJulian Elischer 			num++;
267370de87f2SJulian Elischer 		}
26749ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
26754cf49a43SJulian Elischer 
26764cf49a43SJulian Elischer 		/* Get response struct */
2677069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
26784cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2679069154d5SJulian Elischer 		if (resp == NULL) {
26804cf49a43SJulian Elischer 			error = ENOMEM;
26814cf49a43SJulian Elischer 			break;
26824cf49a43SJulian Elischer 		}
2683069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
26844cf49a43SJulian Elischer 
26854cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
26864cf49a43SJulian Elischer 		tl->numtypes = 0;
26879ed346baSBosko Milekic 		mtx_lock(&ng_typelist_mtx);
2688069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
26894cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
26904cf49a43SJulian Elischer 
26914cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
26924cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26936e551fb6SDavid E. O'Brien 				    __func__, "types");
26944cf49a43SJulian Elischer 				break;
26954cf49a43SJulian Elischer 			}
269687e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2697c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
26984cf49a43SJulian Elischer 			tl->numtypes++;
26994cf49a43SJulian Elischer 		}
27009ed346baSBosko Milekic 		mtx_unlock(&ng_typelist_mtx);
27014cf49a43SJulian Elischer 		break;
27024cf49a43SJulian Elischer 	    }
27034cf49a43SJulian Elischer 
2704f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2705f8307e12SArchie Cobbs 	    {
27067133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2707f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2708f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2709069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2710f8307e12SArchie Cobbs 
2711f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2712f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
27134c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
27144c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
27154c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
27166b795970SJulian Elischer 			TRAP_ERROR();
2717f8307e12SArchie Cobbs 			error = EINVAL;
2718f8307e12SArchie Cobbs 			break;
2719f8307e12SArchie Cobbs 		}
2720f8307e12SArchie Cobbs 
2721f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2722069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2723069154d5SJulian Elischer 		if (resp == NULL) {
2724f8307e12SArchie Cobbs 			error = ENOMEM;
2725f8307e12SArchie Cobbs 			break;
2726f8307e12SArchie Cobbs 		}
2727069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2728f8307e12SArchie Cobbs 
2729f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2730f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2731f8307e12SArchie Cobbs 
2732f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
273330400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2734f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2735f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2736f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2737f8307e12SArchie Cobbs 				break;
2738f8307e12SArchie Cobbs 		}
2739f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2740f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2741f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2742f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2743f8307e12SArchie Cobbs 					break;
2744f8307e12SArchie Cobbs 			}
2745f8307e12SArchie Cobbs 			if (c->name == NULL) {
2746069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2747f8307e12SArchie Cobbs 				error = ENOSYS;
2748f8307e12SArchie Cobbs 				break;
2749f8307e12SArchie Cobbs 			}
2750f8307e12SArchie Cobbs 		}
2751f8307e12SArchie Cobbs 
2752f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2753f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2754f8307e12SArchie Cobbs 		    "%s", c->name);
2755f8307e12SArchie Cobbs 
2756f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2757f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2758f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
275970de87f2SJulian Elischer 		if (argstype == NULL) {
2760f8307e12SArchie Cobbs 			*ascii->data = '\0';
276170de87f2SJulian Elischer 		} else {
2762f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2763f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2764f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2765069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2766f8307e12SArchie Cobbs 				break;
2767f8307e12SArchie Cobbs 			}
2768f8307e12SArchie Cobbs 		}
2769f8307e12SArchie Cobbs 
2770f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2771f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2772f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2773069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2774f8307e12SArchie Cobbs 		break;
2775f8307e12SArchie Cobbs 	    }
2776f8307e12SArchie Cobbs 
2777f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2778f8307e12SArchie Cobbs 	    {
2779f8307e12SArchie Cobbs 		int bufSize = 2000;	/* XXX hard coded constant */
2780f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2781f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2782069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
278352ec4a03SArchie Cobbs 		int off = 0;
2784f8307e12SArchie Cobbs 
2785f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2786f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
27874c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
27884c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
27894c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
27904c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
27916b795970SJulian Elischer 			TRAP_ERROR();
2792f8307e12SArchie Cobbs 			error = EINVAL;
2793f8307e12SArchie Cobbs 			break;
2794f8307e12SArchie Cobbs 		}
2795f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2796f8307e12SArchie Cobbs 
2797f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2798069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2799069154d5SJulian Elischer 		if (resp == NULL) {
2800f8307e12SArchie Cobbs 			error = ENOMEM;
2801f8307e12SArchie Cobbs 			break;
2802f8307e12SArchie Cobbs 		}
2803069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2804f8307e12SArchie Cobbs 
2805f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2806f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2807f8307e12SArchie Cobbs 
2808f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
280930400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2810f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2811f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2812f8307e12SArchie Cobbs 				break;
2813f8307e12SArchie Cobbs 		}
2814f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2815f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2816f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2817f8307e12SArchie Cobbs 					break;
2818f8307e12SArchie Cobbs 			}
2819f8307e12SArchie Cobbs 			if (c->name == NULL) {
2820069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2821f8307e12SArchie Cobbs 				error = ENOSYS;
2822f8307e12SArchie Cobbs 				break;
2823f8307e12SArchie Cobbs 			}
2824f8307e12SArchie Cobbs 		}
2825f8307e12SArchie Cobbs 
2826f8307e12SArchie Cobbs 		/* Convert command name to binary */
2827f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2828f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2829f8307e12SArchie Cobbs 
2830f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2831f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2832f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
283370de87f2SJulian Elischer 		if (argstype == NULL) {
2834f8307e12SArchie Cobbs 			bufSize = 0;
283570de87f2SJulian Elischer 		} else {
2836f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2837f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2838069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2839f8307e12SArchie Cobbs 				break;
2840f8307e12SArchie Cobbs 			}
2841f8307e12SArchie Cobbs 		}
2842f8307e12SArchie Cobbs 
2843f8307e12SArchie Cobbs 		/* Return the result */
2844f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2845069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2846f8307e12SArchie Cobbs 		break;
2847f8307e12SArchie Cobbs 	    }
2848f8307e12SArchie Cobbs 
28497095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
28504cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
28514cf49a43SJulian Elischer 		/*
28524cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
28534cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2854069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
28554cf49a43SJulian Elischer 		 * when control passes back to us.
28564cf49a43SJulian Elischer 		 */
285730400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2858069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
285930400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
28604cf49a43SJulian Elischer 		}
28614cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
28624cf49a43SJulian Elischer 	default:
28636b795970SJulian Elischer 		TRAP_ERROR();
28644cf49a43SJulian Elischer 		error = EINVAL;
28654cf49a43SJulian Elischer 	}
2866069154d5SJulian Elischer 	/*
2867069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2868069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2869069154d5SJulian Elischer 	 * Don't free it if it is so.
2870069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2871069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2872069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2873069154d5SJulian Elischer 	 */
2874069154d5SJulian Elischer out:
2875069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2876069154d5SJulian Elischer 	NG_FREE_MSG(msg);
28774cf49a43SJulian Elischer 	return (error);
28784cf49a43SJulian Elischer }
28794cf49a43SJulian Elischer 
28804cf49a43SJulian Elischer /************************************************************************
28818253c060SGleb Smirnoff 			Queue element get/free routines
28828253c060SGleb Smirnoff ************************************************************************/
28838253c060SGleb Smirnoff 
28848253c060SGleb Smirnoff uma_zone_t			ng_qzone;
28856aa6d011SAlexander Motin uma_zone_t			ng_qdzone;
2886f2fbb838SAlexander Motin static int			numthreads = 0; /* number of queue threads */
2887ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2888ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
28898253c060SGleb Smirnoff 
2890f2fbb838SAlexander Motin TUNABLE_INT("net.graph.threads", &numthreads);
2891f2fbb838SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2892f2fbb838SAlexander Motin     0, "Number of queue processing threads");
28938253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
28948253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
28956aa6d011SAlexander Motin     0, "Maximum number of non-data queue items to allocate");
2896ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
28976aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
28986aa6d011SAlexander Motin     0, "Maximum number of data queue items to allocate");
28998253c060SGleb Smirnoff 
29008253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29018253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
29028253c060SGleb Smirnoff static int			allocated;	/* number of items malloc'd */
29038253c060SGleb Smirnoff #endif
29048253c060SGleb Smirnoff 
29058253c060SGleb Smirnoff /*
29068253c060SGleb Smirnoff  * Get a queue entry.
29078253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
29088253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
29098253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
29108253c060SGleb Smirnoff  * an interrupt.
29118253c060SGleb Smirnoff  */
29128253c060SGleb Smirnoff static __inline item_p
29136aa6d011SAlexander Motin ng_alloc_item(int type, int flags)
29148253c060SGleb Smirnoff {
29156aa6d011SAlexander Motin 	item_p item;
29168253c060SGleb Smirnoff 
29176aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29186aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
291942282202SGleb Smirnoff 
29206aa6d011SAlexander Motin 	item = uma_zalloc((type == NGQF_DATA)?ng_qdzone:ng_qzone,
29216aa6d011SAlexander Motin 	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
29228253c060SGleb Smirnoff 
29238253c060SGleb Smirnoff 	if (item) {
29246aa6d011SAlexander Motin 		item->el_flags = type;
29256aa6d011SAlexander Motin #ifdef	NETGRAPH_DEBUG
29268253c060SGleb Smirnoff 		mtx_lock(&ngq_mtx);
29278253c060SGleb Smirnoff 		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
29288253c060SGleb Smirnoff 		allocated++;
29298253c060SGleb Smirnoff 		mtx_unlock(&ngq_mtx);
29308253c060SGleb Smirnoff #endif
29316aa6d011SAlexander Motin 	}
29328253c060SGleb Smirnoff 
29338253c060SGleb Smirnoff 	return (item);
29348253c060SGleb Smirnoff }
29358253c060SGleb Smirnoff 
29368253c060SGleb Smirnoff /*
29378253c060SGleb Smirnoff  * Release a queue entry
29388253c060SGleb Smirnoff  */
29398253c060SGleb Smirnoff void
29408253c060SGleb Smirnoff ng_free_item(item_p item)
29418253c060SGleb Smirnoff {
29428253c060SGleb Smirnoff 	/*
29438253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
29448253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
29458253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
29468253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
29478253c060SGleb Smirnoff 	 * or we release them again here.
29488253c060SGleb Smirnoff 	 */
29498253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
29508253c060SGleb Smirnoff 	case NGQF_DATA:
29518253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
29528253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
29538253c060SGleb Smirnoff 		break;
29548253c060SGleb Smirnoff 	case NGQF_MESG:
29558253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
29568253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
29578253c060SGleb Smirnoff 		break;
29588253c060SGleb Smirnoff 	case NGQF_FN:
2959e088dd4cSAlexander Motin 	case NGQF_FN2:
29608253c060SGleb Smirnoff 		/* nothing to free really, */
29618253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
29628253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
29638253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
29648253c060SGleb Smirnoff 		break;
29658253c060SGleb Smirnoff 	}
29668253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
29678253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
29688253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
29698253c060SGleb Smirnoff 
29708253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29718253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
29728253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
29738253c060SGleb Smirnoff 	allocated--;
29748253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
29758253c060SGleb Smirnoff #endif
29766aa6d011SAlexander Motin 	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA)?
29776aa6d011SAlexander Motin 	    ng_qdzone:ng_qzone, item);
29786aa6d011SAlexander Motin }
29796aa6d011SAlexander Motin 
29806aa6d011SAlexander Motin /*
29816aa6d011SAlexander Motin  * Change type of the queue entry.
29826aa6d011SAlexander Motin  * Possibly reallocates it from another UMA zone.
29836aa6d011SAlexander Motin  */
29846aa6d011SAlexander Motin static __inline item_p
29856aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags)
29866aa6d011SAlexander Motin {
29876aa6d011SAlexander Motin 	item_p item;
29886aa6d011SAlexander Motin 	int from, to;
29896aa6d011SAlexander Motin 
29906aa6d011SAlexander Motin 	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
29916aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29926aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
29936aa6d011SAlexander Motin 
29946aa6d011SAlexander Motin 	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
29956aa6d011SAlexander Motin 	to = (type == NGQF_DATA);
29966aa6d011SAlexander Motin 	if (from != to) {
29976aa6d011SAlexander Motin 		/* If reallocation is required do it and copy item. */
29986aa6d011SAlexander Motin 		if ((item = ng_alloc_item(type, flags)) == NULL) {
29996aa6d011SAlexander Motin 			ng_free_item(pitem);
30006aa6d011SAlexander Motin 			return (NULL);
30016aa6d011SAlexander Motin 		}
30026aa6d011SAlexander Motin 		*item = *pitem;
30036aa6d011SAlexander Motin 		ng_free_item(pitem);
30046aa6d011SAlexander Motin 	} else
30056aa6d011SAlexander Motin 		item = pitem;
30066aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
30076aa6d011SAlexander Motin 
30086aa6d011SAlexander Motin 	return (item);
30098253c060SGleb Smirnoff }
30108253c060SGleb Smirnoff 
30118253c060SGleb Smirnoff /************************************************************************
30124cf49a43SJulian Elischer 			Module routines
30134cf49a43SJulian Elischer ************************************************************************/
30144cf49a43SJulian Elischer 
30154cf49a43SJulian Elischer /*
30164cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
30174cf49a43SJulian Elischer  */
30184cf49a43SJulian Elischer int
30194cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
30204cf49a43SJulian Elischer {
30214cf49a43SJulian Elischer 	struct ng_type *const type = data;
30224cf49a43SJulian Elischer 	int s, error = 0;
30234cf49a43SJulian Elischer 
30244cf49a43SJulian Elischer 	switch (event) {
30254cf49a43SJulian Elischer 	case MOD_LOAD:
30264cf49a43SJulian Elischer 
30274cf49a43SJulian Elischer 		/* Register new netgraph node type */
30284cf49a43SJulian Elischer 		s = splnet();
30294cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
30304cf49a43SJulian Elischer 			splx(s);
30314cf49a43SJulian Elischer 			break;
30324cf49a43SJulian Elischer 		}
30334cf49a43SJulian Elischer 
30344cf49a43SJulian Elischer 		/* Call type specific code */
30354cf49a43SJulian Elischer 		if (type->mod_event != NULL)
3036069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
30379ed346baSBosko Milekic 				mtx_lock(&ng_typelist_mtx);
3038c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
30394cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
30409ed346baSBosko Milekic 				mtx_unlock(&ng_typelist_mtx);
3041069154d5SJulian Elischer 			}
30424cf49a43SJulian Elischer 		splx(s);
30434cf49a43SJulian Elischer 		break;
30444cf49a43SJulian Elischer 
30454cf49a43SJulian Elischer 	case MOD_UNLOAD:
30464cf49a43SJulian Elischer 		s = splnet();
3047c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
30484cf49a43SJulian Elischer 			error = EBUSY;
3049c73b94a2SJulian Elischer 		} else {
3050c73b94a2SJulian Elischer 			if (type->refs == 0) {
3051c73b94a2SJulian Elischer 				/* failed load, nothing to undo */
3052c73b94a2SJulian Elischer 				splx(s);
3053c73b94a2SJulian Elischer 				break;
3054c73b94a2SJulian Elischer 			}
30554cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
30564cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
30574cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
30584cf49a43SJulian Elischer 					splx(s);
30594cf49a43SJulian Elischer 					break;
30604cf49a43SJulian Elischer 				}
30614cf49a43SJulian Elischer 			}
30629ed346baSBosko Milekic 			mtx_lock(&ng_typelist_mtx);
30634cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
30649ed346baSBosko Milekic 			mtx_unlock(&ng_typelist_mtx);
30654cf49a43SJulian Elischer 		}
30664cf49a43SJulian Elischer 		splx(s);
30674cf49a43SJulian Elischer 		break;
30684cf49a43SJulian Elischer 
30694cf49a43SJulian Elischer 	default:
30704cf49a43SJulian Elischer 		if (type->mod_event != NULL)
30714cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
30724cf49a43SJulian Elischer 		else
30733e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
30744cf49a43SJulian Elischer 		break;
30754cf49a43SJulian Elischer 	}
30764cf49a43SJulian Elischer 	return (error);
30774cf49a43SJulian Elischer }
30784cf49a43SJulian Elischer 
3079aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS
3080aef8f344SMarko Zec static const vnet_modinfo_t vnet_netgraph_modinfo = {
3081aef8f344SMarko Zec 	.vmi_id		= VNET_MOD_NETGRAPH,
3082aef8f344SMarko Zec 	.vmi_name	= "netgraph",
3083aef8f344SMarko Zec 	.vmi_size	= sizeof(struct vnet_netgraph),
3084bc29160dSMarko Zec 	.vmi_dependson	= VNET_MOD_LOIF,
3085bc29160dSMarko Zec 	.vmi_iattach	= vnet_netgraph_iattach,
3086bc29160dSMarko Zec #ifdef VIMAGE
3087bc29160dSMarko Zec 	.vmi_idetach	= vnet_netgraph_idetach
3088aef8f344SMarko Zec #endif
3089aef8f344SMarko Zec };
3090aef8f344SMarko Zec #endif
3091aef8f344SMarko Zec 
3092aef8f344SMarko Zec static int
3093bc29160dSMarko Zec vnet_netgraph_iattach(const void *unused __unused)
3094aef8f344SMarko Zec {
3095aef8f344SMarko Zec 	INIT_VNET_NETGRAPH(curvnet);
3096aef8f344SMarko Zec 
3097aef8f344SMarko Zec 	V_nextID = 1;
3098aef8f344SMarko Zec 
3099aef8f344SMarko Zec 	return (0);
3100aef8f344SMarko Zec }
3101aef8f344SMarko Zec 
3102bc29160dSMarko Zec #ifdef VIMAGE
3103bc29160dSMarko Zec static int
3104bc29160dSMarko Zec vnet_netgraph_idetach(const void *unused __unused)
3105bc29160dSMarko Zec {
3106bc29160dSMarko Zec 	INIT_VNET_NETGRAPH(curvnet);
3107bc29160dSMarko Zec 	node_p node, last_killed = NULL;
3108bc29160dSMarko Zec 
3109bc29160dSMarko Zec 	while ((node = LIST_FIRST(&V_ng_nodelist)) != NULL) {
3110bc29160dSMarko Zec 		if (node == last_killed) {
3111bc29160dSMarko Zec 			/* This should never happen */
3112bc29160dSMarko Zec 			node->nd_flags |= NGF_REALLY_DIE;
3113bc29160dSMarko Zec 			printf("netgraph node %s needs NGF_REALLY_DIE\n",
3114bc29160dSMarko Zec 			    node->nd_name);
3115bc29160dSMarko Zec 			ng_rmnode(node, NULL, NULL, 0);
3116bc29160dSMarko Zec 			/* This must never happen */
3117bc29160dSMarko Zec 			if (node == LIST_FIRST(&V_ng_nodelist))
3118bc29160dSMarko Zec 				panic("netgraph node %s won't die",
3119bc29160dSMarko Zec 				    node->nd_name);
3120bc29160dSMarko Zec 		}
3121bc29160dSMarko Zec 		ng_rmnode(node, NULL, NULL, 0);
3122bc29160dSMarko Zec 		last_killed = node;
3123bc29160dSMarko Zec 	}
3124bc29160dSMarko Zec 
3125bc29160dSMarko Zec 	return (0);
3126bc29160dSMarko Zec }
3127bc29160dSMarko Zec #endif /* VIMAGE */
3128bc29160dSMarko Zec 
31294cf49a43SJulian Elischer /*
31304cf49a43SJulian Elischer  * Handle loading and unloading for this code.
31314cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
31324cf49a43SJulian Elischer  */
31334cf49a43SJulian Elischer static int
31344cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
31354cf49a43SJulian Elischer {
3136f2fbb838SAlexander Motin 	struct proc *p;
3137f2fbb838SAlexander Motin 	struct thread *td;
3138f2fbb838SAlexander Motin 	int i, error = 0;
31394cf49a43SJulian Elischer 
31404cf49a43SJulian Elischer 	switch (event) {
31414cf49a43SJulian Elischer 	case MOD_LOAD:
31421489164fSGleb Smirnoff 		/* Initialize everything. */
3143aef8f344SMarko Zec #ifndef VIMAGE_GLOBALS
3144aef8f344SMarko Zec 		vnet_mod_register(&vnet_netgraph_modinfo);
3145aef8f344SMarko Zec #else
3146aef8f344SMarko Zec 		vnet_netgraph_iattach(NULL);
3147aef8f344SMarko Zec #endif
31482c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3149efd8e7c9SDon Lewis 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3150efd8e7c9SDon Lewis 		    MTX_DEF);
3151efd8e7c9SDon Lewis 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3152efd8e7c9SDon Lewis 		    MTX_DEF);
3153cfea3f85SAlexander Motin 		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3154cfea3f85SAlexander Motin 		    MTX_DEF);
3155ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3156ac5dd141SGleb Smirnoff 		    MTX_DEF);
31571489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3158cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3159cfea3f85SAlexander Motin 		    MTX_DEF);
31601489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3161efd8e7c9SDon Lewis 		    MTX_DEF);
31621489164fSGleb Smirnoff #endif
31631489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
31641489164fSGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
31651489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
31666aa6d011SAlexander Motin 		ng_qdzone = uma_zcreate("NetGraph data items", sizeof(struct ng_item),
31676aa6d011SAlexander Motin 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
31686aa6d011SAlexander Motin 		uma_zone_set_max(ng_qdzone, maxdata);
3169f2fbb838SAlexander Motin 		/* Autoconfigure number of threads. */
3170f2fbb838SAlexander Motin 		if (numthreads <= 0)
3171f2fbb838SAlexander Motin 			numthreads = mp_ncpus;
3172f2fbb838SAlexander Motin 		/* Create threads. */
3173f2fbb838SAlexander Motin     		p = NULL; /* start with no process */
3174f2fbb838SAlexander Motin 		for (i = 0; i < numthreads; i++) {
3175f2fbb838SAlexander Motin 			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3176f2fbb838SAlexander Motin 			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3177f2fbb838SAlexander Motin 				numthreads = i;
3178f2fbb838SAlexander Motin 				break;
3179f2fbb838SAlexander Motin 			}
3180f2fbb838SAlexander Motin 		}
31814cf49a43SJulian Elischer 		break;
31824cf49a43SJulian Elischer 	case MOD_UNLOAD:
318364efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
31844cf49a43SJulian Elischer 		error = EBUSY;
31854cf49a43SJulian Elischer 		break;
31864cf49a43SJulian Elischer 	default:
31874cf49a43SJulian Elischer 		error = EOPNOTSUPP;
31884cf49a43SJulian Elischer 		break;
31894cf49a43SJulian Elischer 	}
31904cf49a43SJulian Elischer 	return (error);
31914cf49a43SJulian Elischer }
31924cf49a43SJulian Elischer 
31934cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
31944cf49a43SJulian Elischer 	"netgraph",
31954cf49a43SJulian Elischer 	ngb_mod_event,
31964cf49a43SJulian Elischer 	(NULL)
31974cf49a43SJulian Elischer };
3198aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
3199bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3200bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3201bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
32024cf49a43SJulian Elischer 
320330400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
320430400f03SJulian Elischer void
320530400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
320630400f03SJulian Elischer {
320730400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
320830400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
320930400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
321030400f03SJulian Elischer 		hook->lastfile, hook->lastline);
321130400f03SJulian Elischer 	if (line) {
321230400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
321330400f03SJulian Elischer 	}
321430400f03SJulian Elischer }
321530400f03SJulian Elischer 
321630400f03SJulian Elischer void
321730400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
321830400f03SJulian Elischer {
321930400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
32206b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
322130400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
322230400f03SJulian Elischer 		node->nd_refs, node->nd_name);
322330400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
322430400f03SJulian Elischer 		node->lastfile, node->lastline);
322530400f03SJulian Elischer 	if (line) {
322630400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
322730400f03SJulian Elischer 	}
322830400f03SJulian Elischer }
322930400f03SJulian Elischer 
3230069154d5SJulian Elischer void
3231069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3232069154d5SJulian Elischer {
3233069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3234069154d5SJulian Elischer 		item->lastfile, item->lastline);
32356b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
32366b795970SJulian Elischer 	case NGQF_DATA:
3237069154d5SJulian Elischer 		printf(" - [data]\n");
32386b795970SJulian Elischer 		break;
32396b795970SJulian Elischer 	case NGQF_MESG:
32406b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
32416b795970SJulian Elischer 		break;
32426b795970SJulian Elischer 	case NGQF_FN:
3243857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3244857304e6SRuslan Ermilov 			_NGI_FN(item),
3245857304e6SRuslan Ermilov 			_NGI_NODE(item),
3246857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3247857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3248857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3249857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3250857304e6SRuslan Ermilov 		break;
3251e088dd4cSAlexander Motin 	case NGQF_FN2:
3252eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3253857304e6SRuslan Ermilov 			_NGI_FN2(item),
32546064e568SGleb Smirnoff 			_NGI_NODE(item),
32556064e568SGleb Smirnoff 			_NGI_HOOK(item),
32566b795970SJulian Elischer 			item->body.fn.fn_arg1,
32576b795970SJulian Elischer 			item->body.fn.fn_arg2,
32586b795970SJulian Elischer 			item->body.fn.fn_arg2);
32596b795970SJulian Elischer 		break;
3260069154d5SJulian Elischer 	}
326130400f03SJulian Elischer 	if (line) {
3262069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
32636064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
326430400f03SJulian Elischer 			printf("node %p ([%x])\n",
32656064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3266069154d5SJulian Elischer 		}
326730400f03SJulian Elischer 	}
326830400f03SJulian Elischer }
326930400f03SJulian Elischer 
327030400f03SJulian Elischer static void
327130400f03SJulian Elischer ng_dumpitems(void)
327230400f03SJulian Elischer {
327330400f03SJulian Elischer 	item_p item;
327430400f03SJulian Elischer 	int i = 1;
327530400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
327630400f03SJulian Elischer 		printf("[%d] ", i++);
327730400f03SJulian Elischer 		dumpitem(item, NULL, 0);
327830400f03SJulian Elischer 	}
327930400f03SJulian Elischer }
328030400f03SJulian Elischer 
328130400f03SJulian Elischer static void
328230400f03SJulian Elischer ng_dumpnodes(void)
328330400f03SJulian Elischer {
328430400f03SJulian Elischer 	node_p node;
328530400f03SJulian Elischer 	int i = 1;
328653f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
328730400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
328830400f03SJulian Elischer 		printf("[%d] ", i++);
328930400f03SJulian Elischer 		dumpnode(node, NULL, 0);
329030400f03SJulian Elischer 	}
329153f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
329230400f03SJulian Elischer }
329330400f03SJulian Elischer 
329430400f03SJulian Elischer static void
329530400f03SJulian Elischer ng_dumphooks(void)
329630400f03SJulian Elischer {
329730400f03SJulian Elischer 	hook_p hook;
329830400f03SJulian Elischer 	int i = 1;
329953f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
330030400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
330130400f03SJulian Elischer 		printf("[%d] ", i++);
330230400f03SJulian Elischer 		dumphook(hook, NULL, 0);
330330400f03SJulian Elischer 	}
330453f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
330530400f03SJulian Elischer }
3306069154d5SJulian Elischer 
3307069154d5SJulian Elischer static int
3308069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3309069154d5SJulian Elischer {
3310069154d5SJulian Elischer 	int error;
3311069154d5SJulian Elischer 	int val;
3312069154d5SJulian Elischer 	int i;
3313069154d5SJulian Elischer 
3314069154d5SJulian Elischer 	val = allocated;
3315069154d5SJulian Elischer 	i = 1;
3316041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
33176b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
33186b795970SJulian Elischer 		return (error);
33196b795970SJulian Elischer 	if (val == 42) {
332030400f03SJulian Elischer 		ng_dumpitems();
332130400f03SJulian Elischer 		ng_dumpnodes();
332230400f03SJulian Elischer 		ng_dumphooks();
3323069154d5SJulian Elischer 	}
33246b795970SJulian Elischer 	return (0);
3325069154d5SJulian Elischer }
3326069154d5SJulian Elischer 
33276b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
33286b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
332930400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3330069154d5SJulian Elischer 
3331069154d5SJulian Elischer 
3332069154d5SJulian Elischer /***********************************************************************
3333069154d5SJulian Elischer * Worklist routines
3334069154d5SJulian Elischer **********************************************************************/
3335069154d5SJulian Elischer /*
3336069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3337f2fbb838SAlexander Motin  * try get an item to process off it. Remove the node from the list.
3338069154d5SJulian Elischer  */
3339069154d5SJulian Elischer static void
3340f2fbb838SAlexander Motin ngthread(void *arg)
3341069154d5SJulian Elischer {
3342069154d5SJulian Elischer 	for (;;) {
3343394cb30aSAlexander Motin 		node_p  node;
3344394cb30aSAlexander Motin 
3345394cb30aSAlexander Motin 		/* Get node from the worklist. */
33462c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
3347f2fbb838SAlexander Motin 		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3348f2fbb838SAlexander Motin 			NG_WORKLIST_SLEEP();
33499852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
33502c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3351bc29160dSMarko Zec 		CURVNET_SET(node->nd_vnet);
33522955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
33532955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3354069154d5SJulian Elischer 		/*
3355069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3356069154d5SJulian Elischer 		 * that the list had on it.
3357069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3358069154d5SJulian Elischer 		 * let you have another item off the queue.
3359069154d5SJulian Elischer 		 * All this time, keep the reference
3360069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3361069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3362069154d5SJulian Elischer 		 */
3363069154d5SJulian Elischer 		for (;;) {
3364394cb30aSAlexander Motin 			item_p item;
3365714fb865SGleb Smirnoff 			int rw;
3366714fb865SGleb Smirnoff 
33672c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
33689852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3369069154d5SJulian Elischer 			if (item == NULL) {
33709852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
33712c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3372069154d5SJulian Elischer 				break; /* go look for another node */
3373069154d5SJulian Elischer 			} else {
33742c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
33755951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3376714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
33775951069aSJulian Elischer 				NG_NODE_UNREF(node);
3378069154d5SJulian Elischer 			}
3379069154d5SJulian Elischer 		}
3380a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3381bc29160dSMarko Zec 		CURVNET_RESTORE();
3382069154d5SJulian Elischer 	}
3383069154d5SJulian Elischer }
3384069154d5SJulian Elischer 
338533338e73SJulian Elischer /*
338633338e73SJulian Elischer  * XXX
338733338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
338833338e73SJulian Elischer  * to be outside the mutex zone
338933338e73SJulian Elischer  */
3390069154d5SJulian Elischer static void
3391394cb30aSAlexander Motin ng_worklist_add(node_p node)
3392069154d5SJulian Elischer {
3393f912c0f7SGleb Smirnoff 
33945bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3395f912c0f7SGleb Smirnoff 
33969852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3397069154d5SJulian Elischer 		/*
3398069154d5SJulian Elischer 		 * If we are not already on the work queue,
3399069154d5SJulian Elischer 		 * then put us on.
3400069154d5SJulian Elischer 		 */
34019852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3402394cb30aSAlexander Motin 		NG_NODE_REF(node); /* XXX fafe in mutex? */
34032c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
34049852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
34052c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
34062955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
34072955ee18SGleb Smirnoff 		    node->nd_ID, node);
3408f2fbb838SAlexander Motin 		NG_WORKLIST_WAKEUP();
3409394cb30aSAlexander Motin 	} else {
34102955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
34112955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3412394cb30aSAlexander Motin 	}
3413069154d5SJulian Elischer }
3414069154d5SJulian Elischer 
3415069154d5SJulian Elischer 
3416069154d5SJulian Elischer /***********************************************************************
3417069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3418069154d5SJulian Elischer ***********************************************************************/
3419069154d5SJulian Elischer 
342030400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
342130400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
34224cf49a43SJulian Elischer 	do {								\
34231acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3424069154d5SJulian Elischer 			printf("item already has node");		\
34253de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
34261acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3427069154d5SJulian Elischer 		}							\
34281acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3429069154d5SJulian Elischer 			printf("item already has hook");		\
34303de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
34311acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3432069154d5SJulian Elischer 		}							\
3433069154d5SJulian Elischer 	} while (0)
3434069154d5SJulian Elischer #else
343530400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3436069154d5SJulian Elischer #endif
3437069154d5SJulian Elischer 
3438069154d5SJulian Elischer /*
34398ed370fdSJulian Elischer  * Put mbuf into the item.
3440069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3441069154d5SJulian Elischer  * (or equivalent)
3442069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3443069154d5SJulian Elischer  * remote node might go away in this timescale.
3444069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3445069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
34464be59335SGleb Smirnoff  * here to be able to do this.
3447069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3448069154d5SJulian Elischer  *
3449069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3450069154d5SJulian Elischer  */
3451069154d5SJulian Elischer item_p
345242282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3453069154d5SJulian Elischer {
3454069154d5SJulian Elischer 	item_p item;
3455069154d5SJulian Elischer 
34566aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3457069154d5SJulian Elischer 		NG_FREE_M(m);
3458069154d5SJulian Elischer 		return (NULL);
3459069154d5SJulian Elischer 	}
346030400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34616aa6d011SAlexander Motin 	item->el_flags |= NGQF_READER;
3462069154d5SJulian Elischer 	NGI_M(item) = m;
3463069154d5SJulian Elischer 	return (item);
3464069154d5SJulian Elischer }
3465069154d5SJulian Elischer 
3466069154d5SJulian Elischer /*
3467069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3468069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3469069154d5SJulian Elischer  * to work out what some of the fields should be.
3470069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3471069154d5SJulian Elischer  * (or equivalent)
3472069154d5SJulian Elischer  */
3473069154d5SJulian Elischer item_p
347442282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3475069154d5SJulian Elischer {
3476069154d5SJulian Elischer 	item_p item;
3477069154d5SJulian Elischer 
34786aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3479069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3480069154d5SJulian Elischer 		return (NULL);
3481069154d5SJulian Elischer 	}
348230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
34836f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
34846f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
34856aa6d011SAlexander Motin 		item->el_flags |= NGQF_READER;
34866f683eeeSGleb Smirnoff 	else
34876aa6d011SAlexander Motin 		item->el_flags |= NGQF_WRITER;
3488069154d5SJulian Elischer 	/*
3489069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3490069154d5SJulian Elischer 	 */
3491069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3492facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3493069154d5SJulian Elischer 	return (item);
3494069154d5SJulian Elischer }
3495069154d5SJulian Elischer 
3496069154d5SJulian Elischer 
3497069154d5SJulian Elischer 
34981acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
34996b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
35006b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3501069154d5SJulian Elischer 			if (retaddr) {					\
3502069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
35034cf49a43SJulian Elischer 			} else {					\
3504069154d5SJulian Elischer 				/*					\
3505069154d5SJulian Elischer 				 * The old return address should be ok.	\
3506069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3507069154d5SJulian Elischer 				 * here.				\
3508069154d5SJulian Elischer 				 */					\
3509069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3510069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3511069154d5SJulian Elischer 						= ng_node2ID(here);	\
3512069154d5SJulian Elischer 				}					\
3513069154d5SJulian Elischer 			}						\
35144cf49a43SJulian Elischer 		}							\
35154cf49a43SJulian Elischer 	} while (0)
35164cf49a43SJulian Elischer 
35174cf49a43SJulian Elischer int
3518069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
35194cf49a43SJulian Elischer {
35201acb27c6SJulian Elischer 	hook_p peer;
35211acb27c6SJulian Elischer 	node_p peernode;
352230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3523069154d5SJulian Elischer 	/*
3524069154d5SJulian Elischer 	 * Quick sanity check..
352530400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
352630400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
352730400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3528069154d5SJulian Elischer 	 */
3529a04e9846SAlexander Motin 	if ((hook == NULL) ||
3530a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(hook) ||
3531a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3532a04e9846SAlexander Motin 	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3533069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35346b795970SJulian Elischer 		TRAP_ERROR();
3535e08d3e3cSJulian Elischer 		return (ENETDOWN);
35364cf49a43SJulian Elischer 	}
35374cf49a43SJulian Elischer 
35384cf49a43SJulian Elischer 	/*
3539069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
35404cf49a43SJulian Elischer 	 */
35411acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
35421acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
3543a04e9846SAlexander Motin 	NGI_SET_HOOK(item, peer);
35441acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
35458b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3546069154d5SJulian Elischer 	return (0);
3547069154d5SJulian Elischer }
3548069154d5SJulian Elischer 
35494cf49a43SJulian Elischer int
3550069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
35514cf49a43SJulian Elischer {
35524cf49a43SJulian Elischer 	node_p	dest = NULL;
3553069154d5SJulian Elischer 	hook_p	hook = NULL;
35544cf49a43SJulian Elischer 	int	error;
3555069154d5SJulian Elischer 
355630400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3557069154d5SJulian Elischer 	/*
3558069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3559069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3560069154d5SJulian Elischer 	 */
3561069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3562069154d5SJulian Elischer 	if (error) {
3563069154d5SJulian Elischer 		NG_FREE_ITEM(item);
356430400f03SJulian Elischer 		return (error);
3565069154d5SJulian Elischer 	}
35661acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
35671acb27c6SJulian Elischer 	if ( hook) {
356830400f03SJulian Elischer 		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
35691acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
35701acb27c6SJulian Elischer 	}
35711acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3572069154d5SJulian Elischer 	return (0);
3573069154d5SJulian Elischer }
3574069154d5SJulian Elischer 
3575069154d5SJulian Elischer int
3576069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3577069154d5SJulian Elischer {
3578069154d5SJulian Elischer 	node_p dest;
3579069154d5SJulian Elischer 
358030400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3581069154d5SJulian Elischer 	/*
3582069154d5SJulian Elischer 	 * Find the target node.
3583069154d5SJulian Elischer 	 */
3584069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3585069154d5SJulian Elischer 	if (dest == NULL) {
3586069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35876b795970SJulian Elischer 		TRAP_ERROR();
3588069154d5SJulian Elischer 		return(EINVAL);
3589069154d5SJulian Elischer 	}
3590069154d5SJulian Elischer 	/* Fill out the contents */
35911acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
35921acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
35931acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3594069154d5SJulian Elischer 	return (0);
3595069154d5SJulian Elischer }
3596069154d5SJulian Elischer 
3597069154d5SJulian Elischer /*
3598069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3599069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3600069154d5SJulian Elischer  * Useful for removing that hook :-)
3601069154d5SJulian Elischer  */
3602069154d5SJulian Elischer item_p
3603069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3604069154d5SJulian Elischer {
3605069154d5SJulian Elischer 	item_p item;
36064cf49a43SJulian Elischer 
3607859a4d16SJulian Elischer 	/*
3608859a4d16SJulian Elischer 	 * Find the target node.
3609859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3610859a4d16SJulian Elischer 	 * to the address.
3611859a4d16SJulian Elischer 	 */
36126aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3613069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3614069154d5SJulian Elischer 		return (NULL);
36154cf49a43SJulian Elischer 	}
36164cf49a43SJulian Elischer 
36174cf49a43SJulian Elischer 	/* Fill out the contents */
36186aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
361930400f03SJulian Elischer 	NG_NODE_REF(here);
36201acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
36211acb27c6SJulian Elischer 	if (hook) {
362230400f03SJulian Elischer 		NG_HOOK_REF(hook);
36231acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36241acb27c6SJulian Elischer 	}
3625069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3626069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3627069154d5SJulian Elischer 	return (item);
36284cf49a43SJulian Elischer }
36294cf49a43SJulian Elischer 
3630e088dd4cSAlexander Motin /*
3631e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3632e088dd4cSAlexander Motin  */
3633e088dd4cSAlexander Motin 
363442282202SGleb Smirnoff int
3635b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3636b332b91fSGleb Smirnoff {
3637b332b91fSGleb Smirnoff 
3638b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3639b332b91fSGleb Smirnoff }
3640b332b91fSGleb Smirnoff 
3641b332b91fSGleb Smirnoff int
3642aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
364342282202SGleb Smirnoff 	int flags)
36446b795970SJulian Elischer {
36456b795970SJulian Elischer 	item_p item;
36466b795970SJulian Elischer 
36476aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
36486b795970SJulian Elischer 		return (ENOMEM);
36496b795970SJulian Elischer 	}
36506aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3651a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
36521acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
36531acb27c6SJulian Elischer 	if (hook) {
36546b795970SJulian Elischer 		NG_HOOK_REF(hook);
36551acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36566b795970SJulian Elischer 	}
36576b795970SJulian Elischer 	NGI_FN(item) = fn;
36586b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
36596b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
366042282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
36616b795970SJulian Elischer }
36626b795970SJulian Elischer 
36634cf49a43SJulian Elischer /*
3664b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3665b332b91fSGleb Smirnoff  *
3666b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3667b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3668b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3669b332b91fSGleb Smirnoff  * be used.
3670e088dd4cSAlexander Motin  */
3671e088dd4cSAlexander Motin int
3672b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3673b332b91fSGleb Smirnoff 	int arg2, int flags)
3674e088dd4cSAlexander Motin {
3675e088dd4cSAlexander Motin 	item_p item;
3676e088dd4cSAlexander Motin 
3677b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3678b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3679e088dd4cSAlexander Motin 
3680e088dd4cSAlexander Motin 	/*
3681b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3682b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3683e088dd4cSAlexander Motin 	 */
3684b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
36856aa6d011SAlexander Motin 		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3686e088dd4cSAlexander Motin 			return (ENOMEM);
36876aa6d011SAlexander Motin 		if (pitem != NULL)
36886aa6d011SAlexander Motin 			item->apply = pitem->apply;
3689ed75521fSAlexander Motin 	} else {
36906aa6d011SAlexander Motin 		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
36916aa6d011SAlexander Motin 			return (ENOMEM);
3692ed75521fSAlexander Motin 	}
3693b332b91fSGleb Smirnoff 
36946aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3695e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3696e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3697e088dd4cSAlexander Motin 	if (hook) {
3698e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3699e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3700e088dd4cSAlexander Motin 	}
3701e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3702e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3703e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3704e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3705e088dd4cSAlexander Motin }
3706e088dd4cSAlexander Motin 
3707e088dd4cSAlexander Motin /*
3708d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3709d2ca21a9SJulian Elischer  */
3710d2ca21a9SJulian Elischer static void
37111fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3712d2ca21a9SJulian Elischer {
3713d2ca21a9SJulian Elischer 	item_p item = arg;
3714d2ca21a9SJulian Elischer 
3715bc29160dSMarko Zec 	CURVNET_SET(NGI_NODE(item)->nd_vnet);
3716d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3717bc29160dSMarko Zec 	CURVNET_RESTORE();
3718d2ca21a9SJulian Elischer }
3719d2ca21a9SJulian Elischer 
3720d2ca21a9SJulian Elischer 
372130bef41bSGleb Smirnoff int
3722f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3723d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3724d2ca21a9SJulian Elischer {
37251bf8e0faSGleb Smirnoff 	item_p item, oitem;
3726d2ca21a9SJulian Elischer 
37276aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
372830bef41bSGleb Smirnoff 		return (ENOMEM);
372930bef41bSGleb Smirnoff 
37306aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3731d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3732d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3733d2ca21a9SJulian Elischer 	if (hook) {
3734d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3735d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3736d2ca21a9SJulian Elischer 	}
3737d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3738d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3739d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
37401bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
37411bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
37421bf8e0faSGleb Smirnoff 	    oitem != NULL)
37431bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
374430bef41bSGleb Smirnoff 	return (0);
3745d2ca21a9SJulian Elischer }
3746d2ca21a9SJulian Elischer 
3747d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3748d2ca21a9SJulian Elischer int
3749f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3750d2ca21a9SJulian Elischer {
3751d2ca21a9SJulian Elischer 	item_p item;
375230bef41bSGleb Smirnoff 	int rval;
3753d2ca21a9SJulian Elischer 
375403b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
375503b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
375603b25f5dSGleb Smirnoff 
37573eadb26dSGleb Smirnoff 	rval = callout_stop(c);
375830bef41bSGleb Smirnoff 	item = c->c_arg;
375930bef41bSGleb Smirnoff 	/* Do an extra check */
37601fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
376130bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3762d2ca21a9SJulian Elischer 		/*
3763d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3764d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3765d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3766d2ca21a9SJulian Elischer 		 */
3767d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3768d2ca21a9SJulian Elischer 	}
37691bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
377030bef41bSGleb Smirnoff 
377130bef41bSGleb Smirnoff 	return (rval);
3772d2ca21a9SJulian Elischer }
3773d2ca21a9SJulian Elischer 
3774d2ca21a9SJulian Elischer /*
3775069154d5SJulian Elischer  * Set the address, if none given, give the node here.
37764cf49a43SJulian Elischer  */
3777069154d5SJulian Elischer void
3778069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
37794cf49a43SJulian Elischer {
3780069154d5SJulian Elischer 	if (retaddr) {
3781069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3782069154d5SJulian Elischer 	} else {
3783069154d5SJulian Elischer 		/*
3784069154d5SJulian Elischer 		 * The old return address should be ok.
3785069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3786069154d5SJulian Elischer 		 */
3787069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3788069154d5SJulian Elischer 	}
3789069154d5SJulian Elischer }
3790069154d5SJulian Elischer 
3791069154d5SJulian Elischer #define TESTING
3792069154d5SJulian Elischer #ifdef TESTING
3793069154d5SJulian Elischer /* just test all the macros */
3794069154d5SJulian Elischer void
3795069154d5SJulian Elischer ng_macro_test(item_p item);
3796069154d5SJulian Elischer void
3797069154d5SJulian Elischer ng_macro_test(item_p item)
3798069154d5SJulian Elischer {
3799069154d5SJulian Elischer 	node_p node = NULL;
3800069154d5SJulian Elischer 	hook_p hook = NULL;
38014cf49a43SJulian Elischer 	struct mbuf *m;
38024cf49a43SJulian Elischer 	struct ng_mesg *msg;
3803069154d5SJulian Elischer 	ng_ID_t retaddr;
3804069154d5SJulian Elischer 	int	error;
38054cf49a43SJulian Elischer 
3806069154d5SJulian Elischer 	NGI_GET_M(item, m);
3807069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3808069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
38098ed370fdSJulian Elischer 	NG_SEND_DATA(error, hook, m, NULL);
3810069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3811069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
381230400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3813069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3814069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3815069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3816069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
38174cf49a43SJulian Elischer }
3818069154d5SJulian Elischer #endif /* TESTING */
38194cf49a43SJulian Elischer 
3820