xref: /freebsd/sys/netgraph/ng_base.c (revision 687adb703d1bf1e8ad5a3ab3ce506b0caad97496)
1c398230bSWarner Losh /*-
24cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
34cf49a43SJulian Elischer  * All rights reserved.
44cf49a43SJulian Elischer  *
54cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
64cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
74cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
84cf49a43SJulian Elischer  * provided, however, that:
94cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
104cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
114cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
124cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
134cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
144cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
154cf49a43SJulian Elischer  *
164cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
174cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
184cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
194cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
204cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
214cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
224cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
234cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
244cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
254cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
264cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
274cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
284cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
294cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
304cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
314cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
324cf49a43SJulian Elischer  * OF SUCH DAMAGE.
334cf49a43SJulian Elischer  *
34cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
35cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
364cf49a43SJulian Elischer  *
374cf49a43SJulian Elischer  * $FreeBSD$
384cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
394cf49a43SJulian Elischer  */
404cf49a43SJulian Elischer 
414cf49a43SJulian Elischer /*
424cf49a43SJulian Elischer  * This file implements the base netgraph code.
434cf49a43SJulian Elischer  */
444cf49a43SJulian Elischer 
454cf49a43SJulian Elischer #include <sys/param.h>
46b99a7379SGleb Smirnoff #include <sys/systm.h>
4777a58296SGleb Smirnoff #include <sys/ctype.h>
48*687adb70SGleb Smirnoff #include <sys/hash.h>
49f33ca0c9SMarcel Moolenaar #include <sys/kdb.h>
504cf49a43SJulian Elischer #include <sys/kernel.h>
5119afcd98SGleb Smirnoff #include <sys/kthread.h>
523b33fbe7SGleb Smirnoff #include <sys/ktr.h>
53104a9b7eSAlexander Kabaev #include <sys/limits.h>
54c4282b74SGleb Smirnoff #include <sys/lock.h>
554cf49a43SJulian Elischer #include <sys/malloc.h>
564cf49a43SJulian Elischer #include <sys/mbuf.h>
5719afcd98SGleb Smirnoff #include <sys/proc.h>
5877a58296SGleb Smirnoff #include <sys/queue.h>
5919afcd98SGleb Smirnoff #include <sys/refcount.h>
6019afcd98SGleb Smirnoff #include <sys/rwlock.h>
6119afcd98SGleb Smirnoff #include <sys/smp.h>
62bfa7e882SJulian Elischer #include <sys/sysctl.h>
6377a58296SGleb Smirnoff #include <sys/syslog.h>
64f2fbb838SAlexander Motin #include <sys/unistd.h>
65394cb30aSAlexander Motin #include <machine/cpu.h>
664cf49a43SJulian Elischer 
674cf49a43SJulian Elischer #include <net/netisr.h>
68eddfbb76SRobert Watson #include <net/vnet.h>
694cf49a43SJulian Elischer 
704cf49a43SJulian Elischer #include <netgraph/ng_message.h>
714cf49a43SJulian Elischer #include <netgraph/netgraph.h>
72f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
734cf49a43SJulian Elischer 
749d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION);
7599ff8176SPeter Wemm 
76ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */
77ac5dd141SGleb Smirnoff static struct mtx	ng_topo_mtx;
78ac5dd141SGleb Smirnoff 
7930400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
80cfea3f85SAlexander Motin static struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
811489164fSGleb Smirnoff static struct mtx	ngq_mtx;	/* protects the queue item list */
8230400f03SJulian Elischer 
8330400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
8430400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8530400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
8630400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8730400f03SJulian Elischer 
8830400f03SJulian Elischer static void ng_dumpitems(void);
8930400f03SJulian Elischer static void ng_dumpnodes(void);
9030400f03SJulian Elischer static void ng_dumphooks(void);
9130400f03SJulian Elischer 
9230400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
93954c4772SJulian Elischer /*
94954c4772SJulian Elischer  * DEAD versions of the structures.
95954c4772SJulian Elischer  * In order to avoid races, it is sometimes neccesary to point
96954c4772SJulian Elischer  * at SOMETHING even though theoretically, the current entity is
97954c4772SJulian Elischer  * INVALID. Use these to avoid these races.
98954c4772SJulian Elischer  */
99954c4772SJulian Elischer struct ng_type ng_deadtype = {
100954c4772SJulian Elischer 	NG_ABI_VERSION,
101954c4772SJulian Elischer 	"dead",
102954c4772SJulian Elischer 	NULL,	/* modevent */
103954c4772SJulian Elischer 	NULL,	/* constructor */
104954c4772SJulian Elischer 	NULL,	/* rcvmsg */
105954c4772SJulian Elischer 	NULL,	/* shutdown */
106954c4772SJulian Elischer 	NULL,	/* newhook */
107954c4772SJulian Elischer 	NULL,	/* findhook */
108954c4772SJulian Elischer 	NULL,	/* connect */
109954c4772SJulian Elischer 	NULL,	/* rcvdata */
110954c4772SJulian Elischer 	NULL,	/* disconnect */
111954c4772SJulian Elischer 	NULL, 	/* cmdlist */
112954c4772SJulian Elischer };
11330400f03SJulian Elischer 
114954c4772SJulian Elischer struct ng_node ng_deadnode = {
115954c4772SJulian Elischer 	"dead",
116954c4772SJulian Elischer 	&ng_deadtype,
117be4252b3SJulian Elischer 	NGF_INVALID,
118954c4772SJulian Elischer 	0,	/* numhooks */
119954c4772SJulian Elischer 	NULL,	/* private */
120954c4772SJulian Elischer 	0,	/* ID */
12113e403fdSAntoine Brodin 	LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
122954c4772SJulian Elischer 	{},	/* all_nodes list entry */
123954c4772SJulian Elischer 	{},	/* id hashtable list entry */
124954c4772SJulian Elischer 	{	0,
1259852972bSAlexander Motin 		0,
126954c4772SJulian Elischer 		{}, /* should never use! (should hang) */
1279852972bSAlexander Motin 		{}, /* workqueue entry */
1289852972bSAlexander Motin 		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
129954c4772SJulian Elischer 	},
1309852972bSAlexander Motin 	1,	/* refs */
131a40b7874SMarko Zec 	NULL,	/* vnet */
132954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
133954c4772SJulian Elischer 	ND_MAGIC,
134954c4772SJulian Elischer 	__FILE__,
135954c4772SJulian Elischer 	__LINE__,
136954c4772SJulian Elischer 	{NULL}
137954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
138954c4772SJulian Elischer };
139954c4772SJulian Elischer 
140954c4772SJulian Elischer struct ng_hook ng_deadhook = {
141954c4772SJulian Elischer 	"dead",
142954c4772SJulian Elischer 	NULL,		/* private */
143954c4772SJulian Elischer 	HK_INVALID | HK_DEAD,
144e58d779dSGleb Smirnoff 	0,		/* undefined data link type */
145954c4772SJulian Elischer 	&ng_deadhook,	/* Peer is self */
146954c4772SJulian Elischer 	&ng_deadnode,	/* attached to deadnode */
147954c4772SJulian Elischer 	{},		/* hooks list */
148c4b5eea4SJulian Elischer 	NULL,		/* override rcvmsg() */
149c4b5eea4SJulian Elischer 	NULL,		/* override rcvdata() */
1509852972bSAlexander Motin 	1,		/* refs always >= 1 */
151954c4772SJulian Elischer #ifdef	NETGRAPH_DEBUG
152954c4772SJulian Elischer 	HK_MAGIC,
153954c4772SJulian Elischer 	__FILE__,
154954c4772SJulian Elischer 	__LINE__,
155954c4772SJulian Elischer 	{NULL}
156954c4772SJulian Elischer #endif	/* NETGRAPH_DEBUG */
157954c4772SJulian Elischer };
158954c4772SJulian Elischer 
159954c4772SJulian Elischer /*
160954c4772SJulian Elischer  * END DEAD STRUCTURES
161954c4772SJulian Elischer  */
162069154d5SJulian Elischer /* List nodes with unallocated work */
1639852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
164b57a7965SJulian Elischer static struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
1654cf49a43SJulian Elischer 
1664cf49a43SJulian Elischer /* List of installed types */
167069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
168c4282b74SGleb Smirnoff static struct rwlock	ng_typelist_lock;
169c4282b74SGleb Smirnoff #define	TYPELIST_RLOCK()	rw_rlock(&ng_typelist_lock)
170c4282b74SGleb Smirnoff #define	TYPELIST_RUNLOCK()	rw_runlock(&ng_typelist_lock)
171c4282b74SGleb Smirnoff #define	TYPELIST_WLOCK()	rw_wlock(&ng_typelist_lock)
172c4282b74SGleb Smirnoff #define	TYPELIST_WUNLOCK()	rw_wunlock(&ng_typelist_lock)
1734cf49a43SJulian Elischer 
174*687adb70SGleb Smirnoff /* Hash related definitions. */
175*687adb70SGleb Smirnoff LIST_HEAD(nodehash, ng_node);
176*687adb70SGleb Smirnoff static VNET_DEFINE(struct nodehash *, ng_ID_hash);
177*687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_ID_hmask);
178*687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_nodes);
179*687adb70SGleb Smirnoff static VNET_DEFINE(struct nodehash *, ng_name_hash);
180*687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_name_hmask);
181*687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_named_nodes);
1821e77c105SRobert Watson #define	V_ng_ID_hash		VNET(ng_ID_hash)
183*687adb70SGleb Smirnoff #define	V_ng_ID_hmask		VNET(ng_ID_hmask)
184*687adb70SGleb Smirnoff #define	V_ng_nodes		VNET(ng_nodes)
185*687adb70SGleb Smirnoff #define	V_ng_name_hash		VNET(ng_name_hash)
186*687adb70SGleb Smirnoff #define	V_ng_name_hmask		VNET(ng_name_hmask)
187*687adb70SGleb Smirnoff #define	V_ng_named_nodes	VNET(ng_named_nodes)
188eddfbb76SRobert Watson 
189c4282b74SGleb Smirnoff static struct rwlock	ng_idhash_lock;
190c4282b74SGleb Smirnoff #define	IDHASH_RLOCK()		rw_rlock(&ng_idhash_lock)
191c4282b74SGleb Smirnoff #define	IDHASH_RUNLOCK()	rw_runlock(&ng_idhash_lock)
192c4282b74SGleb Smirnoff #define	IDHASH_WLOCK()		rw_wlock(&ng_idhash_lock)
193c4282b74SGleb Smirnoff #define	IDHASH_WUNLOCK()	rw_wunlock(&ng_idhash_lock)
194c4282b74SGleb Smirnoff 
1950f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */
196*687adb70SGleb Smirnoff #define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1))
1970f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node)					\
1980f150d04SJulian Elischer 	do { 								\
199c4282b74SGleb Smirnoff 		rw_assert(&ng_idhash_lock, RA_LOCKED);			\
200603724d3SBjoern A. Zeeb 		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
2010f150d04SJulian Elischer 						nd_idnodes) {		\
2020f150d04SJulian Elischer 			if (NG_NODE_IS_VALID(node)			\
2030f150d04SJulian Elischer 			&& (NG_NODE_ID(node) == ID)) {			\
2040f150d04SJulian Elischer 				break;					\
2050f150d04SJulian Elischer 			}						\
2060f150d04SJulian Elischer 		}							\
2070f150d04SJulian Elischer 	} while (0)
208069154d5SJulian Elischer 
209c4282b74SGleb Smirnoff static struct rwlock	ng_namehash_lock;
210c4282b74SGleb Smirnoff #define	NAMEHASH_RLOCK()	rw_rlock(&ng_namehash_lock)
211c4282b74SGleb Smirnoff #define	NAMEHASH_RUNLOCK()	rw_runlock(&ng_namehash_lock)
212c4282b74SGleb Smirnoff #define	NAMEHASH_WLOCK()	rw_wlock(&ng_namehash_lock)
213c4282b74SGleb Smirnoff #define	NAMEHASH_WUNLOCK()	rw_wunlock(&ng_namehash_lock)
214dc90cad9SJulian Elischer 
2154cf49a43SJulian Elischer /* Internal functions */
2164cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
217069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
218dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
2194cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
220394cb30aSAlexander Motin static void	ng_worklist_add(node_p node);
221f2fbb838SAlexander Motin static void	ngthread(void *);
22227757487SGleb Smirnoff static int	ng_apply_item(node_p node, item_p item, int rw);
2239852972bSAlexander Motin static void	ng_flush_input_queue(node_p node);
224069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
225e088dd4cSAlexander Motin static int	ng_con_nodes(item_p item, node_p node, const char *name,
226e088dd4cSAlexander Motin 		    node_p node2, const char *name2);
227e088dd4cSAlexander Motin static int	ng_con_part2(node_p node, item_p item, hook_p hook);
228e088dd4cSAlexander Motin static int	ng_con_part3(node_p node, item_p item, hook_p hook);
229*687adb70SGleb Smirnoff static int	ng_mkpeer(node_p node, const char *name, const char *name2,
230*687adb70SGleb Smirnoff 		    char *type);
231*687adb70SGleb Smirnoff static void	ng_name_rehash(void);
232*687adb70SGleb Smirnoff static void	ng_ID_rehash(void);
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 /* Our own netgraph malloc type */
2444cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
245069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
246d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
247d745c852SEd Schouten     "netgraph hook structures");
248d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
249d745c852SEd Schouten     "netgraph node structures");
250d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
251d745c852SEd Schouten     "netgraph item structures");
252069154d5SJulian Elischer 
253069154d5SJulian Elischer /* Should not be visible outside this file */
25430400f03SJulian Elischer 
25530400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
2561ede983cSDag-Erling Smørgrav 	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
25730400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
2581ede983cSDag-Erling Smørgrav 	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
25930400f03SJulian Elischer 
2602c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK_INIT(n)			\
2614abab3d5SWojciech A. Koszek 	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
2622c8dda8dSWojciech A. Koszek #define	NG_QUEUE_LOCK(n)			\
2634abab3d5SWojciech A. Koszek 	mtx_lock(&(n)->q_mtx)
2642c8dda8dSWojciech A. Koszek #define	NG_QUEUE_UNLOCK(n)			\
2654abab3d5SWojciech A. Koszek 	mtx_unlock(&(n)->q_mtx)
2662c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK_INIT()			\
2674abab3d5SWojciech A. Koszek 	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
2682c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_LOCK()			\
2694abab3d5SWojciech A. Koszek 	mtx_lock(&ng_worklist_mtx)
2702c8dda8dSWojciech A. Koszek #define	NG_WORKLIST_UNLOCK()			\
2714abab3d5SWojciech A. Koszek 	mtx_unlock(&ng_worklist_mtx)
272f2fbb838SAlexander Motin #define	NG_WORKLIST_SLEEP()			\
273f2fbb838SAlexander Motin 	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
274f2fbb838SAlexander Motin #define	NG_WORKLIST_WAKEUP()			\
275f2fbb838SAlexander Motin 	wakeup_one(&ng_worklist)
2762c8dda8dSWojciech A. Koszek 
27730400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
27830400f03SJulian Elischer /*
27930400f03SJulian Elischer  * In debug mode:
28030400f03SJulian Elischer  * In an attempt to help track reference count screwups
28130400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
28230400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
28330400f03SJulian Elischer  * after they have been freed.
28430400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
28530400f03SJulian Elischer  */
28630400f03SJulian Elischer static __inline hook_p
28730400f03SJulian Elischer ng_alloc_hook(void)
28830400f03SJulian Elischer {
28930400f03SJulian Elischer 	hook_p hook;
29030400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
2919ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
29230400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
29330400f03SJulian Elischer 	if (hook) {
29430400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
29530400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
29630400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
29730400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
2989ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
29930400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
30030400f03SJulian Elischer 	} else {
3019ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
30230400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
30330400f03SJulian Elischer 		if (hook) {
30430400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
3059ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
30630400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
3079ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
30830400f03SJulian Elischer 		}
30930400f03SJulian Elischer 	}
31030400f03SJulian Elischer 	return (hook);
31130400f03SJulian Elischer }
31230400f03SJulian Elischer 
31330400f03SJulian Elischer static __inline node_p
31430400f03SJulian Elischer ng_alloc_node(void)
31530400f03SJulian Elischer {
31630400f03SJulian Elischer 	node_p node;
31730400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
3189ed346baSBosko Milekic 	mtx_lock(&ng_nodelist_mtx);
31930400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
32030400f03SJulian Elischer 	if (node) {
32130400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
32230400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
32330400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
32430400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
3259ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
32630400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
32730400f03SJulian Elischer 	} else {
3289ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);
32930400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
33030400f03SJulian Elischer 		if (node) {
33130400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
3329ed346baSBosko Milekic 			mtx_lock(&ng_nodelist_mtx);
33330400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
3349ed346baSBosko Milekic 			mtx_unlock(&ng_nodelist_mtx);
33530400f03SJulian Elischer 		}
33630400f03SJulian Elischer 	}
33730400f03SJulian Elischer 	return (node);
33830400f03SJulian Elischer }
33930400f03SJulian Elischer 
34030400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
34130400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
34230400f03SJulian Elischer 
34330400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
34430400f03SJulian Elischer 	do {								\
3459ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);				\
34630400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
34730400f03SJulian Elischer 		hook->hk_magic = 0;					\
3489ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);				\
34930400f03SJulian Elischer 	} while (0)
35030400f03SJulian Elischer 
35130400f03SJulian Elischer #define NG_FREE_NODE(node)						\
35230400f03SJulian Elischer 	do {								\
3539ed346baSBosko Milekic 		mtx_lock(&ng_nodelist_mtx);				\
35430400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
35530400f03SJulian Elischer 		node->nd_magic = 0;					\
3569ed346baSBosko Milekic 		mtx_unlock(&ng_nodelist_mtx);				\
35730400f03SJulian Elischer 	} while (0)
35830400f03SJulian Elischer 
35930400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
36030400f03SJulian Elischer 
36130400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
36230400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
36330400f03SJulian Elischer 
3641ede983cSDag-Erling Smørgrav #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
3651ede983cSDag-Erling Smørgrav #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
36630400f03SJulian Elischer 
36730400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
36830400f03SJulian Elischer 
369f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */
3704cf49a43SJulian Elischer #ifndef TRAP_ERROR
3716b795970SJulian Elischer #define TRAP_ERROR()
3724cf49a43SJulian Elischer #endif
3734cf49a43SJulian Elischer 
3743e288e62SDimitry Andric static VNET_DEFINE(ng_ID_t, nextID) = 1;
3751e77c105SRobert Watson #define	V_nextID			VNET(nextID)
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) {
6115633ca71SGleb Smirnoff 			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 {
6374cf49a43SJulian Elischer 	node_p node;
6384cf49a43SJulian Elischer 
6394cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
6404cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
6416b795970SJulian Elischer 		TRAP_ERROR();
6424cf49a43SJulian Elischer 		return (EINVAL);
6434cf49a43SJulian Elischer 	}
6444cf49a43SJulian Elischer 
6454cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
64630400f03SJulian Elischer 	NG_ALLOC_NODE(node);
6474cf49a43SJulian Elischer 	if (node == NULL) {
6486b795970SJulian Elischer 		TRAP_ERROR();
6494cf49a43SJulian Elischer 		return (ENOMEM);
6504cf49a43SJulian Elischer 	}
65130400f03SJulian Elischer 	node->nd_type = type;
652bc29160dSMarko Zec #ifdef VIMAGE
653bc29160dSMarko Zec 	node->nd_vnet = curvnet;
654bc29160dSMarko Zec #endif
65530400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
6564cf49a43SJulian Elischer 	type->refs++;
6574cf49a43SJulian Elischer 
6582c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
6599852972bSAlexander Motin 	STAILQ_INIT(&node->nd_input_queue.queue);
66030400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
6614cf49a43SJulian Elischer 
6624cf49a43SJulian Elischer 	/* Initialize hook list for new node */
66330400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
6644cf49a43SJulian Elischer 
665*687adb70SGleb Smirnoff 	/* Get an ID and put us in the hash chain. */
666c4282b74SGleb Smirnoff 	IDHASH_WLOCK();
66730400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
668069154d5SJulian Elischer 		node_p node2 = NULL;
669ac957cd2SJulian Elischer 		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
6700f150d04SJulian Elischer 
67130400f03SJulian Elischer 		/* Is there a problem with the new number? */
6720f150d04SJulian Elischer 		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
6730f150d04SJulian Elischer 		if ((node->nd_ID != 0) && (node2 == NULL)) {
67430400f03SJulian Elischer 			break;
675069154d5SJulian Elischer 		}
67630400f03SJulian Elischer 	}
677*687adb70SGleb Smirnoff 	V_ng_nodes++;
678*687adb70SGleb Smirnoff 	if (V_ng_nodes * 2 > V_ng_ID_hmask)
679*687adb70SGleb Smirnoff 		ng_ID_rehash();
6804bd1b557SGleb Smirnoff 	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
6814bd1b557SGleb Smirnoff 	    nd_idnodes);
682c4282b74SGleb Smirnoff 	IDHASH_WUNLOCK();
683dc90cad9SJulian Elischer 
6844cf49a43SJulian Elischer 	/* Done */
6854cf49a43SJulian Elischer 	*nodepp = node;
6864cf49a43SJulian Elischer 	return (0);
6874cf49a43SJulian Elischer }
6884cf49a43SJulian Elischer 
6894cf49a43SJulian Elischer /*
6904cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
69164efc707SRobert Watson  * its shutdown method, or do the default shutdown if there is
6924cf49a43SJulian Elischer  * no type-specific method.
6934cf49a43SJulian Elischer  *
69464efc707SRobert Watson  * We can only be called from a shutdown message, so we know we have
6953e4084c8SJulian Elischer  * a writer lock, and therefore exclusive access. It also means
6963e4084c8SJulian Elischer  * that we should not be on the work queue, but we check anyhow.
697069154d5SJulian Elischer  *
698069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
69964efc707SRobert Watson  * allocates a new node in which case, this one is irretrievably going away,
7003e4084c8SJulian Elischer  * or cleans up anything it needs, and just makes the node valid again,
7013e4084c8SJulian Elischer  * in which case we allow the node to survive.
7023e4084c8SJulian Elischer  *
70364efc707SRobert Watson  * XXX We need to think of how to tell a persistent node that we
7043e4084c8SJulian Elischer  * REALLY need to go away because the hardware has gone or we
7053e4084c8SJulian Elischer  * are rebooting.... etc.
7064cf49a43SJulian Elischer  */
7074cf49a43SJulian Elischer void
7081acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
7094cf49a43SJulian Elischer {
7103e4084c8SJulian Elischer 	hook_p hook;
7113e4084c8SJulian Elischer 
7124cf49a43SJulian Elischer 	/* Check if it's already shutting down */
713be4252b3SJulian Elischer 	if ((node->nd_flags & NGF_CLOSING) != 0)
7144cf49a43SJulian Elischer 		return;
7154cf49a43SJulian Elischer 
7161acb27c6SJulian Elischer 	if (node == &ng_deadnode) {
7171acb27c6SJulian Elischer 		printf ("shutdown called on deadnode\n");
7181acb27c6SJulian Elischer 		return;
7191acb27c6SJulian Elischer 	}
7201acb27c6SJulian Elischer 
7214cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
72230400f03SJulian Elischer 	NG_NODE_REF(node);
7234cf49a43SJulian Elischer 
72430400f03SJulian Elischer 	/*
72530400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
72630400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
727be4252b3SJulian Elischer 	 * note that NGF_INVALID does not do this as it's also set during
72830400f03SJulian Elischer 	 * creation
72930400f03SJulian Elischer 	 */
730be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
7314cf49a43SJulian Elischer 
732991fc65aSJulian Elischer 	/* If node has its pre-shutdown method, then call it first*/
733991fc65aSJulian Elischer 	if (node->nd_type && node->nd_type->close)
734991fc65aSJulian Elischer 		(*node->nd_type->close)(node);
735991fc65aSJulian Elischer 
7363e4084c8SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
7373e4084c8SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
7383e4084c8SJulian Elischer 		ng_destroy_hook(hook);
73930400f03SJulian Elischer 
740069154d5SJulian Elischer 	/*
741069154d5SJulian Elischer 	 * Drain the input queue forceably.
74230400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
74330400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
74430400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
745b57a7965SJulian Elischer 	 * Also removes us from worklist if needed.
746069154d5SJulian Elischer 	 */
7479852972bSAlexander Motin 	ng_flush_input_queue(node);
748069154d5SJulian Elischer 
749069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
75030400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
75130400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
75230400f03SJulian Elischer 		if (NG_NODE_IS_VALID(node)) {
75330400f03SJulian Elischer 			/*
75430400f03SJulian Elischer 			 * Well, blow me down if the node code hasn't declared
75530400f03SJulian Elischer 			 * that it doesn't want to die.
75630400f03SJulian Elischer 			 * Presumably it is a persistant node.
7571acb27c6SJulian Elischer 			 * If we REALLY want it to go away,
7581acb27c6SJulian Elischer 			 *  e.g. hardware going away,
759be4252b3SJulian Elischer 			 * Our caller should set NGF_REALLY_DIE in nd_flags.
76030400f03SJulian Elischer 			 */
761be4252b3SJulian Elischer 			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
7621acb27c6SJulian Elischer 			NG_NODE_UNREF(node); /* Assume they still have theirs */
76330400f03SJulian Elischer 			return;
7644cf49a43SJulian Elischer 		}
7651acb27c6SJulian Elischer 	} else {				/* do the default thing */
7661acb27c6SJulian Elischer 		NG_NODE_UNREF(node);
7671acb27c6SJulian Elischer 	}
7684cf49a43SJulian Elischer 
76930400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
77030400f03SJulian Elischer 
77130400f03SJulian Elischer 	/*
77230400f03SJulian Elischer 	 * Remove extra reference, possibly the last
77330400f03SJulian Elischer 	 * Possible other holders of references may include
77430400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
77530400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
77630400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
77730400f03SJulian Elischer 	 */
77830400f03SJulian Elischer 	NG_NODE_UNREF(node);
7794cf49a43SJulian Elischer }
7804cf49a43SJulian Elischer 
7815951069aSJulian Elischer /*
7825951069aSJulian Elischer  * Remove a reference to the node, possibly the last.
7835951069aSJulian Elischer  * deadnode always acts as it it were the last.
7845951069aSJulian Elischer  */
7853fbdf774SGleb Smirnoff void
78630400f03SJulian Elischer ng_unref_node(node_p node)
7874cf49a43SJulian Elischer {
7886b795970SJulian Elischer 
7893fbdf774SGleb Smirnoff 	if (node == &ng_deadnode)
7903fbdf774SGleb Smirnoff 		return;
7916b795970SJulian Elischer 
7923fbdf774SGleb Smirnoff 	if (refcount_release(&node->nd_refs)) { /* we were the last */
793069154d5SJulian Elischer 
79430400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
795c4282b74SGleb Smirnoff 		NAMEHASH_WLOCK();
796*687adb70SGleb Smirnoff 		if (NG_NODE_HAS_NAME(node)) {
797*687adb70SGleb Smirnoff 			V_ng_named_nodes--;
79830400f03SJulian Elischer 			LIST_REMOVE(node, nd_nodes);
799*687adb70SGleb Smirnoff 		}
800c4282b74SGleb Smirnoff 		NAMEHASH_WUNLOCK();
801069154d5SJulian Elischer 
802c4282b74SGleb Smirnoff 		IDHASH_WLOCK();
803*687adb70SGleb Smirnoff 		V_ng_nodes--;
80430400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
805c4282b74SGleb Smirnoff 		IDHASH_WUNLOCK();
806069154d5SJulian Elischer 
80712574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
808069154d5SJulian Elischer 		NG_FREE_NODE(node);
8094cf49a43SJulian Elischer 	}
8104cf49a43SJulian Elischer }
8114cf49a43SJulian Elischer 
8124cf49a43SJulian Elischer /************************************************************************
813dc90cad9SJulian Elischer 			Node ID handling
814dc90cad9SJulian Elischer ************************************************************************/
815dc90cad9SJulian Elischer static node_p
816069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
817dc90cad9SJulian Elischer {
81830400f03SJulian Elischer 	node_p node;
819*687adb70SGleb Smirnoff 
820c4282b74SGleb Smirnoff 	IDHASH_RLOCK();
8210f150d04SJulian Elischer 	NG_IDHASH_FIND(ID, node);
82230400f03SJulian Elischer 	if (node)
82330400f03SJulian Elischer 		NG_NODE_REF(node);
824c4282b74SGleb Smirnoff 	IDHASH_RUNLOCK();
82530400f03SJulian Elischer 	return(node);
826dc90cad9SJulian Elischer }
827dc90cad9SJulian Elischer 
828dc90cad9SJulian Elischer ng_ID_t
829dc90cad9SJulian Elischer ng_node2ID(node_p node)
830dc90cad9SJulian Elischer {
83170de87f2SJulian Elischer 	return (node ? NG_NODE_ID(node) : 0);
832dc90cad9SJulian Elischer }
833dc90cad9SJulian Elischer 
834dc90cad9SJulian Elischer /************************************************************************
8354cf49a43SJulian Elischer 			Node name handling
8364cf49a43SJulian Elischer ************************************************************************/
8374cf49a43SJulian Elischer 
8384cf49a43SJulian Elischer /*
8394bd1b557SGleb Smirnoff  * Assign a node a name.
8404cf49a43SJulian Elischer  */
8414cf49a43SJulian Elischer int
8424cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
8434cf49a43SJulian Elischer {
844*687adb70SGleb Smirnoff 	uint32_t hash;
845069154d5SJulian Elischer 	node_p node2;
846*687adb70SGleb Smirnoff 	int i;
8474cf49a43SJulian Elischer 
8484cf49a43SJulian Elischer 	/* Check the name is valid */
84987e2c66aSHartmut Brandt 	for (i = 0; i < NG_NODESIZ; i++) {
8504cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
8514cf49a43SJulian Elischer 			break;
8524cf49a43SJulian Elischer 	}
8534cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
8546b795970SJulian Elischer 		TRAP_ERROR();
8554cf49a43SJulian Elischer 		return (EINVAL);
8564cf49a43SJulian Elischer 	}
857dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
8586b795970SJulian Elischer 		TRAP_ERROR();
8594cf49a43SJulian Elischer 		return (EINVAL);
8604cf49a43SJulian Elischer 	}
8614cf49a43SJulian Elischer 
862*687adb70SGleb Smirnoff 	NAMEHASH_WLOCK();
863*687adb70SGleb Smirnoff 	if (V_ng_named_nodes * 2 > V_ng_name_hmask)
864*687adb70SGleb Smirnoff 		ng_name_rehash();
865*687adb70SGleb Smirnoff 
866*687adb70SGleb Smirnoff 	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
867*687adb70SGleb Smirnoff 	/* Check the name isn't already being used. */
868*687adb70SGleb Smirnoff 	LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes)
869*687adb70SGleb Smirnoff 		if (NG_NODE_IS_VALID(node2) &&
870*687adb70SGleb Smirnoff 		    (strcmp(NG_NODE_NAME(node2), name) == 0)) {
871*687adb70SGleb Smirnoff 			NAMEHASH_WUNLOCK();
8724cf49a43SJulian Elischer 			return (EADDRINUSE);
8734cf49a43SJulian Elischer 		}
8744cf49a43SJulian Elischer 
875*687adb70SGleb Smirnoff 	if (NG_NODE_HAS_NAME(node))
876cfea3f85SAlexander Motin 		LIST_REMOVE(node, nd_nodes);
877*687adb70SGleb Smirnoff 	else
878*687adb70SGleb Smirnoff 		V_ng_named_nodes++;
879*687adb70SGleb Smirnoff 	/* Copy it. */
880*687adb70SGleb Smirnoff 	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
881*687adb70SGleb Smirnoff 	/* Update name hash. */
882603724d3SBjoern A. Zeeb 	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
883c4282b74SGleb Smirnoff 	NAMEHASH_WUNLOCK();
884cfea3f85SAlexander Motin 
8854cf49a43SJulian Elischer 	return (0);
8864cf49a43SJulian Elischer }
8874cf49a43SJulian Elischer 
8884cf49a43SJulian Elischer /*
8894cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
8904cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
8914cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
8924cf49a43SJulian Elischer  *
8934cf49a43SJulian Elischer  * Returns the node if found, else NULL.
894069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
895e1e8f51bSRobert Watson  * Note it acquires a reference on the node so you can be sure it's still
896e1e8f51bSRobert Watson  * there.
8974cf49a43SJulian Elischer  */
8984cf49a43SJulian Elischer node_p
899069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
9004cf49a43SJulian Elischer {
901dc90cad9SJulian Elischer 	node_p node;
902dc90cad9SJulian Elischer 	ng_ID_t temp;
903cfea3f85SAlexander Motin 	int	hash;
9044cf49a43SJulian Elischer 
9054cf49a43SJulian Elischer 	/* "." means "this node" */
906069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
90730400f03SJulian Elischer 		NG_NODE_REF(here);
908069154d5SJulian Elischer 		return(here);
909069154d5SJulian Elischer 	}
9104cf49a43SJulian Elischer 
9114cf49a43SJulian Elischer 	/* Check for name-by-ID */
912dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
913069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
9144cf49a43SJulian Elischer 	}
9154cf49a43SJulian Elischer 
916*687adb70SGleb Smirnoff 	/* Find node by name. */
917*687adb70SGleb Smirnoff 	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
918c4282b74SGleb Smirnoff 	NAMEHASH_RLOCK();
919c4282b74SGleb Smirnoff 	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes)
920cfea3f85SAlexander Motin 		if (NG_NODE_IS_VALID(node) &&
921cfea3f85SAlexander Motin 		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
922c4282b74SGleb Smirnoff 			NG_NODE_REF(node);
9234cf49a43SJulian Elischer 			break;
9244cf49a43SJulian Elischer 		}
925c4282b74SGleb Smirnoff 	NAMEHASH_RUNLOCK();
926c4282b74SGleb Smirnoff 
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 */
9424bd1b557SGleb Smirnoff 	if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
9434bd1b557SGleb Smirnoff 	    (!isxdigit(name[1])))
94470de87f2SJulian Elischer 		return ((ng_ID_t)0);
9454cf49a43SJulian Elischer 
9462b70adcbSArchie Cobbs 	/* Decode number */
9472b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
9484bd1b557SGleb Smirnoff 	if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
94912f035e0SJulian Elischer 		return ((ng_ID_t)0);
9504bd1b557SGleb Smirnoff 
9514bd1b557SGleb Smirnoff 	return ((ng_ID_t)val);
9524cf49a43SJulian Elischer }
9534cf49a43SJulian Elischer 
9544cf49a43SJulian Elischer /*
9554cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
9564cf49a43SJulian Elischer  * when shutting down and removing the node.
9574cf49a43SJulian Elischer  */
9584cf49a43SJulian Elischer void
9594cf49a43SJulian Elischer ng_unname(node_p node)
9604cf49a43SJulian Elischer {
9614cf49a43SJulian Elischer }
9624cf49a43SJulian Elischer 
963*687adb70SGleb Smirnoff /*
964*687adb70SGleb Smirnoff  * Allocate a bigger name hash.
965*687adb70SGleb Smirnoff  */
966*687adb70SGleb Smirnoff static void
967*687adb70SGleb Smirnoff ng_name_rehash()
968*687adb70SGleb Smirnoff {
969*687adb70SGleb Smirnoff 	struct nodehash *new;
970*687adb70SGleb Smirnoff 	uint32_t hash;
971*687adb70SGleb Smirnoff 	u_long hmask;
972*687adb70SGleb Smirnoff 	node_p node, node2;
973*687adb70SGleb Smirnoff 	int i;
974*687adb70SGleb Smirnoff 
975*687adb70SGleb Smirnoff 	new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
976*687adb70SGleb Smirnoff 	    HASH_NOWAIT);
977*687adb70SGleb Smirnoff 	if (new == NULL)
978*687adb70SGleb Smirnoff 		return;
979*687adb70SGleb Smirnoff 
980*687adb70SGleb Smirnoff 	for (i = 0; i <= V_ng_name_hmask; i++)
981*687adb70SGleb Smirnoff 		LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) {
982*687adb70SGleb Smirnoff #ifdef INVARIANTS
983*687adb70SGleb Smirnoff 			LIST_REMOVE(node, nd_nodes);
984*687adb70SGleb Smirnoff #endif
985*687adb70SGleb Smirnoff 			hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask;
986*687adb70SGleb Smirnoff 			LIST_INSERT_HEAD(&new[hash], node, nd_nodes);
987*687adb70SGleb Smirnoff 		}
988*687adb70SGleb Smirnoff 
989*687adb70SGleb Smirnoff 	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
990*687adb70SGleb Smirnoff 	V_ng_name_hash = new;
991*687adb70SGleb Smirnoff 	V_ng_name_hmask = hmask;
992*687adb70SGleb Smirnoff }
993*687adb70SGleb Smirnoff 
994*687adb70SGleb Smirnoff /*
995*687adb70SGleb Smirnoff  * Allocate a bigger ID hash.
996*687adb70SGleb Smirnoff  */
997*687adb70SGleb Smirnoff static void
998*687adb70SGleb Smirnoff ng_ID_rehash()
999*687adb70SGleb Smirnoff {
1000*687adb70SGleb Smirnoff 	struct nodehash *new;
1001*687adb70SGleb Smirnoff 	uint32_t hash;
1002*687adb70SGleb Smirnoff 	u_long hmask;
1003*687adb70SGleb Smirnoff 	node_p node, node2;
1004*687adb70SGleb Smirnoff 	int i;
1005*687adb70SGleb Smirnoff 
1006*687adb70SGleb Smirnoff 	new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
1007*687adb70SGleb Smirnoff 	    HASH_NOWAIT);
1008*687adb70SGleb Smirnoff 	if (new == NULL)
1009*687adb70SGleb Smirnoff 		return;
1010*687adb70SGleb Smirnoff 
1011*687adb70SGleb Smirnoff 	for (i = 0; i <= V_ng_ID_hmask; i++)
1012*687adb70SGleb Smirnoff 		LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) {
1013*687adb70SGleb Smirnoff #ifdef INVARIANTS
1014*687adb70SGleb Smirnoff 			LIST_REMOVE(node, nd_idnodes);
1015*687adb70SGleb Smirnoff #endif
1016*687adb70SGleb Smirnoff 			hash = (node->nd_ID % (hmask + 1));
1017*687adb70SGleb Smirnoff 			LIST_INSERT_HEAD(&new[hash], node, nd_idnodes);
1018*687adb70SGleb Smirnoff 		}
1019*687adb70SGleb Smirnoff 
1020*687adb70SGleb Smirnoff 	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
1021*687adb70SGleb Smirnoff 	V_ng_ID_hash = new;
1022*687adb70SGleb Smirnoff 	V_ng_ID_hmask = hmask;
1023*687adb70SGleb Smirnoff }
1024*687adb70SGleb Smirnoff 
10254cf49a43SJulian Elischer /************************************************************************
10264cf49a43SJulian Elischer 			Hook routines
10274cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
10283e4084c8SJulian Elischer  brief moment within these routines. On invalidation or during creation
10293e4084c8SJulian Elischer  they are connected to the 'dead' hook.
10304cf49a43SJulian Elischer ************************************************************************/
10314cf49a43SJulian Elischer 
10324cf49a43SJulian Elischer /*
10334cf49a43SJulian Elischer  * Remove a hook reference
10344cf49a43SJulian Elischer  */
103530400f03SJulian Elischer void
10364cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
10374cf49a43SJulian Elischer {
10386b795970SJulian Elischer 
10393fbdf774SGleb Smirnoff 	if (hook == &ng_deadhook)
10406b795970SJulian Elischer 		return;
1041018fe3d1SAlexander Motin 
10423fbdf774SGleb Smirnoff 	if (refcount_release(&hook->hk_refs)) { /* we were the last */
1043f573da1aSAlexander Motin 		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
10446b795970SJulian Elischer 			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
1045069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
1046069154d5SJulian Elischer 	}
10474cf49a43SJulian Elischer }
10484cf49a43SJulian Elischer 
10494cf49a43SJulian Elischer /*
10504cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
10513e4084c8SJulian Elischer  * Assumes node is locked. (XXX not yet true )
10524cf49a43SJulian Elischer  */
10534cf49a43SJulian Elischer static int
10544cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
10554cf49a43SJulian Elischer {
10564cf49a43SJulian Elischer 	hook_p hook;
10574cf49a43SJulian Elischer 	int error = 0;
10584cf49a43SJulian Elischer 
10594cf49a43SJulian Elischer 	/* Check that the given name is good */
10604cf49a43SJulian Elischer 	if (name == NULL) {
10616b795970SJulian Elischer 		TRAP_ERROR();
10624cf49a43SJulian Elischer 		return (EINVAL);
10634cf49a43SJulian Elischer 	}
1064899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
10656b795970SJulian Elischer 		TRAP_ERROR();
10664cf49a43SJulian Elischer 		return (EEXIST);
10674cf49a43SJulian Elischer 	}
10684cf49a43SJulian Elischer 
10694cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
107030400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
10714cf49a43SJulian Elischer 	if (hook == NULL) {
10726b795970SJulian Elischer 		TRAP_ERROR();
10734cf49a43SJulian Elischer 		return (ENOMEM);
10744cf49a43SJulian Elischer 	}
10753e4084c8SJulian Elischer 	hook->hk_refs = 1;		/* add a reference for us to return */
107630400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
10773e4084c8SJulian Elischer 	hook->hk_peer = &ng_deadhook;	/* start off this way */
107830400f03SJulian Elischer 	hook->hk_node = node;
107930400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
10804cf49a43SJulian Elischer 
10813e4084c8SJulian Elischer 	/* Set hook name */
108287e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
10833e4084c8SJulian Elischer 
10843e4084c8SJulian Elischer 	/*
10853e4084c8SJulian Elischer 	 * Check if the node type code has something to say about it
10863e4084c8SJulian Elischer 	 * If it fails, the unref of the hook will also unref the node.
10873e4084c8SJulian Elischer 	 */
1088954c4772SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1089954c4772SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
109030400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
1091069154d5SJulian Elischer 			return (error);
1092069154d5SJulian Elischer 		}
1093954c4772SJulian Elischer 	}
10944cf49a43SJulian Elischer 	/*
10954cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
10964cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
10974cf49a43SJulian Elischer 	 */
109830400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
109930400f03SJulian Elischer 	node->nd_numhooks++;
11003e4084c8SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
11014cf49a43SJulian Elischer 
11024cf49a43SJulian Elischer 	if (hookp)
11034cf49a43SJulian Elischer 		*hookp = hook;
11043e4084c8SJulian Elischer 	return (0);
11054cf49a43SJulian Elischer }
11064cf49a43SJulian Elischer 
11074cf49a43SJulian Elischer /*
1108899e9c4eSArchie Cobbs  * Find a hook
1109899e9c4eSArchie Cobbs  *
1110899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
1111899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
11123e4084c8SJulian Elischer  * XXX Possibly we should add a reference to the hook?
1113899e9c4eSArchie Cobbs  */
1114899e9c4eSArchie Cobbs hook_p
1115899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
1116899e9c4eSArchie Cobbs {
1117899e9c4eSArchie Cobbs 	hook_p hook;
1118899e9c4eSArchie Cobbs 
111930400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
112030400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
112130400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
11224bd1b557SGleb Smirnoff 		if (NG_HOOK_IS_VALID(hook) &&
11234bd1b557SGleb Smirnoff 		    (strcmp(NG_HOOK_NAME(hook), name) == 0))
1124899e9c4eSArchie Cobbs 			return (hook);
1125899e9c4eSArchie Cobbs 	}
1126899e9c4eSArchie Cobbs 	return (NULL);
1127899e9c4eSArchie Cobbs }
1128899e9c4eSArchie Cobbs 
1129899e9c4eSArchie Cobbs /*
11304cf49a43SJulian Elischer  * Destroy a hook
11314cf49a43SJulian Elischer  *
11324cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
11334cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
11343e4084c8SJulian Elischer  * from each other first. We reconnect the peer hook to the 'dead'
11353e4084c8SJulian Elischer  * hook so that it can still exist after we depart. We then
11363e4084c8SJulian Elischer  * send the peer its own destroy message. This ensures that we only
11373e4084c8SJulian Elischer  * interact with the peer's structures when it is locked processing that
11383e4084c8SJulian Elischer  * message. We hold a reference to the peer hook so we are guaranteed that
11393e4084c8SJulian Elischer  * the peer hook and node are still going to exist until
11403e4084c8SJulian Elischer  * we are finished there as the hook holds a ref on the node.
11413e4084c8SJulian Elischer  * We run this same code again on the peer hook, but that time it is already
11423e4084c8SJulian Elischer  * attached to the 'dead' hook.
11436b795970SJulian Elischer  *
11446b795970SJulian Elischer  * This routine is called at all stages of hook creation
11456b795970SJulian Elischer  * on error detection and must be able to handle any such stage.
11464cf49a43SJulian Elischer  */
11474cf49a43SJulian Elischer void
11484cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
11494cf49a43SJulian Elischer {
1150ac5dd141SGleb Smirnoff 	hook_p peer;
1151ac5dd141SGleb Smirnoff 	node_p node;
11524cf49a43SJulian Elischer 
11536b795970SJulian Elischer 	if (hook == &ng_deadhook) {	/* better safe than sorry */
11546b795970SJulian Elischer 		printf("ng_destroy_hook called on deadhook\n");
11556b795970SJulian Elischer 		return;
11566b795970SJulian Elischer 	}
1157ac5dd141SGleb Smirnoff 
1158ac5dd141SGleb Smirnoff 	/*
1159ac5dd141SGleb Smirnoff 	 * Protect divorce process with mutex, to avoid races on
1160ac5dd141SGleb Smirnoff 	 * simultaneous disconnect.
1161ac5dd141SGleb Smirnoff 	 */
1162ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1163ac5dd141SGleb Smirnoff 
1164ac5dd141SGleb Smirnoff 	hook->hk_flags |= HK_INVALID;
1165ac5dd141SGleb Smirnoff 
1166ac5dd141SGleb Smirnoff 	peer = NG_HOOK_PEER(hook);
1167ac5dd141SGleb Smirnoff 	node = NG_HOOK_NODE(hook);
1168ac5dd141SGleb Smirnoff 
11693e4084c8SJulian Elischer 	if (peer && (peer != &ng_deadhook)) {
11703e4084c8SJulian Elischer 		/*
11713e4084c8SJulian Elischer 		 * Set the peer to point to ng_deadhook
11723e4084c8SJulian Elischer 		 * from this moment on we are effectively independent it.
11733e4084c8SJulian Elischer 		 * send it an rmhook message of it's own.
11743e4084c8SJulian Elischer 		 */
11753e4084c8SJulian Elischer 		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
11763e4084c8SJulian Elischer 		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
11776b795970SJulian Elischer 		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
11786b795970SJulian Elischer 			/*
11796b795970SJulian Elischer 			 * If it's already divorced from a node,
11806b795970SJulian Elischer 			 * just free it.
11816b795970SJulian Elischer 			 */
1182ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11836b795970SJulian Elischer 		} else {
1184ac5dd141SGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
11856b795970SJulian Elischer 			ng_rmhook_self(peer); 	/* Send it a surprise */
11866b795970SJulian Elischer 		}
118752fa3556SJulian Elischer 		NG_HOOK_UNREF(peer);		/* account for peer link */
118852fa3556SJulian Elischer 		NG_HOOK_UNREF(hook);		/* account for peer link */
1189ac5dd141SGleb Smirnoff 	} else
1190ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1191ac5dd141SGleb Smirnoff 
1192ac5dd141SGleb Smirnoff 	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
11934cf49a43SJulian Elischer 
11944cf49a43SJulian Elischer 	/*
11954cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
11964cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
11974cf49a43SJulian Elischer 	 */
11986b795970SJulian Elischer 	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
11996b795970SJulian Elischer 		return;
12006b795970SJulian Elischer 	}
120130400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
120230400f03SJulian Elischer 	node->nd_numhooks--;
120330400f03SJulian Elischer 	if (node->nd_type->disconnect) {
12044cf49a43SJulian Elischer 		/*
12056b795970SJulian Elischer 		 * The type handler may elect to destroy the node so don't
120664efc707SRobert Watson 		 * trust its existence after this point. (except
12076b795970SJulian Elischer 		 * that we still hold a reference on it. (which we
12086b795970SJulian Elischer 		 * inherrited from the hook we are destroying)
12094cf49a43SJulian Elischer 		 */
121030400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
12114cf49a43SJulian Elischer 	}
12126b795970SJulian Elischer 
12136b795970SJulian Elischer 	/*
12146b795970SJulian Elischer 	 * Note that because we will point to ng_deadnode, the original node
12156b795970SJulian Elischer 	 * is not decremented automatically so we do that manually.
12166b795970SJulian Elischer 	 */
12176b795970SJulian Elischer 	_NG_HOOK_NODE(hook) = &ng_deadnode;
12186b795970SJulian Elischer 	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
12196b795970SJulian Elischer 	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
12204cf49a43SJulian Elischer }
12214cf49a43SJulian Elischer 
12224cf49a43SJulian Elischer /*
12234cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
12244cf49a43SJulian Elischer  * is effectively bypassed.
12254cf49a43SJulian Elischer  */
12264cf49a43SJulian Elischer int
12274cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
12284cf49a43SJulian Elischer {
122930400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
12306b795970SJulian Elischer 		TRAP_ERROR();
12314cf49a43SJulian Elischer 		return (EINVAL);
123230400f03SJulian Elischer 	}
1233a7da736aSGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1234c3189b3fSGleb Smirnoff 	if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) {
1235c3189b3fSGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1236c3189b3fSGleb Smirnoff 		return (EINVAL);
1237c3189b3fSGleb Smirnoff 	}
123830400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
123930400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
12404cf49a43SJulian Elischer 
12413e4084c8SJulian Elischer 	hook1->hk_peer = &ng_deadhook;
12423e4084c8SJulian Elischer 	hook2->hk_peer = &ng_deadhook;
1243a7da736aSGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
12443e4084c8SJulian Elischer 
1245cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook1);
1246cf3254aaSGleb Smirnoff 	NG_HOOK_UNREF(hook2);
1247cf3254aaSGleb Smirnoff 
12484cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
12494cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
12504cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
12514cf49a43SJulian Elischer 	return (0);
12524cf49a43SJulian Elischer }
12534cf49a43SJulian Elischer 
12544cf49a43SJulian Elischer /*
12554cf49a43SJulian Elischer  * Install a new netgraph type
12564cf49a43SJulian Elischer  */
12574cf49a43SJulian Elischer int
12584cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
12594cf49a43SJulian Elischer {
12604cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
12614cf49a43SJulian Elischer 
12624cf49a43SJulian Elischer 	/* Check version and type name fields */
12634bd1b557SGleb Smirnoff 	if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
12644bd1b557SGleb Smirnoff 	    (namelen >= NG_TYPESIZ)) {
12656b795970SJulian Elischer 		TRAP_ERROR();
12668ed370fdSJulian Elischer 		if (tp->version != NG_ABI_VERSION) {
12674bd1b557SGleb Smirnoff 			printf("Netgraph: Node type rejected. ABI mismatch. "
12684bd1b557SGleb Smirnoff 			    "Suggest recompile\n");
12698ed370fdSJulian Elischer 		}
12704cf49a43SJulian Elischer 		return (EINVAL);
12714cf49a43SJulian Elischer 	}
12724cf49a43SJulian Elischer 
12734cf49a43SJulian Elischer 	/* Check for name collision */
12744cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
12756b795970SJulian Elischer 		TRAP_ERROR();
12764cf49a43SJulian Elischer 		return (EEXIST);
12774cf49a43SJulian Elischer 	}
12784cf49a43SJulian Elischer 
1279069154d5SJulian Elischer 	/* Link in new type */
1280c4282b74SGleb Smirnoff 	TYPELIST_WLOCK();
1281069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1282c73b94a2SJulian Elischer 	tp->refs = 1;	/* first ref is linked list */
1283c4282b74SGleb Smirnoff 	TYPELIST_WUNLOCK();
12844cf49a43SJulian Elischer 	return (0);
12854cf49a43SJulian Elischer }
12864cf49a43SJulian Elischer 
12874cf49a43SJulian Elischer /*
1288c31b4a53SJulian Elischer  * unlink a netgraph type
1289c31b4a53SJulian Elischer  * If no examples exist
1290c31b4a53SJulian Elischer  */
1291c31b4a53SJulian Elischer int
1292c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp)
1293c31b4a53SJulian Elischer {
1294c31b4a53SJulian Elischer 	/* Check for name collision */
1295c31b4a53SJulian Elischer 	if (tp->refs != 1) {
1296c31b4a53SJulian Elischer 		TRAP_ERROR();
1297c31b4a53SJulian Elischer 		return (EBUSY);
1298c31b4a53SJulian Elischer 	}
1299c31b4a53SJulian Elischer 
1300c31b4a53SJulian Elischer 	/* Unlink type */
1301c4282b74SGleb Smirnoff 	TYPELIST_WLOCK();
1302c31b4a53SJulian Elischer 	LIST_REMOVE(tp, types);
1303c4282b74SGleb Smirnoff 	TYPELIST_WUNLOCK();
1304c31b4a53SJulian Elischer 	return (0);
1305c31b4a53SJulian Elischer }
1306c31b4a53SJulian Elischer 
1307c31b4a53SJulian Elischer /*
13084cf49a43SJulian Elischer  * Look for a type of the name given
13094cf49a43SJulian Elischer  */
13104cf49a43SJulian Elischer struct ng_type *
13114cf49a43SJulian Elischer ng_findtype(const char *typename)
13124cf49a43SJulian Elischer {
13134cf49a43SJulian Elischer 	struct ng_type *type;
13144cf49a43SJulian Elischer 
1315c4282b74SGleb Smirnoff 	TYPELIST_RLOCK();
1316069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
13174cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
13184cf49a43SJulian Elischer 			break;
13194cf49a43SJulian Elischer 	}
1320c4282b74SGleb Smirnoff 	TYPELIST_RUNLOCK();
13214cf49a43SJulian Elischer 	return (type);
13224cf49a43SJulian Elischer }
13234cf49a43SJulian Elischer 
13244cf49a43SJulian Elischer /************************************************************************
13254cf49a43SJulian Elischer 			Composite routines
13264cf49a43SJulian Elischer ************************************************************************/
13274cf49a43SJulian Elischer /*
13286b795970SJulian Elischer  * Connect two nodes using the specified hooks, using queued functions.
13294cf49a43SJulian Elischer  */
1330e088dd4cSAlexander Motin static int
1331e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook)
13324cf49a43SJulian Elischer {
1333e088dd4cSAlexander Motin 	int	error = 0;
13344cf49a43SJulian Elischer 
13356b795970SJulian Elischer 	/*
13366b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
13376b795970SJulian Elischer 	 * Our caller has a reference on the hook.
13386b795970SJulian Elischer 	 * Our caller has a reference on the node.
13396b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13406b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13411acb27c6SJulian Elischer 	 * We are all set up except for the final call to the node, and
13421acb27c6SJulian Elischer 	 * the clearing of the INVALID flag.
13436b795970SJulian Elischer 	 */
13446b795970SJulian Elischer 	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
13456b795970SJulian Elischer 		/*
13466b795970SJulian Elischer 		 * The node must have been freed again since we last visited
13476b795970SJulian Elischer 		 * here. ng_destry_hook() has this effect but nothing else does.
13486b795970SJulian Elischer 		 * We should just release our references and
13496b795970SJulian Elischer 		 * free anything we can think of.
13506b795970SJulian Elischer 		 * Since we know it's been destroyed, and it's our caller
13516b795970SJulian Elischer 		 * that holds the references, just return.
13526b795970SJulian Elischer 		 */
1353e088dd4cSAlexander Motin 		ERROUT(ENOENT);
13546b795970SJulian Elischer 	}
13556b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1356e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
13576b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
13581acb27c6SJulian Elischer 			printf("failed in ng_con_part3()\n");
1359e088dd4cSAlexander Motin 			ERROUT(error);
13604cf49a43SJulian Elischer 		}
13616b795970SJulian Elischer 	}
13626b795970SJulian Elischer 	/*
13636b795970SJulian Elischer 	 *  XXX this is wrong for SMP. Possibly we need
13646b795970SJulian Elischer 	 * to separate out 'create' and 'invalid' flags.
13656b795970SJulian Elischer 	 * should only set flags on hooks we have locked under our node.
13666b795970SJulian Elischer 	 */
13676b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID;
1368e088dd4cSAlexander Motin done:
1369e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1370e088dd4cSAlexander Motin 	return (error);
13716b795970SJulian Elischer }
13726b795970SJulian Elischer 
1373e088dd4cSAlexander Motin static int
1374e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook)
13756b795970SJulian Elischer {
1376ac5dd141SGleb Smirnoff 	hook_p	peer;
1377e088dd4cSAlexander Motin 	int	error = 0;
13786b795970SJulian Elischer 
13796b795970SJulian Elischer 	/*
13806b795970SJulian Elischer 	 * When we run, we know that the node 'node' is locked for us.
13816b795970SJulian Elischer 	 * Our caller has a reference on the hook.
13826b795970SJulian Elischer 	 * Our caller has a reference on the node.
13836b795970SJulian Elischer 	 * (In this case our caller is ng_apply_item() ).
13846b795970SJulian Elischer 	 * The peer hook has a reference on the hook.
13856b795970SJulian Elischer 	 * our node pointer points to the 'dead' node.
13866b795970SJulian Elischer 	 * First check the hook name is unique.
13871acb27c6SJulian Elischer 	 * Should not happen because we checked before queueing this.
13886b795970SJulian Elischer 	 */
13896b795970SJulian Elischer 	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
13906b795970SJulian Elischer 		TRAP_ERROR();
13916b795970SJulian Elischer 		ng_destroy_hook(hook); /* should destroy peer too */
13921acb27c6SJulian Elischer 		printf("failed in ng_con_part2()\n");
1393e088dd4cSAlexander Motin 		ERROUT(EEXIST);
13946b795970SJulian Elischer 	}
13956b795970SJulian Elischer 	/*
13966b795970SJulian Elischer 	 * Check if the node type code has something to say about it
13976b795970SJulian Elischer 	 * If it fails, the unref of the hook will also unref the attached node,
13986b795970SJulian Elischer 	 * however since that node is 'ng_deadnode' this will do nothing.
13996b795970SJulian Elischer 	 * The peer hook will also be destroyed.
14006b795970SJulian Elischer 	 */
14016b795970SJulian Elischer 	if (node->nd_type->newhook != NULL) {
1402e088dd4cSAlexander Motin 		if ((error = (*node->nd_type->newhook)(node, hook,
1403e088dd4cSAlexander Motin 		    hook->hk_name))) {
14046b795970SJulian Elischer 			ng_destroy_hook(hook); /* should destroy peer too */
14051acb27c6SJulian Elischer 			printf("failed in ng_con_part2()\n");
1406e088dd4cSAlexander Motin 			ERROUT(error);
14076b795970SJulian Elischer 		}
14086b795970SJulian Elischer 	}
14096b795970SJulian Elischer 
14106b795970SJulian Elischer 	/*
14116b795970SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
14126b795970SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
14136b795970SJulian Elischer 	 */
14146b795970SJulian Elischer 	hook->hk_node = node;		/* just overwrite ng_deadnode */
14156b795970SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
14166b795970SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
14176b795970SJulian Elischer 	node->nd_numhooks++;
14186b795970SJulian Elischer 	NG_HOOK_REF(hook);	/* one for the node */
14196b795970SJulian Elischer 
14206b795970SJulian Elischer 	/*
142164efc707SRobert Watson 	 * We now have a symmetrical situation, where both hooks have been
14225951069aSJulian Elischer 	 * linked to their nodes, the newhook methods have been called
14236b795970SJulian Elischer 	 * And the references are all correct. The hooks are still marked
14246b795970SJulian Elischer 	 * as invalid, as we have not called the 'connect' methods
14256b795970SJulian Elischer 	 * yet.
142664efc707SRobert Watson 	 * We can call the local one immediately as we have the
14276b795970SJulian Elischer 	 * node locked, but we need to queue the remote one.
14286b795970SJulian Elischer 	 */
14296b795970SJulian Elischer 	if (hook->hk_node->nd_type->connect) {
1430e088dd4cSAlexander Motin 		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
14316b795970SJulian Elischer 			ng_destroy_hook(hook);	/* also zaps peer */
14321acb27c6SJulian Elischer 			printf("failed in ng_con_part2(A)\n");
1433e088dd4cSAlexander Motin 			ERROUT(error);
14346b795970SJulian Elischer 		}
14356b795970SJulian Elischer 	}
1436ac5dd141SGleb Smirnoff 
1437ac5dd141SGleb Smirnoff 	/*
1438ac5dd141SGleb Smirnoff 	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1439ac5dd141SGleb Smirnoff 	 */
1440ac5dd141SGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
1441ac5dd141SGleb Smirnoff 	peer = hook->hk_peer;
1442ac5dd141SGleb Smirnoff 	if (peer == &ng_deadhook) {
1443ac5dd141SGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1444ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(B)\n");
1445ac5dd141SGleb Smirnoff 		ng_destroy_hook(hook);
1446e088dd4cSAlexander Motin 		ERROUT(ENOENT);
1447ac5dd141SGleb Smirnoff 	}
1448ac5dd141SGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
1449ac5dd141SGleb Smirnoff 
1450b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1451b332b91fSGleb Smirnoff 	    NULL, 0, NG_REUSE_ITEM))) {
1452ac5dd141SGleb Smirnoff 		printf("failed in ng_con_part2(C)\n");
14531acb27c6SJulian Elischer 		ng_destroy_hook(hook);	/* also zaps peer */
1454e088dd4cSAlexander Motin 		return (error);		/* item was consumed. */
14551acb27c6SJulian Elischer 	}
14566b795970SJulian Elischer 	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1457e088dd4cSAlexander Motin 	return (0);			/* item was consumed. */
1458e088dd4cSAlexander Motin done:
1459e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
1460e088dd4cSAlexander Motin 	return (error);
14614cf49a43SJulian Elischer }
14624cf49a43SJulian Elischer 
14634cf49a43SJulian Elischer /*
14646b795970SJulian Elischer  * Connect this node with another node. We assume that this node is
14656b795970SJulian Elischer  * currently locked, as we are only called from an NGM_CONNECT message.
14664cf49a43SJulian Elischer  */
14676b795970SJulian Elischer static int
1468e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name,
1469e088dd4cSAlexander Motin     node_p node2, const char *name2)
14704cf49a43SJulian Elischer {
14714cf49a43SJulian Elischer 	int	error;
14724cf49a43SJulian Elischer 	hook_p	hook;
14734cf49a43SJulian Elischer 	hook_p	hook2;
14744cf49a43SJulian Elischer 
14751acb27c6SJulian Elischer 	if (ng_findhook(node2, name2) != NULL) {
14761acb27c6SJulian Elischer 		return(EEXIST);
14771acb27c6SJulian Elischer 	}
14783e4084c8SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
14794cf49a43SJulian Elischer 		return (error);
14806b795970SJulian Elischer 	/* Allocate the other hook and link it up */
14816b795970SJulian Elischer 	NG_ALLOC_HOOK(hook2);
148219724144SGleb Smirnoff 	if (hook2 == NULL) {
14836b795970SJulian Elischer 		TRAP_ERROR();
14846b795970SJulian Elischer 		ng_destroy_hook(hook);	/* XXX check ref counts so far */
14856b795970SJulian Elischer 		NG_HOOK_UNREF(hook);	/* including our ref */
14866b795970SJulian Elischer 		return (ENOMEM);
14876b795970SJulian Elischer 	}
14886b795970SJulian Elischer 	hook2->hk_refs = 1;		/* start with a reference for us. */
14896b795970SJulian Elischer 	hook2->hk_flags = HK_INVALID;
14906b795970SJulian Elischer 	hook2->hk_peer = hook;		/* Link the two together */
14916b795970SJulian Elischer 	hook->hk_peer = hook2;
14926b795970SJulian Elischer 	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
14936b795970SJulian Elischer 	NG_HOOK_REF(hook2);
14946b795970SJulian Elischer 	hook2->hk_node = &ng_deadnode;
149587e2c66aSHartmut Brandt 	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
14966b795970SJulian Elischer 
14976b795970SJulian Elischer 	/*
14986b795970SJulian Elischer 	 * Queue the function above.
14996b795970SJulian Elischer 	 * Procesing continues in that function in the lock context of
15006b795970SJulian Elischer 	 * the other node.
15016b795970SJulian Elischer 	 */
1502b332b91fSGleb Smirnoff 	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1503b332b91fSGleb Smirnoff 	    NG_NOFLAGS))) {
15043fb87c24SAlexander Motin 		printf("failed in ng_con_nodes(): %d\n", error);
15053fb87c24SAlexander Motin 		ng_destroy_hook(hook);	/* also zaps peer */
15063fb87c24SAlexander Motin 	}
15076b795970SJulian Elischer 
15086b795970SJulian Elischer 	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
15096b795970SJulian Elischer 	NG_HOOK_UNREF(hook2);
15103fb87c24SAlexander Motin 	return (error);
15114cf49a43SJulian Elischer }
15126b795970SJulian Elischer 
15136b795970SJulian Elischer /*
15146b795970SJulian Elischer  * Make a peer and connect.
15156b795970SJulian Elischer  * We assume that the local node is locked.
15166b795970SJulian Elischer  * The new node probably doesn't need a lock until
15176b795970SJulian Elischer  * it has a hook, because it cannot really have any work until then,
15186b795970SJulian Elischer  * but we should think about it a bit more.
15196b795970SJulian Elischer  *
15206b795970SJulian Elischer  * The problem may come if the other node also fires up
15216b795970SJulian Elischer  * some hardware or a timer or some other source of activation,
15226b795970SJulian Elischer  * also it may already get a command msg via it's ID.
15236b795970SJulian Elischer  *
15246b795970SJulian Elischer  * We could use the same method as ng_con_nodes() but we'd have
15256b795970SJulian Elischer  * to add ability to remove the node when failing. (Not hard, just
15266b795970SJulian Elischer  * make arg1 point to the node to remove).
15276b795970SJulian Elischer  * Unless of course we just ignore failure to connect and leave
15286b795970SJulian Elischer  * an unconnected node?
15296b795970SJulian Elischer  */
15306b795970SJulian Elischer static int
15316b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
15326b795970SJulian Elischer {
15336b795970SJulian Elischer 	node_p	node2;
15344c9b5910SGleb Smirnoff 	hook_p	hook1, hook2;
15356b795970SJulian Elischer 	int	error;
15366b795970SJulian Elischer 
15376b795970SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
15386b795970SJulian Elischer 		return (error);
15396b795970SJulian Elischer 	}
15406b795970SJulian Elischer 
15416b795970SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
15421acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15436b795970SJulian Elischer 		return (error);
15446b795970SJulian Elischer 	}
15456b795970SJulian Elischer 
15466b795970SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
15471acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15486b795970SJulian Elischer 		ng_destroy_hook(hook1);
15496b795970SJulian Elischer 		NG_HOOK_UNREF(hook1);
15506b795970SJulian Elischer 		return (error);
15516b795970SJulian Elischer 	}
15526b795970SJulian Elischer 
15536b795970SJulian Elischer 	/*
15546b795970SJulian Elischer 	 * Actually link the two hooks together.
15556b795970SJulian Elischer 	 */
15566b795970SJulian Elischer 	hook1->hk_peer = hook2;
15576b795970SJulian Elischer 	hook2->hk_peer = hook1;
15586b795970SJulian Elischer 
15596b795970SJulian Elischer 	/* Each hook is referenced by the other */
15606b795970SJulian Elischer 	NG_HOOK_REF(hook1);
15616b795970SJulian Elischer 	NG_HOOK_REF(hook2);
15626b795970SJulian Elischer 
15636b795970SJulian Elischer 	/* Give each node the opportunity to veto the pending connection */
15646b795970SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
15656b795970SJulian Elischer 		error = (*hook1->hk_node->nd_type->connect) (hook1);
15666b795970SJulian Elischer 	}
15676b795970SJulian Elischer 
15686b795970SJulian Elischer 	if ((error == 0) && hook2->hk_node->nd_type->connect) {
15696b795970SJulian Elischer 		error = (*hook2->hk_node->nd_type->connect) (hook2);
15706b795970SJulian Elischer 
15716b795970SJulian Elischer 	}
15723e4084c8SJulian Elischer 
15733e4084c8SJulian Elischer 	/*
15743e4084c8SJulian Elischer 	 * drop the references we were holding on the two hooks.
15753e4084c8SJulian Elischer 	 */
15766b795970SJulian Elischer 	if (error) {
15776b795970SJulian Elischer 		ng_destroy_hook(hook2);	/* also zaps hook1 */
15781acb27c6SJulian Elischer 		ng_rmnode(node2, NULL, NULL, 0);
15796b795970SJulian Elischer 	} else {
15806b795970SJulian Elischer 		/* As a last act, allow the hooks to be used */
15816b795970SJulian Elischer 		hook1->hk_flags &= ~HK_INVALID;
15826b795970SJulian Elischer 		hook2->hk_flags &= ~HK_INVALID;
15836b795970SJulian Elischer 	}
15846b795970SJulian Elischer 	NG_HOOK_UNREF(hook1);
15853e4084c8SJulian Elischer 	NG_HOOK_UNREF(hook2);
15863e4084c8SJulian Elischer 	return (error);
15874cf49a43SJulian Elischer }
15886b795970SJulian Elischer 
1589069154d5SJulian Elischer /************************************************************************
1590069154d5SJulian Elischer 		Utility routines to send self messages
1591069154d5SJulian Elischer ************************************************************************/
1592069154d5SJulian Elischer 
15931acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */
159464efc707SRobert Watson /* Should add arg "immediately" to jump the queue */
1595069154d5SJulian Elischer int
159615cea89fSAlexander Motin ng_rmnode_self(node_p node)
1597069154d5SJulian Elischer {
15981acb27c6SJulian Elischer 	int		error;
15994cf49a43SJulian Elischer 
16001acb27c6SJulian Elischer 	if (node == &ng_deadnode)
16011acb27c6SJulian Elischer 		return (0);
1602be4252b3SJulian Elischer 	node->nd_flags |= NGF_INVALID;
1603be4252b3SJulian Elischer 	if (node->nd_flags & NGF_CLOSING)
16041acb27c6SJulian Elischer 		return (0);
1605069154d5SJulian Elischer 
160615cea89fSAlexander Motin 	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
16071acb27c6SJulian Elischer 	return (error);
1608069154d5SJulian Elischer }
1609069154d5SJulian Elischer 
16101acb27c6SJulian Elischer static void
16116b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
16126b795970SJulian Elischer {
16136b795970SJulian Elischer 	ng_destroy_hook(hook);
16141acb27c6SJulian Elischer 	return ;
16156b795970SJulian Elischer }
16166b795970SJulian Elischer 
1617954c4772SJulian Elischer int
1618954c4772SJulian Elischer ng_rmhook_self(hook_p hook)
1619954c4772SJulian Elischer {
16206b795970SJulian Elischer 	int		error;
1621954c4772SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
1622954c4772SJulian Elischer 
16236b795970SJulian Elischer 	if (node == &ng_deadnode)
16246b795970SJulian Elischer 		return (0);
16256b795970SJulian Elischer 
16266b795970SJulian Elischer 	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
16276b795970SJulian Elischer 	return (error);
1628954c4772SJulian Elischer }
1629954c4772SJulian Elischer 
1630069154d5SJulian Elischer /***********************************************************************
16314cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
16324cf49a43SJulian Elischer  *
16334cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
16344cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
16354cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
16364cf49a43SJulian Elischer  *
16374cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
16384cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
16394cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
16404cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
16414cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
16424cf49a43SJulian Elischer  *
16434cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1644069154d5SJulian Elischer  ***********************************************************************/
16454cf49a43SJulian Elischer int
16464cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
16474cf49a43SJulian Elischer {
16484cf49a43SJulian Elischer 	char	*node, *path, *hook;
16494cf49a43SJulian Elischer 	int	k;
16504cf49a43SJulian Elischer 
16514cf49a43SJulian Elischer 	/*
16524cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
16534cf49a43SJulian Elischer 	 */
16544cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
16554cf49a43SJulian Elischer 	if (*path) {
16564cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
16574cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
16584cf49a43SJulian Elischer 
16594cf49a43SJulian Elischer 		/* Node name must not be empty */
16604cf49a43SJulian Elischer 		if (!*node)
16614cf49a43SJulian Elischer 			return -1;
16624cf49a43SJulian Elischer 
16634cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
16644cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
16654cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
16664cf49a43SJulian Elischer 				if (node[k] == '.')
16674cf49a43SJulian Elischer 					return -1;
16684cf49a43SJulian Elischer 		}
16694cf49a43SJulian Elischer 	} else {
16704cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
16714cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
16724cf49a43SJulian Elischer 	}
16734cf49a43SJulian Elischer 
16744cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
16754cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16764cf49a43SJulian Elischer 		if (path[k] == ':')
16774cf49a43SJulian Elischer 			return -1;
16784cf49a43SJulian Elischer 
16794cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
16804cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
16814cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
16824cf49a43SJulian Elischer 			return -1;
16834cf49a43SJulian Elischer 
16844cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
16854cf49a43SJulian Elischer 	if (path[0] == '.')
16864cf49a43SJulian Elischer 		path++;
16874cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
16884cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
16894cf49a43SJulian Elischer 
16904cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
16914cf49a43SJulian Elischer 	if (*path) {
16924cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
16934cf49a43SJulian Elischer 			if (path[k] == '.') {
16944cf49a43SJulian Elischer 				hook = NULL;
16954cf49a43SJulian Elischer 				break;
16964cf49a43SJulian Elischer 			}
16974cf49a43SJulian Elischer 	} else
16984cf49a43SJulian Elischer 		path = hook = NULL;
16994cf49a43SJulian Elischer 
17004cf49a43SJulian Elischer 	/* Done */
17014cf49a43SJulian Elischer 	if (nodep)
17024cf49a43SJulian Elischer 		*nodep = node;
17034cf49a43SJulian Elischer 	if (pathp)
17044cf49a43SJulian Elischer 		*pathp = path;
17054cf49a43SJulian Elischer 	if (hookp)
17064cf49a43SJulian Elischer 		*hookp = hook;
17074cf49a43SJulian Elischer 	return (0);
17084cf49a43SJulian Elischer }
17094cf49a43SJulian Elischer 
17104cf49a43SJulian Elischer /*
17114cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1712069154d5SJulian Elischer  * return the destination node.
17134cf49a43SJulian Elischer  */
17144cf49a43SJulian Elischer int
17154bd1b557SGleb Smirnoff ng_path2noderef(node_p here, const char *address, node_p *destp,
17164bd1b557SGleb Smirnoff     hook_p *lasthook)
17174cf49a43SJulian Elischer {
171887e2c66aSHartmut Brandt 	char    fullpath[NG_PATHSIZ];
1719a7da736aSGleb Smirnoff 	char   *nodename, *path;
1720069154d5SJulian Elischer 	node_p  node, oldnode;
17214cf49a43SJulian Elischer 
17224cf49a43SJulian Elischer 	/* Initialize */
172330400f03SJulian Elischer 	if (destp == NULL) {
17246b795970SJulian Elischer 		TRAP_ERROR();
17254cf49a43SJulian Elischer 		return EINVAL;
172630400f03SJulian Elischer 	}
17274cf49a43SJulian Elischer 	*destp = NULL;
17284cf49a43SJulian Elischer 
17294cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
17304cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
17314cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
17324cf49a43SJulian Elischer 
17334cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
17344cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
17356b795970SJulian Elischer 		TRAP_ERROR();
17364cf49a43SJulian Elischer 		return EINVAL;
17374cf49a43SJulian Elischer 	}
17384cf49a43SJulian Elischer 
1739069154d5SJulian Elischer 	/*
1740069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1741069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1742069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1743069154d5SJulian Elischer 	 */
17444cf49a43SJulian Elischer 	if (nodename) {
1745069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
17464cf49a43SJulian Elischer 		if (node == NULL) {
17476b795970SJulian Elischer 			TRAP_ERROR();
17484cf49a43SJulian Elischer 			return (ENOENT);
17494cf49a43SJulian Elischer 		}
1750069154d5SJulian Elischer 	} else {
1751069154d5SJulian Elischer 		if (here == NULL) {
17526b795970SJulian Elischer 			TRAP_ERROR();
1753069154d5SJulian Elischer 			return (EINVAL);
1754069154d5SJulian Elischer 		}
17554cf49a43SJulian Elischer 		node = here;
175630400f03SJulian Elischer 		NG_NODE_REF(node);
1757069154d5SJulian Elischer 	}
17584cf49a43SJulian Elischer 
1759a7da736aSGleb Smirnoff 	if (path == NULL) {
1760a7da736aSGleb Smirnoff 		if (lasthook != NULL)
1761a7da736aSGleb Smirnoff 			*lasthook = NULL;
1762a7da736aSGleb Smirnoff 		*destp = node;
1763a7da736aSGleb Smirnoff 		return (0);
1764a7da736aSGleb Smirnoff 	}
1765a7da736aSGleb Smirnoff 
1766069154d5SJulian Elischer 	/*
1767069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1768a7da736aSGleb Smirnoff 	 *
1769a7da736aSGleb Smirnoff 	 * XXXGL: The path may demolish as we go the sequence, but if
1770a7da736aSGleb Smirnoff 	 * we hold the topology mutex at critical places, then, I hope,
1771a7da736aSGleb Smirnoff 	 * we would always have valid pointers in hand, although the
1772a7da736aSGleb Smirnoff 	 * path behind us may no longer exist.
1773069154d5SJulian Elischer 	 */
1774a7da736aSGleb Smirnoff 	for (;;) {
1775a7da736aSGleb Smirnoff 		hook_p hook;
17764cf49a43SJulian Elischer 		char *segment;
17774cf49a43SJulian Elischer 
17784cf49a43SJulian Elischer 		/*
17794cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
1780a7da736aSGleb Smirnoff 		 * found with a NUL; "path" points to the next segment (or the
17814cf49a43SJulian Elischer 		 * NUL at the end).
17824cf49a43SJulian Elischer 		 */
1783a7da736aSGleb Smirnoff 		for (segment = path; *path != '\0'; path++) {
1784a7da736aSGleb Smirnoff 			if (*path == '.') {
1785a7da736aSGleb Smirnoff 				*path++ = '\0';
17864cf49a43SJulian Elischer 				break;
17874cf49a43SJulian Elischer 			}
17884cf49a43SJulian Elischer 		}
17894cf49a43SJulian Elischer 
17904cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1791899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
17924cf49a43SJulian Elischer 
1793a7da736aSGleb Smirnoff 		mtx_lock(&ng_topo_mtx);
17944cf49a43SJulian Elischer 		/* Can't get there from here... */
17954bd1b557SGleb Smirnoff 		if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
17964bd1b557SGleb Smirnoff 		    NG_HOOK_NOT_VALID(hook) ||
17974bd1b557SGleb Smirnoff 		    NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
17986b795970SJulian Elischer 			TRAP_ERROR();
179930400f03SJulian Elischer 			NG_NODE_UNREF(node);
1800a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
18014cf49a43SJulian Elischer 			return (ENOENT);
18024cf49a43SJulian Elischer 		}
18034cf49a43SJulian Elischer 
1804069154d5SJulian Elischer 		/*
1805069154d5SJulian Elischer 		 * Hop on over to the next node
1806069154d5SJulian Elischer 		 * XXX
1807069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1808069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1809069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1810069154d5SJulian Elischer 		 */
1811069154d5SJulian Elischer 		oldnode = node;
181230400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
181330400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
181430400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
181530400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
181630400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1817a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
18186b795970SJulian Elischer 			TRAP_ERROR();
18194cf49a43SJulian Elischer 			return (ENXIO);
18204cf49a43SJulian Elischer 		}
18214cf49a43SJulian Elischer 
1822a7da736aSGleb Smirnoff 		if (*path == '\0') {
1823a7da736aSGleb Smirnoff 			if (lasthook != NULL) {
1824a7da736aSGleb Smirnoff 				if (hook != NULL) {
1825a7da736aSGleb Smirnoff 					*lasthook = NG_HOOK_PEER(hook);
1826a7da736aSGleb Smirnoff 					NG_HOOK_REF(*lasthook);
1827a7da736aSGleb Smirnoff 				} else
1828a7da736aSGleb Smirnoff 					*lasthook = NULL;
1829a7da736aSGleb Smirnoff 			}
1830a7da736aSGleb Smirnoff 			mtx_unlock(&ng_topo_mtx);
18314cf49a43SJulian Elischer 			*destp = node;
1832069154d5SJulian Elischer 			return (0);
1833069154d5SJulian Elischer 		}
1834a7da736aSGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
1835a7da736aSGleb Smirnoff 	}
1836a7da736aSGleb Smirnoff }
1837069154d5SJulian Elischer 
1838069154d5SJulian Elischer /***************************************************************\
1839069154d5SJulian Elischer * Input queue handling.
1840069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1841069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
184264efc707SRobert Watson * Items which cannot be handled immediately are queued.
1843069154d5SJulian Elischer *
1844069154d5SJulian Elischer * read-write queue locking inline functions			*
1845069154d5SJulian Elischer \***************************************************************/
1846069154d5SJulian Elischer 
18479852972bSAlexander Motin static __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
18489852972bSAlexander Motin static __inline item_p	ng_dequeue(node_p node, int *rw);
18499852972bSAlexander Motin static __inline item_p	ng_acquire_read(node_p node, item_p  item);
18509852972bSAlexander Motin static __inline item_p	ng_acquire_write(node_p node, item_p  item);
18519852972bSAlexander Motin static __inline void	ng_leave_read(node_p node);
18529852972bSAlexander Motin static __inline void	ng_leave_write(node_p node);
1853069154d5SJulian Elischer 
1854069154d5SJulian Elischer /*
1855069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1856069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1857069154d5SJulian Elischer  * with them.
1858069154d5SJulian Elischer  *
1859b57a7965SJulian Elischer  * The ordering here may be important! don't shuffle these.
1860069154d5SJulian Elischer  */
1861069154d5SJulian Elischer /*-
1862069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1863069154d5SJulian Elischer                        |
1864069154d5SJulian Elischer                        V
1865069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18664be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
18674be59335SGleb Smirnoff   | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
18684be59335SGleb Smirnoff   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
1869069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
18704be59335SGleb Smirnoff   \___________________________ ____________________________/ | |
18714be59335SGleb Smirnoff                             V                                | |
18724be59335SGleb Smirnoff                   [active reader count]                      | |
1873069154d5SJulian Elischer                                                              | |
18744be59335SGleb Smirnoff             Operation Pending -------------------------------+ |
1875069154d5SJulian Elischer                                                                |
18764be59335SGleb Smirnoff           Active Writer ---------------------------------------+
1877b57a7965SJulian Elischer 
18788f9ac44aSAlexander Motin Node queue has such semantics:
18798f9ac44aSAlexander Motin - All flags modifications are atomic.
18808f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags.
18818f9ac44aSAlexander Motin   As soon as this can't be done with single operation, it is implemented with
18828f9ac44aSAlexander Motin   spin loop and atomic_cmpset().
18838f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set.
18848f9ac44aSAlexander Motin   It is implemented with atomic_cmpset().
18858f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing
18868f9ac44aSAlexander Motin   all queue fields are protected by the mutex.
18878f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while
18888f9ac44aSAlexander Motin   processing. When queue is empty pending flag is removed.
1889069154d5SJulian Elischer */
18908f9ac44aSAlexander Motin 
18914be59335SGleb Smirnoff #define WRITER_ACTIVE	0x00000001
18924be59335SGleb Smirnoff #define OP_PENDING	0x00000002
18934be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004
18944be59335SGleb Smirnoff #define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
18954be59335SGleb Smirnoff #define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
1896b57a7965SJulian Elischer 
1897b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */
18984be59335SGleb Smirnoff /* Mask of bits a new read cares about */
18994be59335SGleb Smirnoff #define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
1900b57a7965SJulian Elischer 
19014be59335SGleb Smirnoff /* Mask of bits a new write cares about */
1902b57a7965SJulian Elischer #define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
1903b57a7965SJulian Elischer 
19044be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */
19054be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
19064be59335SGleb Smirnoff 
19074be59335SGleb Smirnoff /* How to decide what the next queued item is. */
19089852972bSAlexander Motin #define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
19099852972bSAlexander Motin #define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
19104be59335SGleb Smirnoff 
19114be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */
19124be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP)			\
19134be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
19144be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP)			\
19154be59335SGleb Smirnoff 		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1916b57a7965SJulian Elischer 
1917b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */
19184be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
19194be59335SGleb Smirnoff 	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1920394cb30aSAlexander Motin 				QUEUED_WRITER_CAN_PROCEED(QP))
19214be59335SGleb Smirnoff 
1922714fb865SGleb Smirnoff #define NGQRW_R 0
1923714fb865SGleb Smirnoff #define NGQRW_W 1
1924714fb865SGleb Smirnoff 
19259852972bSAlexander Motin #define NGQ2_WORKQ	0x00000001
19269852972bSAlexander Motin 
1927069154d5SJulian Elischer /*
1928069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1929069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1930069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1931069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1932069154d5SJulian Elischer  * on the queue.
1933069154d5SJulian Elischer  */
1934069154d5SJulian Elischer static __inline item_p
19359852972bSAlexander Motin ng_dequeue(node_p node, int *rw)
1936069154d5SJulian Elischer {
1937069154d5SJulian Elischer 	item_p item;
19389852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
1939b57a7965SJulian Elischer 
19408f9ac44aSAlexander Motin 	/* This MUST be called with the mutex held. */
1941d58e678fSGleb Smirnoff 	mtx_assert(&ngq->q_mtx, MA_OWNED);
19428f9ac44aSAlexander Motin 
19438f9ac44aSAlexander Motin 	/* If there is nothing queued, then just return. */
19444be59335SGleb Smirnoff 	if (!QUEUE_ACTIVE(ngq)) {
19452955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
19462955ee18SGleb Smirnoff 		    "queue flags 0x%lx", __func__,
19479852972bSAlexander Motin 		    node->nd_ID, node, ngq->q_flags);
19484be59335SGleb Smirnoff 		return (NULL);
19494be59335SGleb Smirnoff 	}
1950d58e678fSGleb Smirnoff 
19514be59335SGleb Smirnoff 	/*
19524be59335SGleb Smirnoff 	 * From here, we can assume there is a head item.
19534be59335SGleb Smirnoff 	 * We need to find out what it is and if it can be dequeued, given
19544be59335SGleb Smirnoff 	 * the current state of the node.
19554be59335SGleb Smirnoff 	 */
19564be59335SGleb Smirnoff 	if (HEAD_IS_READER(ngq)) {
1957394cb30aSAlexander Motin 		while (1) {
1958394cb30aSAlexander Motin 			long t = ngq->q_flags;
1959394cb30aSAlexander Motin 			if (t & WRITER_ACTIVE) {
19608f9ac44aSAlexander Motin 				/* There is writer, reader can't proceed. */
19614bd1b557SGleb Smirnoff 				CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
19624bd1b557SGleb Smirnoff 				    "reader can't proceed; queue flags 0x%lx",
19634bd1b557SGleb Smirnoff 				    __func__, node->nd_ID, node, t);
19644be59335SGleb Smirnoff 				return (NULL);
19654be59335SGleb Smirnoff 			}
19669852972bSAlexander Motin 			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1967394cb30aSAlexander Motin 			    t + READER_INCREMENT))
1968394cb30aSAlexander Motin 				break;
1969394cb30aSAlexander Motin 			cpu_spinwait();
1970394cb30aSAlexander Motin 		}
19718f9ac44aSAlexander Motin 		/* We have got reader lock for the node. */
1972714fb865SGleb Smirnoff 		*rw = NGQRW_R;
19739852972bSAlexander Motin 	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1974394cb30aSAlexander Motin 	    OP_PENDING + WRITER_ACTIVE)) {
19758f9ac44aSAlexander Motin 		/* We have got writer lock for the node. */
1976714fb865SGleb Smirnoff 		*rw = NGQRW_W;
1977069154d5SJulian Elischer 	} else {
19788f9ac44aSAlexander Motin 		/* There is somebody other, writer can't proceed. */
19794bd1b557SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
19804bd1b557SGleb Smirnoff 		    "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
19814bd1b557SGleb Smirnoff 		    ngq->q_flags);
19824be59335SGleb Smirnoff 		return (NULL);
19834cf49a43SJulian Elischer 	}
19844cf49a43SJulian Elischer 
19854cf49a43SJulian Elischer 	/*
1986069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1987069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
19884cf49a43SJulian Elischer 	 */
19899852972bSAlexander Motin 	item = STAILQ_FIRST(&ngq->queue);
19909852972bSAlexander Motin 	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
19919852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue))
19929852972bSAlexander Motin 		atomic_clear_int(&ngq->q_flags, OP_PENDING);
19934bd1b557SGleb Smirnoff 	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
19944bd1b557SGleb Smirnoff 	    "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
19954bd1b557SGleb Smirnoff 	    "READER", ngq->q_flags);
1996069154d5SJulian Elischer 	return (item);
1997069154d5SJulian Elischer }
1998859a4d16SJulian Elischer 
1999859a4d16SJulian Elischer /*
20008f9ac44aSAlexander Motin  * Queue a packet to be picked up later by someone else.
20018f9ac44aSAlexander Motin  * If the queue could be run now, add node to the queue handler's worklist.
2002859a4d16SJulian Elischer  */
2003069154d5SJulian Elischer static __inline void
20049852972bSAlexander Motin ng_queue_rw(node_p node, item_p  item, int rw)
2005069154d5SJulian Elischer {
20069852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20074be59335SGleb Smirnoff 	if (rw == NGQRW_W)
20084be59335SGleb Smirnoff 		NGI_SET_WRITER(item);
20094be59335SGleb Smirnoff 	else
20104be59335SGleb Smirnoff 		NGI_SET_READER(item);
2011394cb30aSAlexander Motin 
2012394cb30aSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2013394cb30aSAlexander Motin 	/* Set OP_PENDING flag and enqueue the item. */
20149852972bSAlexander Motin 	atomic_set_int(&ngq->q_flags, OP_PENDING);
20159852972bSAlexander Motin 	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
2016394cb30aSAlexander Motin 
20172955ee18SGleb Smirnoff 	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
20189852972bSAlexander Motin 	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
20194be59335SGleb Smirnoff 
20204be59335SGleb Smirnoff 	/*
20214be59335SGleb Smirnoff 	 * We can take the worklist lock with the node locked
20224be59335SGleb Smirnoff 	 * BUT NOT THE REVERSE!
20234be59335SGleb Smirnoff 	 */
20244be59335SGleb Smirnoff 	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
20259852972bSAlexander Motin 		ng_worklist_add(node);
2026394cb30aSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2027069154d5SJulian Elischer }
2028069154d5SJulian Elischer 
20298f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */
2030069154d5SJulian Elischer static __inline item_p
20319852972bSAlexander Motin ng_acquire_read(node_p node, item_p item)
2032069154d5SJulian Elischer {
20339852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2034ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
2035069154d5SJulian Elischer 
2036394cb30aSAlexander Motin 	/* Reader needs node without writer and pending items. */
20374bd1b557SGleb Smirnoff 	for (;;) {
20389852972bSAlexander Motin 		long t = node->nd_input_queue.q_flags;
2039394cb30aSAlexander Motin 		if (t & NGQ_RMASK)
2040394cb30aSAlexander Motin 			break; /* Node is not ready for reader. */
20414bd1b557SGleb Smirnoff 		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
20424bd1b557SGleb Smirnoff 		    t + READER_INCREMENT)) {
2043069154d5SJulian Elischer 	    		/* Successfully grabbed node */
2044394cb30aSAlexander Motin 			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20459852972bSAlexander Motin 			    __func__, node->nd_ID, node, item);
2046069154d5SJulian Elischer 			return (item);
2047069154d5SJulian Elischer 		}
2048394cb30aSAlexander Motin 		cpu_spinwait();
2049394cb30aSAlexander Motin 	};
2050069154d5SJulian Elischer 
2051394cb30aSAlexander Motin 	/* Queue the request for later. */
20529852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_R);
2053f912c0f7SGleb Smirnoff 
2054f912c0f7SGleb Smirnoff 	return (NULL);
2055069154d5SJulian Elischer }
2056069154d5SJulian Elischer 
20578f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */
2058069154d5SJulian Elischer static __inline item_p
20599852972bSAlexander Motin ng_acquire_write(node_p node, item_p item)
2060069154d5SJulian Elischer {
20619852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2062ac5dd141SGleb Smirnoff 	    ("%s: working on deadnode", __func__));
2063ac5dd141SGleb Smirnoff 
2064394cb30aSAlexander Motin 	/* Writer needs completely idle node. */
20654bd1b557SGleb Smirnoff 	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
20664bd1b557SGleb Smirnoff 	    WRITER_ACTIVE)) {
2067394cb30aSAlexander Motin 	    	/* Successfully grabbed node */
20682955ee18SGleb Smirnoff 		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
20699852972bSAlexander Motin 		    __func__, node->nd_ID, node, item);
2070069154d5SJulian Elischer 		return (item);
2071069154d5SJulian Elischer 	}
2072069154d5SJulian Elischer 
2073394cb30aSAlexander Motin 	/* Queue the request for later. */
20749852972bSAlexander Motin 	ng_queue_rw(node, item, NGQRW_W);
2075f912c0f7SGleb Smirnoff 
2076f912c0f7SGleb Smirnoff 	return (NULL);
2077069154d5SJulian Elischer }
2078069154d5SJulian Elischer 
2079262dfd7fSJulian Elischer #if 0
2080262dfd7fSJulian Elischer static __inline item_p
20819852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item)
2082262dfd7fSJulian Elischer {
20839852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
20849852972bSAlexander Motin 	KASSERT(node != &ng_deadnode,
2085262dfd7fSJulian Elischer 	    ("%s: working on deadnode", __func__));
2086262dfd7fSJulian Elischer 
2087262dfd7fSJulian Elischer 	NGI_SET_WRITER(item);
2088262dfd7fSJulian Elischer 
20899852972bSAlexander Motin 	NG_QUEUE_LOCK(ngq);
2090262dfd7fSJulian Elischer 
2091262dfd7fSJulian Elischer 	/*
2092262dfd7fSJulian Elischer 	 * There will never be no readers as we are there ourselves.
2093262dfd7fSJulian Elischer 	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2094262dfd7fSJulian Elischer 	 * The caller we are running from will call ng_leave_read()
2095262dfd7fSJulian Elischer 	 * soon, so we must account for that. We must leave again with the
2096262dfd7fSJulian Elischer 	 * READER lock. If we find other readers, then
2097262dfd7fSJulian Elischer 	 * queue the request for later. However "later" may be rignt now
2098262dfd7fSJulian Elischer 	 * if there are no readers. We don't really care if there are queued
2099262dfd7fSJulian Elischer 	 * items as we will bypass them anyhow.
2100262dfd7fSJulian Elischer 	 */
21019852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
21029852972bSAlexander Motin 	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
21039852972bSAlexander Motin 		NG_QUEUE_UNLOCK(ngq);
2104262dfd7fSJulian Elischer 
2105262dfd7fSJulian Elischer 		/* It's just us, act on the item. */
2106262dfd7fSJulian Elischer 		/* will NOT drop writer lock when done */
2107262dfd7fSJulian Elischer 		ng_apply_item(node, item, 0);
2108262dfd7fSJulian Elischer 
2109262dfd7fSJulian Elischer 		/*
2110262dfd7fSJulian Elischer 		 * Having acted on the item, atomically
21114bd1b557SGleb Smirnoff 		 * downgrade back to READER and finish up.
2112262dfd7fSJulian Elischer 	 	 */
21134bd1b557SGleb Smirnoff 		atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2114262dfd7fSJulian Elischer 
2115262dfd7fSJulian Elischer 		/* Our caller will call ng_leave_read() */
2116262dfd7fSJulian Elischer 		return;
2117262dfd7fSJulian Elischer 	}
2118262dfd7fSJulian Elischer 	/*
2119262dfd7fSJulian Elischer 	 * It's not just us active, so queue us AT THE HEAD.
2120262dfd7fSJulian Elischer 	 * "Why?" I hear you ask.
2121262dfd7fSJulian Elischer 	 * Put us at the head of the queue as we've already been
2122262dfd7fSJulian Elischer 	 * through it once. If there is nothing else waiting,
2123262dfd7fSJulian Elischer 	 * set the correct flags.
2124262dfd7fSJulian Elischer 	 */
21259852972bSAlexander Motin 	if (STAILQ_EMPTY(&ngq->queue)) {
2126262dfd7fSJulian Elischer 		/* We've gone from, 0 to 1 item in the queue */
21279852972bSAlexander Motin 		atomic_set_int(&ngq->q_flags, OP_PENDING);
2128262dfd7fSJulian Elischer 
2129262dfd7fSJulian Elischer 		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
21309852972bSAlexander Motin 		    node->nd_ID, node);
2131262dfd7fSJulian Elischer 	};
21329852972bSAlexander Motin 	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
21339852972bSAlexander Motin 	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
21349852972bSAlexander Motin 	    __func__, node->nd_ID, node, item );
2135262dfd7fSJulian Elischer 
2136262dfd7fSJulian Elischer 	/* Reverse what we did above. That downgrades us back to reader */
21379852972bSAlexander Motin 	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2138394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
21399852972bSAlexander Motin 		ng_worklist_add(node);
21409852972bSAlexander Motin 	NG_QUEUE_UNLOCK(ngq);
2141262dfd7fSJulian Elischer 
2142262dfd7fSJulian Elischer 	return;
2143262dfd7fSJulian Elischer }
2144262dfd7fSJulian Elischer #endif
2145262dfd7fSJulian Elischer 
21468f9ac44aSAlexander Motin /* Release reader lock. */
2147069154d5SJulian Elischer static __inline void
21489852972bSAlexander Motin ng_leave_read(node_p node)
2149069154d5SJulian Elischer {
21509852972bSAlexander Motin 	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
2151069154d5SJulian Elischer }
2152069154d5SJulian Elischer 
21538f9ac44aSAlexander Motin /* Release writer lock. */
2154069154d5SJulian Elischer static __inline void
21559852972bSAlexander Motin ng_leave_write(node_p node)
2156069154d5SJulian Elischer {
21579852972bSAlexander Motin 	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
2158069154d5SJulian Elischer }
2159069154d5SJulian Elischer 
21608f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */
2161069154d5SJulian Elischer static void
21629852972bSAlexander Motin ng_flush_input_queue(node_p node)
2163069154d5SJulian Elischer {
21649852972bSAlexander Motin 	struct ng_queue *ngq = &node->nd_input_queue;
2165069154d5SJulian Elischer 	item_p item;
2166069154d5SJulian Elischer 
21672c8dda8dSWojciech A. Koszek 	NG_QUEUE_LOCK(ngq);
21689852972bSAlexander Motin 	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
21699852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
21709852972bSAlexander Motin 		if (STAILQ_EMPTY(&ngq->queue))
21719852972bSAlexander Motin 			atomic_clear_int(&ngq->q_flags, OP_PENDING);
21722c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
21734be59335SGleb Smirnoff 
21744be59335SGleb Smirnoff 		/* If the item is supplying a callback, call it with an error */
217510e87318SAlexander Motin 		if (item->apply != NULL) {
217610e87318SAlexander Motin 			if (item->depth == 1)
217710e87318SAlexander Motin 				item->apply->error = ENOENT;
217810e87318SAlexander Motin 			if (refcount_release(&item->apply->refs)) {
217910e87318SAlexander Motin 				(*item->apply->apply)(item->apply->context,
218010e87318SAlexander Motin 				    item->apply->error);
218110e87318SAlexander Motin 			}
2182eb2405ddSGleb Smirnoff 		}
2183505fad52SJulian Elischer 		NG_FREE_ITEM(item);
21842c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2185069154d5SJulian Elischer 	}
21862c8dda8dSWojciech A. Koszek 	NG_QUEUE_UNLOCK(ngq);
2187069154d5SJulian Elischer }
2188069154d5SJulian Elischer 
2189069154d5SJulian Elischer /***********************************************************************
2190069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
2191069154d5SJulian Elischer ***********************************************************************/
2192069154d5SJulian Elischer 
2193069154d5SJulian Elischer /*
21941acb27c6SJulian Elischer  * The module code should have filled out the item correctly by this stage:
2195069154d5SJulian Elischer  * Common:
2196069154d5SJulian Elischer  *    reference to destination node.
2197069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
2198e088dd4cSAlexander Motin  *    apply pointer must be or NULL or reference valid struct ng_apply_info.
2199069154d5SJulian Elischer  * Data:
2200069154d5SJulian Elischer  *    pointer to mbuf
2201069154d5SJulian Elischer  * Control_Message:
2202069154d5SJulian Elischer  *    pointer to msg.
2203069154d5SJulian Elischer  *    ID of original sender node. (return address)
22041acb27c6SJulian Elischer  * Function:
22051acb27c6SJulian Elischer  *    Function pointer
22061acb27c6SJulian Elischer  *    void * argument
22071acb27c6SJulian Elischer  *    integer argument
2208069154d5SJulian Elischer  *
2209069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
2210069154d5SJulian Elischer  */
2211069154d5SJulian Elischer 
2212069154d5SJulian Elischer int
221342282202SGleb Smirnoff ng_snd_item(item_p item, int flags)
2214069154d5SJulian Elischer {
2215e088dd4cSAlexander Motin 	hook_p hook;
2216e088dd4cSAlexander Motin 	node_p node;
221742282202SGleb Smirnoff 	int queue, rw;
2218e088dd4cSAlexander Motin 	struct ng_queue *ngq;
221932b33288SGleb Smirnoff 	int error = 0;
2220069154d5SJulian Elischer 
2221f50597f5SAlexander Motin 	/* We are sending item, so it must be present! */
2222f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2223e088dd4cSAlexander Motin 
2224e088dd4cSAlexander Motin #ifdef	NETGRAPH_DEBUG
2225e088dd4cSAlexander Motin 	_ngi_check(item, __FILE__, __LINE__);
2226e088dd4cSAlexander Motin #endif
2227e088dd4cSAlexander Motin 
2228f50597f5SAlexander Motin 	/* Item was sent once more, postpone apply() call. */
2229e088dd4cSAlexander Motin 	if (item->apply)
2230e088dd4cSAlexander Motin 		refcount_acquire(&item->apply->refs);
2231e088dd4cSAlexander Motin 
2232e088dd4cSAlexander Motin 	node = NGI_NODE(item);
2233f50597f5SAlexander Motin 	/* Node is never optional. */
2234f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2235e088dd4cSAlexander Motin 
2236e72a98f4SAlexander Motin 	hook = NGI_HOOK(item);
2237f50597f5SAlexander Motin 	/* Valid hook and mbuf are mandatory for data. */
2238f50597f5SAlexander Motin 	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2239f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
224010204449SJulian Elischer 		if (NGI_M(item) == NULL)
2241e088dd4cSAlexander Motin 			ERROUT(EINVAL);
2242069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
22436f683eeeSGleb Smirnoff 	}
22446f683eeeSGleb Smirnoff 
2245069154d5SJulian Elischer 	/*
2246f50597f5SAlexander Motin 	 * If the item or the node specifies single threading, force
2247f50597f5SAlexander Motin 	 * writer semantics. Similarly, the node may say one hook always
2248f50597f5SAlexander Motin 	 * produces writers. These are overrides.
2249069154d5SJulian Elischer 	 */
2250db3408aeSAlexander Motin 	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2251f50597f5SAlexander Motin 	    (node->nd_flags & NGF_FORCE_WRITER) ||
2252f50597f5SAlexander Motin 	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2253069154d5SJulian Elischer 		rw = NGQRW_W;
2254f50597f5SAlexander Motin 	} else {
2255f50597f5SAlexander Motin 		rw = NGQRW_R;
2256f50597f5SAlexander Motin 	}
22576f683eeeSGleb Smirnoff 
225881a253a4SAlexander Motin 	/*
2259f089869fSMarko Zec 	 * If sender or receiver requests queued delivery, or call graph
2260f089869fSMarko Zec 	 * loops back from outbound to inbound path, or stack usage
226181a253a4SAlexander Motin 	 * level is dangerous - enqueue message.
226281a253a4SAlexander Motin 	 */
226381a253a4SAlexander Motin 	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
226481a253a4SAlexander Motin 		queue = 1;
2265f089869fSMarko Zec 	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2266f089869fSMarko Zec 	    curthread->td_ng_outbound) {
2267f089869fSMarko Zec 		queue = 1;
2268f50597f5SAlexander Motin 	} else {
2269f50597f5SAlexander Motin 		queue = 0;
227081a253a4SAlexander Motin #ifdef GET_STACK_USAGE
2271d4529f98SAlexander Motin 		/*
2272942fe01fSDmitry Morozovsky 		 * Most of netgraph nodes have small stack consumption and
2273f50597f5SAlexander Motin 		 * for them 25% of free stack space is more than enough.
2274d4529f98SAlexander Motin 		 * Nodes/hooks with higher stack usage should be marked as
2275f9773372SDmitry Morozovsky 		 * HI_STACK. For them 50% of stack will be guaranteed then.
2276f50597f5SAlexander Motin 		 * XXX: Values 25% and 50% are completely empirical.
2277d4529f98SAlexander Motin 		 */
2278f50597f5SAlexander Motin 		size_t	st, su, sl;
227981a253a4SAlexander Motin 		GET_STACK_USAGE(st, su);
2280f50597f5SAlexander Motin 		sl = st - su;
22814bd1b557SGleb Smirnoff 		if ((sl * 4 < st) || ((sl * 2 < st) &&
22824bd1b557SGleb Smirnoff 		    ((node->nd_flags & NGF_HI_STACK) || (hook &&
22834bd1b557SGleb Smirnoff 		    (hook->hk_flags & HK_HI_STACK)))))
228481a253a4SAlexander Motin 			queue = 1;
228581a253a4SAlexander Motin #endif
2286f50597f5SAlexander Motin 	}
228781a253a4SAlexander Motin 
2288069154d5SJulian Elischer 	if (queue) {
228910e87318SAlexander Motin 		item->depth = 1;
2290394cb30aSAlexander Motin 		/* Put it on the queue for that node*/
22919852972bSAlexander Motin 		ng_queue_rw(node, item, rw);
2292f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2293069154d5SJulian Elischer 	}
2294069154d5SJulian Elischer 
2295069154d5SJulian Elischer 	/*
2296069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
2297069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
2298069154d5SJulian Elischer 	 */
229932b33288SGleb Smirnoff  	if (rw == NGQRW_R)
23009852972bSAlexander Motin 		item = ng_acquire_read(node, item);
230132b33288SGleb Smirnoff 	else
23029852972bSAlexander Motin 		item = ng_acquire_write(node, item);
23034cf49a43SJulian Elischer 
2304f50597f5SAlexander Motin 	/* Item was queued while trying to get permission. */
2305f50597f5SAlexander Motin 	if (item == NULL)
2306f50597f5SAlexander Motin 		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
2307069154d5SJulian Elischer 
23085951069aSJulian Elischer 	NGI_GET_NODE(item, node); /* zaps stored node */
23095951069aSJulian Elischer 
231010e87318SAlexander Motin 	item->depth++;
231127757487SGleb Smirnoff 	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
2312069154d5SJulian Elischer 
2313394cb30aSAlexander Motin 	/* If something is waiting on queue and ready, schedule it. */
23149852972bSAlexander Motin 	ngq = &node->nd_input_queue;
2315394cb30aSAlexander Motin 	if (QUEUE_ACTIVE(ngq)) {
23162c8dda8dSWojciech A. Koszek 		NG_QUEUE_LOCK(ngq);
2317394cb30aSAlexander Motin 		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
23189852972bSAlexander Motin 			ng_worklist_add(node);
23192c8dda8dSWojciech A. Koszek 		NG_QUEUE_UNLOCK(ngq);
2320394cb30aSAlexander Motin 	}
2321394cb30aSAlexander Motin 
2322394cb30aSAlexander Motin 	/*
2323394cb30aSAlexander Motin 	 * Node may go away as soon as we remove the reference.
2324394cb30aSAlexander Motin 	 * Whatever we do, DO NOT access the node again!
2325394cb30aSAlexander Motin 	 */
2326394cb30aSAlexander Motin 	NG_NODE_UNREF(node);
2327069154d5SJulian Elischer 
23281acb27c6SJulian Elischer 	return (error);
2329e088dd4cSAlexander Motin 
2330e088dd4cSAlexander Motin done:
2331f50597f5SAlexander Motin 	/* If was not sent, apply callback here. */
233210e87318SAlexander Motin 	if (item->apply != NULL) {
233310e87318SAlexander Motin 		if (item->depth == 0 && error != 0)
233410e87318SAlexander Motin 			item->apply->error = error;
233510e87318SAlexander Motin 		if (refcount_release(&item->apply->refs)) {
233610e87318SAlexander Motin 			(*item->apply->apply)(item->apply->context,
233710e87318SAlexander Motin 			    item->apply->error);
233810e87318SAlexander Motin 		}
233910e87318SAlexander Motin 	}
2340e72a98f4SAlexander Motin 
2341e088dd4cSAlexander Motin 	NG_FREE_ITEM(item);
2342e088dd4cSAlexander Motin 	return (error);
2343069154d5SJulian Elischer }
2344069154d5SJulian Elischer 
2345069154d5SJulian Elischer /*
2346069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2347069154d5SJulian Elischer  * It should contain all the information needed
2348069154d5SJulian Elischer  * to run it on the appropriate node/hook.
2349e088dd4cSAlexander Motin  * If there is apply pointer and we own the last reference, call apply().
23504cf49a43SJulian Elischer  */
235127757487SGleb Smirnoff static int
2352714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw)
2353069154d5SJulian Elischer {
2354069154d5SJulian Elischer 	hook_p  hook;
2355069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2356c4b5eea4SJulian Elischer 	ng_rcvmsg_t *rcvmsg;
2357e088dd4cSAlexander Motin 	struct ng_apply_info *apply;
235810e87318SAlexander Motin 	int	error = 0, depth;
2359069154d5SJulian Elischer 
2360f50597f5SAlexander Motin 	/* Node and item are never optional. */
2361f50597f5SAlexander Motin 	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2362f50597f5SAlexander Motin 	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2363f50597f5SAlexander Motin 
23641acb27c6SJulian Elischer 	NGI_GET_HOOK(item, hook); /* clears stored hook */
236530400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2366069154d5SJulian Elischer 	_ngi_check(item, __FILE__, __LINE__);
2367069154d5SJulian Elischer #endif
23688afe16d5SGleb Smirnoff 
23698afe16d5SGleb Smirnoff 	apply = item->apply;
237010e87318SAlexander Motin 	depth = item->depth;
23718afe16d5SGleb Smirnoff 
23726b795970SJulian Elischer 	switch (item->el_flags & NGQF_TYPE) {
2373069154d5SJulian Elischer 	case NGQF_DATA:
2374069154d5SJulian Elischer 		/*
2375069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2376069154d5SJulian Elischer 		 */
2377f50597f5SAlexander Motin 		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2378f50597f5SAlexander Motin 		if (NG_HOOK_NOT_VALID(hook) ||
2379f50597f5SAlexander Motin 		    NG_NODE_NOT_VALID(node)) {
2380a54a69d7SJulian Elischer 			error = EIO;
2381069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2382c4b5eea4SJulian Elischer 			break;
2383069154d5SJulian Elischer 		}
2384c4b5eea4SJulian Elischer 		/*
2385c4b5eea4SJulian Elischer 		 * If no receive method, just silently drop it.
23864bd1b557SGleb Smirnoff 		 * Give preference to the hook over-ride method.
2387c4b5eea4SJulian Elischer 		 */
23884bd1b557SGleb Smirnoff 		if ((!(rcvdata = hook->hk_rcvdata)) &&
23894bd1b557SGleb Smirnoff 		    (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
2390c4b5eea4SJulian Elischer 			error = 0;
2391c4b5eea4SJulian Elischer 			NG_FREE_ITEM(item);
2392c4b5eea4SJulian Elischer 			break;
2393c4b5eea4SJulian Elischer 		}
2394a54a69d7SJulian Elischer 		error = (*rcvdata)(hook, item);
2395069154d5SJulian Elischer 		break;
2396069154d5SJulian Elischer 	case NGQF_MESG:
2397e72a98f4SAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook)) {
2398069154d5SJulian Elischer 			/*
2399e72a98f4SAlexander Motin 			 * The hook has been zapped then we can't use it.
2400e72a98f4SAlexander Motin 			 * Immediately drop its reference.
2401069154d5SJulian Elischer 			 * The message may not need it.
2402069154d5SJulian Elischer 			 */
240330400f03SJulian Elischer 			NG_HOOK_UNREF(hook);
2404069154d5SJulian Elischer 			hook = NULL;
2405069154d5SJulian Elischer 		}
2406069154d5SJulian Elischer 		/*
2407069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2408069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2409069154d5SJulian Elischer 		 */
241030400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
24116b795970SJulian Elischer 			TRAP_ERROR();
2412a54a69d7SJulian Elischer 			error = EINVAL;
2413069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2414e72a98f4SAlexander Motin 			break;
2415e72a98f4SAlexander Motin 		}
2416069154d5SJulian Elischer 		/*
2417069154d5SJulian Elischer 		 * Call the appropriate message handler for the object.
2418069154d5SJulian Elischer 		 * It is up to the message handler to free the message.
2419069154d5SJulian Elischer 		 * If it's a generic message, handle it generically,
2420e72a98f4SAlexander Motin 		 * otherwise call the type's message handler (if it exists).
2421069154d5SJulian Elischer 		 * XXX (race). Remember that a queued message may
2422069154d5SJulian Elischer 		 * reference a node or hook that has just been
2423069154d5SJulian Elischer 		 * invalidated. It will exist as the queue code
2424069154d5SJulian Elischer 		 * is holding a reference, but..
2425069154d5SJulian Elischer 		 */
2426e72a98f4SAlexander Motin 		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2427e72a98f4SAlexander Motin 		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2428a54a69d7SJulian Elischer 			error = ng_generic_msg(node, item, hook);
2429c4b5eea4SJulian Elischer 			break;
2430c4b5eea4SJulian Elischer 		}
2431e72a98f4SAlexander Motin 		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2432e72a98f4SAlexander Motin 		    (!(rcvmsg = node->nd_type->rcvmsg))) {
24336b795970SJulian Elischer 			TRAP_ERROR();
2434a54a69d7SJulian Elischer 			error = 0;
2435069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2436c4b5eea4SJulian Elischer 			break;
2437069154d5SJulian Elischer 		}
2438a54a69d7SJulian Elischer 		error = (*rcvmsg)(node, item, hook);
2439069154d5SJulian Elischer 		break;
24406b795970SJulian Elischer 	case NGQF_FN:
2441e088dd4cSAlexander Motin 	case NGQF_FN2:
2442e088dd4cSAlexander Motin 		/*
244374c9119dSAlexander Motin 		 * In the case of the shutdown message we allow it to hit
2444e088dd4cSAlexander Motin 		 * even if the node is invalid.
2445e088dd4cSAlexander Motin 		 */
244674c9119dSAlexander Motin 		if (NG_NODE_NOT_VALID(node) &&
244774c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmnode) {
2448e088dd4cSAlexander Motin 			TRAP_ERROR();
2449e088dd4cSAlexander Motin 			error = EINVAL;
2450e088dd4cSAlexander Motin 			NG_FREE_ITEM(item);
2451e088dd4cSAlexander Motin 			break;
2452e088dd4cSAlexander Motin 		}
245374c9119dSAlexander Motin 		/* Same is about some internal functions and invalid hook. */
245474c9119dSAlexander Motin 		if (hook && NG_HOOK_NOT_VALID(hook) &&
245574c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part2 &&
245674c9119dSAlexander Motin 		    NGI_FN2(item) != &ng_con_part3 &&
245774c9119dSAlexander Motin 		    NGI_FN(item) != &ng_rmhook_part2) {
245874c9119dSAlexander Motin 			TRAP_ERROR();
245974c9119dSAlexander Motin 			error = EINVAL;
246074c9119dSAlexander Motin 			NG_FREE_ITEM(item);
246174c9119dSAlexander Motin 			break;
246274c9119dSAlexander Motin 		}
246374c9119dSAlexander Motin 
2464b332b91fSGleb Smirnoff 		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2465b332b91fSGleb Smirnoff 			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2466b332b91fSGleb Smirnoff 			    NGI_ARG2(item));
2467b332b91fSGleb Smirnoff 			NG_FREE_ITEM(item);
2468b332b91fSGleb Smirnoff 		} else	/* it is NGQF_FN2 */
2469e088dd4cSAlexander Motin 			error = (*NGI_FN2(item))(node, item, hook);
2470e088dd4cSAlexander Motin 		break;
2471069154d5SJulian Elischer 	}
2472069154d5SJulian Elischer 	/*
2473069154d5SJulian Elischer 	 * We held references on some of the resources
2474069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2475069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2476069154d5SJulian Elischer 	 */
2477e72a98f4SAlexander Motin 	if (hook)
247830400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2479069154d5SJulian Elischer 
2480f50597f5SAlexander Motin  	if (rw == NGQRW_R)
24819852972bSAlexander Motin 		ng_leave_read(node);
2482f50597f5SAlexander Motin 	else
24839852972bSAlexander Motin 		ng_leave_write(node);
24848afe16d5SGleb Smirnoff 
24858afe16d5SGleb Smirnoff 	/* Apply callback. */
248610e87318SAlexander Motin 	if (apply != NULL) {
248710e87318SAlexander Motin 		if (depth == 1 && error != 0)
248810e87318SAlexander Motin 			apply->error = error;
248910e87318SAlexander Motin 		if (refcount_release(&apply->refs))
249010e87318SAlexander Motin 			(*apply->apply)(apply->context, apply->error);
249110e87318SAlexander Motin 	}
24928afe16d5SGleb Smirnoff 
249327757487SGleb Smirnoff 	return (error);
2494069154d5SJulian Elischer }
2495069154d5SJulian Elischer 
2496069154d5SJulian Elischer /***********************************************************************
2497069154d5SJulian Elischer  * Implement the 'generic' control messages
2498069154d5SJulian Elischer  ***********************************************************************/
2499069154d5SJulian Elischer static int
2500069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
25014cf49a43SJulian Elischer {
25024cf49a43SJulian Elischer 	int error = 0;
2503069154d5SJulian Elischer 	struct ng_mesg *msg;
2504069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
25054cf49a43SJulian Elischer 
2506069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
25074cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
25086b795970SJulian Elischer 		TRAP_ERROR();
2509069154d5SJulian Elischer 		error = EINVAL;
2510069154d5SJulian Elischer 		goto out;
25114cf49a43SJulian Elischer 	}
25124cf49a43SJulian Elischer 	switch (msg->header.cmd) {
25134cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
25141acb27c6SJulian Elischer 		ng_rmnode(here, NULL, NULL, 0);
25154cf49a43SJulian Elischer 		break;
25164cf49a43SJulian Elischer 	case NGM_MKPEER:
25174cf49a43SJulian Elischer 	    {
25184cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
25194cf49a43SJulian Elischer 
25204cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
25216b795970SJulian Elischer 			TRAP_ERROR();
2522069154d5SJulian Elischer 			error = EINVAL;
2523069154d5SJulian Elischer 			break;
25244cf49a43SJulian Elischer 		}
25254cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
25264cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
25274cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
25284cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
25294cf49a43SJulian Elischer 		break;
25304cf49a43SJulian Elischer 	    }
25314cf49a43SJulian Elischer 	case NGM_CONNECT:
25324cf49a43SJulian Elischer 	    {
25334cf49a43SJulian Elischer 		struct ngm_connect *const con =
25344cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
25354cf49a43SJulian Elischer 		node_p node2;
25364cf49a43SJulian Elischer 
25374cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
25386b795970SJulian Elischer 			TRAP_ERROR();
2539069154d5SJulian Elischer 			error = EINVAL;
2540069154d5SJulian Elischer 			break;
25414cf49a43SJulian Elischer 		}
25424cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
25434cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
25444cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2545069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2546069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
25474cf49a43SJulian Elischer 		if (error)
25484cf49a43SJulian Elischer 			break;
2549e088dd4cSAlexander Motin 		error = ng_con_nodes(item, here, con->ourhook,
2550e088dd4cSAlexander Motin 		    node2, con->peerhook);
255130400f03SJulian Elischer 		NG_NODE_UNREF(node2);
25524cf49a43SJulian Elischer 		break;
25534cf49a43SJulian Elischer 	    }
25544cf49a43SJulian Elischer 	case NGM_NAME:
25554cf49a43SJulian Elischer 	    {
25564cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
25574cf49a43SJulian Elischer 
25584cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
25596b795970SJulian Elischer 			TRAP_ERROR();
2560069154d5SJulian Elischer 			error = EINVAL;
2561069154d5SJulian Elischer 			break;
25624cf49a43SJulian Elischer 		}
25634cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
25644cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
25654cf49a43SJulian Elischer 		break;
25664cf49a43SJulian Elischer 	    }
25674cf49a43SJulian Elischer 	case NGM_RMHOOK:
25684cf49a43SJulian Elischer 	    {
25694cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
25704cf49a43SJulian Elischer 		hook_p hook;
25714cf49a43SJulian Elischer 
25724cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
25736b795970SJulian Elischer 			TRAP_ERROR();
2574069154d5SJulian Elischer 			error = EINVAL;
2575069154d5SJulian Elischer 			break;
25764cf49a43SJulian Elischer 		}
25774cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2578899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
25794cf49a43SJulian Elischer 			ng_destroy_hook(hook);
25804cf49a43SJulian Elischer 		break;
25814cf49a43SJulian Elischer 	    }
25824cf49a43SJulian Elischer 	case NGM_NODEINFO:
25834cf49a43SJulian Elischer 	    {
25844cf49a43SJulian Elischer 		struct nodeinfo *ni;
25854cf49a43SJulian Elischer 
2586069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
25874cf49a43SJulian Elischer 		if (resp == NULL) {
25884cf49a43SJulian Elischer 			error = ENOMEM;
25894cf49a43SJulian Elischer 			break;
25904cf49a43SJulian Elischer 		}
25914cf49a43SJulian Elischer 
25924cf49a43SJulian Elischer 		/* Fill in node info */
2593069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
259430400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
259587e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
259687e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2597dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
259830400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
25994cf49a43SJulian Elischer 		break;
26004cf49a43SJulian Elischer 	    }
26014cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
26024cf49a43SJulian Elischer 	    {
260330400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
26044cf49a43SJulian Elischer 		struct hooklist *hl;
26054cf49a43SJulian Elischer 		struct nodeinfo *ni;
26064cf49a43SJulian Elischer 		hook_p hook;
26074cf49a43SJulian Elischer 
26084cf49a43SJulian Elischer 		/* Get response struct */
26094bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*hl) +
26104bd1b557SGleb Smirnoff 		    (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2611069154d5SJulian Elischer 		if (resp == NULL) {
26124cf49a43SJulian Elischer 			error = ENOMEM;
26134cf49a43SJulian Elischer 			break;
26144cf49a43SJulian Elischer 		}
2615069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
26164cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
26174cf49a43SJulian Elischer 
26184cf49a43SJulian Elischer 		/* Fill in node info */
261930400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
262087e2c66aSHartmut Brandt 			strcpy(ni->name, NG_NODE_NAME(here));
262187e2c66aSHartmut Brandt 		strcpy(ni->type, here->nd_type->name);
2622dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
26234cf49a43SJulian Elischer 
26244cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
26254cf49a43SJulian Elischer 		ni->hooks = 0;
262630400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
26274cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
26284cf49a43SJulian Elischer 
26294cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
26304cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
26316e551fb6SDavid E. O'Brien 				    __func__, "hooks");
26324cf49a43SJulian Elischer 				break;
26334cf49a43SJulian Elischer 			}
263430400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
26354cf49a43SJulian Elischer 				continue;
263687e2c66aSHartmut Brandt 			strcpy(link->ourhook, NG_HOOK_NAME(hook));
263787e2c66aSHartmut Brandt 			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
263830400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
263987e2c66aSHartmut Brandt 				strcpy(link->nodeinfo.name,
264087e2c66aSHartmut Brandt 				    NG_PEER_NODE_NAME(hook));
264187e2c66aSHartmut Brandt 			strcpy(link->nodeinfo.type,
264287e2c66aSHartmut Brandt 			   NG_PEER_NODE(hook)->nd_type->name);
264330400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
264430400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
26454cf49a43SJulian Elischer 			ni->hooks++;
26464cf49a43SJulian Elischer 		}
26474cf49a43SJulian Elischer 		break;
26484cf49a43SJulian Elischer 	    }
26494cf49a43SJulian Elischer 
26504cf49a43SJulian Elischer 	case NGM_LISTNODES:
26514cf49a43SJulian Elischer 	    {
26524cf49a43SJulian Elischer 		struct namelist *nl;
26534cf49a43SJulian Elischer 		node_p node;
2654*687adb70SGleb Smirnoff 		int i;
2655*687adb70SGleb Smirnoff 
2656*687adb70SGleb Smirnoff 		IDHASH_RLOCK();
2657*687adb70SGleb Smirnoff 		/* Get response struct. */
2658*687adb70SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2659*687adb70SGleb Smirnoff 		    (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT | M_ZERO);
2660*687adb70SGleb Smirnoff 		if (resp == NULL) {
2661*687adb70SGleb Smirnoff 			IDHASH_RUNLOCK();
2662*687adb70SGleb Smirnoff 			error = ENOMEM;
2663*687adb70SGleb Smirnoff 			break;
2664*687adb70SGleb Smirnoff 		}
2665*687adb70SGleb Smirnoff 		nl = (struct namelist *) resp->data;
2666*687adb70SGleb Smirnoff 
2667*687adb70SGleb Smirnoff 		/* Cycle through the lists of nodes. */
2668*687adb70SGleb Smirnoff 		nl->numnames = 0;
2669*687adb70SGleb Smirnoff 		for (i = 0; i <= V_ng_ID_hmask; i++) {
2670*687adb70SGleb Smirnoff 			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
2671*687adb70SGleb Smirnoff 				struct nodeinfo *const np =
2672*687adb70SGleb Smirnoff 				    &nl->nodeinfo[nl->numnames];
2673*687adb70SGleb Smirnoff 
2674*687adb70SGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2675*687adb70SGleb Smirnoff 					continue;
2676*687adb70SGleb Smirnoff 				if (NG_NODE_HAS_NAME(node))
2677*687adb70SGleb Smirnoff 					strcpy(np->name, NG_NODE_NAME(node));
2678*687adb70SGleb Smirnoff 				strcpy(np->type, node->nd_type->name);
2679*687adb70SGleb Smirnoff 				np->id = ng_node2ID(node);
2680*687adb70SGleb Smirnoff 				np->hooks = node->nd_numhooks;
2681*687adb70SGleb Smirnoff 				KASSERT(nl->numnames < V_ng_nodes,
2682*687adb70SGleb Smirnoff 				    ("%s: no space", __func__));
2683*687adb70SGleb Smirnoff 				nl->numnames++;
2684*687adb70SGleb Smirnoff 			}
2685*687adb70SGleb Smirnoff 		}
2686*687adb70SGleb Smirnoff 		IDHASH_RUNLOCK();
2687*687adb70SGleb Smirnoff 		break;
2688*687adb70SGleb Smirnoff 	    }
2689*687adb70SGleb Smirnoff 	case NGM_LISTNAMES:
2690*687adb70SGleb Smirnoff 	    {
2691*687adb70SGleb Smirnoff 		struct namelist *nl;
2692*687adb70SGleb Smirnoff 		node_p node;
2693*687adb70SGleb Smirnoff 		int i;
26944cf49a43SJulian Elischer 
2695c4282b74SGleb Smirnoff 		NAMEHASH_RLOCK();
2696*687adb70SGleb Smirnoff 		/* Get response struct. */
26974bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2698*687adb70SGleb Smirnoff 		    (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
2699069154d5SJulian Elischer 		if (resp == NULL) {
2700c4282b74SGleb Smirnoff 			NAMEHASH_RUNLOCK();
27014cf49a43SJulian Elischer 			error = ENOMEM;
27024cf49a43SJulian Elischer 			break;
27034cf49a43SJulian Elischer 		}
2704069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
27054cf49a43SJulian Elischer 
2706*687adb70SGleb Smirnoff 		/* Cycle through the lists of nodes. */
27074cf49a43SJulian Elischer 		nl->numnames = 0;
2708*687adb70SGleb Smirnoff 		for (i = 0; i <= V_ng_name_hmask; i++) {
2709603724d3SBjoern A. Zeeb 			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2710cfea3f85SAlexander Motin 				struct nodeinfo *const np =
2711cfea3f85SAlexander Motin 				    &nl->nodeinfo[nl->numnames];
27124cf49a43SJulian Elischer 
2713b96baf0aSGleb Smirnoff 				if (NG_NODE_NOT_VALID(node))
2714b96baf0aSGleb Smirnoff 					continue;
271587e2c66aSHartmut Brandt 				strcpy(np->name, NG_NODE_NAME(node));
271687e2c66aSHartmut Brandt 				strcpy(np->type, node->nd_type->name);
2717dc90cad9SJulian Elischer 				np->id = ng_node2ID(node);
271830400f03SJulian Elischer 				np->hooks = node->nd_numhooks;
2719*687adb70SGleb Smirnoff 				KASSERT(nl->numnames < V_ng_named_nodes,
2720*687adb70SGleb Smirnoff 				    ("%s: no space", __func__));
27214cf49a43SJulian Elischer 				nl->numnames++;
27224cf49a43SJulian Elischer 			}
2723cfea3f85SAlexander Motin 		}
2724c4282b74SGleb Smirnoff 		NAMEHASH_RUNLOCK();
27254cf49a43SJulian Elischer 		break;
27264cf49a43SJulian Elischer 	    }
27274cf49a43SJulian Elischer 
27284cf49a43SJulian Elischer 	case NGM_LISTTYPES:
27294cf49a43SJulian Elischer 	    {
27304cf49a43SJulian Elischer 		struct typelist *tl;
27314cf49a43SJulian Elischer 		struct ng_type *type;
27324cf49a43SJulian Elischer 		int num = 0;
27334cf49a43SJulian Elischer 
2734c4282b74SGleb Smirnoff 		TYPELIST_RLOCK();
27354cf49a43SJulian Elischer 		/* Count number of types */
2736c4282b74SGleb Smirnoff 		LIST_FOREACH(type, &ng_typelist, types)
27374cf49a43SJulian Elischer 			num++;
27384cf49a43SJulian Elischer 
27394cf49a43SJulian Elischer 		/* Get response struct */
27404bd1b557SGleb Smirnoff 		NG_MKRESPONSE(resp, msg, sizeof(*tl) +
27414bd1b557SGleb Smirnoff 		    (num * sizeof(struct typeinfo)), M_NOWAIT);
2742069154d5SJulian Elischer 		if (resp == NULL) {
2743c4282b74SGleb Smirnoff 			TYPELIST_RUNLOCK();
27444cf49a43SJulian Elischer 			error = ENOMEM;
27454cf49a43SJulian Elischer 			break;
27464cf49a43SJulian Elischer 		}
2747069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
27484cf49a43SJulian Elischer 
27494cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
27504cf49a43SJulian Elischer 		tl->numtypes = 0;
2751069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
27524cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
27534cf49a43SJulian Elischer 
275487e2c66aSHartmut Brandt 			strcpy(tp->type_name, type->name);
2755c73b94a2SJulian Elischer 			tp->numnodes = type->refs - 1; /* don't count list */
2756c4282b74SGleb Smirnoff 			KASSERT(tl->numtypes < num, ("%s: no space", __func__));
27574cf49a43SJulian Elischer 			tl->numtypes++;
27584cf49a43SJulian Elischer 		}
2759c4282b74SGleb Smirnoff 		TYPELIST_RUNLOCK();
27604cf49a43SJulian Elischer 		break;
27614cf49a43SJulian Elischer 	    }
27624cf49a43SJulian Elischer 
2763f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2764f8307e12SArchie Cobbs 	    {
27657133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2766f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2767f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2768069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2769f8307e12SArchie Cobbs 
2770f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2771f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
27724c9b5910SGleb Smirnoff 		if (msg->header.arglen < sizeof(struct ng_mesg) ||
27734c9b5910SGleb Smirnoff 		    (msg->header.arglen - sizeof(struct ng_mesg) <
27744c9b5910SGleb Smirnoff 		    binary->header.arglen)) {
27756b795970SJulian Elischer 			TRAP_ERROR();
2776f8307e12SArchie Cobbs 			error = EINVAL;
2777f8307e12SArchie Cobbs 			break;
2778f8307e12SArchie Cobbs 		}
2779f8307e12SArchie Cobbs 
2780f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2781069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2782069154d5SJulian Elischer 		if (resp == NULL) {
2783f8307e12SArchie Cobbs 			error = ENOMEM;
2784f8307e12SArchie Cobbs 			break;
2785f8307e12SArchie Cobbs 		}
2786069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2787f8307e12SArchie Cobbs 
2788f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2789f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2790f8307e12SArchie Cobbs 
2791f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
27924bd1b557SGleb Smirnoff 		for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
27934bd1b557SGleb Smirnoff 		    c++) {
27944bd1b557SGleb Smirnoff 			if (binary->header.typecookie == c->cookie &&
27954bd1b557SGleb Smirnoff 			    binary->header.cmd == c->cmd)
2796f8307e12SArchie Cobbs 				break;
2797f8307e12SArchie Cobbs 		}
2798f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2799f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
28004bd1b557SGleb Smirnoff 				if (binary->header.typecookie == c->cookie &&
28014bd1b557SGleb Smirnoff 				    binary->header.cmd == c->cmd)
2802f8307e12SArchie Cobbs 					break;
2803f8307e12SArchie Cobbs 			}
2804f8307e12SArchie Cobbs 			if (c->name == NULL) {
2805069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2806f8307e12SArchie Cobbs 				error = ENOSYS;
2807f8307e12SArchie Cobbs 				break;
2808f8307e12SArchie Cobbs 			}
2809f8307e12SArchie Cobbs 		}
2810f8307e12SArchie Cobbs 
2811f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2812f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2813f8307e12SArchie Cobbs 		    "%s", c->name);
2814f8307e12SArchie Cobbs 
2815f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2816f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2817f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
281870de87f2SJulian Elischer 		if (argstype == NULL) {
2819f8307e12SArchie Cobbs 			*ascii->data = '\0';
282070de87f2SJulian Elischer 		} else {
2821f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2822f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2823f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2824069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2825f8307e12SArchie Cobbs 				break;
2826f8307e12SArchie Cobbs 			}
2827f8307e12SArchie Cobbs 		}
2828f8307e12SArchie Cobbs 
2829f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2830f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2831f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2832069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2833f8307e12SArchie Cobbs 		break;
2834f8307e12SArchie Cobbs 	    }
2835f8307e12SArchie Cobbs 
2836f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2837f8307e12SArchie Cobbs 	    {
283898a5a343SMarko Zec 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2839f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2840f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2841069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
284252ec4a03SArchie Cobbs 		int off = 0;
2843f8307e12SArchie Cobbs 
2844f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2845f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
28464c9b5910SGleb Smirnoff 		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
28474c9b5910SGleb Smirnoff 		    (ascii->header.arglen < 1) ||
28484c9b5910SGleb Smirnoff 		    (msg->header.arglen < sizeof(*ascii) +
28494c9b5910SGleb Smirnoff 		    ascii->header.arglen)) {
28506b795970SJulian Elischer 			TRAP_ERROR();
2851f8307e12SArchie Cobbs 			error = EINVAL;
2852f8307e12SArchie Cobbs 			break;
2853f8307e12SArchie Cobbs 		}
2854f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2855f8307e12SArchie Cobbs 
2856f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2857069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2858069154d5SJulian Elischer 		if (resp == NULL) {
2859f8307e12SArchie Cobbs 			error = ENOMEM;
2860f8307e12SArchie Cobbs 			break;
2861f8307e12SArchie Cobbs 		}
2862069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2863f8307e12SArchie Cobbs 
2864f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2865f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2866f8307e12SArchie Cobbs 
2867f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
286830400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2869f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2870f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2871f8307e12SArchie Cobbs 				break;
2872f8307e12SArchie Cobbs 		}
2873f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2874f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2875f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2876f8307e12SArchie Cobbs 					break;
2877f8307e12SArchie Cobbs 			}
2878f8307e12SArchie Cobbs 			if (c->name == NULL) {
2879069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2880f8307e12SArchie Cobbs 				error = ENOSYS;
2881f8307e12SArchie Cobbs 				break;
2882f8307e12SArchie Cobbs 			}
2883f8307e12SArchie Cobbs 		}
2884f8307e12SArchie Cobbs 
2885f8307e12SArchie Cobbs 		/* Convert command name to binary */
2886f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2887f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2888f8307e12SArchie Cobbs 
2889f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2890f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2891f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
289270de87f2SJulian Elischer 		if (argstype == NULL) {
2893f8307e12SArchie Cobbs 			bufSize = 0;
289470de87f2SJulian Elischer 		} else {
28954bd1b557SGleb Smirnoff 			if ((error = ng_parse(argstype, ascii->data, &off,
28964bd1b557SGleb Smirnoff 			    (u_char *)binary->data, &bufSize)) != 0) {
2897069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2898f8307e12SArchie Cobbs 				break;
2899f8307e12SArchie Cobbs 			}
2900f8307e12SArchie Cobbs 		}
2901f8307e12SArchie Cobbs 
2902f8307e12SArchie Cobbs 		/* Return the result */
2903f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2904069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2905f8307e12SArchie Cobbs 		break;
2906f8307e12SArchie Cobbs 	    }
2907f8307e12SArchie Cobbs 
29087095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
29094cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
29104cf49a43SJulian Elischer 		/*
29114cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
29124cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2913069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
29144cf49a43SJulian Elischer 		 * when control passes back to us.
29154cf49a43SJulian Elischer 		 */
291630400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2917069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
291830400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
29194cf49a43SJulian Elischer 		}
29204cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
29214cf49a43SJulian Elischer 	default:
29226b795970SJulian Elischer 		TRAP_ERROR();
29234cf49a43SJulian Elischer 		error = EINVAL;
29244cf49a43SJulian Elischer 	}
2925069154d5SJulian Elischer 	/*
2926069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
29274bd1b557SGleb Smirnoff 	 * to avoid problems with allocating when in tight memory situations.
2928069154d5SJulian Elischer 	 * Don't free it if it is so.
2929069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2930069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2931069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2932069154d5SJulian Elischer 	 */
2933069154d5SJulian Elischer out:
2934069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2935069154d5SJulian Elischer 	NG_FREE_MSG(msg);
29364cf49a43SJulian Elischer 	return (error);
29374cf49a43SJulian Elischer }
29384cf49a43SJulian Elischer 
29394cf49a43SJulian Elischer /************************************************************************
29408253c060SGleb Smirnoff 			Queue element get/free routines
29418253c060SGleb Smirnoff ************************************************************************/
29428253c060SGleb Smirnoff 
29438253c060SGleb Smirnoff uma_zone_t			ng_qzone;
29446aa6d011SAlexander Motin uma_zone_t			ng_qdzone;
2945f2fbb838SAlexander Motin static int			numthreads = 0; /* number of queue threads */
2946ed75521fSAlexander Motin static int			maxalloc = 4096;/* limit the damage of a leak */
2947ed75521fSAlexander Motin static int			maxdata = 512;	/* limit the damage of a DoS */
29488253c060SGleb Smirnoff 
2949f2fbb838SAlexander Motin TUNABLE_INT("net.graph.threads", &numthreads);
2950f2fbb838SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2951f2fbb838SAlexander Motin     0, "Number of queue processing threads");
29528253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc);
29538253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
29546aa6d011SAlexander Motin     0, "Maximum number of non-data queue items to allocate");
2955ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata);
29566aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
29576aa6d011SAlexander Motin     0, "Maximum number of data queue items to allocate");
29588253c060SGleb Smirnoff 
29598253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
29608253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
29618253c060SGleb Smirnoff static int allocated;	/* number of items malloc'd */
29628253c060SGleb Smirnoff #endif
29638253c060SGleb Smirnoff 
29648253c060SGleb Smirnoff /*
29658253c060SGleb Smirnoff  * Get a queue entry.
29668253c060SGleb Smirnoff  * This is usually called when a packet first enters netgraph.
29678253c060SGleb Smirnoff  * By definition, this is usually from an interrupt, or from a user.
29688253c060SGleb Smirnoff  * Users are not so important, but try be quick for the times that it's
29698253c060SGleb Smirnoff  * an interrupt.
29708253c060SGleb Smirnoff  */
29718253c060SGleb Smirnoff static __inline item_p
29726aa6d011SAlexander Motin ng_alloc_item(int type, int flags)
29738253c060SGleb Smirnoff {
29746aa6d011SAlexander Motin 	item_p item;
29758253c060SGleb Smirnoff 
29766aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
29776aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
297842282202SGleb Smirnoff 
29796aa6d011SAlexander Motin 	item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
29806aa6d011SAlexander Motin 	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
29818253c060SGleb Smirnoff 
29828253c060SGleb Smirnoff 	if (item) {
29836aa6d011SAlexander Motin 		item->el_flags = type;
29846aa6d011SAlexander Motin #ifdef	NETGRAPH_DEBUG
29858253c060SGleb Smirnoff 		mtx_lock(&ngq_mtx);
29868253c060SGleb Smirnoff 		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
29878253c060SGleb Smirnoff 		allocated++;
29888253c060SGleb Smirnoff 		mtx_unlock(&ngq_mtx);
29898253c060SGleb Smirnoff #endif
29906aa6d011SAlexander Motin 	}
29918253c060SGleb Smirnoff 
29928253c060SGleb Smirnoff 	return (item);
29938253c060SGleb Smirnoff }
29948253c060SGleb Smirnoff 
29958253c060SGleb Smirnoff /*
29968253c060SGleb Smirnoff  * Release a queue entry
29978253c060SGleb Smirnoff  */
29988253c060SGleb Smirnoff void
29998253c060SGleb Smirnoff ng_free_item(item_p item)
30008253c060SGleb Smirnoff {
30018253c060SGleb Smirnoff 	/*
30028253c060SGleb Smirnoff 	 * The item may hold resources on it's own. We need to free
30038253c060SGleb Smirnoff 	 * these before we can free the item. What they are depends upon
30048253c060SGleb Smirnoff 	 * what kind of item it is. it is important that nodes zero
30058253c060SGleb Smirnoff 	 * out pointers to resources that they remove from the item
30068253c060SGleb Smirnoff 	 * or we release them again here.
30078253c060SGleb Smirnoff 	 */
30088253c060SGleb Smirnoff 	switch (item->el_flags & NGQF_TYPE) {
30098253c060SGleb Smirnoff 	case NGQF_DATA:
30108253c060SGleb Smirnoff 		/* If we have an mbuf still attached.. */
30118253c060SGleb Smirnoff 		NG_FREE_M(_NGI_M(item));
30128253c060SGleb Smirnoff 		break;
30138253c060SGleb Smirnoff 	case NGQF_MESG:
30148253c060SGleb Smirnoff 		_NGI_RETADDR(item) = 0;
30158253c060SGleb Smirnoff 		NG_FREE_MSG(_NGI_MSG(item));
30168253c060SGleb Smirnoff 		break;
30178253c060SGleb Smirnoff 	case NGQF_FN:
3018e088dd4cSAlexander Motin 	case NGQF_FN2:
30198253c060SGleb Smirnoff 		/* nothing to free really, */
30208253c060SGleb Smirnoff 		_NGI_FN(item) = NULL;
30218253c060SGleb Smirnoff 		_NGI_ARG1(item) = NULL;
30228253c060SGleb Smirnoff 		_NGI_ARG2(item) = 0;
30238253c060SGleb Smirnoff 		break;
30248253c060SGleb Smirnoff 	}
30258253c060SGleb Smirnoff 	/* If we still have a node or hook referenced... */
30268253c060SGleb Smirnoff 	_NGI_CLR_NODE(item);
30278253c060SGleb Smirnoff 	_NGI_CLR_HOOK(item);
30288253c060SGleb Smirnoff 
30298253c060SGleb Smirnoff #ifdef	NETGRAPH_DEBUG
30308253c060SGleb Smirnoff 	mtx_lock(&ngq_mtx);
30318253c060SGleb Smirnoff 	TAILQ_REMOVE(&ng_itemlist, item, all);
30328253c060SGleb Smirnoff 	allocated--;
30338253c060SGleb Smirnoff 	mtx_unlock(&ngq_mtx);
30348253c060SGleb Smirnoff #endif
30356aa6d011SAlexander Motin 	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
30366aa6d011SAlexander Motin 	    ng_qdzone : ng_qzone, item);
30376aa6d011SAlexander Motin }
30386aa6d011SAlexander Motin 
30396aa6d011SAlexander Motin /*
30406aa6d011SAlexander Motin  * Change type of the queue entry.
30416aa6d011SAlexander Motin  * Possibly reallocates it from another UMA zone.
30426aa6d011SAlexander Motin  */
30436aa6d011SAlexander Motin static __inline item_p
30446aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags)
30456aa6d011SAlexander Motin {
30466aa6d011SAlexander Motin 	item_p item;
30476aa6d011SAlexander Motin 	int from, to;
30486aa6d011SAlexander Motin 
30496aa6d011SAlexander Motin 	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
30506aa6d011SAlexander Motin 	KASSERT(((type & ~NGQF_TYPE) == 0),
30516aa6d011SAlexander Motin 	    ("%s: incorrect item type: %d", __func__, type));
30526aa6d011SAlexander Motin 
30536aa6d011SAlexander Motin 	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
30546aa6d011SAlexander Motin 	to = (type == NGQF_DATA);
30556aa6d011SAlexander Motin 	if (from != to) {
30566aa6d011SAlexander Motin 		/* If reallocation is required do it and copy item. */
30576aa6d011SAlexander Motin 		if ((item = ng_alloc_item(type, flags)) == NULL) {
30586aa6d011SAlexander Motin 			ng_free_item(pitem);
30596aa6d011SAlexander Motin 			return (NULL);
30606aa6d011SAlexander Motin 		}
30616aa6d011SAlexander Motin 		*item = *pitem;
30626aa6d011SAlexander Motin 		ng_free_item(pitem);
30636aa6d011SAlexander Motin 	} else
30646aa6d011SAlexander Motin 		item = pitem;
30656aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
30666aa6d011SAlexander Motin 
30676aa6d011SAlexander Motin 	return (item);
30688253c060SGleb Smirnoff }
30698253c060SGleb Smirnoff 
30708253c060SGleb Smirnoff /************************************************************************
30714cf49a43SJulian Elischer 			Module routines
30724cf49a43SJulian Elischer ************************************************************************/
30734cf49a43SJulian Elischer 
30744cf49a43SJulian Elischer /*
30754cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
30764cf49a43SJulian Elischer  */
30774cf49a43SJulian Elischer int
30784cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
30794cf49a43SJulian Elischer {
30804cf49a43SJulian Elischer 	struct ng_type *const type = data;
30814bd1b557SGleb Smirnoff 	int error = 0;
30824cf49a43SJulian Elischer 
30834cf49a43SJulian Elischer 	switch (event) {
30844cf49a43SJulian Elischer 	case MOD_LOAD:
30854cf49a43SJulian Elischer 
30864cf49a43SJulian Elischer 		/* Register new netgraph node type */
30874bd1b557SGleb Smirnoff 		if ((error = ng_newtype(type)) != 0)
30884cf49a43SJulian Elischer 			break;
30894cf49a43SJulian Elischer 
30904cf49a43SJulian Elischer 		/* Call type specific code */
30914cf49a43SJulian Elischer 		if (type->mod_event != NULL)
3092069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
3093c4282b74SGleb Smirnoff 				TYPELIST_WLOCK();
3094c73b94a2SJulian Elischer 				type->refs--;	/* undo it */
30954cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
3096c4282b74SGleb Smirnoff 				TYPELIST_WUNLOCK();
3097069154d5SJulian Elischer 			}
30984cf49a43SJulian Elischer 		break;
30994cf49a43SJulian Elischer 
31004cf49a43SJulian Elischer 	case MOD_UNLOAD:
3101c73b94a2SJulian Elischer 		if (type->refs > 1) {		/* make sure no nodes exist! */
31024cf49a43SJulian Elischer 			error = EBUSY;
3103c73b94a2SJulian Elischer 		} else {
31044bd1b557SGleb Smirnoff 			if (type->refs == 0) /* failed load, nothing to undo */
3105c73b94a2SJulian Elischer 				break;
31064cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
31074cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
31084bd1b557SGleb Smirnoff 				if (error != 0)	/* type refuses.. */
31094cf49a43SJulian Elischer 					break;
31104cf49a43SJulian Elischer 			}
3111c4282b74SGleb Smirnoff 			TYPELIST_WLOCK();
31124cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
3113c4282b74SGleb Smirnoff 			TYPELIST_WUNLOCK();
31144cf49a43SJulian Elischer 		}
31154cf49a43SJulian Elischer 		break;
31164cf49a43SJulian Elischer 
31174cf49a43SJulian Elischer 	default:
31184cf49a43SJulian Elischer 		if (type->mod_event != NULL)
31194cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
31204cf49a43SJulian Elischer 		else
31213e019deaSPoul-Henning Kamp 			error = EOPNOTSUPP;		/* XXX ? */
31224cf49a43SJulian Elischer 		break;
31234cf49a43SJulian Elischer 	}
31244cf49a43SJulian Elischer 	return (error);
31254cf49a43SJulian Elischer }
31264cf49a43SJulian Elischer 
3127*687adb70SGleb Smirnoff static void
3128*687adb70SGleb Smirnoff vnet_netgraph_init(const void *unused __unused)
3129*687adb70SGleb Smirnoff {
3130*687adb70SGleb Smirnoff 
3131*687adb70SGleb Smirnoff 	/* We start with small hashes, but they can grow. */
3132*687adb70SGleb Smirnoff 	V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask);
3133*687adb70SGleb Smirnoff 	V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask);
3134*687adb70SGleb Smirnoff }
3135*687adb70SGleb Smirnoff VNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3136*687adb70SGleb Smirnoff     vnet_netgraph_init, NULL);
3137*687adb70SGleb Smirnoff 
3138eddfbb76SRobert Watson #ifdef VIMAGE
3139d0728d71SRobert Watson static void
3140d0728d71SRobert Watson vnet_netgraph_uninit(const void *unused __unused)
3141bc29160dSMarko Zec {
3142a3f93b72SMarko Zec 	node_p node = NULL, last_killed = NULL;
3143a3f93b72SMarko Zec 	int i;
3144bc29160dSMarko Zec 
3145a3f93b72SMarko Zec 	do {
3146a3f93b72SMarko Zec 		/* Find a node to kill */
3147*687adb70SGleb Smirnoff 		IDHASH_RLOCK();
3148*687adb70SGleb Smirnoff 		for (i = 0; i <= V_ng_ID_hmask; i++) {
3149*687adb70SGleb Smirnoff 			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
3150a3f93b72SMarko Zec 				if (node != &ng_deadnode) {
3151a3f93b72SMarko Zec 					NG_NODE_REF(node);
3152a3f93b72SMarko Zec 					break;
3153a3f93b72SMarko Zec 				}
3154a3f93b72SMarko Zec 			}
3155a3f93b72SMarko Zec 			if (node != NULL)
3156a3f93b72SMarko Zec 				break;
3157a3f93b72SMarko Zec 		}
3158*687adb70SGleb Smirnoff 		IDHASH_RUNLOCK();
3159a3f93b72SMarko Zec 
3160a3f93b72SMarko Zec 		/* Attempt to kill it only if it is a regular node */
3161a3f93b72SMarko Zec 		if (node != NULL) {
3162bc29160dSMarko Zec 			if (node == last_killed) {
3163bc29160dSMarko Zec 				/* This should never happen */
31644bd1b557SGleb Smirnoff 				printf("ng node %s needs NGF_REALLY_DIE\n",
31654bd1b557SGleb Smirnoff 				    node->nd_name);
3166a3f93b72SMarko Zec 				if (node->nd_flags & NGF_REALLY_DIE)
3167a3f93b72SMarko Zec 					panic("ng node %s won't die",
3168a3f93b72SMarko Zec 					    node->nd_name);
3169bc29160dSMarko Zec 				node->nd_flags |= NGF_REALLY_DIE;
3170bc29160dSMarko Zec 			}
3171bc29160dSMarko Zec 			ng_rmnode(node, NULL, NULL, 0);
3172a3f93b72SMarko Zec 			NG_NODE_UNREF(node);
3173bc29160dSMarko Zec 			last_killed = node;
3174bc29160dSMarko Zec 		}
3175a3f93b72SMarko Zec 	} while (node != NULL);
3176*687adb70SGleb Smirnoff 
3177*687adb70SGleb Smirnoff 	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
3178*687adb70SGleb Smirnoff 	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask);
3179bc29160dSMarko Zec }
3180320d00eeSGleb Smirnoff VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3181d0728d71SRobert Watson     vnet_netgraph_uninit, NULL);
3182bc29160dSMarko Zec #endif /* VIMAGE */
3183bc29160dSMarko Zec 
31844cf49a43SJulian Elischer /*
31854cf49a43SJulian Elischer  * Handle loading and unloading for this code.
31864cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
31874cf49a43SJulian Elischer  */
31884cf49a43SJulian Elischer static int
31894cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
31904cf49a43SJulian Elischer {
3191f2fbb838SAlexander Motin 	struct proc *p;
3192f2fbb838SAlexander Motin 	struct thread *td;
3193f2fbb838SAlexander Motin 	int i, error = 0;
31944cf49a43SJulian Elischer 
31954cf49a43SJulian Elischer 	switch (event) {
31964cf49a43SJulian Elischer 	case MOD_LOAD:
31971489164fSGleb Smirnoff 		/* Initialize everything. */
31982c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK_INIT();
3199c4282b74SGleb Smirnoff 		rw_init(&ng_typelist_lock, "netgraph types");
3200c4282b74SGleb Smirnoff 		rw_init(&ng_idhash_lock, "netgraph idhash");
3201c4282b74SGleb Smirnoff 		rw_init(&ng_namehash_lock, "netgraph namehash");
3202ac5dd141SGleb Smirnoff 		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3203ac5dd141SGleb Smirnoff 		    MTX_DEF);
32041489164fSGleb Smirnoff #ifdef	NETGRAPH_DEBUG
3205cfea3f85SAlexander Motin 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3206cfea3f85SAlexander Motin 		    MTX_DEF);
32071489164fSGleb Smirnoff 		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3208efd8e7c9SDon Lewis 		    MTX_DEF);
32091489164fSGleb Smirnoff #endif
32101489164fSGleb Smirnoff 		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
321104fdc6c6SGleb Smirnoff 		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, M_WAITOK);
32121489164fSGleb Smirnoff 		uma_zone_set_max(ng_qzone, maxalloc);
32134bd1b557SGleb Smirnoff 		ng_qdzone = uma_zcreate("NetGraph data items",
32144bd1b557SGleb Smirnoff 		    sizeof(struct ng_item), NULL, NULL, NULL, NULL,
321504fdc6c6SGleb Smirnoff 		    UMA_ALIGN_CACHE, M_WAITOK);
32166aa6d011SAlexander Motin 		uma_zone_set_max(ng_qdzone, maxdata);
3217f2fbb838SAlexander Motin 		/* Autoconfigure number of threads. */
3218f2fbb838SAlexander Motin 		if (numthreads <= 0)
3219f2fbb838SAlexander Motin 			numthreads = mp_ncpus;
3220f2fbb838SAlexander Motin 		/* Create threads. */
3221f2fbb838SAlexander Motin     		p = NULL; /* start with no process */
3222f2fbb838SAlexander Motin 		for (i = 0; i < numthreads; i++) {
3223f2fbb838SAlexander Motin 			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3224f2fbb838SAlexander Motin 			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3225f2fbb838SAlexander Motin 				numthreads = i;
3226f2fbb838SAlexander Motin 				break;
3227f2fbb838SAlexander Motin 			}
3228f2fbb838SAlexander Motin 		}
32294cf49a43SJulian Elischer 		break;
32304cf49a43SJulian Elischer 	case MOD_UNLOAD:
323164efc707SRobert Watson 		/* You can't unload it because an interface may be using it. */
32324cf49a43SJulian Elischer 		error = EBUSY;
32334cf49a43SJulian Elischer 		break;
32344cf49a43SJulian Elischer 	default:
32354cf49a43SJulian Elischer 		error = EOPNOTSUPP;
32364cf49a43SJulian Elischer 		break;
32374cf49a43SJulian Elischer 	}
32384cf49a43SJulian Elischer 	return (error);
32394cf49a43SJulian Elischer }
32404cf49a43SJulian Elischer 
32414cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
32424cf49a43SJulian Elischer 	"netgraph",
32434cf49a43SJulian Elischer 	ngb_mod_event,
32444cf49a43SJulian Elischer 	(NULL)
32454cf49a43SJulian Elischer };
3246320d00eeSGleb Smirnoff DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST);
3247bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3248bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
3249bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
32504cf49a43SJulian Elischer 
325130400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
325230400f03SJulian Elischer void
325330400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
325430400f03SJulian Elischer {
325530400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
325630400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
325730400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
325830400f03SJulian Elischer 		hook->lastfile, hook->lastline);
325930400f03SJulian Elischer 	if (line) {
326030400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
3261e5fe87b3SGleb Smirnoff #ifdef KDB
3262e5fe87b3SGleb Smirnoff 		kdb_backtrace();
3263e5fe87b3SGleb Smirnoff #endif
326430400f03SJulian Elischer 	}
326530400f03SJulian Elischer }
326630400f03SJulian Elischer 
326730400f03SJulian Elischer void
326830400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
326930400f03SJulian Elischer {
327030400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
32716b795970SJulian Elischer 		_NG_NODE_ID(node), node->nd_type->name,
327230400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
327330400f03SJulian Elischer 		node->nd_refs, node->nd_name);
327430400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
327530400f03SJulian Elischer 		node->lastfile, node->lastline);
327630400f03SJulian Elischer 	if (line) {
327730400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
3278e5fe87b3SGleb Smirnoff #ifdef KDB
3279e5fe87b3SGleb Smirnoff 		kdb_backtrace();
3280e5fe87b3SGleb Smirnoff #endif
328130400f03SJulian Elischer 	}
328230400f03SJulian Elischer }
328330400f03SJulian Elischer 
3284069154d5SJulian Elischer void
3285069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
3286069154d5SJulian Elischer {
3287069154d5SJulian Elischer 	printf(" ACTIVE item, last used at %s, line %d",
3288069154d5SJulian Elischer 		item->lastfile, item->lastline);
32896b795970SJulian Elischer 	switch(item->el_flags & NGQF_TYPE) {
32906b795970SJulian Elischer 	case NGQF_DATA:
3291069154d5SJulian Elischer 		printf(" - [data]\n");
32926b795970SJulian Elischer 		break;
32936b795970SJulian Elischer 	case NGQF_MESG:
32946b795970SJulian Elischer 		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
32956b795970SJulian Elischer 		break;
32966b795970SJulian Elischer 	case NGQF_FN:
3297857304e6SRuslan Ermilov 		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3298857304e6SRuslan Ermilov 			_NGI_FN(item),
3299857304e6SRuslan Ermilov 			_NGI_NODE(item),
3300857304e6SRuslan Ermilov 			_NGI_HOOK(item),
3301857304e6SRuslan Ermilov 			item->body.fn.fn_arg1,
3302857304e6SRuslan Ermilov 			item->body.fn.fn_arg2,
3303857304e6SRuslan Ermilov 			item->body.fn.fn_arg2);
3304857304e6SRuslan Ermilov 		break;
3305e088dd4cSAlexander Motin 	case NGQF_FN2:
3306eb4687d2SAlexander Motin 		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3307857304e6SRuslan Ermilov 			_NGI_FN2(item),
33086064e568SGleb Smirnoff 			_NGI_NODE(item),
33096064e568SGleb Smirnoff 			_NGI_HOOK(item),
33106b795970SJulian Elischer 			item->body.fn.fn_arg1,
33116b795970SJulian Elischer 			item->body.fn.fn_arg2,
33126b795970SJulian Elischer 			item->body.fn.fn_arg2);
33136b795970SJulian Elischer 		break;
3314069154d5SJulian Elischer 	}
331530400f03SJulian Elischer 	if (line) {
3316069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
33176064e568SGleb Smirnoff 		if (_NGI_NODE(item)) {
331830400f03SJulian Elischer 			printf("node %p ([%x])\n",
33196064e568SGleb Smirnoff 				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
3320069154d5SJulian Elischer 		}
332130400f03SJulian Elischer 	}
332230400f03SJulian Elischer }
332330400f03SJulian Elischer 
332430400f03SJulian Elischer static void
332530400f03SJulian Elischer ng_dumpitems(void)
332630400f03SJulian Elischer {
332730400f03SJulian Elischer 	item_p item;
332830400f03SJulian Elischer 	int i = 1;
332930400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
333030400f03SJulian Elischer 		printf("[%d] ", i++);
333130400f03SJulian Elischer 		dumpitem(item, NULL, 0);
333230400f03SJulian Elischer 	}
333330400f03SJulian Elischer }
333430400f03SJulian Elischer 
333530400f03SJulian Elischer static void
333630400f03SJulian Elischer ng_dumpnodes(void)
333730400f03SJulian Elischer {
333830400f03SJulian Elischer 	node_p node;
333930400f03SJulian Elischer 	int i = 1;
334053f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
334130400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
334230400f03SJulian Elischer 		printf("[%d] ", i++);
334330400f03SJulian Elischer 		dumpnode(node, NULL, 0);
334430400f03SJulian Elischer 	}
334553f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
334630400f03SJulian Elischer }
334730400f03SJulian Elischer 
334830400f03SJulian Elischer static void
334930400f03SJulian Elischer ng_dumphooks(void)
335030400f03SJulian Elischer {
335130400f03SJulian Elischer 	hook_p hook;
335230400f03SJulian Elischer 	int i = 1;
335353f9c5e9SRobert Watson 	mtx_lock(&ng_nodelist_mtx);
335430400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
335530400f03SJulian Elischer 		printf("[%d] ", i++);
335630400f03SJulian Elischer 		dumphook(hook, NULL, 0);
335730400f03SJulian Elischer 	}
335853f9c5e9SRobert Watson 	mtx_unlock(&ng_nodelist_mtx);
335930400f03SJulian Elischer }
3360069154d5SJulian Elischer 
3361069154d5SJulian Elischer static int
3362069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
3363069154d5SJulian Elischer {
3364069154d5SJulian Elischer 	int error;
3365069154d5SJulian Elischer 	int val;
3366069154d5SJulian Elischer 	int i;
3367069154d5SJulian Elischer 
3368069154d5SJulian Elischer 	val = allocated;
3369069154d5SJulian Elischer 	i = 1;
3370041b706bSDavid Malone 	error = sysctl_handle_int(oidp, &val, 0, req);
33716b795970SJulian Elischer 	if (error != 0 || req->newptr == NULL)
33726b795970SJulian Elischer 		return (error);
33736b795970SJulian Elischer 	if (val == 42) {
337430400f03SJulian Elischer 		ng_dumpitems();
337530400f03SJulian Elischer 		ng_dumpnodes();
337630400f03SJulian Elischer 		ng_dumphooks();
3377069154d5SJulian Elischer 	}
33786b795970SJulian Elischer 	return (0);
3379069154d5SJulian Elischer }
3380069154d5SJulian Elischer 
33816b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
33826b795970SJulian Elischer     0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
338330400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
3384069154d5SJulian Elischer 
3385069154d5SJulian Elischer /***********************************************************************
3386069154d5SJulian Elischer * Worklist routines
3387069154d5SJulian Elischer **********************************************************************/
3388069154d5SJulian Elischer /*
3389069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
3390f2fbb838SAlexander Motin  * try get an item to process off it. Remove the node from the list.
3391069154d5SJulian Elischer  */
3392069154d5SJulian Elischer static void
3393f2fbb838SAlexander Motin ngthread(void *arg)
3394069154d5SJulian Elischer {
3395069154d5SJulian Elischer 	for (;;) {
3396394cb30aSAlexander Motin 		node_p  node;
3397394cb30aSAlexander Motin 
3398394cb30aSAlexander Motin 		/* Get node from the worklist. */
33992c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
3400f2fbb838SAlexander Motin 		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3401f2fbb838SAlexander Motin 			NG_WORKLIST_SLEEP();
34029852972bSAlexander Motin 		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
34032c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
3404bc29160dSMarko Zec 		CURVNET_SET(node->nd_vnet);
34052955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
34062955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3407069154d5SJulian Elischer 		/*
3408069154d5SJulian Elischer 		 * We have the node. We also take over the reference
3409069154d5SJulian Elischer 		 * that the list had on it.
3410069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
3411069154d5SJulian Elischer 		 * let you have another item off the queue.
3412069154d5SJulian Elischer 		 * All this time, keep the reference
3413069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
3414069154d5SJulian Elischer 		 * Let the reference go at the last minute.
3415069154d5SJulian Elischer 		 */
3416069154d5SJulian Elischer 		for (;;) {
3417394cb30aSAlexander Motin 			item_p item;
3418714fb865SGleb Smirnoff 			int rw;
3419714fb865SGleb Smirnoff 
34202c8dda8dSWojciech A. Koszek 			NG_QUEUE_LOCK(&node->nd_input_queue);
34219852972bSAlexander Motin 			item = ng_dequeue(node, &rw);
3422069154d5SJulian Elischer 			if (item == NULL) {
34239852972bSAlexander Motin 				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
34242c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
3425069154d5SJulian Elischer 				break; /* go look for another node */
3426069154d5SJulian Elischer 			} else {
34272c8dda8dSWojciech A. Koszek 				NG_QUEUE_UNLOCK(&node->nd_input_queue);
34285951069aSJulian Elischer 				NGI_GET_NODE(item, node); /* zaps stored node */
3429714fb865SGleb Smirnoff 				ng_apply_item(node, item, rw);
34305951069aSJulian Elischer 				NG_NODE_UNREF(node);
3431069154d5SJulian Elischer 			}
3432069154d5SJulian Elischer 		}
3433a96dcd84SJulian Elischer 		NG_NODE_UNREF(node);
3434bc29160dSMarko Zec 		CURVNET_RESTORE();
3435069154d5SJulian Elischer 	}
3436069154d5SJulian Elischer }
3437069154d5SJulian Elischer 
343833338e73SJulian Elischer /*
343933338e73SJulian Elischer  * XXX
344033338e73SJulian Elischer  * It's posible that a debugging NG_NODE_REF may need
344133338e73SJulian Elischer  * to be outside the mutex zone
344233338e73SJulian Elischer  */
3443069154d5SJulian Elischer static void
3444394cb30aSAlexander Motin ng_worklist_add(node_p node)
3445069154d5SJulian Elischer {
3446f912c0f7SGleb Smirnoff 
34475bc15201SGleb Smirnoff 	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3448f912c0f7SGleb Smirnoff 
34499852972bSAlexander Motin 	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
3450069154d5SJulian Elischer 		/*
3451069154d5SJulian Elischer 		 * If we are not already on the work queue,
3452069154d5SJulian Elischer 		 * then put us on.
3453069154d5SJulian Elischer 		 */
34549852972bSAlexander Motin 		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
34554bd1b557SGleb Smirnoff 		NG_NODE_REF(node); /* XXX safe in mutex? */
34562c8dda8dSWojciech A. Koszek 		NG_WORKLIST_LOCK();
34579852972bSAlexander Motin 		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
34582c8dda8dSWojciech A. Koszek 		NG_WORKLIST_UNLOCK();
34592955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
34602955ee18SGleb Smirnoff 		    node->nd_ID, node);
3461f2fbb838SAlexander Motin 		NG_WORKLIST_WAKEUP();
3462394cb30aSAlexander Motin 	} else {
34632955ee18SGleb Smirnoff 		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
34642955ee18SGleb Smirnoff 		    __func__, node->nd_ID, node);
3465394cb30aSAlexander Motin 	}
3466069154d5SJulian Elischer }
3467069154d5SJulian Elischer 
3468069154d5SJulian Elischer /***********************************************************************
3469069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3470069154d5SJulian Elischer ***********************************************************************/
3471069154d5SJulian Elischer 
347230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
347330400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
34744cf49a43SJulian Elischer 	do {								\
34751acb27c6SJulian Elischer 		if (NGI_NODE(item) ) {					\
3476069154d5SJulian Elischer 			printf("item already has node");		\
34773de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
34781acb27c6SJulian Elischer 			NGI_CLR_NODE(item);				\
3479069154d5SJulian Elischer 		}							\
34801acb27c6SJulian Elischer 		if (NGI_HOOK(item) ) {					\
3481069154d5SJulian Elischer 			printf("item already has hook");		\
34823de213ccSRobert Watson 			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
34831acb27c6SJulian Elischer 			NGI_CLR_HOOK(item);				\
3484069154d5SJulian Elischer 		}							\
3485069154d5SJulian Elischer 	} while (0)
3486069154d5SJulian Elischer #else
348730400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3488069154d5SJulian Elischer #endif
3489069154d5SJulian Elischer 
3490069154d5SJulian Elischer /*
34918ed370fdSJulian Elischer  * Put mbuf into the item.
3492069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3493069154d5SJulian Elischer  * (or equivalent)
3494069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3495069154d5SJulian Elischer  * remote node might go away in this timescale.
3496069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3497069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
34984be59335SGleb Smirnoff  * here to be able to do this.
3499069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3500069154d5SJulian Elischer  *
3501069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3502069154d5SJulian Elischer  */
3503069154d5SJulian Elischer item_p
350442282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags)
3505069154d5SJulian Elischer {
3506069154d5SJulian Elischer 	item_p item;
3507069154d5SJulian Elischer 
35086aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3509069154d5SJulian Elischer 		NG_FREE_M(m);
3510069154d5SJulian Elischer 		return (NULL);
3511069154d5SJulian Elischer 	}
351230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
35136aa6d011SAlexander Motin 	item->el_flags |= NGQF_READER;
3514069154d5SJulian Elischer 	NGI_M(item) = m;
3515069154d5SJulian Elischer 	return (item);
3516069154d5SJulian Elischer }
3517069154d5SJulian Elischer 
3518069154d5SJulian Elischer /*
3519069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3520069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3521069154d5SJulian Elischer  * to work out what some of the fields should be.
3522069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3523069154d5SJulian Elischer  * (or equivalent)
3524069154d5SJulian Elischer  */
3525069154d5SJulian Elischer item_p
352642282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags)
3527069154d5SJulian Elischer {
3528069154d5SJulian Elischer 	item_p item;
3529069154d5SJulian Elischer 
35306aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
3531069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3532069154d5SJulian Elischer 		return (NULL);
3533069154d5SJulian Elischer 	}
353430400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
35356f683eeeSGleb Smirnoff 	/* Messages items count as writers unless explicitly exempted. */
35366f683eeeSGleb Smirnoff 	if (msg->header.cmd & NGM_READONLY)
35376aa6d011SAlexander Motin 		item->el_flags |= NGQF_READER;
35386f683eeeSGleb Smirnoff 	else
35396aa6d011SAlexander Motin 		item->el_flags |= NGQF_WRITER;
3540069154d5SJulian Elischer 	/*
3541069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3542069154d5SJulian Elischer 	 */
3543069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3544facfd889SArchie Cobbs 	NGI_RETADDR(item) = 0;
3545069154d5SJulian Elischer 	return (item);
3546069154d5SJulian Elischer }
3547069154d5SJulian Elischer 
35481acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr)				\
35496b795970SJulian Elischer 	do {	/* Data or fn items don't have retaddrs */		\
35506b795970SJulian Elischer 		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
3551069154d5SJulian Elischer 			if (retaddr) {					\
3552069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
35534cf49a43SJulian Elischer 			} else {					\
3554069154d5SJulian Elischer 				/*					\
3555069154d5SJulian Elischer 				 * The old return address should be ok.	\
3556069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3557069154d5SJulian Elischer 				 * here.				\
3558069154d5SJulian Elischer 				 */					\
3559069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3560069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3561069154d5SJulian Elischer 						= ng_node2ID(here);	\
3562069154d5SJulian Elischer 				}					\
3563069154d5SJulian Elischer 			}						\
35644cf49a43SJulian Elischer 		}							\
35654cf49a43SJulian Elischer 	} while (0)
35664cf49a43SJulian Elischer 
35674cf49a43SJulian Elischer int
3568069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
35694cf49a43SJulian Elischer {
35701acb27c6SJulian Elischer 	hook_p peer;
35711acb27c6SJulian Elischer 	node_p peernode;
357230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3573069154d5SJulian Elischer 	/*
3574069154d5SJulian Elischer 	 * Quick sanity check..
357530400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
357630400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
357730400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3578069154d5SJulian Elischer 	 */
3579a7da736aSGleb Smirnoff 	mtx_lock(&ng_topo_mtx);
35804bd1b557SGleb Smirnoff 	if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
3581a04e9846SAlexander Motin 	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3582a04e9846SAlexander Motin 	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
3583069154d5SJulian Elischer 		NG_FREE_ITEM(item);
35846b795970SJulian Elischer 		TRAP_ERROR();
3585a7da736aSGleb Smirnoff 		mtx_unlock(&ng_topo_mtx);
3586e08d3e3cSJulian Elischer 		return (ENETDOWN);
35874cf49a43SJulian Elischer 	}
35884cf49a43SJulian Elischer 
35894cf49a43SJulian Elischer 	/*
3590069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
35914cf49a43SJulian Elischer 	 */
35921acb27c6SJulian Elischer 	NG_HOOK_REF(peer);
35931acb27c6SJulian Elischer 	NG_NODE_REF(peernode);
3594a04e9846SAlexander Motin 	NGI_SET_HOOK(item, peer);
35951acb27c6SJulian Elischer 	NGI_SET_NODE(item, peernode);
35968b68f82fSJulian Elischer 	SET_RETADDR(item, here, retaddr);
3597a7da736aSGleb Smirnoff 
3598a7da736aSGleb Smirnoff 	mtx_unlock(&ng_topo_mtx);
3599a7da736aSGleb Smirnoff 
3600069154d5SJulian Elischer 	return (0);
3601069154d5SJulian Elischer }
3602069154d5SJulian Elischer 
36034cf49a43SJulian Elischer int
3604707d2058SMax Khon ng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
36054cf49a43SJulian Elischer {
36064cf49a43SJulian Elischer 	node_p	dest = NULL;
3607069154d5SJulian Elischer 	hook_p	hook = NULL;
36084cf49a43SJulian Elischer 	int	error;
3609069154d5SJulian Elischer 
361030400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3611069154d5SJulian Elischer 	/*
3612069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3613069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3614069154d5SJulian Elischer 	 */
3615069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3616069154d5SJulian Elischer 	if (error) {
3617069154d5SJulian Elischer 		NG_FREE_ITEM(item);
361830400f03SJulian Elischer 		return (error);
3619069154d5SJulian Elischer 	}
36201acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
3621a7da736aSGleb Smirnoff 	if (hook)
36221acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
3623a7da736aSGleb Smirnoff 
36241acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3625069154d5SJulian Elischer 	return (0);
3626069154d5SJulian Elischer }
3627069154d5SJulian Elischer 
3628069154d5SJulian Elischer int
3629069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3630069154d5SJulian Elischer {
3631069154d5SJulian Elischer 	node_p dest;
3632069154d5SJulian Elischer 
363330400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3634069154d5SJulian Elischer 	/*
3635069154d5SJulian Elischer 	 * Find the target node.
3636069154d5SJulian Elischer 	 */
3637069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3638069154d5SJulian Elischer 	if (dest == NULL) {
3639069154d5SJulian Elischer 		NG_FREE_ITEM(item);
36406b795970SJulian Elischer 		TRAP_ERROR();
3641069154d5SJulian Elischer 		return(EINVAL);
3642069154d5SJulian Elischer 	}
3643069154d5SJulian Elischer 	/* Fill out the contents */
36441acb27c6SJulian Elischer 	NGI_SET_NODE(item, dest);
36451acb27c6SJulian Elischer 	NGI_CLR_HOOK(item);
36461acb27c6SJulian Elischer 	SET_RETADDR(item, here, retaddr);
3647069154d5SJulian Elischer 	return (0);
3648069154d5SJulian Elischer }
3649069154d5SJulian Elischer 
3650069154d5SJulian Elischer /*
3651069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3652069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3653069154d5SJulian Elischer  * Useful for removing that hook :-)
3654069154d5SJulian Elischer  */
3655069154d5SJulian Elischer item_p
3656069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3657069154d5SJulian Elischer {
3658069154d5SJulian Elischer 	item_p item;
36594cf49a43SJulian Elischer 
3660859a4d16SJulian Elischer 	/*
3661859a4d16SJulian Elischer 	 * Find the target node.
3662859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3663859a4d16SJulian Elischer 	 * to the address.
3664859a4d16SJulian Elischer 	 */
36656aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
3666069154d5SJulian Elischer 		NG_FREE_MSG(msg);
3667069154d5SJulian Elischer 		return (NULL);
36684cf49a43SJulian Elischer 	}
36694cf49a43SJulian Elischer 
36704cf49a43SJulian Elischer 	/* Fill out the contents */
36716aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
367230400f03SJulian Elischer 	NG_NODE_REF(here);
36731acb27c6SJulian Elischer 	NGI_SET_NODE(item, here);
36741acb27c6SJulian Elischer 	if (hook) {
367530400f03SJulian Elischer 		NG_HOOK_REF(hook);
36761acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
36771acb27c6SJulian Elischer 	}
3678069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3679069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3680069154d5SJulian Elischer 	return (item);
36814cf49a43SJulian Elischer }
36824cf49a43SJulian Elischer 
3683e088dd4cSAlexander Motin /*
3684e088dd4cSAlexander Motin  * Send ng_item_fn function call to the specified node.
3685e088dd4cSAlexander Motin  */
3686e088dd4cSAlexander Motin 
368742282202SGleb Smirnoff int
3688b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
3689b332b91fSGleb Smirnoff {
3690b332b91fSGleb Smirnoff 
3691b332b91fSGleb Smirnoff 	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
3692b332b91fSGleb Smirnoff }
3693b332b91fSGleb Smirnoff 
3694b332b91fSGleb Smirnoff int
3695aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
369642282202SGleb Smirnoff 	int flags)
36976b795970SJulian Elischer {
36986b795970SJulian Elischer 	item_p item;
36996b795970SJulian Elischer 
37006aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
37016b795970SJulian Elischer 		return (ENOMEM);
37026b795970SJulian Elischer 	}
37036aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3704a96dcd84SJulian Elischer 	NG_NODE_REF(node); /* and one for the item */
37051acb27c6SJulian Elischer 	NGI_SET_NODE(item, node);
37061acb27c6SJulian Elischer 	if (hook) {
37076b795970SJulian Elischer 		NG_HOOK_REF(hook);
37081acb27c6SJulian Elischer 		NGI_SET_HOOK(item, hook);
37096b795970SJulian Elischer 	}
37106b795970SJulian Elischer 	NGI_FN(item) = fn;
37116b795970SJulian Elischer 	NGI_ARG1(item) = arg1;
37126b795970SJulian Elischer 	NGI_ARG2(item) = arg2;
371342282202SGleb Smirnoff 	return(ng_snd_item(item, flags));
37146b795970SJulian Elischer }
37156b795970SJulian Elischer 
37164cf49a43SJulian Elischer /*
3717b332b91fSGleb Smirnoff  * Send ng_item_fn2 function call to the specified node.
3718b332b91fSGleb Smirnoff  *
3719b332b91fSGleb Smirnoff  * If an optional pitem parameter is supplied, its apply
3720b332b91fSGleb Smirnoff  * callback will be copied to the new item. If also NG_REUSE_ITEM
3721b332b91fSGleb Smirnoff  * flag is set, no new item will be allocated, but pitem will
3722b332b91fSGleb Smirnoff  * be used.
3723e088dd4cSAlexander Motin  */
3724e088dd4cSAlexander Motin int
3725b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3726b332b91fSGleb Smirnoff 	int arg2, int flags)
3727e088dd4cSAlexander Motin {
3728e088dd4cSAlexander Motin 	item_p item;
3729e088dd4cSAlexander Motin 
3730b332b91fSGleb Smirnoff 	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3731b332b91fSGleb Smirnoff 	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3732e088dd4cSAlexander Motin 
3733e088dd4cSAlexander Motin 	/*
3734b332b91fSGleb Smirnoff 	 * Allocate a new item if no supplied or
3735b332b91fSGleb Smirnoff 	 * if we can't use supplied one.
3736e088dd4cSAlexander Motin 	 */
3737b332b91fSGleb Smirnoff 	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
37386aa6d011SAlexander Motin 		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3739e088dd4cSAlexander Motin 			return (ENOMEM);
37406aa6d011SAlexander Motin 		if (pitem != NULL)
37416aa6d011SAlexander Motin 			item->apply = pitem->apply;
3742ed75521fSAlexander Motin 	} else {
37436aa6d011SAlexander Motin 		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
37446aa6d011SAlexander Motin 			return (ENOMEM);
3745ed75521fSAlexander Motin 	}
3746b332b91fSGleb Smirnoff 
37476aa6d011SAlexander Motin 	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3748e088dd4cSAlexander Motin 	NG_NODE_REF(node); /* and one for the item */
3749e088dd4cSAlexander Motin 	NGI_SET_NODE(item, node);
3750e088dd4cSAlexander Motin 	if (hook) {
3751e088dd4cSAlexander Motin 		NG_HOOK_REF(hook);
3752e088dd4cSAlexander Motin 		NGI_SET_HOOK(item, hook);
3753e088dd4cSAlexander Motin 	}
3754e088dd4cSAlexander Motin 	NGI_FN2(item) = fn;
3755e088dd4cSAlexander Motin 	NGI_ARG1(item) = arg1;
3756e088dd4cSAlexander Motin 	NGI_ARG2(item) = arg2;
3757e088dd4cSAlexander Motin 	return(ng_snd_item(item, flags));
3758e088dd4cSAlexander Motin }
3759e088dd4cSAlexander Motin 
3760e088dd4cSAlexander Motin /*
3761d2ca21a9SJulian Elischer  * Official timeout routines for Netgraph nodes.
3762d2ca21a9SJulian Elischer  */
3763d2ca21a9SJulian Elischer static void
37641fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg)
3765d2ca21a9SJulian Elischer {
3766d2ca21a9SJulian Elischer 	item_p item = arg;
3767d2ca21a9SJulian Elischer 
3768bc29160dSMarko Zec 	CURVNET_SET(NGI_NODE(item)->nd_vnet);
3769d2ca21a9SJulian Elischer 	ng_snd_item(item, 0);
3770bc29160dSMarko Zec 	CURVNET_RESTORE();
3771d2ca21a9SJulian Elischer }
3772d2ca21a9SJulian Elischer 
377330bef41bSGleb Smirnoff int
3774f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
3775d2ca21a9SJulian Elischer     ng_item_fn *fn, void * arg1, int arg2)
3776d2ca21a9SJulian Elischer {
37771bf8e0faSGleb Smirnoff 	item_p item, oitem;
3778d2ca21a9SJulian Elischer 
37796aa6d011SAlexander Motin 	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
378030bef41bSGleb Smirnoff 		return (ENOMEM);
378130bef41bSGleb Smirnoff 
37826aa6d011SAlexander Motin 	item->el_flags |= NGQF_WRITER;
3783d2ca21a9SJulian Elischer 	NG_NODE_REF(node);		/* and one for the item */
3784d2ca21a9SJulian Elischer 	NGI_SET_NODE(item, node);
3785d2ca21a9SJulian Elischer 	if (hook) {
3786d2ca21a9SJulian Elischer 		NG_HOOK_REF(hook);
3787d2ca21a9SJulian Elischer 		NGI_SET_HOOK(item, hook);
3788d2ca21a9SJulian Elischer 	}
3789d2ca21a9SJulian Elischer 	NGI_FN(item) = fn;
3790d2ca21a9SJulian Elischer 	NGI_ARG1(item) = arg1;
3791d2ca21a9SJulian Elischer 	NGI_ARG2(item) = arg2;
37921bf8e0faSGleb Smirnoff 	oitem = c->c_arg;
37931bf8e0faSGleb Smirnoff 	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
37941bf8e0faSGleb Smirnoff 	    oitem != NULL)
37951bf8e0faSGleb Smirnoff 		NG_FREE_ITEM(oitem);
379630bef41bSGleb Smirnoff 	return (0);
3797d2ca21a9SJulian Elischer }
3798d2ca21a9SJulian Elischer 
3799d2ca21a9SJulian Elischer /* A special modified version of untimeout() */
3800d2ca21a9SJulian Elischer int
3801f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node)
3802d2ca21a9SJulian Elischer {
3803d2ca21a9SJulian Elischer 	item_p item;
380430bef41bSGleb Smirnoff 	int rval;
3805d2ca21a9SJulian Elischer 
380603b25f5dSGleb Smirnoff 	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
380703b25f5dSGleb Smirnoff 	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
380803b25f5dSGleb Smirnoff 
38093eadb26dSGleb Smirnoff 	rval = callout_stop(c);
381030bef41bSGleb Smirnoff 	item = c->c_arg;
381130bef41bSGleb Smirnoff 	/* Do an extra check */
38121fbb36ffSGleb Smirnoff 	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
381330bef41bSGleb Smirnoff 	    (NGI_NODE(item) == node)) {
3814d2ca21a9SJulian Elischer 		/*
3815d2ca21a9SJulian Elischer 		 * We successfully removed it from the queue before it ran
3816d2ca21a9SJulian Elischer 		 * So now we need to unreference everything that was
3817d2ca21a9SJulian Elischer 		 * given extra references. (NG_FREE_ITEM does this).
3818d2ca21a9SJulian Elischer 		 */
3819d2ca21a9SJulian Elischer 		NG_FREE_ITEM(item);
3820d2ca21a9SJulian Elischer 	}
38211bf8e0faSGleb Smirnoff 	c->c_arg = NULL;
382230bef41bSGleb Smirnoff 
382330bef41bSGleb Smirnoff 	return (rval);
3824d2ca21a9SJulian Elischer }
3825d2ca21a9SJulian Elischer 
3826d2ca21a9SJulian Elischer /*
3827069154d5SJulian Elischer  * Set the address, if none given, give the node here.
38284cf49a43SJulian Elischer  */
3829069154d5SJulian Elischer void
3830069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
38314cf49a43SJulian Elischer {
3832069154d5SJulian Elischer 	if (retaddr) {
3833069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3834069154d5SJulian Elischer 	} else {
3835069154d5SJulian Elischer 		/*
3836069154d5SJulian Elischer 		 * The old return address should be ok.
3837069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3838069154d5SJulian Elischer 		 */
3839069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3840069154d5SJulian Elischer 	}
3841069154d5SJulian Elischer }
3842