xref: /freebsd/sys/netgraph/ng_base.c (revision 12574a02a6497fc725bb08d6835f82f7164a38fd)
14cf49a43SJulian Elischer /*
24cf49a43SJulian Elischer  * ng_base.c
34cf49a43SJulian Elischer  *
44cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
54cf49a43SJulian Elischer  * All rights reserved.
64cf49a43SJulian Elischer  *
74cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
84cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
94cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
104cf49a43SJulian Elischer  * provided, however, that:
114cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
124cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
134cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
144cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
154cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
164cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
174cf49a43SJulian Elischer  *
184cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
194cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
204cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
214cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
224cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
234cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
244cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
254cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
264cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
274cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
284cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
294cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
304cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
314cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
324cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
334cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
344cf49a43SJulian Elischer  * OF SUCH DAMAGE.
354cf49a43SJulian Elischer  *
36cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
37cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
384cf49a43SJulian Elischer  *
394cf49a43SJulian Elischer  * $FreeBSD$
404cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
414cf49a43SJulian Elischer  */
424cf49a43SJulian Elischer 
434cf49a43SJulian Elischer /*
444cf49a43SJulian Elischer  * This file implements the base netgraph code.
454cf49a43SJulian Elischer  */
464cf49a43SJulian Elischer 
474cf49a43SJulian Elischer #include <sys/param.h>
484cf49a43SJulian Elischer #include <sys/systm.h>
494cf49a43SJulian Elischer #include <sys/errno.h>
504cf49a43SJulian Elischer #include <sys/kernel.h>
514cf49a43SJulian Elischer #include <sys/malloc.h>
524cf49a43SJulian Elischer #include <sys/syslog.h>
53069154d5SJulian Elischer #include <sys/sysctl.h>
544cf49a43SJulian Elischer #include <sys/linker.h>
554cf49a43SJulian Elischer #include <sys/queue.h>
564cf49a43SJulian Elischer #include <sys/mbuf.h>
575b664c7cSPoul-Henning Kamp #include <sys/ctype.h>
582b70adcbSArchie Cobbs #include <machine/limits.h>
594cf49a43SJulian Elischer 
604cf49a43SJulian Elischer #include <net/netisr.h>
614cf49a43SJulian Elischer 
624cf49a43SJulian Elischer #include <netgraph/ng_message.h>
634cf49a43SJulian Elischer #include <netgraph/netgraph.h>
64f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
654cf49a43SJulian Elischer 
6699ff8176SPeter Wemm MODULE_VERSION(netgraph, 1);
6799ff8176SPeter Wemm 
6830400f03SJulian Elischer /* List of all active nodes */
69069154d5SJulian Elischer static LIST_HEAD(, ng_node) ng_nodelist;
70069154d5SJulian Elischer static struct mtx	ng_nodelist_mtx;
71069154d5SJulian Elischer 
7230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
7330400f03SJulian Elischer 
7430400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes;
7530400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
7630400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks;
7730400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
7830400f03SJulian Elischer 
7930400f03SJulian Elischer static void ng_dumpitems(void);
8030400f03SJulian Elischer static void ng_dumpnodes(void);
8130400f03SJulian Elischer static void ng_dumphooks(void);
8230400f03SJulian Elischer 
8330400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
8430400f03SJulian Elischer 
85069154d5SJulian Elischer /* List nodes with unallocated work */
86069154d5SJulian Elischer static TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
87069154d5SJulian Elischer static struct mtx	ng_worklist_mtx;
884cf49a43SJulian Elischer 
894cf49a43SJulian Elischer /* List of installed types */
90069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
91069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
924cf49a43SJulian Elischer 
93069154d5SJulian Elischer /* Hash related definitions */
94dc90cad9SJulian Elischer /* Don't nead to initialise them because it's a LIST */
95069154d5SJulian Elischer #define ID_HASH_SIZE 32 /* most systems wont need even this many */
96069154d5SJulian Elischer static LIST_HEAD(, ng_node) ng_ID_hash[ID_HASH_SIZE];
97069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
98069154d5SJulian Elischer 
99069154d5SJulian Elischer /* Mutex that protects the free queue item list */
100069154d5SJulian Elischer static volatile item_p		ngqfree;	/* free ones */
101069154d5SJulian Elischer static struct mtx	ngq_mtx;
102dc90cad9SJulian Elischer 
1034cf49a43SJulian Elischer /* Internal functions */
1044cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
1054cf49a43SJulian Elischer static int	ng_connect(hook_p hook1, hook_p hook2);
1064cf49a43SJulian Elischer static void	ng_disconnect_hook(hook_p hook);
107069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
108dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
1094cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
110069154d5SJulian Elischer static void	ng_worklist_remove(node_p node);
1114cf49a43SJulian Elischer static void	ngintr(void);
112069154d5SJulian Elischer static int	ng_apply_item(node_p node, item_p item);
113069154d5SJulian Elischer static void	ng_flush_input_queue(struct ng_queue * ngq);
114069154d5SJulian Elischer static void	ng_setisr(node_p node);
115069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
116069154d5SJulian Elischer 
11730400f03SJulian Elischer /* imported , these used to be externally visible, some may go back */
118069154d5SJulian Elischer int	ng_bypass(hook_p hook1, hook_p hook2);
119069154d5SJulian Elischer void	ng_cutlinks(node_p node);
120069154d5SJulian Elischer int	ng_con_nodes(node_p node, const char *name, node_p node2,
121069154d5SJulian Elischer 	const char *name2);
122069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
123069154d5SJulian Elischer node_p	ng_name2noderef(node_p node, const char *name);
124069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
125069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
126069154d5SJulian Elischer struct	ng_type *ng_findtype(const char *type);
127069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
128069154d5SJulian Elischer int	ng_mkpeer(node_p node, const char *name, const char *name2, char *type);
129069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
130069154d5SJulian Elischer void	ng_rmnode(node_p node);
13130400f03SJulian Elischer void	ng_unname(node_p node);
132069154d5SJulian Elischer 
1334cf49a43SJulian Elischer 
1344cf49a43SJulian Elischer /* Our own netgraph malloc type */
1354cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
136069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
137069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
138069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
139069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_META, "netgraph_meta", "netgraph name storage");
140069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
141069154d5SJulian Elischer 
142069154d5SJulian Elischer /* Should not be visible outside this file */
14330400f03SJulian Elischer 
14430400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \
14530400f03SJulian Elischer 	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
14630400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \
14730400f03SJulian Elischer 	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
14830400f03SJulian Elischer 
14930400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
15030400f03SJulian Elischer /*
15130400f03SJulian Elischer  * In debug mode:
15230400f03SJulian Elischer  * In an attempt to help track reference count screwups
15330400f03SJulian Elischer  * we do not free objects back to the malloc system, but keep them
15430400f03SJulian Elischer  * in a local cache where we can examine them and keep information safely
15530400f03SJulian Elischer  * after they have been freed.
15630400f03SJulian Elischer  * We use this scheme for nodes and hooks, and to some extent for items.
15730400f03SJulian Elischer  */
15830400f03SJulian Elischer static __inline hook_p
15930400f03SJulian Elischer ng_alloc_hook(void)
16030400f03SJulian Elischer {
16130400f03SJulian Elischer 	hook_p hook;
16230400f03SJulian Elischer 	SLIST_ENTRY(ng_hook) temp;
16330400f03SJulian Elischer 	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
16430400f03SJulian Elischer 	hook = LIST_FIRST(&ng_freehooks);
16530400f03SJulian Elischer 	if (hook) {
16630400f03SJulian Elischer 		LIST_REMOVE(hook, hk_hooks);
16730400f03SJulian Elischer 		bcopy(&hook->hk_all, &temp, sizeof(temp));
16830400f03SJulian Elischer 		bzero(hook, sizeof(struct ng_hook));
16930400f03SJulian Elischer 		bcopy(&temp, &hook->hk_all, sizeof(temp));
17030400f03SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
17130400f03SJulian Elischer 		hook->hk_magic = HK_MAGIC;
17230400f03SJulian Elischer 	} else {
17330400f03SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
17430400f03SJulian Elischer 		_NG_ALLOC_HOOK(hook);
17530400f03SJulian Elischer 		if (hook) {
17630400f03SJulian Elischer 			hook->hk_magic = HK_MAGIC;
17730400f03SJulian Elischer 			mtx_enter(&ng_nodelist_mtx, MTX_DEF);
17830400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
17930400f03SJulian Elischer 			mtx_exit(&ng_nodelist_mtx, MTX_DEF);
18030400f03SJulian Elischer 		}
18130400f03SJulian Elischer 	}
18230400f03SJulian Elischer 	return (hook);
18330400f03SJulian Elischer }
18430400f03SJulian Elischer 
18530400f03SJulian Elischer static __inline node_p
18630400f03SJulian Elischer ng_alloc_node(void)
18730400f03SJulian Elischer {
18830400f03SJulian Elischer 	node_p node;
18930400f03SJulian Elischer 	SLIST_ENTRY(ng_node) temp;
19030400f03SJulian Elischer 	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
19130400f03SJulian Elischer 	node = LIST_FIRST(&ng_freenodes);
19230400f03SJulian Elischer 	if (node) {
19330400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
19430400f03SJulian Elischer 		bcopy(&node->nd_all, &temp, sizeof(temp));
19530400f03SJulian Elischer 		bzero(node, sizeof(struct ng_node));
19630400f03SJulian Elischer 		bcopy(&temp, &node->nd_all, sizeof(temp));
19730400f03SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
19830400f03SJulian Elischer 		node->nd_magic = ND_MAGIC;
19930400f03SJulian Elischer 	} else {
20030400f03SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
20130400f03SJulian Elischer 		_NG_ALLOC_NODE(node);
20230400f03SJulian Elischer 		if (node) {
20330400f03SJulian Elischer 			node->nd_magic = ND_MAGIC;
20430400f03SJulian Elischer 			mtx_enter(&ng_nodelist_mtx, MTX_DEF);
20530400f03SJulian Elischer 			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
20630400f03SJulian Elischer 			mtx_exit(&ng_nodelist_mtx, MTX_DEF);
20730400f03SJulian Elischer 		}
20830400f03SJulian Elischer 	}
20930400f03SJulian Elischer 	return (node);
21030400f03SJulian Elischer }
21130400f03SJulian Elischer 
21230400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
21330400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
21430400f03SJulian Elischer 
21530400f03SJulian Elischer 
21630400f03SJulian Elischer #define NG_FREE_HOOK(hook)						\
21730400f03SJulian Elischer 	do {								\
21830400f03SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);			\
21930400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
22030400f03SJulian Elischer 		hook->hk_magic = 0;					\
22130400f03SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);			\
22230400f03SJulian Elischer 	} while (0)
22330400f03SJulian Elischer 
22430400f03SJulian Elischer #define NG_FREE_NODE(node)						\
22530400f03SJulian Elischer 	do {								\
22630400f03SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);			\
22730400f03SJulian Elischer 		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
22830400f03SJulian Elischer 		node->nd_magic = 0;					\
22930400f03SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);			\
23030400f03SJulian Elischer 	} while (0)
23130400f03SJulian Elischer 
23230400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
23330400f03SJulian Elischer 
23430400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
23530400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
23630400f03SJulian Elischer 
237069154d5SJulian Elischer #define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
238069154d5SJulian Elischer #define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
23930400f03SJulian Elischer 
24030400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
24130400f03SJulian Elischer 
242069154d5SJulian Elischer /* Warning: Generally use NG_FREE_ITEM() instead */
243069154d5SJulian Elischer #define NG_FREE_ITEM_REAL(item) do { FREE((item), M_NETGRAPH_ITEM); } while (0)
244069154d5SJulian Elischer 
2454cf49a43SJulian Elischer 
2464cf49a43SJulian Elischer /* Set this to Debugger("X") to catch all errors as they occur */
2474cf49a43SJulian Elischer #ifndef TRAP_ERROR
2484cf49a43SJulian Elischer #define TRAP_ERROR
2494cf49a43SJulian Elischer #endif
2504cf49a43SJulian Elischer 
251dc90cad9SJulian Elischer static	ng_ID_t nextID = 1;
252dc90cad9SJulian Elischer 
253b2da83c2SArchie Cobbs #ifdef INVARIANTS
254b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
255b2da83c2SArchie Cobbs 		struct mbuf *n;						\
256b2da83c2SArchie Cobbs 		int total;						\
257b2da83c2SArchie Cobbs 									\
258b2da83c2SArchie Cobbs 		if (((m)->m_flags & M_PKTHDR) == 0)			\
259b2da83c2SArchie Cobbs 			panic("%s: !PKTHDR", __FUNCTION__);		\
260b2da83c2SArchie Cobbs 		for (total = 0, n = (m); n != NULL; n = n->m_next)	\
261b2da83c2SArchie Cobbs 			total += n->m_len;				\
262b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
263b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
264b2da83c2SArchie Cobbs 			    __FUNCTION__, (m)->m_pkthdr.len, total);	\
265b2da83c2SArchie Cobbs 		}							\
266b2da83c2SArchie Cobbs 	} while (0)
267b2da83c2SArchie Cobbs #else
268b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
269b2da83c2SArchie Cobbs #endif
270b2da83c2SArchie Cobbs 
271dc90cad9SJulian Elischer 
2724cf49a43SJulian Elischer /************************************************************************
273f8307e12SArchie Cobbs 	Parse type definitions for generic messages
274f8307e12SArchie Cobbs ************************************************************************/
275f8307e12SArchie Cobbs 
276f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
277f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
278f8307e12SArchie Cobbs static const struct ng_parse_struct_info				\
279f8307e12SArchie Cobbs 	ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args;	\
280f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
281f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
282f8307e12SArchie Cobbs 	&ng_ ## lo ## _type_info					\
283f8307e12SArchie Cobbs }
284f8307e12SArchie Cobbs 
285f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
286f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
287f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
288f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
289f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
290f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
291f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
292f8307e12SArchie Cobbs 
293f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
294f8307e12SArchie Cobbs    value immediately preceeding the array -- as with struct namelist
295f8307e12SArchie Cobbs    and struct typelist. */
296f8307e12SArchie Cobbs static int
297f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
298f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
299f8307e12SArchie Cobbs {
300f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
301f8307e12SArchie Cobbs }
302f8307e12SArchie Cobbs 
303f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
304f8307e12SArchie Cobbs static int
305f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
306f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
307f8307e12SArchie Cobbs {
308f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
309f8307e12SArchie Cobbs 
310f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
311f8307e12SArchie Cobbs }
312f8307e12SArchie Cobbs 
313f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
314f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
315f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
316f8307e12SArchie Cobbs 	&ng_generic_list_getLength
317f8307e12SArchie Cobbs };
318f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
319f8307e12SArchie Cobbs 	&ng_parse_array_type,
320f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
321f8307e12SArchie Cobbs };
322f8307e12SArchie Cobbs 
323f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
324f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
325f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
326f8307e12SArchie Cobbs 	&ng_generic_list_getLength
327f8307e12SArchie Cobbs };
328f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
329f8307e12SArchie Cobbs 	&ng_parse_array_type,
330f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
331f8307e12SArchie Cobbs };
332f8307e12SArchie Cobbs 
333f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
334f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
335f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
336f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
337f8307e12SArchie Cobbs };
338f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
339f8307e12SArchie Cobbs 	&ng_parse_array_type,
340f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
341f8307e12SArchie Cobbs };
342f8307e12SArchie Cobbs 
343f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
344f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
345f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
346f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
347f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
348f8307e12SArchie Cobbs 
349f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
350f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
351f8307e12SArchie Cobbs 	{
352f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
353f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
354f8307e12SArchie Cobbs 	  "shutdown",
355f8307e12SArchie Cobbs 	  NULL,
356f8307e12SArchie Cobbs 	  NULL
357f8307e12SArchie Cobbs 	},
358f8307e12SArchie Cobbs 	{
359f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
360f8307e12SArchie Cobbs 	  NGM_MKPEER,
361f8307e12SArchie Cobbs 	  "mkpeer",
362f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
363f8307e12SArchie Cobbs 	  NULL
364f8307e12SArchie Cobbs 	},
365f8307e12SArchie Cobbs 	{
366f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
367f8307e12SArchie Cobbs 	  NGM_CONNECT,
368f8307e12SArchie Cobbs 	  "connect",
369f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
370f8307e12SArchie Cobbs 	  NULL
371f8307e12SArchie Cobbs 	},
372f8307e12SArchie Cobbs 	{
373f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
374f8307e12SArchie Cobbs 	  NGM_NAME,
375f8307e12SArchie Cobbs 	  "name",
376f8307e12SArchie Cobbs 	  &ng_generic_name_type,
377f8307e12SArchie Cobbs 	  NULL
378f8307e12SArchie Cobbs 	},
379f8307e12SArchie Cobbs 	{
380f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
381f8307e12SArchie Cobbs 	  NGM_RMHOOK,
382f8307e12SArchie Cobbs 	  "rmhook",
383f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
384f8307e12SArchie Cobbs 	  NULL
385f8307e12SArchie Cobbs 	},
386f8307e12SArchie Cobbs 	{
387f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
388f8307e12SArchie Cobbs 	  NGM_NODEINFO,
389f8307e12SArchie Cobbs 	  "nodeinfo",
390f8307e12SArchie Cobbs 	  NULL,
391f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
392f8307e12SArchie Cobbs 	},
393f8307e12SArchie Cobbs 	{
394f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
395f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
396f8307e12SArchie Cobbs 	  "listhooks",
397f8307e12SArchie Cobbs 	  NULL,
398f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
399f8307e12SArchie Cobbs 	},
400f8307e12SArchie Cobbs 	{
401f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
402f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
403f8307e12SArchie Cobbs 	  "listnames",
404f8307e12SArchie Cobbs 	  NULL,
405f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
406f8307e12SArchie Cobbs 	},
407f8307e12SArchie Cobbs 	{
408f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
409f8307e12SArchie Cobbs 	  NGM_LISTNODES,
410f8307e12SArchie Cobbs 	  "listnodes",
411f8307e12SArchie Cobbs 	  NULL,
412f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
413f8307e12SArchie Cobbs 	},
414f8307e12SArchie Cobbs 	{
415f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
416f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
417f8307e12SArchie Cobbs 	  "listtypes",
418f8307e12SArchie Cobbs 	  NULL,
419f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
420f8307e12SArchie Cobbs 	},
421f8307e12SArchie Cobbs 	{
422f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
4237095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
4247095e097SPoul-Henning Kamp 	  "textconfig",
4257095e097SPoul-Henning Kamp 	  NULL,
4267095e097SPoul-Henning Kamp 	  &ng_parse_string_type
4277095e097SPoul-Henning Kamp 	},
4287095e097SPoul-Henning Kamp 	{
4297095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
430f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
431f8307e12SArchie Cobbs 	  "textstatus",
432f8307e12SArchie Cobbs 	  NULL,
433f8307e12SArchie Cobbs 	  &ng_parse_string_type
434f8307e12SArchie Cobbs 	},
435f8307e12SArchie Cobbs 	{
436f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
437f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
438f8307e12SArchie Cobbs 	  "ascii2binary",
439f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
440f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
441f8307e12SArchie Cobbs 	},
442f8307e12SArchie Cobbs 	{
443f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
444f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
445f8307e12SArchie Cobbs 	  "binary2ascii",
446f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
447f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
448f8307e12SArchie Cobbs 	},
449f8307e12SArchie Cobbs 	{ 0 }
450f8307e12SArchie Cobbs };
451f8307e12SArchie Cobbs 
452f8307e12SArchie Cobbs /************************************************************************
4534cf49a43SJulian Elischer 			Node routines
4544cf49a43SJulian Elischer ************************************************************************/
4554cf49a43SJulian Elischer 
4564cf49a43SJulian Elischer /*
4574cf49a43SJulian Elischer  * Instantiate a node of the requested type
4584cf49a43SJulian Elischer  */
4594cf49a43SJulian Elischer int
4604cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
4614cf49a43SJulian Elischer {
4624cf49a43SJulian Elischer 	struct ng_type *type;
463069154d5SJulian Elischer 	int	error;
4644cf49a43SJulian Elischer 
4654cf49a43SJulian Elischer 	/* Check that the type makes sense */
4664cf49a43SJulian Elischer 	if (typename == NULL) {
4674cf49a43SJulian Elischer 		TRAP_ERROR;
4684cf49a43SJulian Elischer 		return (EINVAL);
4694cf49a43SJulian Elischer 	}
4704cf49a43SJulian Elischer 
4714cf49a43SJulian Elischer 	/* Locate the node type */
4724cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL) {
473cc3daab5SPeter Wemm 		char filename[NG_TYPELEN + 4];
4744cf49a43SJulian Elischer 		linker_file_t lf;
4754cf49a43SJulian Elischer 		int error;
4764cf49a43SJulian Elischer 
4774cf49a43SJulian Elischer 		/* Not found, try to load it as a loadable module */
478453b5565SJulian Elischer 		snprintf(filename, sizeof(filename), "ng_%s", typename);
479cc3daab5SPeter Wemm 		error = linker_load_file(filename, &lf);
4804cf49a43SJulian Elischer 		if (error != 0)
4814cf49a43SJulian Elischer 			return (error);
4824cf49a43SJulian Elischer 		lf->userrefs++;		/* pretend loaded by the syscall */
4834cf49a43SJulian Elischer 
4844cf49a43SJulian Elischer 		/* Try again, as now the type should have linked itself in */
4854cf49a43SJulian Elischer 		if ((type = ng_findtype(typename)) == NULL)
4864cf49a43SJulian Elischer 			return (ENXIO);
4874cf49a43SJulian Elischer 	}
4884cf49a43SJulian Elischer 
489069154d5SJulian Elischer 	/*
490069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
491069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
492069154d5SJulian Elischer 	 */
493069154d5SJulian Elischer 	if (type->constructor != NULL) {
494069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
495069154d5SJulian Elischer 			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
49630400f03SJulian Elischer 				NG_NODE_UNREF(*nodepp);
497069154d5SJulian Elischer 			}
498069154d5SJulian Elischer 		}
499069154d5SJulian Elischer 	} else {
500069154d5SJulian Elischer 		/*
501069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
502069154d5SJulian Elischer 		 * to be made. It must be brought into existance by
503069154d5SJulian Elischer 		 * some external agency. The external acency should
504069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
505069154d5SJulian Elischer 		 * netgraph part initialised.
506069154d5SJulian Elischer 		 */
50730400f03SJulian Elischer 		TRAP_ERROR
508069154d5SJulian Elischer 		error = EINVAL;
509069154d5SJulian Elischer 	}
510069154d5SJulian Elischer 	return (error);
5114cf49a43SJulian Elischer }
5124cf49a43SJulian Elischer 
5134cf49a43SJulian Elischer /*
514069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
515069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
5164cf49a43SJulian Elischer  * The returned node has a reference count of 1.
5174cf49a43SJulian Elischer  */
5184cf49a43SJulian Elischer int
5194cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
5204cf49a43SJulian Elischer {
5214cf49a43SJulian Elischer 	node_p node;
5224cf49a43SJulian Elischer 
5234cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
5244cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
5254cf49a43SJulian Elischer 		TRAP_ERROR;
5264cf49a43SJulian Elischer 		return (EINVAL);
5274cf49a43SJulian Elischer 	}
5284cf49a43SJulian Elischer 
5294cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
53030400f03SJulian Elischer 	NG_ALLOC_NODE(node);
5314cf49a43SJulian Elischer 	if (node == NULL) {
5324cf49a43SJulian Elischer 		TRAP_ERROR;
5334cf49a43SJulian Elischer 		return (ENOMEM);
5344cf49a43SJulian Elischer 	}
53530400f03SJulian Elischer 	node->nd_type = type;
53630400f03SJulian Elischer 	NG_NODE_REF(node);				/* note reference */
5374cf49a43SJulian Elischer 	type->refs++;
5384cf49a43SJulian Elischer 
53930400f03SJulian Elischer 	mtx_init(&node->nd_input_queue.q_mtx, "netgraph node mutex", 0);
54030400f03SJulian Elischer 	node->nd_input_queue.queue = NULL;
54130400f03SJulian Elischer 	node->nd_input_queue.last = &node->nd_input_queue.queue;
54230400f03SJulian Elischer 	node->nd_input_queue.q_flags = 0;
54330400f03SJulian Elischer 	node->nd_input_queue.q_node = node;
5444cf49a43SJulian Elischer 
5454cf49a43SJulian Elischer 	/* Initialize hook list for new node */
54630400f03SJulian Elischer 	LIST_INIT(&node->nd_hooks);
5474cf49a43SJulian Elischer 
548069154d5SJulian Elischer 	/* Link us into the node linked list */
549069154d5SJulian Elischer 	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
55030400f03SJulian Elischer 	LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes);
551069154d5SJulian Elischer 	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
552069154d5SJulian Elischer 
553069154d5SJulian Elischer 
554dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
555069154d5SJulian Elischer 	mtx_enter(&ng_idhash_mtx, MTX_DEF);
55630400f03SJulian Elischer 	for (;;) { /* wrap protection, even if silly */
557069154d5SJulian Elischer 		node_p node2 = NULL;
55830400f03SJulian Elischer 		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
55930400f03SJulian Elischer 		/* Is there a problem with the new number? */
56030400f03SJulian Elischer 		if ((node->nd_ID == 0)
56130400f03SJulian Elischer 		|| (node2 = ng_ID2noderef(node->nd_ID))) {
562069154d5SJulian Elischer 			if (node2) {
56330400f03SJulian Elischer 				NG_NODE_UNREF(node2);
564069154d5SJulian Elischer 				node2 = NULL;
565069154d5SJulian Elischer 			}
56630400f03SJulian Elischer 		} else {
56730400f03SJulian Elischer 			break;
568069154d5SJulian Elischer 		}
56930400f03SJulian Elischer 	}
57030400f03SJulian Elischer 	LIST_INSERT_HEAD(&ng_ID_hash[node->nd_ID % ID_HASH_SIZE],
57130400f03SJulian Elischer 							node, nd_idnodes);
572069154d5SJulian Elischer 	mtx_exit(&ng_idhash_mtx, MTX_DEF);
573dc90cad9SJulian Elischer 
5744cf49a43SJulian Elischer 	/* Done */
5754cf49a43SJulian Elischer 	*nodepp = node;
5764cf49a43SJulian Elischer 	return (0);
5774cf49a43SJulian Elischer }
5784cf49a43SJulian Elischer 
5794cf49a43SJulian Elischer /*
5804cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
5814cf49a43SJulian Elischer  * it's shutdown method, or do the default shutdown if there is
5824cf49a43SJulian Elischer  * no type-specific method.
5834cf49a43SJulian Elischer  *
584069154d5SJulian Elischer  * We can only be called form a shutdown message, so we know we have
585069154d5SJulian Elischer  * a writer lock, and therefore exclusive access.
586069154d5SJulian Elischer  *
587069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
588069154d5SJulian Elischer  * Allocates a new node. This one is irretrievably going away.
5894cf49a43SJulian Elischer  */
5904cf49a43SJulian Elischer void
5914cf49a43SJulian Elischer ng_rmnode(node_p node)
5924cf49a43SJulian Elischer {
5934cf49a43SJulian Elischer 	/* Check if it's already shutting down */
59430400f03SJulian Elischer 	if ((node->nd_flags & NG_CLOSING) != 0)
5954cf49a43SJulian Elischer 		return;
5964cf49a43SJulian Elischer 
5974cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
59830400f03SJulian Elischer 	NG_NODE_REF(node);
5994cf49a43SJulian Elischer 
60030400f03SJulian Elischer 	/*
60130400f03SJulian Elischer 	 * Mark it invalid so any newcomers know not to try use it
60230400f03SJulian Elischer 	 * Also add our own mark so we can't recurse
60330400f03SJulian Elischer 	 * note that NG_INVALID does not do this as it's also set during
60430400f03SJulian Elischer 	 * creation
60530400f03SJulian Elischer 	 */
60630400f03SJulian Elischer 	node->nd_flags |= NG_INVALID|NG_CLOSING;
6074cf49a43SJulian Elischer 
6084cf49a43SJulian Elischer 	ng_cutlinks(node);
60930400f03SJulian Elischer 
610069154d5SJulian Elischer 	/*
611069154d5SJulian Elischer 	 * Drain the input queue forceably.
61230400f03SJulian Elischer 	 * it has no hooks so what's it going to do, bleed on someone?
61330400f03SJulian Elischer 	 * Theoretically we came here from a queue entry that was added
61430400f03SJulian Elischer 	 * Just before the queue was closed, so it should be empty anyway.
615069154d5SJulian Elischer 	 */
61630400f03SJulian Elischer 	ng_flush_input_queue(&node->nd_input_queue);
617069154d5SJulian Elischer 
618069154d5SJulian Elischer 	/*
619069154d5SJulian Elischer 	 * Take us off the work queue if we are there.
62030400f03SJulian Elischer 	 * We definatly have no work to be done.
621069154d5SJulian Elischer 	 */
622069154d5SJulian Elischer 	ng_worklist_remove(node);
623069154d5SJulian Elischer 
624069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
62530400f03SJulian Elischer 	if (node->nd_type && node->nd_type->shutdown) {
62630400f03SJulian Elischer 		(*node->nd_type->shutdown)(node);
627069154d5SJulian Elischer 	} else {				/* do the default thing */
62830400f03SJulian Elischer 		NG_NODE_UNREF(node);
62930400f03SJulian Elischer 	}
63030400f03SJulian Elischer 	if (NG_NODE_IS_VALID(node)) {
63130400f03SJulian Elischer 		/*
63230400f03SJulian Elischer 		 * Well, blow me down if the node code hasn't declared
63330400f03SJulian Elischer 		 * that it doesn't want to die.
63430400f03SJulian Elischer 		 * Presumably it is a persistant node.
63530400f03SJulian Elischer 		 * XXX we need a way to tell the node
63630400f03SJulian Elischer 		 * "No, really.. the hardware's going away.. REALLY die"
63730400f03SJulian Elischer 		 * We need a way
63830400f03SJulian Elischer 		 */
63930400f03SJulian Elischer 		return;
6404cf49a43SJulian Elischer 	}
6414cf49a43SJulian Elischer 
64230400f03SJulian Elischer 	ng_unname(node); /* basically a NOP these days */
64330400f03SJulian Elischer 
64430400f03SJulian Elischer 	/*
64530400f03SJulian Elischer 	 * Remove extra reference, possibly the last
64630400f03SJulian Elischer 	 * Possible other holders of references may include
64730400f03SJulian Elischer 	 * timeout callouts, but theoretically the node's supposed to
64830400f03SJulian Elischer 	 * have cancelled them. Possibly hardware dependencies may
64930400f03SJulian Elischer 	 * force a driver to 'linger' with a reference.
65030400f03SJulian Elischer 	 */
65130400f03SJulian Elischer 	NG_NODE_UNREF(node);
6524cf49a43SJulian Elischer }
6534cf49a43SJulian Elischer 
6544cf49a43SJulian Elischer /*
6554cf49a43SJulian Elischer  * Called by the destructor to remove any STANDARD external references
65630400f03SJulian Elischer  * May one day have it's own message to call it..
6574cf49a43SJulian Elischer  */
6584cf49a43SJulian Elischer void
6594cf49a43SJulian Elischer ng_cutlinks(node_p node)
6604cf49a43SJulian Elischer {
6614cf49a43SJulian Elischer 	hook_p  hook;
6624cf49a43SJulian Elischer 
6634cf49a43SJulian Elischer 	/* Make sure that this is set to stop infinite loops */
66430400f03SJulian Elischer 	node->nd_flags |= NG_INVALID;
6654cf49a43SJulian Elischer 
666069154d5SJulian Elischer 	/*
667069154d5SJulian Elischer 	 * Drain the input queue forceably.
668069154d5SJulian Elischer 	 * We also do this in ng_rmnode
669069154d5SJulian Elischer 	 * to make sure we get all code paths.
670069154d5SJulian Elischer 	 */
67130400f03SJulian Elischer 	ng_flush_input_queue(&node->nd_input_queue);
6724cf49a43SJulian Elischer 
6734cf49a43SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
67430400f03SJulian Elischer 	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
6754cf49a43SJulian Elischer 		ng_destroy_hook(hook);
6764cf49a43SJulian Elischer }
6774cf49a43SJulian Elischer 
6784cf49a43SJulian Elischer /*
6794cf49a43SJulian Elischer  * Remove a reference to the node, possibly the last
6804cf49a43SJulian Elischer  */
6814cf49a43SJulian Elischer void
68230400f03SJulian Elischer ng_unref_node(node_p node)
6834cf49a43SJulian Elischer {
68430400f03SJulian Elischer 	int     v;
68530400f03SJulian Elischer 	do {
68630400f03SJulian Elischer 		v = node->nd_refs;
68730400f03SJulian Elischer 	} while (! atomic_cmpset_int(&node->nd_refs, v, v - 1));
688e8a49db2SJulian Elischer 
68930400f03SJulian Elischer 	if (v == 1) { /* we were the last */
690069154d5SJulian Elischer 
691069154d5SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
69230400f03SJulian Elischer 		node->nd_type->refs--; /* XXX maybe should get types lock? */
69330400f03SJulian Elischer 		LIST_REMOVE(node, nd_nodes);
694069154d5SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
695069154d5SJulian Elischer 
696069154d5SJulian Elischer 		mtx_enter(&ng_idhash_mtx, MTX_DEF);
69730400f03SJulian Elischer 		LIST_REMOVE(node, nd_idnodes);
698069154d5SJulian Elischer 		mtx_exit(&ng_idhash_mtx, MTX_DEF);
699069154d5SJulian Elischer 
70012574a02SJulian Elischer 		mtx_destroy(&node->nd_input_queue.q_mtx);
701069154d5SJulian Elischer 		NG_FREE_NODE(node);
7024cf49a43SJulian Elischer 	}
7034cf49a43SJulian Elischer }
7044cf49a43SJulian Elischer 
7054cf49a43SJulian Elischer /************************************************************************
706dc90cad9SJulian Elischer 			Node ID handling
707dc90cad9SJulian Elischer ************************************************************************/
708dc90cad9SJulian Elischer static node_p
709069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
710dc90cad9SJulian Elischer {
71130400f03SJulian Elischer 	node_p node;
712069154d5SJulian Elischer 	mtx_enter(&ng_idhash_mtx, MTX_DEF);
71330400f03SJulian Elischer 	LIST_FOREACH(node, &ng_ID_hash[ID % ID_HASH_SIZE], nd_idnodes) {
71430400f03SJulian Elischer 		if (node->nd_ID == ID)
715dc90cad9SJulian Elischer 			break;
716dc90cad9SJulian Elischer 	}
71730400f03SJulian Elischer 	if(node)
71830400f03SJulian Elischer 		NG_NODE_REF(node);
719069154d5SJulian Elischer 	mtx_exit(&ng_idhash_mtx, MTX_DEF);
72030400f03SJulian Elischer 	return(node);
721dc90cad9SJulian Elischer }
722dc90cad9SJulian Elischer 
723dc90cad9SJulian Elischer ng_ID_t
724dc90cad9SJulian Elischer ng_node2ID(node_p node)
725dc90cad9SJulian Elischer {
72630400f03SJulian Elischer 	return (node?node->nd_ID:0);
727dc90cad9SJulian Elischer }
728dc90cad9SJulian Elischer 
729dc90cad9SJulian Elischer /************************************************************************
7304cf49a43SJulian Elischer 			Node name handling
7314cf49a43SJulian Elischer ************************************************************************/
7324cf49a43SJulian Elischer 
7334cf49a43SJulian Elischer /*
7344cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
7354cf49a43SJulian Elischer  */
7364cf49a43SJulian Elischer int
7374cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
7384cf49a43SJulian Elischer {
7394cf49a43SJulian Elischer 	int i;
740069154d5SJulian Elischer 	node_p node2;
7414cf49a43SJulian Elischer 
7424cf49a43SJulian Elischer 	/* Check the name is valid */
7434cf49a43SJulian Elischer 	for (i = 0; i < NG_NODELEN + 1; i++) {
7444cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
7454cf49a43SJulian Elischer 			break;
7464cf49a43SJulian Elischer 	}
7474cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
7484cf49a43SJulian Elischer 		TRAP_ERROR;
7494cf49a43SJulian Elischer 		return (EINVAL);
7504cf49a43SJulian Elischer 	}
751dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
7524cf49a43SJulian Elischer 		TRAP_ERROR;
7534cf49a43SJulian Elischer 		return (EINVAL);
7544cf49a43SJulian Elischer 	}
7554cf49a43SJulian Elischer 
7564cf49a43SJulian Elischer 	/* Check the name isn't already being used */
757069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
75830400f03SJulian Elischer 		NG_NODE_UNREF(node2);
7594cf49a43SJulian Elischer 		TRAP_ERROR;
7604cf49a43SJulian Elischer 		return (EADDRINUSE);
7614cf49a43SJulian Elischer 	}
7624cf49a43SJulian Elischer 
763069154d5SJulian Elischer 	/* copy it */
76430400f03SJulian Elischer 	strncpy(NG_NODE_NAME(node), name, NG_NODELEN);
7654cf49a43SJulian Elischer 
7664cf49a43SJulian Elischer 	return (0);
7674cf49a43SJulian Elischer }
7684cf49a43SJulian Elischer 
7694cf49a43SJulian Elischer /*
7704cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
7714cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
7724cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
7734cf49a43SJulian Elischer  *
7744cf49a43SJulian Elischer  * Returns the node if found, else NULL.
775069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
77630400f03SJulian Elischer  * Note it aquires a reference on the node so you can be sure it's still there.
7774cf49a43SJulian Elischer  */
7784cf49a43SJulian Elischer node_p
779069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
7804cf49a43SJulian Elischer {
781dc90cad9SJulian Elischer 	node_p node;
782dc90cad9SJulian Elischer 	ng_ID_t temp;
7834cf49a43SJulian Elischer 
7844cf49a43SJulian Elischer 	/* "." means "this node" */
785069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
78630400f03SJulian Elischer 		NG_NODE_REF(here);
787069154d5SJulian Elischer 		return(here);
788069154d5SJulian Elischer 	}
7894cf49a43SJulian Elischer 
7904cf49a43SJulian Elischer 	/* Check for name-by-ID */
791dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
792069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
7934cf49a43SJulian Elischer 	}
7944cf49a43SJulian Elischer 
7954cf49a43SJulian Elischer 	/* Find node by name */
796069154d5SJulian Elischer 	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
79730400f03SJulian Elischer 	LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
79830400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(node)
79930400f03SJulian Elischer 		&& (strcmp(NG_NODE_NAME(node), name) == 0))
8004cf49a43SJulian Elischer 			break;
8014cf49a43SJulian Elischer 	}
802069154d5SJulian Elischer 	if (node)
80330400f03SJulian Elischer 		NG_NODE_REF(node);
804069154d5SJulian Elischer 	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
8054cf49a43SJulian Elischer 	return (node);
8064cf49a43SJulian Elischer }
8074cf49a43SJulian Elischer 
8084cf49a43SJulian Elischer /*
809dc90cad9SJulian Elischer  * Decode a ID name, eg. "[f03034de]". Returns 0 if the
810dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
8114cf49a43SJulian Elischer  */
812dc90cad9SJulian Elischer static ng_ID_t
8134cf49a43SJulian Elischer ng_decodeidname(const char *name)
8144cf49a43SJulian Elischer {
8152b70adcbSArchie Cobbs 	const int len = strlen(name);
81625792ef3SArchie Cobbs 	char *eptr;
8172b70adcbSArchie Cobbs 	u_long val;
8184cf49a43SJulian Elischer 
8192b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
8202b70adcbSArchie Cobbs 	if (len < 3 || name[0] != '[' || name[len - 1] != ']'
821ef050c81SJulian Elischer 	    || !isxdigit(name[1]))
8222b70adcbSArchie Cobbs 		return (0);
8234cf49a43SJulian Elischer 
8242b70adcbSArchie Cobbs 	/* Decode number */
8252b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
826ef050c81SJulian Elischer 	if (eptr - name != len - 1 || val == ULONG_MAX || val == 0)
82712f035e0SJulian Elischer 		return ((ng_ID_t)0);
8282b70adcbSArchie Cobbs 	return (ng_ID_t)val;
8294cf49a43SJulian Elischer }
8304cf49a43SJulian Elischer 
8314cf49a43SJulian Elischer /*
8324cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
8334cf49a43SJulian Elischer  * when shutting down and removing the node.
8344cf49a43SJulian Elischer  */
8354cf49a43SJulian Elischer void
8364cf49a43SJulian Elischer ng_unname(node_p node)
8374cf49a43SJulian Elischer {
83830400f03SJulian Elischer 	bzero(NG_NODE_NAME(node), NG_NODELEN);
8394cf49a43SJulian Elischer }
8404cf49a43SJulian Elischer 
8414cf49a43SJulian Elischer /************************************************************************
8424cf49a43SJulian Elischer 			Hook routines
8434cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
8444cf49a43SJulian Elischer  brief moment within these routines.
8454cf49a43SJulian Elischer ************************************************************************/
8464cf49a43SJulian Elischer 
8474cf49a43SJulian Elischer /*
8484cf49a43SJulian Elischer  * Remove a hook reference
8494cf49a43SJulian Elischer  */
85030400f03SJulian Elischer void
8514cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
8524cf49a43SJulian Elischer {
85330400f03SJulian Elischer 	int     v;
85430400f03SJulian Elischer 	do {
85530400f03SJulian Elischer 		v = hook->hk_refs;
85630400f03SJulian Elischer 	} while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1));
857e8a49db2SJulian Elischer 
85830400f03SJulian Elischer 	if (v == 1) { /* we were the last */
85930400f03SJulian Elischer 		if (NG_HOOK_NODE(hook)) {
86030400f03SJulian Elischer 			NG_NODE_UNREF((NG_HOOK_NODE(hook)));
86130400f03SJulian Elischer 			hook->hk_node = NULL;
862069154d5SJulian Elischer 		}
863069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
864069154d5SJulian Elischer 	}
8654cf49a43SJulian Elischer }
8664cf49a43SJulian Elischer 
8674cf49a43SJulian Elischer /*
8684cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
8694cf49a43SJulian Elischer  */
8704cf49a43SJulian Elischer static int
8714cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
8724cf49a43SJulian Elischer {
8734cf49a43SJulian Elischer 	hook_p hook;
8744cf49a43SJulian Elischer 	int error = 0;
8754cf49a43SJulian Elischer 
8764cf49a43SJulian Elischer 	/* Check that the given name is good */
8774cf49a43SJulian Elischer 	if (name == NULL) {
8784cf49a43SJulian Elischer 		TRAP_ERROR;
8794cf49a43SJulian Elischer 		return (EINVAL);
8804cf49a43SJulian Elischer 	}
881899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
8824cf49a43SJulian Elischer 		TRAP_ERROR;
8834cf49a43SJulian Elischer 		return (EEXIST);
8844cf49a43SJulian Elischer 	}
8854cf49a43SJulian Elischer 
8864cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
88730400f03SJulian Elischer 	NG_ALLOC_HOOK(hook);
8884cf49a43SJulian Elischer 	if (hook == NULL) {
8894cf49a43SJulian Elischer 		TRAP_ERROR;
8904cf49a43SJulian Elischer 		return (ENOMEM);
8914cf49a43SJulian Elischer 	}
89230400f03SJulian Elischer 	hook->hk_refs = 1;
89330400f03SJulian Elischer 	hook->hk_flags = HK_INVALID;
89430400f03SJulian Elischer 	hook->hk_node = node;
89530400f03SJulian Elischer 	NG_NODE_REF(node);		/* each hook counts as a reference */
8964cf49a43SJulian Elischer 
8974cf49a43SJulian Elischer 	/* Check if the node type code has something to say about it */
89830400f03SJulian Elischer 	if (node->nd_type->newhook != NULL)
89930400f03SJulian Elischer 		if ((error = (*node->nd_type->newhook)(node, hook, name)) != 0) {
90030400f03SJulian Elischer 			NG_HOOK_UNREF(hook);	/* this frees the hook */
901069154d5SJulian Elischer 			return (error);
902069154d5SJulian Elischer 		}
9034cf49a43SJulian Elischer 
9044cf49a43SJulian Elischer 	/*
9054cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
9064cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
907069154d5SJulian Elischer 	 * The reference we have is for this linkage.
9084cf49a43SJulian Elischer 	 */
90930400f03SJulian Elischer 	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
91030400f03SJulian Elischer 	node->nd_numhooks++;
9114cf49a43SJulian Elischer 
9124cf49a43SJulian Elischer 	/* Set hook name */
91330400f03SJulian Elischer 	strncpy(NG_HOOK_NAME(hook), name, NG_HOOKLEN);
9144cf49a43SJulian Elischer 	if (hookp)
9154cf49a43SJulian Elischer 		*hookp = hook;
9164cf49a43SJulian Elischer 	return (error);
9174cf49a43SJulian Elischer }
9184cf49a43SJulian Elischer 
9194cf49a43SJulian Elischer /*
9204cf49a43SJulian Elischer  * Connect a pair of hooks. Only used internally.
9214cf49a43SJulian Elischer  */
9224cf49a43SJulian Elischer static int
9234cf49a43SJulian Elischer ng_connect(hook_p hook1, hook_p hook2)
9244cf49a43SJulian Elischer {
9254cf49a43SJulian Elischer 	int     error;
9264cf49a43SJulian Elischer 
92730400f03SJulian Elischer 	hook1->hk_peer = hook2;
92830400f03SJulian Elischer 	hook2->hk_peer = hook1;
9294cf49a43SJulian Elischer 
9304cf49a43SJulian Elischer 	/* Give each node the opportunity to veto the impending connection */
93130400f03SJulian Elischer 	if (hook1->hk_node->nd_type->connect) {
93230400f03SJulian Elischer 		if ((error = (*hook1->hk_node->nd_type->connect) (hook1))) {
9334cf49a43SJulian Elischer 			ng_destroy_hook(hook1);	/* also zaps hook2 */
9344cf49a43SJulian Elischer 			return (error);
9354cf49a43SJulian Elischer 		}
9364cf49a43SJulian Elischer 	}
93730400f03SJulian Elischer 	if (hook2->hk_node->nd_type->connect) {
93830400f03SJulian Elischer 		if ((error = (*hook2->hk_node->nd_type->connect) (hook2))) {
9394cf49a43SJulian Elischer 			ng_destroy_hook(hook2);	/* also zaps hook1 */
9404cf49a43SJulian Elischer 			return (error);
9414cf49a43SJulian Elischer 		}
9424cf49a43SJulian Elischer 	}
94330400f03SJulian Elischer 	hook1->hk_flags &= ~HK_INVALID;
94430400f03SJulian Elischer 	hook2->hk_flags &= ~HK_INVALID;
9454cf49a43SJulian Elischer 	return (0);
9464cf49a43SJulian Elischer }
9474cf49a43SJulian Elischer 
9484cf49a43SJulian Elischer /*
949899e9c4eSArchie Cobbs  * Find a hook
950899e9c4eSArchie Cobbs  *
951899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
952899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
953899e9c4eSArchie Cobbs  */
954899e9c4eSArchie Cobbs hook_p
955899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
956899e9c4eSArchie Cobbs {
957899e9c4eSArchie Cobbs 	hook_p hook;
958899e9c4eSArchie Cobbs 
95930400f03SJulian Elischer 	if (node->nd_type->findhook != NULL)
96030400f03SJulian Elischer 		return (*node->nd_type->findhook)(node, name);
96130400f03SJulian Elischer 	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
96230400f03SJulian Elischer 		if (strcmp(NG_HOOK_NAME(hook), name) == 0)
963899e9c4eSArchie Cobbs 			return (hook);
964899e9c4eSArchie Cobbs 	}
965899e9c4eSArchie Cobbs 	return (NULL);
966899e9c4eSArchie Cobbs }
967899e9c4eSArchie Cobbs 
968899e9c4eSArchie Cobbs /*
9694cf49a43SJulian Elischer  * Destroy a hook
9704cf49a43SJulian Elischer  *
9714cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
9724cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
9734cf49a43SJulian Elischer  * from each other first.
9744cf49a43SJulian Elischer  */
9754cf49a43SJulian Elischer void
9764cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
9774cf49a43SJulian Elischer {
97830400f03SJulian Elischer 	hook_p peer = NG_HOOK_PEER(hook);
9794cf49a43SJulian Elischer 
98030400f03SJulian Elischer 	hook->hk_flags |= HK_INVALID;		/* as soon as possible */
9814cf49a43SJulian Elischer 	if (peer) {
98230400f03SJulian Elischer 		peer->hk_flags |= HK_INVALID;	/* as soon as possible */
98330400f03SJulian Elischer 		hook->hk_peer = NULL;
98430400f03SJulian Elischer 		peer->hk_peer = NULL;
9854cf49a43SJulian Elischer 		ng_disconnect_hook(peer);
9864cf49a43SJulian Elischer 	}
9874cf49a43SJulian Elischer 	ng_disconnect_hook(hook);
9884cf49a43SJulian Elischer }
9894cf49a43SJulian Elischer 
9904cf49a43SJulian Elischer /*
9914cf49a43SJulian Elischer  * Notify the node of the hook's demise. This may result in more actions
9924cf49a43SJulian Elischer  * (e.g. shutdown) but we don't do that ourselves and don't know what
9934cf49a43SJulian Elischer  * happens there. If there is no appropriate handler, then just remove it
9944cf49a43SJulian Elischer  * (and decrement the reference count of it's node which in turn might
9954cf49a43SJulian Elischer  * make something happen).
9964cf49a43SJulian Elischer  */
9974cf49a43SJulian Elischer static void
9984cf49a43SJulian Elischer ng_disconnect_hook(hook_p hook)
9994cf49a43SJulian Elischer {
100030400f03SJulian Elischer 	node_p node = NG_HOOK_NODE(hook);
10014cf49a43SJulian Elischer 
10024cf49a43SJulian Elischer 	/*
10034cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
10044cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
10054cf49a43SJulian Elischer 	 */
100630400f03SJulian Elischer 	LIST_REMOVE(hook, hk_hooks);
100730400f03SJulian Elischer 	node->nd_numhooks--;
100830400f03SJulian Elischer 	if (node->nd_type->disconnect) {
10094cf49a43SJulian Elischer 		/*
10104cf49a43SJulian Elischer 		 * The type handler may elect to destroy the peer so don't
10114cf49a43SJulian Elischer 		 * trust its existance after this point.
10124cf49a43SJulian Elischer 		 */
101330400f03SJulian Elischer 		(*node->nd_type->disconnect) (hook);
10144cf49a43SJulian Elischer 	}
101530400f03SJulian Elischer 	NG_HOOK_UNREF(hook);
10164cf49a43SJulian Elischer }
10174cf49a43SJulian Elischer 
10184cf49a43SJulian Elischer /*
10194cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
10204cf49a43SJulian Elischer  * is effectively bypassed.
10214cf49a43SJulian Elischer  */
10224cf49a43SJulian Elischer int
10234cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
10244cf49a43SJulian Elischer {
102530400f03SJulian Elischer 	if (hook1->hk_node != hook2->hk_node) {
102630400f03SJulian Elischer 		TRAP_ERROR
10274cf49a43SJulian Elischer 		return (EINVAL);
102830400f03SJulian Elischer 	}
102930400f03SJulian Elischer 	hook1->hk_peer->hk_peer = hook2->hk_peer;
103030400f03SJulian Elischer 	hook2->hk_peer->hk_peer = hook1->hk_peer;
10314cf49a43SJulian Elischer 
10324cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
103330400f03SJulian Elischer 	hook1->hk_peer = NULL;
103430400f03SJulian Elischer 	hook2->hk_peer = NULL;
10354cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
10364cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
10374cf49a43SJulian Elischer 	return (0);
10384cf49a43SJulian Elischer }
10394cf49a43SJulian Elischer 
10404cf49a43SJulian Elischer /*
10414cf49a43SJulian Elischer  * Install a new netgraph type
10424cf49a43SJulian Elischer  */
10434cf49a43SJulian Elischer int
10444cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
10454cf49a43SJulian Elischer {
10464cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
10474cf49a43SJulian Elischer 
10484cf49a43SJulian Elischer 	/* Check version and type name fields */
1049589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
1050589f6ed8SJulian Elischer 	|| (namelen == 0)
1051589f6ed8SJulian Elischer 	|| (namelen > NG_TYPELEN)) {
10524cf49a43SJulian Elischer 		TRAP_ERROR;
10534cf49a43SJulian Elischer 		return (EINVAL);
10544cf49a43SJulian Elischer 	}
10554cf49a43SJulian Elischer 
10564cf49a43SJulian Elischer 	/* Check for name collision */
10574cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
10584cf49a43SJulian Elischer 		TRAP_ERROR;
10594cf49a43SJulian Elischer 		return (EEXIST);
10604cf49a43SJulian Elischer 	}
10614cf49a43SJulian Elischer 
10624cf49a43SJulian Elischer 	tp->refs = 0;
1063069154d5SJulian Elischer 
1064069154d5SJulian Elischer 	/* Link in new type */
1065069154d5SJulian Elischer 	mtx_enter(&ng_typelist_mtx, MTX_DEF);
1066069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
1067069154d5SJulian Elischer 	mtx_exit(&ng_typelist_mtx, MTX_DEF);
10684cf49a43SJulian Elischer 	return (0);
10694cf49a43SJulian Elischer }
10704cf49a43SJulian Elischer 
10714cf49a43SJulian Elischer /*
10724cf49a43SJulian Elischer  * Look for a type of the name given
10734cf49a43SJulian Elischer  */
10744cf49a43SJulian Elischer struct ng_type *
10754cf49a43SJulian Elischer ng_findtype(const char *typename)
10764cf49a43SJulian Elischer {
10774cf49a43SJulian Elischer 	struct ng_type *type;
10784cf49a43SJulian Elischer 
1079069154d5SJulian Elischer 	mtx_enter(&ng_typelist_mtx, MTX_DEF);
1080069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
10814cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
10824cf49a43SJulian Elischer 			break;
10834cf49a43SJulian Elischer 	}
1084069154d5SJulian Elischer 	mtx_exit(&ng_typelist_mtx, MTX_DEF);
10854cf49a43SJulian Elischer 	return (type);
10864cf49a43SJulian Elischer }
10874cf49a43SJulian Elischer 
10884cf49a43SJulian Elischer /************************************************************************
10894cf49a43SJulian Elischer 			Composite routines
10904cf49a43SJulian Elischer ************************************************************************/
10914cf49a43SJulian Elischer 
10924cf49a43SJulian Elischer /*
10934cf49a43SJulian Elischer  * Make a peer and connect. The order is arranged to minimise
10944cf49a43SJulian Elischer  * the work needed to back out in case of error.
10954cf49a43SJulian Elischer  */
10964cf49a43SJulian Elischer int
10974cf49a43SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
10984cf49a43SJulian Elischer {
10994cf49a43SJulian Elischer 	node_p  node2;
11004cf49a43SJulian Elischer 	hook_p  hook;
11014cf49a43SJulian Elischer 	hook_p  hook2;
11024cf49a43SJulian Elischer 	int     error;
11034cf49a43SJulian Elischer 
11044cf49a43SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))
11054cf49a43SJulian Elischer 		return (error);
11064cf49a43SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
11074cf49a43SJulian Elischer 		ng_destroy_hook(hook);
11084cf49a43SJulian Elischer 		return (error);
11094cf49a43SJulian Elischer 	}
11104cf49a43SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
11114cf49a43SJulian Elischer 		ng_rmnode(node2);
11124cf49a43SJulian Elischer 		ng_destroy_hook(hook);
11134cf49a43SJulian Elischer 		return (error);
11144cf49a43SJulian Elischer 	}
11154cf49a43SJulian Elischer 
11164cf49a43SJulian Elischer 	/*
11174cf49a43SJulian Elischer 	 * Actually link the two hooks together.. on failure they are
11184cf49a43SJulian Elischer 	 * destroyed so we don't have to do that here.
11194cf49a43SJulian Elischer 	 */
11204cf49a43SJulian Elischer 	if ((error = ng_connect(hook, hook2)))
11214cf49a43SJulian Elischer 		ng_rmnode(node2);
11224cf49a43SJulian Elischer 	return (error);
11234cf49a43SJulian Elischer }
11244cf49a43SJulian Elischer 
11254cf49a43SJulian Elischer /*
11264cf49a43SJulian Elischer  * Connect two nodes using the specified hooks
11274cf49a43SJulian Elischer  */
11284cf49a43SJulian Elischer int
11294cf49a43SJulian Elischer ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
11304cf49a43SJulian Elischer {
11314cf49a43SJulian Elischer 	int     error;
11324cf49a43SJulian Elischer 	hook_p  hook;
11334cf49a43SJulian Elischer 	hook_p  hook2;
11344cf49a43SJulian Elischer 
11354cf49a43SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))
11364cf49a43SJulian Elischer 		return (error);
11374cf49a43SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
11384cf49a43SJulian Elischer 		ng_destroy_hook(hook);
11394cf49a43SJulian Elischer 		return (error);
11404cf49a43SJulian Elischer 	}
11414cf49a43SJulian Elischer 	return (ng_connect(hook, hook2));
11424cf49a43SJulian Elischer }
1143069154d5SJulian Elischer /************************************************************************
1144069154d5SJulian Elischer 		Utility routines to send self messages
1145069154d5SJulian Elischer ************************************************************************/
1146069154d5SJulian Elischer /*
1147069154d5SJulian Elischer  * Static version of shutdown message. we don't want to need resources
1148069154d5SJulian Elischer  * to shut down (we may be doing it to release resources because we ran out.
1149069154d5SJulian Elischer  */
1150069154d5SJulian Elischer static struct	ng_mesg  ng_msg_shutdown = {
1151069154d5SJulian Elischer 	{NG_VERSION,		/* u_char */
1152069154d5SJulian Elischer 	0,			/* u_char spare */
1153069154d5SJulian Elischer 	0,			/* u_int16_t arglen */
1154069154d5SJulian Elischer 	NGF_STATIC,		/* u_int32_t flags */
1155069154d5SJulian Elischer 	0,			/* u_int32_t token */
1156069154d5SJulian Elischer 	NGM_GENERIC_COOKIE,	/* u_int32_t */
1157069154d5SJulian Elischer 	NGM_SHUTDOWN,		/* u_int32_t */
1158069154d5SJulian Elischer 	"shutdown"}		/* u_char[16] */
1159069154d5SJulian Elischer };
1160069154d5SJulian Elischer 
1161069154d5SJulian Elischer int
1162069154d5SJulian Elischer ng_rmnode_self(node_p here)
1163069154d5SJulian Elischer {
1164069154d5SJulian Elischer 	item_p	item;
1165069154d5SJulian Elischer 	struct	ng_mesg	*msg;
11664cf49a43SJulian Elischer 
11674cf49a43SJulian Elischer 	/*
1168069154d5SJulian Elischer 	 * Use the static version to avoid needing
1169069154d5SJulian Elischer 	 * memory allocation to succeed.
1170069154d5SJulian Elischer 	 * The message is never written to and always the same.
1171069154d5SJulian Elischer 	 */
1172069154d5SJulian Elischer 	msg = &ng_msg_shutdown;
1173069154d5SJulian Elischer 
1174069154d5SJulian Elischer 	/*
1175069154d5SJulian Elischer 	 * Try get a queue item to send it with.
1176069154d5SJulian Elischer 	 * Hopefully since it has a reserve, we can get one.
1177069154d5SJulian Elischer 	 * If we can't we are screwed anyhow.
1178069154d5SJulian Elischer 	 * Increase the chances by flushing our queue first.
1179069154d5SJulian Elischer 	 * We may free an item, (if we were the hog).
1180069154d5SJulian Elischer 	 * Work in progress is allowed to complete.
1181069154d5SJulian Elischer 	 * We also pretty much ensure that we come straight
1182069154d5SJulian Elischer 	 * back in to do the shutdown. It may be a good idea
1183069154d5SJulian Elischer 	 * to hold a reference actually to stop it from all
1184069154d5SJulian Elischer 	 * going up in smoke.
1185069154d5SJulian Elischer 	 */
118630400f03SJulian Elischer /*	ng_flush_input_queue(&here->nd_input_queue); will mask problem  */
1187069154d5SJulian Elischer 	item = ng_package_msg_self(here, NULL, msg);
1188069154d5SJulian Elischer 	if (item == NULL) { /* it would have freed the msg except static */
1189069154d5SJulian Elischer 		/* try again after flushing our queue */
119030400f03SJulian Elischer 		ng_flush_input_queue(&here->nd_input_queue);
1191069154d5SJulian Elischer 		item = ng_package_msg_self(here, NULL, msg);
1192069154d5SJulian Elischer 		if (item == NULL) {
1193069154d5SJulian Elischer 			printf("failed to free node 0x%x\n", ng_node2ID(here));
1194069154d5SJulian Elischer 			return (ENOMEM);
1195069154d5SJulian Elischer 		}
1196069154d5SJulian Elischer 	}
1197069154d5SJulian Elischer 	return (ng_snd_item(item, 0));
1198069154d5SJulian Elischer }
1199069154d5SJulian Elischer 
1200069154d5SJulian Elischer /***********************************************************************
12014cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
12024cf49a43SJulian Elischer  *
12034cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
12044cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
12054cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
12064cf49a43SJulian Elischer  *
12074cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
12084cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
12094cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
12104cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
12114cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
12124cf49a43SJulian Elischer  *
12134cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1214069154d5SJulian Elischer  ***********************************************************************/
12154cf49a43SJulian Elischer int
12164cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
12174cf49a43SJulian Elischer {
12184cf49a43SJulian Elischer 	char   *node, *path, *hook;
12194cf49a43SJulian Elischer 	int     k;
12204cf49a43SJulian Elischer 
12214cf49a43SJulian Elischer 	/*
12224cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
12234cf49a43SJulian Elischer 	 */
12244cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
12254cf49a43SJulian Elischer 	if (*path) {
12264cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
12274cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
12284cf49a43SJulian Elischer 
12294cf49a43SJulian Elischer 		/* Node name must not be empty */
12304cf49a43SJulian Elischer 		if (!*node)
12314cf49a43SJulian Elischer 			return -1;
12324cf49a43SJulian Elischer 
12334cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
12344cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
12354cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
12364cf49a43SJulian Elischer 				if (node[k] == '.')
12374cf49a43SJulian Elischer 					return -1;
12384cf49a43SJulian Elischer 		}
12394cf49a43SJulian Elischer 	} else {
12404cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
12414cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
12424cf49a43SJulian Elischer 	}
12434cf49a43SJulian Elischer 
12444cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
12454cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
12464cf49a43SJulian Elischer 		if (path[k] == ':')
12474cf49a43SJulian Elischer 			return -1;
12484cf49a43SJulian Elischer 
12494cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
12504cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
12514cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
12524cf49a43SJulian Elischer 			return -1;
12534cf49a43SJulian Elischer 
12544cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
12554cf49a43SJulian Elischer 	if (path[0] == '.')
12564cf49a43SJulian Elischer 		path++;
12574cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
12584cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
12594cf49a43SJulian Elischer 
12604cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
12614cf49a43SJulian Elischer 	if (*path) {
12624cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
12634cf49a43SJulian Elischer 			if (path[k] == '.') {
12644cf49a43SJulian Elischer 				hook = NULL;
12654cf49a43SJulian Elischer 				break;
12664cf49a43SJulian Elischer 			}
12674cf49a43SJulian Elischer 	} else
12684cf49a43SJulian Elischer 		path = hook = NULL;
12694cf49a43SJulian Elischer 
12704cf49a43SJulian Elischer 	/* Done */
12714cf49a43SJulian Elischer 	if (nodep)
12724cf49a43SJulian Elischer 		*nodep = node;
12734cf49a43SJulian Elischer 	if (pathp)
12744cf49a43SJulian Elischer 		*pathp = path;
12754cf49a43SJulian Elischer 	if (hookp)
12764cf49a43SJulian Elischer 		*hookp = hook;
12774cf49a43SJulian Elischer 	return (0);
12784cf49a43SJulian Elischer }
12794cf49a43SJulian Elischer 
12804cf49a43SJulian Elischer /*
12814cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1282069154d5SJulian Elischer  * return the destination node.
12834cf49a43SJulian Elischer  */
12844cf49a43SJulian Elischer int
1285069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1286069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
12874cf49a43SJulian Elischer {
12884cf49a43SJulian Elischer 	char    fullpath[NG_PATHLEN + 1];
12894cf49a43SJulian Elischer 	char   *nodename, *path, pbuf[2];
1290069154d5SJulian Elischer 	node_p  node, oldnode;
12914cf49a43SJulian Elischer 	char   *cp;
1292a4ec03cfSJulian Elischer 	hook_p hook = NULL;
12934cf49a43SJulian Elischer 
12944cf49a43SJulian Elischer 	/* Initialize */
129530400f03SJulian Elischer 	if (destp == NULL) {
129630400f03SJulian Elischer 		TRAP_ERROR
12974cf49a43SJulian Elischer 		return EINVAL;
129830400f03SJulian Elischer 	}
12994cf49a43SJulian Elischer 	*destp = NULL;
13004cf49a43SJulian Elischer 
13014cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
13024cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
13034cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
13044cf49a43SJulian Elischer 
13054cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
13064cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
13074cf49a43SJulian Elischer 		TRAP_ERROR;
13084cf49a43SJulian Elischer 		return EINVAL;
13094cf49a43SJulian Elischer 	}
13104cf49a43SJulian Elischer 	if (path == NULL) {
13114cf49a43SJulian Elischer 		pbuf[0] = '.';	/* Needs to be writable */
13124cf49a43SJulian Elischer 		pbuf[1] = '\0';
13134cf49a43SJulian Elischer 		path = pbuf;
13144cf49a43SJulian Elischer 	}
13154cf49a43SJulian Elischer 
1316069154d5SJulian Elischer 	/*
1317069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1318069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1319069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1320069154d5SJulian Elischer 	 */
13214cf49a43SJulian Elischer 	if (nodename) {
1322069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
13234cf49a43SJulian Elischer 		if (node == NULL) {
132430400f03SJulian Elischer 			TRAP_ERROR
13254cf49a43SJulian Elischer 			return (ENOENT);
13264cf49a43SJulian Elischer 		}
1327069154d5SJulian Elischer 	} else {
1328069154d5SJulian Elischer 		if (here == NULL) {
1329069154d5SJulian Elischer 			TRAP_ERROR
1330069154d5SJulian Elischer 			return (EINVAL);
1331069154d5SJulian Elischer 		}
13324cf49a43SJulian Elischer 		node = here;
133330400f03SJulian Elischer 		NG_NODE_REF(node);
1334069154d5SJulian Elischer 	}
13354cf49a43SJulian Elischer 
1336069154d5SJulian Elischer 	/*
1337069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1338069154d5SJulian Elischer 	 * XXX
1339069154d5SJulian Elischer 	 * We actually cannot guarantee that the sequence
1340069154d5SJulian Elischer 	 * is not being demolished as we crawl along it
1341069154d5SJulian Elischer 	 * without extra-ordinary locking etc.
1342069154d5SJulian Elischer 	 * So this is a bit dodgy to say the least.
1343069154d5SJulian Elischer 	 * We can probably hold up some things by holding
1344069154d5SJulian Elischer 	 * the nodelist mutex for the time of this
1345069154d5SJulian Elischer 	 * crawl if we wanted.. At least that way we wouldn't have to
1346069154d5SJulian Elischer 	 * worry about the nodes dissappearing, but the hooks would still
1347069154d5SJulian Elischer 	 * be a problem.
1348069154d5SJulian Elischer 	 */
13494cf49a43SJulian Elischer 	for (cp = path; node != NULL && *cp != '\0'; ) {
13504cf49a43SJulian Elischer 		char *segment;
13514cf49a43SJulian Elischer 
13524cf49a43SJulian Elischer 		/*
13534cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
13544cf49a43SJulian Elischer 		 * found with a NUL; "cp" points to the next segment (or the
13554cf49a43SJulian Elischer 		 * NUL at the end).
13564cf49a43SJulian Elischer 		 */
13574cf49a43SJulian Elischer 		for (segment = cp; *cp != '\0'; cp++) {
13584cf49a43SJulian Elischer 			if (*cp == '.') {
13594cf49a43SJulian Elischer 				*cp++ = '\0';
13604cf49a43SJulian Elischer 				break;
13614cf49a43SJulian Elischer 			}
13624cf49a43SJulian Elischer 		}
13634cf49a43SJulian Elischer 
13644cf49a43SJulian Elischer 		/* Empty segment */
13654cf49a43SJulian Elischer 		if (*segment == '\0')
13664cf49a43SJulian Elischer 			continue;
13674cf49a43SJulian Elischer 
13684cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1369899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
13704cf49a43SJulian Elischer 
13714cf49a43SJulian Elischer 		/* Can't get there from here... */
13724cf49a43SJulian Elischer 		if (hook == NULL
137330400f03SJulian Elischer 		    || NG_HOOK_PEER(hook) == NULL
137430400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(hook)
137530400f03SJulian Elischer 		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
13764cf49a43SJulian Elischer 			TRAP_ERROR;
137730400f03SJulian Elischer 			NG_NODE_UNREF(node);
137830400f03SJulian Elischer #if 0
137930400f03SJulian Elischer 			printf("hooknotvalid %s %s %d %d %d %d ",
138030400f03SJulian Elischer 					path,
138130400f03SJulian Elischer 					segment,
138230400f03SJulian Elischer 					hook == NULL,
138330400f03SJulian Elischer 		     			NG_HOOK_PEER(hook) == NULL,
138430400f03SJulian Elischer 		     			NG_HOOK_NOT_VALID(hook),
138530400f03SJulian Elischer 		     			NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
138630400f03SJulian Elischer #endif
13874cf49a43SJulian Elischer 			return (ENOENT);
13884cf49a43SJulian Elischer 		}
13894cf49a43SJulian Elischer 
1390069154d5SJulian Elischer 		/*
1391069154d5SJulian Elischer 		 * Hop on over to the next node
1392069154d5SJulian Elischer 		 * XXX
1393069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1394069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1395069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1396069154d5SJulian Elischer 		 */
1397069154d5SJulian Elischer 		oldnode = node;
139830400f03SJulian Elischer 		if ((node = NG_PEER_NODE(hook)))
139930400f03SJulian Elischer 			NG_NODE_REF(node);	/* XXX RACE */
140030400f03SJulian Elischer 		NG_NODE_UNREF(oldnode);	/* XXX another race */
140130400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
140230400f03SJulian Elischer 			NG_NODE_UNREF(node);	/* XXX more races */
1403069154d5SJulian Elischer 			node = NULL;
1404069154d5SJulian Elischer 		}
14054cf49a43SJulian Elischer 	}
14064cf49a43SJulian Elischer 
14074cf49a43SJulian Elischer 	/* If node somehow missing, fail here (probably this is not needed) */
14084cf49a43SJulian Elischer 	if (node == NULL) {
14094cf49a43SJulian Elischer 		TRAP_ERROR;
14104cf49a43SJulian Elischer 		return (ENXIO);
14114cf49a43SJulian Elischer 	}
14124cf49a43SJulian Elischer 
14134cf49a43SJulian Elischer 	/* Done */
14144cf49a43SJulian Elischer 	*destp = node;
14151bdebe4dSArchie Cobbs 	if (lasthook != NULL)
141630400f03SJulian Elischer 		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
1417069154d5SJulian Elischer 	return (0);
1418069154d5SJulian Elischer }
1419069154d5SJulian Elischer 
1420069154d5SJulian Elischer /***************************************************************\
1421069154d5SJulian Elischer * Input queue handling.
1422069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1423069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
1424069154d5SJulian Elischer * Items which cannot be handled immeditly are queued.
1425069154d5SJulian Elischer *
1426069154d5SJulian Elischer * read-write queue locking inline functions			*
1427069154d5SJulian Elischer \***************************************************************/
1428069154d5SJulian Elischer 
1429069154d5SJulian Elischer static __inline item_p ng_dequeue(struct ng_queue * ngq);
1430069154d5SJulian Elischer static __inline item_p ng_acquire_read(struct ng_queue * ngq,
1431069154d5SJulian Elischer 					item_p  item);
1432069154d5SJulian Elischer static __inline item_p ng_acquire_write(struct ng_queue * ngq,
1433069154d5SJulian Elischer 					item_p  item);
1434069154d5SJulian Elischer static __inline void	ng_leave_read(struct ng_queue * ngq);
1435069154d5SJulian Elischer static __inline void	ng_leave_write(struct ng_queue * ngq);
1436069154d5SJulian Elischer static __inline void	ng_queue_rw(struct ng_queue * ngq,
1437069154d5SJulian Elischer 					item_p  item, int rw);
1438069154d5SJulian Elischer 
1439069154d5SJulian Elischer /*
1440069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1441069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1442069154d5SJulian Elischer  * with them.
1443069154d5SJulian Elischer  *
1444069154d5SJulian Elischer  * The ordering here is important! don't shuffle these. If you add
1445069154d5SJulian Elischer  * READ_PENDING to the word when it has READ_PENDING already set, you
1446069154d5SJulian Elischer  * generate a carry into the reader count, this you atomically add a reader,
1447069154d5SJulian Elischer  * and remove the pending reader count! Similarly for the pending writer
1448069154d5SJulian Elischer  * flag, adding WRITE_PENDING generates a carry and sets the WRITER_ACTIVE
1449069154d5SJulian Elischer  * flag, while clearing WRITE_PENDING. When 'SINGLE_THREAD_ONLY' is set, then
1450069154d5SJulian Elischer  * it is only permitted to do WRITER operations. Reader operations will
1451069154d5SJulian Elischer  * result in errors.
1452069154d5SJulian Elischer  * But that "hack" is unnecessary: "cpp" can do the math for us!
1453069154d5SJulian Elischer  */
1454069154d5SJulian Elischer /*-
1455069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1456069154d5SJulian Elischer                        |
1457069154d5SJulian Elischer                        V
1458069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
1459069154d5SJulian Elischer | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1460069154d5SJulian Elischer |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W|S|
1461069154d5SJulian Elischer | | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P|T|
1462069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
1463069154d5SJulian Elischer \_________________________ ____________________________/ | | | |
1464069154d5SJulian Elischer                           V                              | | | |
1465069154d5SJulian Elischer                 [active reader count]                    | | | |
1466069154d5SJulian Elischer                                                          | | | |
1467069154d5SJulian Elischer         Read Pending ------------------------------------+ | | |
1468069154d5SJulian Elischer                                                            | | |
1469069154d5SJulian Elischer         Active Writer -------------------------------------+ | |
1470069154d5SJulian Elischer                                                              | |
1471069154d5SJulian Elischer         Write Pending ---------------------------------------+ |
1472069154d5SJulian Elischer                                                                |
1473069154d5SJulian Elischer         Single Threading Only ---------------------------------+
1474069154d5SJulian Elischer */
1475069154d5SJulian Elischer #define	SINGLE_THREAD_ONLY 0x00000001	/* if set, even reads single thread */
1476069154d5SJulian Elischer #define WRITE_PENDING	0x00000002
1477069154d5SJulian Elischer #define	WRITER_ACTIVE	0x00000004
1478069154d5SJulian Elischer #define READ_PENDING	0x00000008
1479069154d5SJulian Elischer #define	READER_INCREMENT 0x00000010
1480069154d5SJulian Elischer #define	READER_MASK	0xfffffff0	/* Not valid if WRITER_ACTIVE is set */
1481069154d5SJulian Elischer #define SAFETY_BARRIER	0x00100000	/* 64K items queued should be enough */
1482069154d5SJulian Elischer /*
1483069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1484069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1485069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1486069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1487069154d5SJulian Elischer  * on the queue.
1488069154d5SJulian Elischer  *
1489069154d5SJulian Elischer  * This MUST MUST MUST be called with the mutex held.
1490069154d5SJulian Elischer  */
1491069154d5SJulian Elischer static __inline item_p
1492069154d5SJulian Elischer ng_dequeue(struct ng_queue *ngq)
1493069154d5SJulian Elischer {
1494069154d5SJulian Elischer 	item_p item;
1495069154d5SJulian Elischer 	u_int		add_arg;
1496069154d5SJulian Elischer 	/*
1497069154d5SJulian Elischer 	 * If there is a writer, then the answer is "no". Everything else
1498069154d5SJulian Elischer 	 * stops when there is a WRITER.
1499069154d5SJulian Elischer 	 */
1500069154d5SJulian Elischer 	if (ngq->q_flags & WRITER_ACTIVE) {
1501069154d5SJulian Elischer 		return (NULL);
1502069154d5SJulian Elischer 	}
1503069154d5SJulian Elischer 	/* Now take a look at what's on the queue and what's running */
1504069154d5SJulian Elischer 	if ((ngq->q_flags & ~(READER_MASK | SINGLE_THREAD_ONLY)) == READ_PENDING) {
1505069154d5SJulian Elischer 		/*
1506069154d5SJulian Elischer 		 * It was a reader and we have no write active. We don't care
1507069154d5SJulian Elischer 		 * how many readers are already active. Adjust the count for
1508069154d5SJulian Elischer 		 * the item we are about to dequeue. Adding READ_PENDING to
1509069154d5SJulian Elischer 		 * the exisiting READ_PENDING clears it and generates a carry
1510069154d5SJulian Elischer 		 * into the reader count.
1511069154d5SJulian Elischer 		 */
1512069154d5SJulian Elischer 		add_arg = READ_PENDING;
1513069154d5SJulian Elischer 	} else if ((ngq->q_flags & ~SINGLE_THREAD_ONLY) == WRITE_PENDING) {
1514069154d5SJulian Elischer 		/*
1515069154d5SJulian Elischer 		 * There is a pending write, no readers and no active writer.
1516069154d5SJulian Elischer 		 * This means we can go ahead with the pending writer. Note
1517069154d5SJulian Elischer 		 * the fact that we now have a writer, ready for when we take
1518069154d5SJulian Elischer 		 * it off the queue.
1519069154d5SJulian Elischer 		 *
1520069154d5SJulian Elischer 		 * We don't need to worry about a possible collision with the
1521069154d5SJulian Elischer 		 * fasttrack reader.
1522069154d5SJulian Elischer 		 *
1523069154d5SJulian Elischer 		 * The fasttrack thread may take a long time to discover that we
1524069154d5SJulian Elischer 		 * are running so we would have an inconsistent state in the
1525069154d5SJulian Elischer 		 * flags for a while. Since we ignore the reader count
1526069154d5SJulian Elischer 		 * entirely when the WRITER_ACTIVE flag is set, this should
1527069154d5SJulian Elischer 		 * not matter (in fact it is defined that way). If it tests
1528069154d5SJulian Elischer 		 * the flag before this operation, the WRITE_PENDING flag
1529069154d5SJulian Elischer 		 * will make it fail, and if it tests it later, the
1530069154d5SJulian Elischer 		 * ACTIVE_WRITER flag will do the same. If it is SO slow that
1531069154d5SJulian Elischer 		 * we have actually completed the operation, and neither flag
1532069154d5SJulian Elischer 		 * is set (nor the READ_PENDING) by the time that it tests
1533069154d5SJulian Elischer 		 * the flags, then it is actually ok for it to continue. If
1534069154d5SJulian Elischer 		 * it completes and we've finished and the read pending is
1535069154d5SJulian Elischer 		 * set it still fails.
1536069154d5SJulian Elischer 		 *
1537069154d5SJulian Elischer 		 * So we can just ignore it,  as long as we can ensure that the
1538069154d5SJulian Elischer 		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
1539069154d5SJulian Elischer 		 * state is atomic.
1540069154d5SJulian Elischer 		 *
1541069154d5SJulian Elischer 		 * After failing, first it will be held back by the mutex, then
1542069154d5SJulian Elischer 		 * when it can proceed, it will queue its request, then it
1543069154d5SJulian Elischer 		 * would arrive at this function. Usually it will have to
1544069154d5SJulian Elischer 		 * leave empty handed because the ACTIVE WRITER bit wil be
1545069154d5SJulian Elischer 		 * set.
1546069154d5SJulian Elischer 		 */
1547069154d5SJulian Elischer 		/*
1548069154d5SJulian Elischer 		 * Adjust the flags for the item we are about to dequeue.
1549069154d5SJulian Elischer 		 * Adding WRITE_PENDING to the exisiting WRITE_PENDING clears
1550069154d5SJulian Elischer 		 * it and generates a carry into the WRITER_ACTIVE flag, all
1551069154d5SJulian Elischer 		 * atomically.
1552069154d5SJulian Elischer 		 */
1553069154d5SJulian Elischer 		add_arg = WRITE_PENDING;
1554069154d5SJulian Elischer 		/*
1555069154d5SJulian Elischer 		 * We want to write "active writer, no readers " Now go make
1556069154d5SJulian Elischer 		 * it true. In fact there may be a number in the readers
1557069154d5SJulian Elischer 		 * count but we know it is not true and will be fixed soon.
1558069154d5SJulian Elischer 		 * We will fix the flags for the next pending entry in a
1559069154d5SJulian Elischer 		 * moment.
1560069154d5SJulian Elischer 		 */
1561069154d5SJulian Elischer 	} else {
1562069154d5SJulian Elischer 		/*
1563069154d5SJulian Elischer 		 * We can't dequeue anything.. return and say so. Probably we
1564069154d5SJulian Elischer 		 * have a write pending and the readers count is non zero. If
1565069154d5SJulian Elischer 		 * we got here because a reader hit us just at the wrong
1566069154d5SJulian Elischer 		 * moment with the fasttrack code, and put us in a strange
1567069154d5SJulian Elischer 		 * state, then it will be through in just a moment, (as soon
1568069154d5SJulian Elischer 		 * as we release the mutex) and keep things moving.
1569069154d5SJulian Elischer 		 */
15704cf49a43SJulian Elischer 		return (0);
15714cf49a43SJulian Elischer 	}
15724cf49a43SJulian Elischer 
15734cf49a43SJulian Elischer 	/*
1574069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1575069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
15764cf49a43SJulian Elischer 	 */
1577069154d5SJulian Elischer 	item = ngq->queue;
1578069154d5SJulian Elischer 	ngq->queue = item->el_next;
1579069154d5SJulian Elischer 	if (ngq->last == &(item->el_next)) {
15804cf49a43SJulian Elischer 		/*
1581069154d5SJulian Elischer 		 * that was the last entry in the queue so set the 'last
1582069154d5SJulian Elischer 		 * pointer up correctly and make sure the pending flags are
1583069154d5SJulian Elischer 		 * clear.
15844cf49a43SJulian Elischer 		 */
1585069154d5SJulian Elischer 		ngq->last = &(ngq->queue);
1586859a4d16SJulian Elischer 		/*
1587069154d5SJulian Elischer 		 * Whatever flag was set is cleared and the carry sets the
1588069154d5SJulian Elischer 		 * correct new active state/count. So we don't need to change
1589069154d5SJulian Elischer 		 * add_arg.
1590859a4d16SJulian Elischer 		 */
1591859a4d16SJulian Elischer 	} else {
1592069154d5SJulian Elischer 		if ((ngq->queue->el_flags & NGQF_TYPE) == NGQF_READER) {
1593069154d5SJulian Elischer 			/*
1594069154d5SJulian Elischer 			 * If we had a READ_PENDING and have another one, we
1595069154d5SJulian Elischer 			 * just want to add READ_PENDING twice (the same as
1596069154d5SJulian Elischer 			 * adding READER_INCREMENT). If we had WRITE_PENDING,
1597069154d5SJulian Elischer 			 * we want to add READ_PENDING + WRITE_PENDING to
1598069154d5SJulian Elischer 			 * clear the old WRITE_PENDING, set ACTIVE_WRITER,
1599069154d5SJulian Elischer 			 * and set READ_PENDING. Either way we just add
1600069154d5SJulian Elischer 			 * READ_PENDING to whatever we already had.
1601069154d5SJulian Elischer 			 */
1602069154d5SJulian Elischer 			add_arg += READ_PENDING;
1603069154d5SJulian Elischer 		} else {
1604069154d5SJulian Elischer 			/*
1605069154d5SJulian Elischer 			 * If we had a WRITE_PENDING and have another one, we
1606069154d5SJulian Elischer 			 * just want to add WRITE_PENDING twice (the same as
1607069154d5SJulian Elischer 			 * adding ACTIVE_WRITER). If we had READ_PENDING, we
1608069154d5SJulian Elischer 			 * want to add READ_PENDING + WRITE_PENDING to clear
1609069154d5SJulian Elischer 			 * the old READ_PENDING, increment the readers, and
1610069154d5SJulian Elischer 			 * set WRITE_PENDING. Either way we just add
1611069154d5SJulian Elischer 			 * WRITE_PENDING to whatever we already had.
1612069154d5SJulian Elischer 			 */
1613069154d5SJulian Elischer 			add_arg += WRITE_PENDING;
1614859a4d16SJulian Elischer 		}
1615859a4d16SJulian Elischer 	}
1616069154d5SJulian Elischer 	atomic_add_long(&ngq->q_flags, add_arg);
1617069154d5SJulian Elischer 	/*
1618069154d5SJulian Elischer 	 * We have successfully cleared the old pending flag, set the new one
1619069154d5SJulian Elischer 	 * if it is needed, and incremented the appropriate active field.
1620069154d5SJulian Elischer 	 * (all in one atomic addition.. wow)
1621069154d5SJulian Elischer 	 */
1622069154d5SJulian Elischer 	return (item);
1623069154d5SJulian Elischer }
1624859a4d16SJulian Elischer 
1625859a4d16SJulian Elischer /*
1626069154d5SJulian Elischer  * Queue a packet to be picked up by someone else.
1627069154d5SJulian Elischer  * We really don't care who, but we can't or don't want to hang around
1628069154d5SJulian Elischer  * to process it ourselves. We are probably an interrupt routine..
1629069154d5SJulian Elischer  * 1 = writer, 0 = reader
1630069154d5SJulian Elischer  * We should set something to indicate NETISR requested
1631069154d5SJulian Elischer  * If it's the first item queued.
1632859a4d16SJulian Elischer  */
1633069154d5SJulian Elischer #define NGQRW_R 0
1634069154d5SJulian Elischer #define NGQRW_W 1
1635069154d5SJulian Elischer static __inline void
1636069154d5SJulian Elischer ng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
1637069154d5SJulian Elischer {
1638069154d5SJulian Elischer 	item->el_next = NULL;	/* maybe not needed */
1639069154d5SJulian Elischer 	*ngq->last = item;
1640069154d5SJulian Elischer 	/*
1641069154d5SJulian Elischer 	 * If it was the first item in the queue then we need to
1642069154d5SJulian Elischer 	 * set the last pointer and the type flags.
1643069154d5SJulian Elischer 	 */
1644069154d5SJulian Elischer 	if (ngq->last == &(ngq->queue)) {
1645069154d5SJulian Elischer 		/*
1646069154d5SJulian Elischer 		 * When called with constants for rw, the optimiser will
1647069154d5SJulian Elischer 		 * remove the unneeded branch below.
1648069154d5SJulian Elischer 		 */
1649069154d5SJulian Elischer 		if (rw == NGQRW_W) {
1650069154d5SJulian Elischer 			atomic_add_long(&ngq->q_flags, WRITE_PENDING);
1651069154d5SJulian Elischer 		} else {
1652069154d5SJulian Elischer 			atomic_add_long(&ngq->q_flags, READ_PENDING);
1653069154d5SJulian Elischer 		}
1654069154d5SJulian Elischer 	}
1655069154d5SJulian Elischer 	ngq->last = &(item->el_next);
1656069154d5SJulian Elischer }
1657069154d5SJulian Elischer 
1658069154d5SJulian Elischer 
1659069154d5SJulian Elischer /*
1660069154d5SJulian Elischer  * This function 'cheats' in that it first tries to 'grab' the use of the
1661069154d5SJulian Elischer  * node, without going through the mutex. We can do this becasue of the
1662069154d5SJulian Elischer  * semantics of the lock. The semantics include a clause that says that the
1663069154d5SJulian Elischer  * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It
1664069154d5SJulian Elischer  * also says that the WRITER_ACTIVE flag cannot be set if the readers count
1665069154d5SJulian Elischer  * is not zero. Note that this talks about what is valid to SET the
1666069154d5SJulian Elischer  * WRITER_ACTIVE flag, because from the moment it is set, the value if the
1667069154d5SJulian Elischer  * reader count is immaterial, and not valid. The two 'pending' flags have a
1668069154d5SJulian Elischer  * similar effect, in that If they are orthogonal to the two active fields in
1669069154d5SJulian Elischer  * how they are set, but if either is set, the attempted 'grab' need to be
1670069154d5SJulian Elischer  * backed out because there is earlier work, and we maintain ordering in the
1671069154d5SJulian Elischer  * queue. The result of this is that the reader request can try obtain use of
1672069154d5SJulian Elischer  * the node with only a single atomic addition, and without any of the mutex
1673069154d5SJulian Elischer  * overhead. If this fails the operation degenerates to the same as for other
1674069154d5SJulian Elischer  * cases.
1675069154d5SJulian Elischer  *
1676069154d5SJulian Elischer  */
1677069154d5SJulian Elischer static __inline item_p
1678069154d5SJulian Elischer ng_acquire_read(struct ng_queue *ngq, item_p item)
1679069154d5SJulian Elischer {
1680069154d5SJulian Elischer 
1681069154d5SJulian Elischer 	/* ######### Hack alert ######### */
1682069154d5SJulian Elischer 	atomic_add_long(&ngq->q_flags, READER_INCREMENT);
1683069154d5SJulian Elischer 	if ((ngq->q_flags & (~READER_MASK)) == 0) {
1684069154d5SJulian Elischer 		/* Successfully grabbed node */
1685069154d5SJulian Elischer 		return (item);
1686069154d5SJulian Elischer 	}
1687069154d5SJulian Elischer 	/* undo the damage if we didn't succeed */
1688069154d5SJulian Elischer 	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
1689069154d5SJulian Elischer 
1690069154d5SJulian Elischer 	/* ######### End Hack alert ######### */
1691069154d5SJulian Elischer 	mtx_enter((&ngq->q_mtx), MTX_SPIN);
1692069154d5SJulian Elischer 	/*
1693069154d5SJulian Elischer 	 * Try again. Another processor (or interrupt for that matter) may
1694069154d5SJulian Elischer 	 * have removed the last queued item that was stopping us from
1695069154d5SJulian Elischer 	 * running, between the previous test, and the moment that we took
1696069154d5SJulian Elischer 	 * the mutex. (Or maybe a writer completed.)
1697069154d5SJulian Elischer 	 */
1698069154d5SJulian Elischer 	if ((ngq->q_flags & (~READER_MASK)) == 0) {
1699069154d5SJulian Elischer 		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
1700069154d5SJulian Elischer 		mtx_exit((&ngq->q_mtx), MTX_SPIN);
1701069154d5SJulian Elischer 		return (item);
1702069154d5SJulian Elischer 	}
1703069154d5SJulian Elischer 
1704069154d5SJulian Elischer 	/*
1705069154d5SJulian Elischer 	 * Quick check that we are doing things right.
1706069154d5SJulian Elischer 	 */
1707069154d5SJulian Elischer 	if (ngq->q_flags & SINGLE_THREAD_ONLY) {
1708069154d5SJulian Elischer 		panic("Calling single treaded queue incorrectly");
1709069154d5SJulian Elischer 	}
1710069154d5SJulian Elischer 
1711069154d5SJulian Elischer 	/*
1712069154d5SJulian Elischer 	 * and queue the request for later.
1713069154d5SJulian Elischer 	 */
1714069154d5SJulian Elischer 	item->el_flags |= NGQF_TYPE;
1715069154d5SJulian Elischer 	ng_queue_rw(ngq, item, NGQRW_R);
1716069154d5SJulian Elischer 
1717069154d5SJulian Elischer 	/*
1718069154d5SJulian Elischer 	 * Ok, so that's the item successfully queued for later. So now we
1719069154d5SJulian Elischer 	 * see if we can dequeue something to run instead.
1720069154d5SJulian Elischer 	 */
1721069154d5SJulian Elischer 	item = ng_dequeue(ngq);
1722069154d5SJulian Elischer 	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
1723069154d5SJulian Elischer 	return (item);
1724069154d5SJulian Elischer }
1725069154d5SJulian Elischer 
1726069154d5SJulian Elischer static __inline item_p
1727069154d5SJulian Elischer ng_acquire_write(struct ng_queue *ngq, item_p item)
1728069154d5SJulian Elischer {
1729069154d5SJulian Elischer restart:
1730069154d5SJulian Elischer 	mtx_enter(&(ngq->q_mtx), MTX_SPIN);
1731069154d5SJulian Elischer 	/*
1732069154d5SJulian Elischer 	 * If there are no readers, no writer, and no pending packets, then
1733069154d5SJulian Elischer 	 * we can just go ahead. In all other situations we need to queue the
1734069154d5SJulian Elischer 	 * request
1735069154d5SJulian Elischer 	 */
1736069154d5SJulian Elischer 	if ((ngq->q_flags & (~SINGLE_THREAD_ONLY)) == 0) {
1737069154d5SJulian Elischer 		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
1738069154d5SJulian Elischer 		mtx_exit((&ngq->q_mtx), MTX_SPIN);
1739069154d5SJulian Elischer 		if (ngq->q_flags & READER_MASK) {
1740069154d5SJulian Elischer 			/* Collision with fast-track reader */
1741069154d5SJulian Elischer 			atomic_add_long(&ngq->q_flags, -WRITER_ACTIVE);
1742069154d5SJulian Elischer 			goto restart;
1743069154d5SJulian Elischer 		}
1744069154d5SJulian Elischer 
1745069154d5SJulian Elischer 		return (item);
1746069154d5SJulian Elischer 	}
1747069154d5SJulian Elischer 
1748069154d5SJulian Elischer 	/*
1749069154d5SJulian Elischer 	 * and queue the request for later.
1750069154d5SJulian Elischer 	 */
1751069154d5SJulian Elischer 	item->el_flags &= ~NGQF_TYPE;
1752069154d5SJulian Elischer 	ng_queue_rw(ngq, item, NGQRW_W);
1753069154d5SJulian Elischer 
1754069154d5SJulian Elischer 	/*
1755069154d5SJulian Elischer 	 * Ok, so that's the item successfully queued for later. So now we
1756069154d5SJulian Elischer 	 * see if we can dequeue something to run instead.
1757069154d5SJulian Elischer 	 */
1758069154d5SJulian Elischer 	item = ng_dequeue(ngq);
1759069154d5SJulian Elischer 	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
1760069154d5SJulian Elischer 	return (item);
1761069154d5SJulian Elischer }
1762069154d5SJulian Elischer 
1763069154d5SJulian Elischer static __inline void
1764069154d5SJulian Elischer ng_leave_read(struct ng_queue *ngq)
1765069154d5SJulian Elischer {
1766069154d5SJulian Elischer 	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
1767069154d5SJulian Elischer }
1768069154d5SJulian Elischer 
1769069154d5SJulian Elischer static __inline void
1770069154d5SJulian Elischer ng_leave_write(struct ng_queue *ngq)
1771069154d5SJulian Elischer {
1772069154d5SJulian Elischer 	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
1773069154d5SJulian Elischer }
1774069154d5SJulian Elischer 
1775069154d5SJulian Elischer static void
1776069154d5SJulian Elischer ng_flush_input_queue(struct ng_queue * ngq)
1777069154d5SJulian Elischer {
1778069154d5SJulian Elischer 	item_p item;
1779069154d5SJulian Elischer 	u_int		add_arg;
1780069154d5SJulian Elischer 	mtx_enter(&ngq->q_mtx, MTX_SPIN);
1781069154d5SJulian Elischer 	for (;;) {
1782069154d5SJulian Elischer 		/* Now take a look at what's on the queue */
1783069154d5SJulian Elischer 		if (ngq->q_flags & READ_PENDING) {
1784069154d5SJulian Elischer 			add_arg = -READ_PENDING;
1785069154d5SJulian Elischer 		} else if (ngq->q_flags & WRITE_PENDING) {
1786069154d5SJulian Elischer 			add_arg = -WRITE_PENDING;
1787069154d5SJulian Elischer 		} else {
1788069154d5SJulian Elischer 			break;
1789069154d5SJulian Elischer 		}
1790069154d5SJulian Elischer 
1791069154d5SJulian Elischer 		item = ngq->queue;
1792069154d5SJulian Elischer 		ngq->queue = item->el_next;
1793069154d5SJulian Elischer 		if (ngq->last == &(item->el_next)) {
1794069154d5SJulian Elischer 			ngq->last = &(ngq->queue);
1795069154d5SJulian Elischer 		} else {
1796069154d5SJulian Elischer 			if ((ngq->queue->el_flags & NGQF_TYPE) == NGQF_READER) {
1797069154d5SJulian Elischer 				add_arg += READ_PENDING;
1798069154d5SJulian Elischer 			} else {
1799069154d5SJulian Elischer 				add_arg += WRITE_PENDING;
1800069154d5SJulian Elischer 			}
1801069154d5SJulian Elischer 		}
1802069154d5SJulian Elischer 		atomic_add_long(&ngq->q_flags, add_arg);
1803069154d5SJulian Elischer 
1804069154d5SJulian Elischer 		mtx_exit(&ngq->q_mtx, MTX_SPIN);
1805069154d5SJulian Elischer 		NG_FREE_ITEM(item);
1806069154d5SJulian Elischer 		mtx_enter(&ngq->q_mtx, MTX_SPIN);
1807069154d5SJulian Elischer 	}
1808069154d5SJulian Elischer 	mtx_exit(&ngq->q_mtx, MTX_SPIN);
1809069154d5SJulian Elischer }
1810069154d5SJulian Elischer 
1811069154d5SJulian Elischer /***********************************************************************
1812069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
1813069154d5SJulian Elischer ***********************************************************************/
1814069154d5SJulian Elischer 
1815069154d5SJulian Elischer /*
1816069154d5SJulian Elischer  * MACRO WILL DO THE JOB OF CALLING ng_package_msg IN CALLER
1817069154d5SJulian Elischer  * before we are called. The user code should have filled out the item
1818069154d5SJulian Elischer  * correctly by this stage:
1819069154d5SJulian Elischer  * Common:
1820069154d5SJulian Elischer  *    reference to destination node.
1821069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
1822069154d5SJulian Elischer  * Data:
1823069154d5SJulian Elischer  *    pointer to mbuf
1824069154d5SJulian Elischer  *    pointer to metadata
1825069154d5SJulian Elischer  * Control_Message:
1826069154d5SJulian Elischer  *    pointer to msg.
1827069154d5SJulian Elischer  *    ID of original sender node. (return address)
1828069154d5SJulian Elischer  *
1829069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
1830069154d5SJulian Elischer  * ng_package_msg()
1831069154d5SJulian Elischer  * ng_package_data() do much of the work.
1832069154d5SJulian Elischer  * ng_retarget_msg
1833069154d5SJulian Elischer  * ng_retarget_data
1834069154d5SJulian Elischer  */
1835069154d5SJulian Elischer 
1836069154d5SJulian Elischer int
1837069154d5SJulian Elischer ng_snd_item(item_p item, int queue)
1838069154d5SJulian Elischer {
1839069154d5SJulian Elischer 	hook_p hook = item->el_hook;
1840069154d5SJulian Elischer 	node_p dest = item->el_dest;
1841069154d5SJulian Elischer 	int rw;
1842069154d5SJulian Elischer 	int error = 0, ierror;
1843069154d5SJulian Elischer 	item_p	oitem;
184430400f03SJulian Elischer 	struct ng_queue * ngq = &dest->nd_input_queue;
1845069154d5SJulian Elischer 
184630400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
1847069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1848069154d5SJulian Elischer #endif
1849069154d5SJulian Elischer 
1850069154d5SJulian Elischer 	if (item == NULL) {
185130400f03SJulian Elischer 		TRAP_ERROR;
1852069154d5SJulian Elischer 		return (EINVAL);	/* failed to get queue element */
1853069154d5SJulian Elischer 	}
1854069154d5SJulian Elischer 	if (dest == NULL) {
1855069154d5SJulian Elischer 		NG_FREE_ITEM(item);
185630400f03SJulian Elischer 		TRAP_ERROR;
1857069154d5SJulian Elischer 		return (EINVAL);	/* No address */
1858069154d5SJulian Elischer 	}
1859069154d5SJulian Elischer 	if ((item->el_flags & NGQF_D_M) == NGQF_DATA) {
1860069154d5SJulian Elischer 		/*
1861069154d5SJulian Elischer 		 * DATA MESSAGE
1862069154d5SJulian Elischer 		 * Delivered to a node via a non-optional hook.
1863069154d5SJulian Elischer 		 * Both should be present in the item even though
1864069154d5SJulian Elischer 		 * the node is derivable from the hook.
1865069154d5SJulian Elischer 		 * References are held on both by the item.
1866069154d5SJulian Elischer 		 */
186730400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
1868069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1869069154d5SJulian Elischer #endif
1870069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
1871069154d5SJulian Elischer 		if (hook == NULL) {
1872069154d5SJulian Elischer 			NG_FREE_ITEM(item);
187330400f03SJulian Elischer 			TRAP_ERROR;
1874069154d5SJulian Elischer 			return(EINVAL);
1875069154d5SJulian Elischer 		}
187630400f03SJulian Elischer 		if ((NG_HOOK_NOT_VALID(hook))
187730400f03SJulian Elischer 		|| (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) {
1878069154d5SJulian Elischer 			NG_FREE_ITEM(item);
1879069154d5SJulian Elischer 			return (ENOTCONN);
1880859a4d16SJulian Elischer 		}
188130400f03SJulian Elischer 		if ((hook->hk_flags & HK_QUEUE)) {
1882069154d5SJulian Elischer 			queue = 1;
18834cf49a43SJulian Elischer 		}
1884069154d5SJulian Elischer 		/* By default data is a reader in the locking scheme */
1885069154d5SJulian Elischer 		item->el_flags |= NGQF_READER;
1886069154d5SJulian Elischer 		rw = NGQRW_R;
1887069154d5SJulian Elischer 	} else {
18884cf49a43SJulian Elischer 		/*
1889069154d5SJulian Elischer 		 * CONTROL MESSAGE
1890069154d5SJulian Elischer 		 * Delivered to a node.
1891069154d5SJulian Elischer 		 * Hook is optional.
1892069154d5SJulian Elischer 		 * References are held by the item on the node and
1893069154d5SJulian Elischer 		 * the hook if it is present.
18944cf49a43SJulian Elischer 		 */
189530400f03SJulian Elischer 		if (hook && (hook->hk_flags & HK_QUEUE)) {
1896069154d5SJulian Elischer 			queue = 1;
1897069154d5SJulian Elischer 		}
1898069154d5SJulian Elischer 		/* Data messages count as writers unles explicitly exempted */
1899069154d5SJulian Elischer 		if (NGI_MSG(item)->header.cmd & NGM_READONLY) {
1900069154d5SJulian Elischer 			item->el_flags |= NGQF_READER;
1901069154d5SJulian Elischer 			rw = NGQRW_R;
1902069154d5SJulian Elischer 		} else {
1903069154d5SJulian Elischer 			item->el_flags &= ~NGQF_TYPE;
1904069154d5SJulian Elischer 			rw = NGQRW_W;
1905069154d5SJulian Elischer 		}
1906069154d5SJulian Elischer 	}
1907069154d5SJulian Elischer 	/*
1908069154d5SJulian Elischer 	 * If the node specifies single threading, force writer semantics
1909069154d5SJulian Elischer 	 * Similarly the node may say one hook always produces writers.
1910069154d5SJulian Elischer 	 * These are over-rides.
1911069154d5SJulian Elischer 	 */
1912069154d5SJulian Elischer 	if ((ngq->q_flags & SINGLE_THREAD_ONLY)
191330400f03SJulian Elischer 	|| (dest->nd_flags & NG_FORCE_WRITER)
191430400f03SJulian Elischer 	|| (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
1915069154d5SJulian Elischer 			rw = NGQRW_W;
1916069154d5SJulian Elischer 			item->el_flags &= ~NGQF_TYPE;
1917069154d5SJulian Elischer 	}
1918069154d5SJulian Elischer 	if (queue) {
1919069154d5SJulian Elischer 		/* Put it on the queue for that node*/
192030400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
1921069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1922069154d5SJulian Elischer #endif
1923069154d5SJulian Elischer 		mtx_enter(&(ngq->q_mtx), MTX_SPIN);
1924069154d5SJulian Elischer 		ng_queue_rw(ngq, item, rw);
1925069154d5SJulian Elischer 		mtx_exit(&(ngq->q_mtx), MTX_SPIN);
1926069154d5SJulian Elischer 		/*
1927069154d5SJulian Elischer 		 * If there are active elements then we can rely on
1928069154d5SJulian Elischer 		 * them. if not we should not rely on another packet
1929069154d5SJulian Elischer 		 * coming here by another path,
1930069154d5SJulian Elischer 		 * so it is best to put us in the netisr list.
1931069154d5SJulian Elischer 		 */
1932069154d5SJulian Elischer 		if ((ngq->q_flags & (READER_MASK|WRITER_ACTIVE)) == 0) {
1933069154d5SJulian Elischer 			ng_setisr(ngq->q_node);
1934069154d5SJulian Elischer 		}
1935069154d5SJulian Elischer 		return (0);
1936069154d5SJulian Elischer 	}
1937069154d5SJulian Elischer 	/*
1938069154d5SJulian Elischer 	 * Take a queue item and a node and see if we can apply the item to
1939069154d5SJulian Elischer 	 * the node. We may end up getting a different item to apply instead.
1940069154d5SJulian Elischer 	 * Will allow for a piggyback reply only in the case where
1941069154d5SJulian Elischer 	 * there is no queueing.
1942069154d5SJulian Elischer 	 */
1943069154d5SJulian Elischer 
1944069154d5SJulian Elischer 	oitem = item;
1945069154d5SJulian Elischer 	/*
1946069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
1947069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
1948069154d5SJulian Elischer 	 */
1949069154d5SJulian Elischer  	if (rw == NGQRW_R) {
1950069154d5SJulian Elischer 		item = ng_acquire_read(ngq, item);
1951069154d5SJulian Elischer 	} else {
1952069154d5SJulian Elischer 		item = ng_acquire_write(ngq, item);
19534cf49a43SJulian Elischer 	}
19544cf49a43SJulian Elischer 
19554cf49a43SJulian Elischer 	/*
1956069154d5SJulian Elischer 	 * May have come back with a different item.
1957069154d5SJulian Elischer 	 * or maybe none at all. The one we started with will
1958069154d5SJulian Elischer 	 * have been queued in thises cases.
1959069154d5SJulian Elischer 	 */
1960069154d5SJulian Elischer 	if (item == NULL) {
1961069154d5SJulian Elischer 		return (0);
1962069154d5SJulian Elischer 	}
1963069154d5SJulian Elischer 
196430400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
1965069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1966069154d5SJulian Elischer #endif
1967069154d5SJulian Elischer 	ierror = ng_apply_item(dest, item); /* drops r/w lock when done */
1968069154d5SJulian Elischer 
1969069154d5SJulian Elischer 	/* only return an error if it was our initial item.. (compat hack) */
1970069154d5SJulian Elischer 	if (oitem == item) {
1971069154d5SJulian Elischer 		error = ierror;
1972069154d5SJulian Elischer 	}
1973069154d5SJulian Elischer 
1974069154d5SJulian Elischer 	/*
1975069154d5SJulian Elischer 	 * Now we've handled the packet we brought, (or a friend of it) let's
1976069154d5SJulian Elischer 	 * look for any other packets that may have been queued up. We hold
1977069154d5SJulian Elischer 	 * no locks, so if someone puts something in the queue after
1978069154d5SJulian Elischer 	 * we check that it is empty, it is their problem
1979069154d5SJulian Elischer 	 * to ensure it is processed. If we have the netisr thread cme in here
1980069154d5SJulian Elischer 	 * while we still say we have stuff to do, we may get a boost
1981069154d5SJulian Elischer 	 * in SMP systems. :-)
1982069154d5SJulian Elischer 	 */
1983069154d5SJulian Elischer 	for (;;) {
1984069154d5SJulian Elischer 		/* quick hack to save all that mutex stuff */
1985069154d5SJulian Elischer 		if ((ngq->q_flags & (WRITE_PENDING | READ_PENDING)) == 0) {
198630400f03SJulian Elischer 			if (dest->nd_flags & NG_WORKQ)
1987069154d5SJulian Elischer 				ng_worklist_remove(dest);
1988069154d5SJulian Elischer 			return (0);
1989069154d5SJulian Elischer 		}
1990069154d5SJulian Elischer 		/*
1991069154d5SJulian Elischer 		 * dequeue acquires and adjusts the input_queue as it dequeues
1992069154d5SJulian Elischer 		 * packets. It acquires the rw lock as needed.
1993069154d5SJulian Elischer 		 */
1994069154d5SJulian Elischer 		mtx_enter(&ngq->q_mtx, MTX_SPIN);
1995069154d5SJulian Elischer 		item = ng_dequeue(ngq);
1996069154d5SJulian Elischer 		mtx_exit(&ngq->q_mtx, MTX_SPIN);
1997069154d5SJulian Elischer 		if (!item) {
1998069154d5SJulian Elischer 			/*
1999069154d5SJulian Elischer 			 * If we have no work to do
2000069154d5SJulian Elischer 			 * then we certainly don't need to be
2001069154d5SJulian Elischer 			 * on the worklist.
2002069154d5SJulian Elischer 			 */
200330400f03SJulian Elischer 			if (dest->nd_flags & NG_WORKQ)
2004069154d5SJulian Elischer 				ng_worklist_remove(dest);
2005069154d5SJulian Elischer 			return (0);
2006069154d5SJulian Elischer 		}
200730400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2008069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
2009069154d5SJulian Elischer #endif
2010069154d5SJulian Elischer 
2011069154d5SJulian Elischer 		/*
2012069154d5SJulian Elischer 		 * We have the appropriate lock, so run the item.
2013069154d5SJulian Elischer 		 * When finished it will drop the lock accordingly
2014069154d5SJulian Elischer 		 */
2015069154d5SJulian Elischer 
2016069154d5SJulian Elischer 		ierror = ng_apply_item(dest, item);
2017069154d5SJulian Elischer 		/*
2018069154d5SJulian Elischer 		 * only return an error if it was our initial
2019069154d5SJulian Elischer 		 * item.. (compat hack)
2020069154d5SJulian Elischer 		 */
2021069154d5SJulian Elischer 		if (oitem == item) {
2022069154d5SJulian Elischer 			error = ierror;
2023069154d5SJulian Elischer 		}
2024069154d5SJulian Elischer 	}
2025069154d5SJulian Elischer 	return (0);
2026069154d5SJulian Elischer }
2027069154d5SJulian Elischer 
2028069154d5SJulian Elischer /*
2029069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
2030069154d5SJulian Elischer  * It should contain all the information needed
2031069154d5SJulian Elischer  * to run it on the appropriate node/hook.
20324cf49a43SJulian Elischer  */
20334cf49a43SJulian Elischer static int
2034069154d5SJulian Elischer ng_apply_item(node_p node, item_p item)
2035069154d5SJulian Elischer {
2036069154d5SJulian Elischer 	hook_p  hook;
2037069154d5SJulian Elischer 	int was_reader = ((item->el_flags & NGQF_TYPE));
2038069154d5SJulian Elischer 	int error = 0;
2039069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
2040069154d5SJulian Elischer 
2041069154d5SJulian Elischer 	hook = item->el_hook;
204230400f03SJulian Elischer 	item->el_hook = NULL;	/* so NG_FREE_ITEM doesn't NG_HOOK_UNREF() */
2043069154d5SJulian Elischer 	/* We already have the node.. assume responsibility */
2044069154d5SJulian Elischer 	/* And the reference */
2045069154d5SJulian Elischer 	/* node = item->el_dest; */
2046069154d5SJulian Elischer 	item->el_dest = NULL;	/* same as for the hook above */
204730400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2048069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
2049069154d5SJulian Elischer #endif
2050069154d5SJulian Elischer 
2051069154d5SJulian Elischer 	switch (item->el_flags & NGQF_D_M) {
2052069154d5SJulian Elischer 	case NGQF_DATA:
2053069154d5SJulian Elischer 		/*
2054069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
2055069154d5SJulian Elischer 		 */
2056069154d5SJulian Elischer 
2057069154d5SJulian Elischer 		if ((hook == NULL)
205830400f03SJulian Elischer 		|| NG_HOOK_NOT_VALID(hook)
205930400f03SJulian Elischer 		|| NG_NODE_NOT_VALID(NG_HOOK_NODE(hook))
206030400f03SJulian Elischer 		|| ((rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata) == NULL)) {
2061069154d5SJulian Elischer 			error = EIO;
2062069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2063069154d5SJulian Elischer 		} else {
2064069154d5SJulian Elischer 			error = (*rcvdata)(hook, item);
2065069154d5SJulian Elischer 		}
2066069154d5SJulian Elischer 		break;
2067069154d5SJulian Elischer 	case NGQF_MESG:
2068069154d5SJulian Elischer 
2069069154d5SJulian Elischer 		if (hook) {
2070069154d5SJulian Elischer 			item->el_hook = NULL;
207130400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook)) {
2072069154d5SJulian Elischer 			/*
2073069154d5SJulian Elischer 			 * If the hook has been zapped then we can't use it.
2074069154d5SJulian Elischer 			 * Immediatly drop its reference.
2075069154d5SJulian Elischer 			 * The message may not need it.
2076069154d5SJulian Elischer 			 */
207730400f03SJulian Elischer 				NG_HOOK_UNREF(hook);
2078069154d5SJulian Elischer 				hook = NULL;
2079069154d5SJulian Elischer 			}
2080069154d5SJulian Elischer 		}
2081069154d5SJulian Elischer 		/*
2082069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
2083069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
2084069154d5SJulian Elischer 		 */
208530400f03SJulian Elischer 		if (NG_NODE_NOT_VALID(node)) {
208630400f03SJulian Elischer 			TRAP_ERROR;
2087069154d5SJulian Elischer 			error = EINVAL;
2088069154d5SJulian Elischer 			NG_FREE_ITEM(item);
2089069154d5SJulian Elischer 		} else {
2090069154d5SJulian Elischer 			/*
2091069154d5SJulian Elischer 			 * Call the appropriate message handler for the object.
2092069154d5SJulian Elischer 			 * It is up to the message handler to free the message.
2093069154d5SJulian Elischer 			 * If it's a generic message, handle it generically,
2094069154d5SJulian Elischer 			 * otherwise call the type's message handler
2095069154d5SJulian Elischer 			 * (if it exists)
2096069154d5SJulian Elischer 			 * XXX (race). Remember that a queued message may
2097069154d5SJulian Elischer 			 * reference a node or hook that has just been
2098069154d5SJulian Elischer 			 * invalidated. It will exist as the queue code
2099069154d5SJulian Elischer 			 * is holding a reference, but..
2100069154d5SJulian Elischer 			 */
2101069154d5SJulian Elischer 
2102069154d5SJulian Elischer 			struct ng_mesg *msg = NGI_MSG(item);
2103069154d5SJulian Elischer 
2104069154d5SJulian Elischer 			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
2105069154d5SJulian Elischer 			&& ((msg->header.flags & NGF_RESP) == 0)) {
2106069154d5SJulian Elischer 				error = ng_generic_msg(node, item, hook);
2107069154d5SJulian Elischer 			} else {
210830400f03SJulian Elischer 				if ((node)->nd_type->rcvmsg != NULL) {
210930400f03SJulian Elischer 					error = (*(node)->nd_type->rcvmsg)((node),
2110069154d5SJulian Elischer 						(item), (hook));
2111069154d5SJulian Elischer 				} else {
2112069154d5SJulian Elischer 					TRAP_ERROR;
2113069154d5SJulian Elischer 					error = EINVAL; /* XXX */
2114069154d5SJulian Elischer 					NG_FREE_ITEM(item);
2115069154d5SJulian Elischer 				}
2116069154d5SJulian Elischer 			}
2117069154d5SJulian Elischer 			/* item is now invalid */
2118069154d5SJulian Elischer 		}
2119069154d5SJulian Elischer 		break;
2120069154d5SJulian Elischer 	}
2121069154d5SJulian Elischer 	/*
2122069154d5SJulian Elischer 	 * We held references on some of the resources
2123069154d5SJulian Elischer 	 * that we took from the item. Now that we have
2124069154d5SJulian Elischer 	 * finished doing everything, drop those references.
2125069154d5SJulian Elischer 	 */
2126069154d5SJulian Elischer 	if (hook) {
212730400f03SJulian Elischer 		NG_HOOK_UNREF(hook);
2128069154d5SJulian Elischer 	}
2129069154d5SJulian Elischer 
2130069154d5SJulian Elischer 	if (was_reader) {
213130400f03SJulian Elischer 		ng_leave_read(&node->nd_input_queue);
2132069154d5SJulian Elischer 	} else {
213330400f03SJulian Elischer 		ng_leave_write(&node->nd_input_queue);
2134069154d5SJulian Elischer 	}
213530400f03SJulian Elischer 	NG_NODE_UNREF(node);
2136069154d5SJulian Elischer 	return (error);
2137069154d5SJulian Elischer }
2138069154d5SJulian Elischer 
2139069154d5SJulian Elischer /***********************************************************************
2140069154d5SJulian Elischer  * Implement the 'generic' control messages
2141069154d5SJulian Elischer  ***********************************************************************/
2142069154d5SJulian Elischer static int
2143069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
21444cf49a43SJulian Elischer {
21454cf49a43SJulian Elischer 	int error = 0;
2146069154d5SJulian Elischer 	struct ng_mesg *msg;
2147069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
21484cf49a43SJulian Elischer 
2149069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
21504cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
215130400f03SJulian Elischer 		TRAP_ERROR
2152069154d5SJulian Elischer 		error = EINVAL;
2153069154d5SJulian Elischer 		goto out;
21544cf49a43SJulian Elischer 	}
21554cf49a43SJulian Elischer 	switch (msg->header.cmd) {
21564cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
21574cf49a43SJulian Elischer 		ng_rmnode(here);
21584cf49a43SJulian Elischer 		break;
21594cf49a43SJulian Elischer 	case NGM_MKPEER:
21604cf49a43SJulian Elischer 	    {
21614cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
21624cf49a43SJulian Elischer 
21634cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
216430400f03SJulian Elischer 			TRAP_ERROR
2165069154d5SJulian Elischer 			error = EINVAL;
2166069154d5SJulian Elischer 			break;
21674cf49a43SJulian Elischer 		}
21684cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
21694cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
21704cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
21714cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
21724cf49a43SJulian Elischer 		break;
21734cf49a43SJulian Elischer 	    }
21744cf49a43SJulian Elischer 	case NGM_CONNECT:
21754cf49a43SJulian Elischer 	    {
21764cf49a43SJulian Elischer 		struct ngm_connect *const con =
21774cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
21784cf49a43SJulian Elischer 		node_p node2;
21794cf49a43SJulian Elischer 
21804cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
218130400f03SJulian Elischer 			TRAP_ERROR
2182069154d5SJulian Elischer 			error = EINVAL;
2183069154d5SJulian Elischer 			break;
21844cf49a43SJulian Elischer 		}
21854cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
21864cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
21874cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2188069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2189069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
21904cf49a43SJulian Elischer 		if (error)
21914cf49a43SJulian Elischer 			break;
21924cf49a43SJulian Elischer 		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
219330400f03SJulian Elischer 		NG_NODE_UNREF(node2);
21944cf49a43SJulian Elischer 		break;
21954cf49a43SJulian Elischer 	    }
21964cf49a43SJulian Elischer 	case NGM_NAME:
21974cf49a43SJulian Elischer 	    {
21984cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
21994cf49a43SJulian Elischer 
22004cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
220130400f03SJulian Elischer 			TRAP_ERROR
2202069154d5SJulian Elischer 			error = EINVAL;
2203069154d5SJulian Elischer 			break;
22044cf49a43SJulian Elischer 		}
22054cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
22064cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
22074cf49a43SJulian Elischer 		break;
22084cf49a43SJulian Elischer 	    }
22094cf49a43SJulian Elischer 	case NGM_RMHOOK:
22104cf49a43SJulian Elischer 	    {
22114cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
22124cf49a43SJulian Elischer 		hook_p hook;
22134cf49a43SJulian Elischer 
22144cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
221530400f03SJulian Elischer 			TRAP_ERROR
2216069154d5SJulian Elischer 			error = EINVAL;
2217069154d5SJulian Elischer 			break;
22184cf49a43SJulian Elischer 		}
22194cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2220899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
22214cf49a43SJulian Elischer 			ng_destroy_hook(hook);
22224cf49a43SJulian Elischer 		break;
22234cf49a43SJulian Elischer 	    }
22244cf49a43SJulian Elischer 	case NGM_NODEINFO:
22254cf49a43SJulian Elischer 	    {
22264cf49a43SJulian Elischer 		struct nodeinfo *ni;
22274cf49a43SJulian Elischer 
2228069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
22294cf49a43SJulian Elischer 		if (resp == NULL) {
22304cf49a43SJulian Elischer 			error = ENOMEM;
22314cf49a43SJulian Elischer 			break;
22324cf49a43SJulian Elischer 		}
22334cf49a43SJulian Elischer 
22344cf49a43SJulian Elischer 		/* Fill in node info */
2235069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
223630400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
223730400f03SJulian Elischer 			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
223830400f03SJulian Elischer 		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
2239dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
224030400f03SJulian Elischer 		ni->hooks = here->nd_numhooks;
22414cf49a43SJulian Elischer 		break;
22424cf49a43SJulian Elischer 	    }
22434cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
22444cf49a43SJulian Elischer 	    {
224530400f03SJulian Elischer 		const int nhooks = here->nd_numhooks;
22464cf49a43SJulian Elischer 		struct hooklist *hl;
22474cf49a43SJulian Elischer 		struct nodeinfo *ni;
22484cf49a43SJulian Elischer 		hook_p hook;
22494cf49a43SJulian Elischer 
22504cf49a43SJulian Elischer 		/* Get response struct */
2251069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
22524cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2253069154d5SJulian Elischer 		if (resp == NULL) {
22544cf49a43SJulian Elischer 			error = ENOMEM;
22554cf49a43SJulian Elischer 			break;
22564cf49a43SJulian Elischer 		}
2257069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
22584cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
22594cf49a43SJulian Elischer 
22604cf49a43SJulian Elischer 		/* Fill in node info */
226130400f03SJulian Elischer 		if (NG_NODE_HAS_NAME(here))
226230400f03SJulian Elischer 			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
226330400f03SJulian Elischer 		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
2264dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
22654cf49a43SJulian Elischer 
22664cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
22674cf49a43SJulian Elischer 		ni->hooks = 0;
226830400f03SJulian Elischer 		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
22694cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
22704cf49a43SJulian Elischer 
22714cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
22724cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
22734cf49a43SJulian Elischer 				    __FUNCTION__, "hooks");
22744cf49a43SJulian Elischer 				break;
22754cf49a43SJulian Elischer 			}
227630400f03SJulian Elischer 			if (NG_HOOK_NOT_VALID(hook))
22774cf49a43SJulian Elischer 				continue;
227830400f03SJulian Elischer 			strncpy(link->ourhook, NG_HOOK_NAME(hook), NG_HOOKLEN);
227930400f03SJulian Elischer 			strncpy(link->peerhook,
228030400f03SJulian Elischer 				NG_PEER_HOOK_NAME(hook), NG_HOOKLEN);
228130400f03SJulian Elischer 			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
22824cf49a43SJulian Elischer 				strncpy(link->nodeinfo.name,
228330400f03SJulian Elischer 				    NG_PEER_NODE_NAME(hook), NG_NODELEN);
22844cf49a43SJulian Elischer 			strncpy(link->nodeinfo.type,
228530400f03SJulian Elischer 			   NG_PEER_NODE(hook)->nd_type->name, NG_TYPELEN);
228630400f03SJulian Elischer 			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
228730400f03SJulian Elischer 			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
22884cf49a43SJulian Elischer 			ni->hooks++;
22894cf49a43SJulian Elischer 		}
22904cf49a43SJulian Elischer 		break;
22914cf49a43SJulian Elischer 	    }
22924cf49a43SJulian Elischer 
22934cf49a43SJulian Elischer 	case NGM_LISTNAMES:
22944cf49a43SJulian Elischer 	case NGM_LISTNODES:
22954cf49a43SJulian Elischer 	    {
22964cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
22974cf49a43SJulian Elischer 		struct namelist *nl;
22984cf49a43SJulian Elischer 		node_p node;
22994cf49a43SJulian Elischer 		int num = 0;
23004cf49a43SJulian Elischer 
2301069154d5SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
23024cf49a43SJulian Elischer 		/* Count number of nodes */
230330400f03SJulian Elischer 		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
230430400f03SJulian Elischer 			if (unnamed || NG_NODE_HAS_NAME(node))
23054cf49a43SJulian Elischer 				num++;
23064cf49a43SJulian Elischer 		}
2307069154d5SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
23084cf49a43SJulian Elischer 
23094cf49a43SJulian Elischer 		/* Get response struct */
2310069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
23114cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2312069154d5SJulian Elischer 		if (resp == NULL) {
23134cf49a43SJulian Elischer 			error = ENOMEM;
23144cf49a43SJulian Elischer 			break;
23154cf49a43SJulian Elischer 		}
2316069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
23174cf49a43SJulian Elischer 
23184cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
23194cf49a43SJulian Elischer 		nl->numnames = 0;
2320069154d5SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
232130400f03SJulian Elischer 		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
23224cf49a43SJulian Elischer 			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
23234cf49a43SJulian Elischer 
23244cf49a43SJulian Elischer 			if (nl->numnames >= num) {
23254cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
23264cf49a43SJulian Elischer 				    __FUNCTION__, "nodes");
23274cf49a43SJulian Elischer 				break;
23284cf49a43SJulian Elischer 			}
232930400f03SJulian Elischer 			if (NG_NODE_NOT_VALID(node))
23304cf49a43SJulian Elischer 				continue;
233130400f03SJulian Elischer 			if (!unnamed && (! NG_NODE_HAS_NAME(node)))
23324cf49a43SJulian Elischer 				continue;
233330400f03SJulian Elischer 			if (NG_NODE_HAS_NAME(node))
233430400f03SJulian Elischer 				strncpy(np->name, NG_NODE_NAME(node), NG_NODELEN);
233530400f03SJulian Elischer 			strncpy(np->type, node->nd_type->name, NG_TYPELEN);
2336dc90cad9SJulian Elischer 			np->id = ng_node2ID(node);
233730400f03SJulian Elischer 			np->hooks = node->nd_numhooks;
23384cf49a43SJulian Elischer 			nl->numnames++;
23394cf49a43SJulian Elischer 		}
2340069154d5SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
23414cf49a43SJulian Elischer 		break;
23424cf49a43SJulian Elischer 	    }
23434cf49a43SJulian Elischer 
23444cf49a43SJulian Elischer 	case NGM_LISTTYPES:
23454cf49a43SJulian Elischer 	    {
23464cf49a43SJulian Elischer 		struct typelist *tl;
23474cf49a43SJulian Elischer 		struct ng_type *type;
23484cf49a43SJulian Elischer 		int num = 0;
23494cf49a43SJulian Elischer 
2350069154d5SJulian Elischer 		mtx_enter(&ng_typelist_mtx, MTX_DEF);
23514cf49a43SJulian Elischer 		/* Count number of types */
2352069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types)
23534cf49a43SJulian Elischer 			num++;
2354069154d5SJulian Elischer 		mtx_exit(&ng_typelist_mtx, MTX_DEF);
23554cf49a43SJulian Elischer 
23564cf49a43SJulian Elischer 		/* Get response struct */
2357069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
23584cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2359069154d5SJulian Elischer 		if (resp == NULL) {
23604cf49a43SJulian Elischer 			error = ENOMEM;
23614cf49a43SJulian Elischer 			break;
23624cf49a43SJulian Elischer 		}
2363069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
23644cf49a43SJulian Elischer 
23654cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
23664cf49a43SJulian Elischer 		tl->numtypes = 0;
2367069154d5SJulian Elischer 		mtx_enter(&ng_typelist_mtx, MTX_DEF);
2368069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
23694cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
23704cf49a43SJulian Elischer 
23714cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
23724cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
23734cf49a43SJulian Elischer 				    __FUNCTION__, "types");
23744cf49a43SJulian Elischer 				break;
23754cf49a43SJulian Elischer 			}
2376a096e45aSArchie Cobbs 			strncpy(tp->type_name, type->name, NG_TYPELEN);
23774cf49a43SJulian Elischer 			tp->numnodes = type->refs;
23784cf49a43SJulian Elischer 			tl->numtypes++;
23794cf49a43SJulian Elischer 		}
2380069154d5SJulian Elischer 		mtx_exit(&ng_typelist_mtx, MTX_DEF);
23814cf49a43SJulian Elischer 		break;
23824cf49a43SJulian Elischer 	    }
23834cf49a43SJulian Elischer 
2384f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2385f8307e12SArchie Cobbs 	    {
23867133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2387f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2388f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2389069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2390f8307e12SArchie Cobbs 
2391f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2392f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
2393f8307e12SArchie Cobbs 		if (msg->header.arglen < sizeof(struct ng_mesg)
2394f8307e12SArchie Cobbs 		    || msg->header.arglen - sizeof(struct ng_mesg)
2395f8307e12SArchie Cobbs 		      < binary->header.arglen) {
239630400f03SJulian Elischer 			TRAP_ERROR
2397f8307e12SArchie Cobbs 			error = EINVAL;
2398f8307e12SArchie Cobbs 			break;
2399f8307e12SArchie Cobbs 		}
2400f8307e12SArchie Cobbs 
2401f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2402069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2403069154d5SJulian Elischer 		if (resp == NULL) {
2404f8307e12SArchie Cobbs 			error = ENOMEM;
2405f8307e12SArchie Cobbs 			break;
2406f8307e12SArchie Cobbs 		}
2407069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2408f8307e12SArchie Cobbs 
2409f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2410f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2411f8307e12SArchie Cobbs 
2412f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
241330400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2414f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2415f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2416f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2417f8307e12SArchie Cobbs 				break;
2418f8307e12SArchie Cobbs 		}
2419f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2420f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2421f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2422f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2423f8307e12SArchie Cobbs 					break;
2424f8307e12SArchie Cobbs 			}
2425f8307e12SArchie Cobbs 			if (c->name == NULL) {
2426069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2427f8307e12SArchie Cobbs 				error = ENOSYS;
2428f8307e12SArchie Cobbs 				break;
2429f8307e12SArchie Cobbs 			}
2430f8307e12SArchie Cobbs 		}
2431f8307e12SArchie Cobbs 
2432f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2433f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2434f8307e12SArchie Cobbs 		    "%s", c->name);
2435f8307e12SArchie Cobbs 
2436f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2437f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2438f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
2439f8307e12SArchie Cobbs 		if (argstype == NULL)
2440f8307e12SArchie Cobbs 			*ascii->data = '\0';
2441f8307e12SArchie Cobbs 		else {
2442f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2443f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2444f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2445069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2446f8307e12SArchie Cobbs 				break;
2447f8307e12SArchie Cobbs 			}
2448f8307e12SArchie Cobbs 		}
2449f8307e12SArchie Cobbs 
2450f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2451f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2452f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2453069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2454f8307e12SArchie Cobbs 		break;
2455f8307e12SArchie Cobbs 	    }
2456f8307e12SArchie Cobbs 
2457f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2458f8307e12SArchie Cobbs 	    {
2459f8307e12SArchie Cobbs 		int bufSize = 2000;	/* XXX hard coded constant */
2460f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2461f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2462069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
246352ec4a03SArchie Cobbs 		int off = 0;
2464f8307e12SArchie Cobbs 
2465f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2466f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
2467f8307e12SArchie Cobbs 		if (msg->header.arglen < sizeof(*ascii) + 1
2468f8307e12SArchie Cobbs 		    || ascii->header.arglen < 1
2469f8307e12SArchie Cobbs 		    || msg->header.arglen
2470f8307e12SArchie Cobbs 		      < sizeof(*ascii) + ascii->header.arglen) {
247130400f03SJulian Elischer 			TRAP_ERROR
2472f8307e12SArchie Cobbs 			error = EINVAL;
2473f8307e12SArchie Cobbs 			break;
2474f8307e12SArchie Cobbs 		}
2475f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2476f8307e12SArchie Cobbs 
2477f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2478069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2479069154d5SJulian Elischer 		if (resp == NULL) {
2480f8307e12SArchie Cobbs 			error = ENOMEM;
2481f8307e12SArchie Cobbs 			break;
2482f8307e12SArchie Cobbs 		}
2483069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2484f8307e12SArchie Cobbs 
2485f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2486f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2487f8307e12SArchie Cobbs 
2488f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
248930400f03SJulian Elischer 		for (c = here->nd_type->cmdlist;
2490f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2491f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2492f8307e12SArchie Cobbs 				break;
2493f8307e12SArchie Cobbs 		}
2494f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2495f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2496f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2497f8307e12SArchie Cobbs 					break;
2498f8307e12SArchie Cobbs 			}
2499f8307e12SArchie Cobbs 			if (c->name == NULL) {
2500069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2501f8307e12SArchie Cobbs 				error = ENOSYS;
2502f8307e12SArchie Cobbs 				break;
2503f8307e12SArchie Cobbs 			}
2504f8307e12SArchie Cobbs 		}
2505f8307e12SArchie Cobbs 
2506f8307e12SArchie Cobbs 		/* Convert command name to binary */
2507f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2508f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2509f8307e12SArchie Cobbs 
2510f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2511f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2512f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
2513f8307e12SArchie Cobbs 		if (argstype == NULL)
2514f8307e12SArchie Cobbs 			bufSize = 0;
2515f8307e12SArchie Cobbs 		else {
2516f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2517f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2518069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2519f8307e12SArchie Cobbs 				break;
2520f8307e12SArchie Cobbs 			}
2521f8307e12SArchie Cobbs 		}
2522f8307e12SArchie Cobbs 
2523f8307e12SArchie Cobbs 		/* Return the result */
2524f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2525069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2526f8307e12SArchie Cobbs 		break;
2527f8307e12SArchie Cobbs 	    }
2528f8307e12SArchie Cobbs 
25297095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
25304cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
25314cf49a43SJulian Elischer 		/*
25324cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
25334cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2534069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
25354cf49a43SJulian Elischer 		 * when control passes back to us.
25364cf49a43SJulian Elischer 		 */
253730400f03SJulian Elischer 		if (here->nd_type->rcvmsg != NULL) {
2538069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
253930400f03SJulian Elischer 			return((*here->nd_type->rcvmsg)(here, item, lasthook));
25404cf49a43SJulian Elischer 		}
25414cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
25424cf49a43SJulian Elischer 	default:
25434cf49a43SJulian Elischer 		TRAP_ERROR;
25444cf49a43SJulian Elischer 		error = EINVAL;
25454cf49a43SJulian Elischer 	}
2546069154d5SJulian Elischer 	/*
2547069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2548069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2549069154d5SJulian Elischer 	 * Don't free it if it is so.
2550069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2551069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2552069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2553069154d5SJulian Elischer 	 */
2554069154d5SJulian Elischer out:
2555069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2556069154d5SJulian Elischer 	if ( msg && ((msg->header.flags & NGF_STATIC) == 0))
2557069154d5SJulian Elischer 		NG_FREE_MSG(msg);
25584cf49a43SJulian Elischer 	return (error);
25594cf49a43SJulian Elischer }
25604cf49a43SJulian Elischer 
25614cf49a43SJulian Elischer /*
2562a096e45aSArchie Cobbs  * Copy a 'meta'.
2563a096e45aSArchie Cobbs  *
2564a096e45aSArchie Cobbs  * Returns new meta, or NULL if original meta is NULL or ENOMEM.
2565a096e45aSArchie Cobbs  */
2566a096e45aSArchie Cobbs meta_p
2567a096e45aSArchie Cobbs ng_copy_meta(meta_p meta)
2568a096e45aSArchie Cobbs {
2569a096e45aSArchie Cobbs 	meta_p meta2;
2570a096e45aSArchie Cobbs 
2571a096e45aSArchie Cobbs 	if (meta == NULL)
2572a096e45aSArchie Cobbs 		return (NULL);
2573069154d5SJulian Elischer 	MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH_META, M_NOWAIT);
2574a096e45aSArchie Cobbs 	if (meta2 == NULL)
2575a096e45aSArchie Cobbs 		return (NULL);
2576a096e45aSArchie Cobbs 	meta2->allocated_len = meta->used_len;
2577a096e45aSArchie Cobbs 	bcopy(meta, meta2, meta->used_len);
2578a096e45aSArchie Cobbs 	return (meta2);
2579a096e45aSArchie Cobbs }
2580a096e45aSArchie Cobbs 
25814cf49a43SJulian Elischer /************************************************************************
25824cf49a43SJulian Elischer 			Module routines
25834cf49a43SJulian Elischer ************************************************************************/
25844cf49a43SJulian Elischer 
25854cf49a43SJulian Elischer /*
25864cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
25874cf49a43SJulian Elischer  */
25884cf49a43SJulian Elischer int
25894cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
25904cf49a43SJulian Elischer {
25914cf49a43SJulian Elischer 	struct ng_type *const type = data;
25924cf49a43SJulian Elischer 	int s, error = 0;
25934cf49a43SJulian Elischer 
25944cf49a43SJulian Elischer 	switch (event) {
25954cf49a43SJulian Elischer 	case MOD_LOAD:
25964cf49a43SJulian Elischer 
25974cf49a43SJulian Elischer 		/* Register new netgraph node type */
25984cf49a43SJulian Elischer 		s = splnet();
25994cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
26004cf49a43SJulian Elischer 			splx(s);
26014cf49a43SJulian Elischer 			break;
26024cf49a43SJulian Elischer 		}
26034cf49a43SJulian Elischer 
26044cf49a43SJulian Elischer 		/* Call type specific code */
26054cf49a43SJulian Elischer 		if (type->mod_event != NULL)
2606069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
2607069154d5SJulian Elischer 				mtx_enter(&ng_typelist_mtx, MTX_DEF);
26084cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
2609069154d5SJulian Elischer 				mtx_exit(&ng_typelist_mtx, MTX_DEF);
2610069154d5SJulian Elischer 			}
26114cf49a43SJulian Elischer 		splx(s);
26124cf49a43SJulian Elischer 		break;
26134cf49a43SJulian Elischer 
26144cf49a43SJulian Elischer 	case MOD_UNLOAD:
26154cf49a43SJulian Elischer 		s = splnet();
26164cf49a43SJulian Elischer 		if (type->refs != 0)		/* make sure no nodes exist! */
26174cf49a43SJulian Elischer 			error = EBUSY;
26184cf49a43SJulian Elischer 		else {
26194cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
26204cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
26214cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
26224cf49a43SJulian Elischer 					splx(s);
26234cf49a43SJulian Elischer 					break;
26244cf49a43SJulian Elischer 				}
26254cf49a43SJulian Elischer 			}
2626069154d5SJulian Elischer 			mtx_enter(&ng_typelist_mtx, MTX_DEF);
26274cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
2628069154d5SJulian Elischer 			mtx_exit(&ng_typelist_mtx, MTX_DEF);
26294cf49a43SJulian Elischer 		}
26304cf49a43SJulian Elischer 		splx(s);
26314cf49a43SJulian Elischer 		break;
26324cf49a43SJulian Elischer 
26334cf49a43SJulian Elischer 	default:
26344cf49a43SJulian Elischer 		if (type->mod_event != NULL)
26354cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
26364cf49a43SJulian Elischer 		else
26374cf49a43SJulian Elischer 			error = 0;		/* XXX ? */
26384cf49a43SJulian Elischer 		break;
26394cf49a43SJulian Elischer 	}
26404cf49a43SJulian Elischer 	return (error);
26414cf49a43SJulian Elischer }
26424cf49a43SJulian Elischer 
26434cf49a43SJulian Elischer /*
26444cf49a43SJulian Elischer  * Handle loading and unloading for this code.
26454cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
26464cf49a43SJulian Elischer  */
26474cf49a43SJulian Elischer static int
26484cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
26494cf49a43SJulian Elischer {
26504cf49a43SJulian Elischer 	int s, error = 0;
26514cf49a43SJulian Elischer 
26524cf49a43SJulian Elischer 	switch (event) {
26534cf49a43SJulian Elischer 	case MOD_LOAD:
26544cf49a43SJulian Elischer 		/* Register line discipline */
2655069154d5SJulian Elischer 		mtx_init(&ng_worklist_mtx, "netgraph worklist mutex", 0);
2656069154d5SJulian Elischer 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", 0);
2657069154d5SJulian Elischer 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", 0);
2658069154d5SJulian Elischer 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", 0);
2659069154d5SJulian Elischer 		mtx_init(&ngq_mtx, "netgraph netisr mutex", 0);
26604cf49a43SJulian Elischer 		s = splimp();
26614cf49a43SJulian Elischer 		error = register_netisr(NETISR_NETGRAPH, ngintr);
26624cf49a43SJulian Elischer 		splx(s);
26634cf49a43SJulian Elischer 		break;
26644cf49a43SJulian Elischer 	case MOD_UNLOAD:
26654cf49a43SJulian Elischer 		/* You cant unload it because an interface may be using it.  */
26664cf49a43SJulian Elischer 		error = EBUSY;
26674cf49a43SJulian Elischer 		break;
26684cf49a43SJulian Elischer 	default:
26694cf49a43SJulian Elischer 		error = EOPNOTSUPP;
26704cf49a43SJulian Elischer 		break;
26714cf49a43SJulian Elischer 	}
26724cf49a43SJulian Elischer 	return (error);
26734cf49a43SJulian Elischer }
26744cf49a43SJulian Elischer 
26754cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
26764cf49a43SJulian Elischer 	"netgraph",
26774cf49a43SJulian Elischer 	ngb_mod_event,
26784cf49a43SJulian Elischer 	(NULL)
26794cf49a43SJulian Elischer };
26804cf49a43SJulian Elischer DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
26814cf49a43SJulian Elischer 
26824cf49a43SJulian Elischer /************************************************************************
2683069154d5SJulian Elischer 			Queue element get/free routines
26844cf49a43SJulian Elischer ************************************************************************/
26854cf49a43SJulian Elischer 
26864cf49a43SJulian Elischer 
2687069154d5SJulian Elischer static int			allocated;	/* number of items malloc'd */
2688069154d5SJulian Elischer static int			maxalloc = 128;	/* limit the damage of a leak */
2689069154d5SJulian Elischer static const int		ngqfreemax = 64;/* cache at most this many */
2690069154d5SJulian Elischer static const int		ngqfreelow = 4; /* try malloc if free < this */
2691069154d5SJulian Elischer static volatile int		ngqfreesize;	/* number of cached entries */
269230400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2693069154d5SJulian Elischer static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2694069154d5SJulian Elischer #endif
26954cf49a43SJulian Elischer /*
26964cf49a43SJulian Elischer  * Get a queue entry
2697069154d5SJulian Elischer  * This is usually called when a packet first enters netgraph.
2698069154d5SJulian Elischer  * By definition, this is usually from an interrupt, or from a user.
2699069154d5SJulian Elischer  * Users are not so important, but try be quick for the times that it's
2700069154d5SJulian Elischer  * an interrupt. Use atomic operations to cope with collisions
2701069154d5SJulian Elischer  * with interrupts and other processors. Assumes MALLOC is SMP safe.
2702069154d5SJulian Elischer  * XXX If reserve is low, we should try to get 2 from malloc as this
2703069154d5SJulian Elischer  * would indicate it often fails.
27044cf49a43SJulian Elischer  */
2705069154d5SJulian Elischer static item_p
27064cf49a43SJulian Elischer ng_getqblk(void)
27074cf49a43SJulian Elischer {
2708069154d5SJulian Elischer 	item_p item = NULL;
27094cf49a43SJulian Elischer 
2710069154d5SJulian Elischer 	/*
2711069154d5SJulian Elischer 	 * Try get a cached queue block, or else allocate a new one
2712069154d5SJulian Elischer 	 * If we are less than our reserve, try malloc. If malloc
2713069154d5SJulian Elischer 	 * fails, then that's what the reserve is for...
2714069154d5SJulian Elischer 	 * Don't completely trust ngqfreesize, as it is subject
2715069154d5SJulian Elischer 	 * to races.. (it'll eventually catch up but may be out by one or two
2716069154d5SJulian Elischer 	 * for brief moments(under SMP or interrupts).
2717069154d5SJulian Elischer 	 * ngqfree is the final arbiter. We have our little reserve
2718069154d5SJulian Elischer 	 * because we use M_NOWAIT for malloc. This just helps us
2719069154d5SJulian Elischer 	 * avoid dropping packets while not increasing the time
2720069154d5SJulian Elischer 	 * we take to service the interrupt (on average) (we hope).
2721069154d5SJulian Elischer 	 */
2722069154d5SJulian Elischer 	for (;;) {
2723069154d5SJulian Elischer 		if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) {
2724069154d5SJulian Elischer 			if (allocated < maxalloc) {  /* don't leak forever */
2725069154d5SJulian Elischer 				MALLOC(item, item_p ,
2726069154d5SJulian Elischer 				    sizeof(*item), M_NETGRAPH_ITEM,
2727069154d5SJulian Elischer 				    (M_NOWAIT | M_ZERO));
2728069154d5SJulian Elischer 				if (item) {
272930400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2730069154d5SJulian Elischer 					TAILQ_INSERT_TAIL(&ng_itemlist,
2731069154d5SJulian Elischer 								item, all);
273230400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
2733069154d5SJulian Elischer 					atomic_add_int(&allocated, 1);
2734069154d5SJulian Elischer 					break;
27354cf49a43SJulian Elischer 				}
2736069154d5SJulian Elischer 			}
2737069154d5SJulian Elischer 		}
2738069154d5SJulian Elischer 
2739069154d5SJulian Elischer 		/*
2740069154d5SJulian Elischer 		 * We didn't or couldn't malloc.
2741069154d5SJulian Elischer 		 * try get one from our cache.
2742069154d5SJulian Elischer 		 * item must be NULL to get here.
2743069154d5SJulian Elischer 		 */
2744069154d5SJulian Elischer 		if ((item = ngqfree) != NULL) {
2745069154d5SJulian Elischer 			/*
2746069154d5SJulian Elischer 			 * Atomically try grab the first item
2747069154d5SJulian Elischer 			 * and put it's successor in its place.
2748069154d5SJulian Elischer 			 * If we fail, just try again.. someone else
2749069154d5SJulian Elischer 			 * beat us to this one or freed one.
2750069154d5SJulian Elischer 			 * Don't worry about races with ngqfreesize.
2751069154d5SJulian Elischer 			 * Close enough is good enough..
2752069154d5SJulian Elischer 			 */
2753069154d5SJulian Elischer 			if (atomic_cmpset_ptr(&ngqfree, item, item->el_next)) {
2754069154d5SJulian Elischer 				atomic_subtract_int(&ngqfreesize, 1);
2755069154d5SJulian Elischer 				break;
2756069154d5SJulian Elischer 			}
2757069154d5SJulian Elischer 			item = NULL;
27584cf49a43SJulian Elischer 		} else {
2759069154d5SJulian Elischer 			/* We really ran out */
2760069154d5SJulian Elischer 			break;
27614cf49a43SJulian Elischer 		}
2762069154d5SJulian Elischer 	}
2763069154d5SJulian Elischer 	item->el_flags &= ~NGQF_FREE;
2764069154d5SJulian Elischer 	return (item);
27654cf49a43SJulian Elischer }
27664cf49a43SJulian Elischer 
27674cf49a43SJulian Elischer /*
27684cf49a43SJulian Elischer  * Release a queue entry
27694cf49a43SJulian Elischer  */
2770069154d5SJulian Elischer void
2771069154d5SJulian Elischer ng_free_item(item_p item)
2772069154d5SJulian Elischer {
2773069154d5SJulian Elischer 
2774069154d5SJulian Elischer 	/*
2775069154d5SJulian Elischer 	 * The item may hold resources on it's own. We need to free
2776069154d5SJulian Elischer 	 * these before we can free the item. What they are depends upon
2777069154d5SJulian Elischer 	 * what kind of item it is. it is important that nodes zero
2778069154d5SJulian Elischer 	 * out pointers to resources that they remove from the item
2779069154d5SJulian Elischer 	 * or we release them again here.
2780069154d5SJulian Elischer 	 */
2781069154d5SJulian Elischer 	if (item->el_flags & NGQF_FREE) {
2782069154d5SJulian Elischer 		panic(" Freeing free queue item");
2783069154d5SJulian Elischer 	}
2784069154d5SJulian Elischer 	switch (item->el_flags & NGQF_D_M) {
2785069154d5SJulian Elischer 	case NGQF_DATA:
2786069154d5SJulian Elischer 		/* If we have an mbuf and metadata still attached.. */
2787069154d5SJulian Elischer 		NG_FREE_M(_NGI_M(item));
2788069154d5SJulian Elischer 		NG_FREE_META(_NGI_META(item));
2789069154d5SJulian Elischer 		break;
2790069154d5SJulian Elischer 	case NGQF_MESG:
2791069154d5SJulian Elischer 		_NGI_RETADDR(item) = NULL;
2792069154d5SJulian Elischer 		NG_FREE_MSG(_NGI_MSG(item));
2793069154d5SJulian Elischer 		break;
2794069154d5SJulian Elischer 	}
2795069154d5SJulian Elischer 		/* If we still have a node or hook referenced... */
2796069154d5SJulian Elischer 	if (item->el_dest) {
279730400f03SJulian Elischer 		NG_NODE_UNREF(item->el_dest);
2798069154d5SJulian Elischer 		item->el_dest = NULL;
2799069154d5SJulian Elischer 	}
2800069154d5SJulian Elischer 	if (item->el_hook) {
280130400f03SJulian Elischer 		NG_HOOK_UNREF(item->el_hook);
2802069154d5SJulian Elischer 		item->el_hook = NULL;
2803069154d5SJulian Elischer 	}
2804069154d5SJulian Elischer 	item->el_flags |= NGQF_FREE;
2805069154d5SJulian Elischer 
2806069154d5SJulian Elischer 	/*
2807069154d5SJulian Elischer 	 * We have freed any resources held by the item.
2808069154d5SJulian Elischer 	 * now we can free the item itself.
2809069154d5SJulian Elischer 	 */
2810069154d5SJulian Elischer 	if (ngqfreesize < ngqfreemax) { /* don't worry about races */
2811069154d5SJulian Elischer 		for (;;) {
2812069154d5SJulian Elischer 			item->el_next = ngqfree;
2813069154d5SJulian Elischer 			if (atomic_cmpset_ptr(&ngqfree, item->el_next, item)) {
2814069154d5SJulian Elischer 				break;
2815069154d5SJulian Elischer 			}
2816069154d5SJulian Elischer 		}
2817069154d5SJulian Elischer 		atomic_add_int(&ngqfreesize, 1);
2818069154d5SJulian Elischer 	} else {
2819069154d5SJulian Elischer 		/* This is the only place that should use this Macro */
282030400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2821069154d5SJulian Elischer 		TAILQ_REMOVE(&ng_itemlist, item, all);
282230400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
2823069154d5SJulian Elischer 		NG_FREE_ITEM_REAL(item);
2824069154d5SJulian Elischer 		atomic_subtract_int(&allocated, 1);
2825069154d5SJulian Elischer 	}
2826069154d5SJulian Elischer }
2827069154d5SJulian Elischer 
282830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
282930400f03SJulian Elischer void
283030400f03SJulian Elischer dumphook (hook_p hook, char *file, int line)
283130400f03SJulian Elischer {
283230400f03SJulian Elischer 	printf("hook: name %s, %d refs, Last touched:\n",
283330400f03SJulian Elischer 		_NG_HOOK_NAME(hook), hook->hk_refs);
283430400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
283530400f03SJulian Elischer 		hook->lastfile, hook->lastline);
283630400f03SJulian Elischer 	if (line) {
283730400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
283830400f03SJulian Elischer 	}
283930400f03SJulian Elischer }
284030400f03SJulian Elischer 
284130400f03SJulian Elischer void
284230400f03SJulian Elischer dumpnode(node_p node, char *file, int line)
284330400f03SJulian Elischer {
284430400f03SJulian Elischer 	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
284530400f03SJulian Elischer 		ng_node2ID(node), node->nd_type->name,
284630400f03SJulian Elischer 		node->nd_numhooks, node->nd_flags,
284730400f03SJulian Elischer 		node->nd_refs, node->nd_name);
284830400f03SJulian Elischer 	printf("	Last active @ %s, line %d\n",
284930400f03SJulian Elischer 		node->lastfile, node->lastline);
285030400f03SJulian Elischer 	if (line) {
285130400f03SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
285230400f03SJulian Elischer 	}
285330400f03SJulian Elischer }
285430400f03SJulian Elischer 
2855069154d5SJulian Elischer void
2856069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
2857069154d5SJulian Elischer {
2858069154d5SJulian Elischer 	if (item->el_flags & NGQF_FREE) {
2859069154d5SJulian Elischer 		printf(" Free item, freed at %s, line %d\n",
2860069154d5SJulian Elischer 			item->lastfile, item->lastline);
2861069154d5SJulian Elischer 	} else {
2862069154d5SJulian Elischer 		printf(" ACTIVE item, last used at %s, line %d",
2863069154d5SJulian Elischer 			item->lastfile, item->lastline);
2864069154d5SJulian Elischer 		if ((item->el_flags & NGQF_D_M) == NGQF_MESG) {
2865069154d5SJulian Elischer 			printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
2866069154d5SJulian Elischer 		} else {
2867069154d5SJulian Elischer 			printf(" - [data]\n");
2868069154d5SJulian Elischer 		}
2869069154d5SJulian Elischer 	}
287030400f03SJulian Elischer 	if (line) {
2871069154d5SJulian Elischer 		printf(" problem discovered at file %s, line %d\n", file, line);
287230400f03SJulian Elischer 		if (item->el_dest) {
287330400f03SJulian Elischer 			printf("node %p ([%x])\n",
2874069154d5SJulian Elischer 				item->el_dest, ng_node2ID(item->el_dest));
2875069154d5SJulian Elischer 		}
287630400f03SJulian Elischer 	}
287730400f03SJulian Elischer }
287830400f03SJulian Elischer 
287930400f03SJulian Elischer static void
288030400f03SJulian Elischer ng_dumpitems(void)
288130400f03SJulian Elischer {
288230400f03SJulian Elischer 	item_p item;
288330400f03SJulian Elischer 	int i = 1;
288430400f03SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
288530400f03SJulian Elischer 		printf("[%d] ", i++);
288630400f03SJulian Elischer 		dumpitem(item, NULL, 0);
288730400f03SJulian Elischer 	}
288830400f03SJulian Elischer }
288930400f03SJulian Elischer 
289030400f03SJulian Elischer static void
289130400f03SJulian Elischer ng_dumpnodes(void)
289230400f03SJulian Elischer {
289330400f03SJulian Elischer 	node_p node;
289430400f03SJulian Elischer 	int i = 1;
289530400f03SJulian Elischer 	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
289630400f03SJulian Elischer 		printf("[%d] ", i++);
289730400f03SJulian Elischer 		dumpnode(node, NULL, 0);
289830400f03SJulian Elischer 	}
289930400f03SJulian Elischer }
290030400f03SJulian Elischer 
290130400f03SJulian Elischer static void
290230400f03SJulian Elischer ng_dumphooks(void)
290330400f03SJulian Elischer {
290430400f03SJulian Elischer 	hook_p hook;
290530400f03SJulian Elischer 	int i = 1;
290630400f03SJulian Elischer 	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
290730400f03SJulian Elischer 		printf("[%d] ", i++);
290830400f03SJulian Elischer 		dumphook(hook, NULL, 0);
290930400f03SJulian Elischer 	}
291030400f03SJulian Elischer }
2911069154d5SJulian Elischer 
2912069154d5SJulian Elischer static int
2913069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
2914069154d5SJulian Elischer {
291530400f03SJulian Elischer 	static int count;
2916069154d5SJulian Elischer 	int error;
2917069154d5SJulian Elischer 	int val;
2918069154d5SJulian Elischer 	int i;
2919069154d5SJulian Elischer 
2920069154d5SJulian Elischer 	val = allocated;
2921069154d5SJulian Elischer 	i = 1;
2922069154d5SJulian Elischer 	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
292330400f03SJulian Elischer 	if(count++  & 1) { /* for some reason sysctl calls it twice */
292430400f03SJulian Elischer 		ng_dumpitems();
292530400f03SJulian Elischer 		ng_dumpnodes();
292630400f03SJulian Elischer 		ng_dumphooks();
2927069154d5SJulian Elischer 	}
2928069154d5SJulian Elischer 	return error;
2929069154d5SJulian Elischer }
2930069154d5SJulian Elischer 
2931069154d5SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RD,
2932069154d5SJulian Elischer     0, 0, sysctl_debug_ng_dump_items, "I", "Number of allocated items");
293330400f03SJulian Elischer #endif	/* NETGRAPH_DEBUG */
2934069154d5SJulian Elischer 
2935069154d5SJulian Elischer 
2936069154d5SJulian Elischer /***********************************************************************
2937069154d5SJulian Elischer * Worklist routines
2938069154d5SJulian Elischer **********************************************************************/
2939069154d5SJulian Elischer /* NETISR thread enters here */
2940069154d5SJulian Elischer /*
2941069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
2942069154d5SJulian Elischer  * try get an item to process off it.
2943069154d5SJulian Elischer  * If there are no more, remove the node from the list.
2944069154d5SJulian Elischer  */
2945069154d5SJulian Elischer static void
2946069154d5SJulian Elischer ngintr(void)
2947069154d5SJulian Elischer {
2948069154d5SJulian Elischer 	item_p item;
2949069154d5SJulian Elischer 	node_p  node = NULL;
2950069154d5SJulian Elischer 
2951069154d5SJulian Elischer 	for (;;) {
2952069154d5SJulian Elischer 		mtx_enter(&ng_worklist_mtx, MTX_SPIN);
2953069154d5SJulian Elischer 		node = TAILQ_FIRST(&ng_worklist);
2954069154d5SJulian Elischer 		if (!node) {
2955069154d5SJulian Elischer 			mtx_exit(&ng_worklist_mtx, MTX_SPIN);
2956069154d5SJulian Elischer 			break;
2957069154d5SJulian Elischer 		}
295830400f03SJulian Elischer 		TAILQ_REMOVE(&ng_worklist, node, nd_work);
2959069154d5SJulian Elischer 		mtx_exit(&ng_worklist_mtx, MTX_SPIN);
2960069154d5SJulian Elischer 		/*
2961069154d5SJulian Elischer 		 * We have the node. We also take over the reference
2962069154d5SJulian Elischer 		 * that the list had on it.
2963069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
2964069154d5SJulian Elischer 		 * let you have another item off the queue.
2965069154d5SJulian Elischer 		 * All this time, keep the reference
2966069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
2967069154d5SJulian Elischer 		 * Let the reference go at the last minute.
2968069154d5SJulian Elischer 		 */
2969069154d5SJulian Elischer 		for (;;) {
297030400f03SJulian Elischer 			mtx_enter(&node->nd_input_queue.q_mtx, MTX_SPIN);
297130400f03SJulian Elischer 			item = ng_dequeue(&node->nd_input_queue);
2972069154d5SJulian Elischer 			if (item == NULL) {
2973069154d5SJulian Elischer 				/*
2974069154d5SJulian Elischer 				 * Say we are on the queue as long as
2975069154d5SJulian Elischer 				 * we are processing it here.
2976069154d5SJulian Elischer 				 * it probably wouldn't come here while we
2977069154d5SJulian Elischer 				 * are processing anyhow.
2978069154d5SJulian Elischer 				 */
297930400f03SJulian Elischer 				node->nd_flags &= ~NG_WORKQ;
298030400f03SJulian Elischer 				mtx_exit(&node->nd_input_queue.q_mtx, MTX_SPIN);
298130400f03SJulian Elischer 				NG_NODE_UNREF(node);
2982069154d5SJulian Elischer 				break; /* go look for another node */
2983069154d5SJulian Elischer 			} else {
298430400f03SJulian Elischer 				mtx_exit(&node->nd_input_queue.q_mtx, MTX_SPIN);
298530400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
2986069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
2987069154d5SJulian Elischer #endif
2988069154d5SJulian Elischer 				ng_apply_item(node, item);
2989069154d5SJulian Elischer 			}
2990069154d5SJulian Elischer 		}
2991069154d5SJulian Elischer 	}
2992069154d5SJulian Elischer }
2993069154d5SJulian Elischer 
2994069154d5SJulian Elischer static void
2995069154d5SJulian Elischer ng_worklist_remove(node_p node)
2996069154d5SJulian Elischer {
2997069154d5SJulian Elischer 	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
299830400f03SJulian Elischer 	if (node->nd_flags & NG_WORKQ) {
299930400f03SJulian Elischer 		TAILQ_REMOVE(&ng_worklist, node, nd_work);
300030400f03SJulian Elischer 		NG_NODE_UNREF(node);
3001069154d5SJulian Elischer 	}
300230400f03SJulian Elischer 	node->nd_flags &= ~NG_WORKQ;
3003069154d5SJulian Elischer 	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
3004069154d5SJulian Elischer }
3005069154d5SJulian Elischer 
3006069154d5SJulian Elischer static void
3007069154d5SJulian Elischer ng_setisr(node_p node)
3008069154d5SJulian Elischer {
3009069154d5SJulian Elischer 	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
301030400f03SJulian Elischer 	if ((node->nd_flags & NG_WORKQ) == 0) {
3011069154d5SJulian Elischer 		/*
3012069154d5SJulian Elischer 		 * If we are not already on the work queue,
3013069154d5SJulian Elischer 		 * then put us on.
3014069154d5SJulian Elischer 		 */
301530400f03SJulian Elischer 		node->nd_flags |= NG_WORKQ;
301630400f03SJulian Elischer 		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
301730400f03SJulian Elischer 		NG_NODE_REF(node);
3018069154d5SJulian Elischer 	}
3019069154d5SJulian Elischer 	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
3020069154d5SJulian Elischer 	schednetisr(NETISR_NETGRAPH);
3021069154d5SJulian Elischer }
3022069154d5SJulian Elischer 
3023069154d5SJulian Elischer 
3024069154d5SJulian Elischer /***********************************************************************
3025069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
3026069154d5SJulian Elischer ***********************************************************************/
3027069154d5SJulian Elischer 
302830400f03SJulian Elischer #ifdef	NETGRAPH_DEBUG
302930400f03SJulian Elischer #define	ITEM_DEBUG_CHECKS						\
30304cf49a43SJulian Elischer 	do {								\
3031069154d5SJulian Elischer 		if (item->el_dest ) {					\
3032069154d5SJulian Elischer 			printf("item already has node");		\
3033069154d5SJulian Elischer 			Debugger("has node");				\
303430400f03SJulian Elischer 			NG_NODE_UNREF(item->el_dest);			\
3035069154d5SJulian Elischer 			item->el_dest = NULL;				\
3036069154d5SJulian Elischer 		}							\
3037069154d5SJulian Elischer 		if (item->el_hook ) {					\
3038069154d5SJulian Elischer 			printf("item already has hook");		\
3039069154d5SJulian Elischer 			Debugger("has hook");				\
304030400f03SJulian Elischer 			NG_HOOK_UNREF(item->el_hook);			\
3041069154d5SJulian Elischer 			item->el_hook = NULL;				\
3042069154d5SJulian Elischer 		}							\
3043069154d5SJulian Elischer 	} while (0)
3044069154d5SJulian Elischer #else
304530400f03SJulian Elischer #define ITEM_DEBUG_CHECKS
3046069154d5SJulian Elischer #endif
3047069154d5SJulian Elischer 
3048069154d5SJulian Elischer /*
3049069154d5SJulian Elischer  * Put elements into the item.
3050069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3051069154d5SJulian Elischer  * (or equivalent)
3052069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
3053069154d5SJulian Elischer  * remote node might go away in this timescale.
3054069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
3055069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
3056069154d5SJulian Elischer  * here to eb able to do this.
3057069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
3058069154d5SJulian Elischer  *
3059069154d5SJulian Elischer  * This is possibly in the critical path for new data.
3060069154d5SJulian Elischer  */
3061069154d5SJulian Elischer item_p
3062069154d5SJulian Elischer ng_package_data(struct mbuf *m, meta_p meta)
3063069154d5SJulian Elischer {
3064069154d5SJulian Elischer 	item_p item;
3065069154d5SJulian Elischer 
3066069154d5SJulian Elischer 	if ((item = ng_getqblk()) == NULL) {
3067069154d5SJulian Elischer 		NG_FREE_M(m);
3068069154d5SJulian Elischer 		NG_FREE_META(meta);
3069069154d5SJulian Elischer 		return (NULL);
3070069154d5SJulian Elischer 	}
307130400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3072069154d5SJulian Elischer 	item->el_flags = NGQF_DATA;
3073069154d5SJulian Elischer 	item->el_next = NULL;
3074069154d5SJulian Elischer 	NGI_M(item) = m;
3075069154d5SJulian Elischer 	NGI_META(item) = meta;
3076069154d5SJulian Elischer 	return (item);
3077069154d5SJulian Elischer }
3078069154d5SJulian Elischer 
3079069154d5SJulian Elischer /*
3080069154d5SJulian Elischer  * Allocate a queue item and put items into it..
3081069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
3082069154d5SJulian Elischer  * to work out what some of the fields should be.
3083069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
3084069154d5SJulian Elischer  * (or equivalent)
3085069154d5SJulian Elischer  */
3086069154d5SJulian Elischer item_p
3087069154d5SJulian Elischer ng_package_msg(struct ng_mesg *msg)
3088069154d5SJulian Elischer {
3089069154d5SJulian Elischer 	item_p item;
3090069154d5SJulian Elischer 
3091069154d5SJulian Elischer 	if ((item = ng_getqblk()) == NULL) {
3092069154d5SJulian Elischer 		if ((msg->header.flags & NGF_STATIC) == 0) {
3093069154d5SJulian Elischer 			NG_FREE_MSG(msg);
3094069154d5SJulian Elischer 		}
3095069154d5SJulian Elischer 		return (NULL);
3096069154d5SJulian Elischer 	}
309730400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3098069154d5SJulian Elischer 	item->el_flags = NGQF_MESG;
3099069154d5SJulian Elischer 	item->el_next = NULL;
3100069154d5SJulian Elischer 	/*
3101069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
3102069154d5SJulian Elischer 	 */
3103069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3104069154d5SJulian Elischer 	NGI_RETADDR(item) = NULL;
3105069154d5SJulian Elischer 	return (item);
3106069154d5SJulian Elischer }
3107069154d5SJulian Elischer 
3108069154d5SJulian Elischer 
3109069154d5SJulian Elischer 
3110069154d5SJulian Elischer #define SET_RETADDR							\
3111069154d5SJulian Elischer 	do {	/* Data items don't have retaddrs */			\
3112069154d5SJulian Elischer 		if ((item->el_flags & NGQF_D_M) == NGQF_MESG) {		\
3113069154d5SJulian Elischer 			if (retaddr) {					\
3114069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
31154cf49a43SJulian Elischer 			} else {					\
3116069154d5SJulian Elischer 				/*					\
3117069154d5SJulian Elischer 				 * The old return address should be ok.	\
3118069154d5SJulian Elischer 				 * If there isn't one, use the address	\
3119069154d5SJulian Elischer 				 * here.				\
3120069154d5SJulian Elischer 				 */					\
3121069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
3122069154d5SJulian Elischer 					NGI_RETADDR(item)		\
3123069154d5SJulian Elischer 						= ng_node2ID(here);	\
3124069154d5SJulian Elischer 				}					\
3125069154d5SJulian Elischer 			}						\
31264cf49a43SJulian Elischer 		}							\
31274cf49a43SJulian Elischer 	} while (0)
31284cf49a43SJulian Elischer 
31294cf49a43SJulian Elischer int
3130069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
31314cf49a43SJulian Elischer {
313230400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3133069154d5SJulian Elischer 	/*
3134069154d5SJulian Elischer 	 * Quick sanity check..
313530400f03SJulian Elischer 	 * Since a hook holds a reference on it's node, once we know
313630400f03SJulian Elischer 	 * that the peer is still connected (even if invalid,) we know
313730400f03SJulian Elischer 	 * that the peer node is present, though maybe invalid.
3138069154d5SJulian Elischer 	 */
3139069154d5SJulian Elischer 	if ((hook == NULL)
314030400f03SJulian Elischer 	|| NG_HOOK_NOT_VALID(hook)
314130400f03SJulian Elischer 	|| (NG_HOOK_PEER(hook) == NULL)
314230400f03SJulian Elischer 	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
314330400f03SJulian Elischer 	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
3144069154d5SJulian Elischer 		NG_FREE_ITEM(item);
314530400f03SJulian Elischer 		TRAP_ERROR
3146069154d5SJulian Elischer 		return (EINVAL);
31474cf49a43SJulian Elischer 	}
31484cf49a43SJulian Elischer 
31494cf49a43SJulian Elischer 	/*
3150069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
31514cf49a43SJulian Elischer 	 */
315230400f03SJulian Elischer 	item->el_hook = NG_HOOK_PEER(hook);
315330400f03SJulian Elischer 	NG_HOOK_REF(item->el_hook); /* Don't let it go while on the queue */
315430400f03SJulian Elischer 	item->el_dest = NG_PEER_NODE(hook);
315530400f03SJulian Elischer 	NG_NODE_REF(item->el_dest); /* Nor this */
3156069154d5SJulian Elischer 	SET_RETADDR;
3157069154d5SJulian Elischer 	return (0);
3158069154d5SJulian Elischer }
3159069154d5SJulian Elischer 
31604cf49a43SJulian Elischer int
3161069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
31624cf49a43SJulian Elischer {
31634cf49a43SJulian Elischer 	node_p  dest = NULL;
3164069154d5SJulian Elischer 	hook_p	hook = NULL;
31654cf49a43SJulian Elischer 	int     error;
3166069154d5SJulian Elischer 
316730400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3168069154d5SJulian Elischer 	/*
3169069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
3170069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
3171069154d5SJulian Elischer 	 */
3172069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
3173069154d5SJulian Elischer 	if (error) {
3174069154d5SJulian Elischer 		NG_FREE_ITEM(item);
317530400f03SJulian Elischer 		return (error);
3176069154d5SJulian Elischer 	}
3177069154d5SJulian Elischer 	item->el_dest = dest;
3178069154d5SJulian Elischer 	if (( item->el_hook = hook))
317930400f03SJulian Elischer 		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
3180069154d5SJulian Elischer 	SET_RETADDR;
3181069154d5SJulian Elischer 	return (0);
3182069154d5SJulian Elischer }
3183069154d5SJulian Elischer 
3184069154d5SJulian Elischer int
3185069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
3186069154d5SJulian Elischer {
3187069154d5SJulian Elischer 	node_p dest;
3188069154d5SJulian Elischer 
318930400f03SJulian Elischer 	ITEM_DEBUG_CHECKS;
3190069154d5SJulian Elischer 	/*
3191069154d5SJulian Elischer 	 * Find the target node.
3192069154d5SJulian Elischer 	 */
3193069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
3194069154d5SJulian Elischer 	if (dest == NULL) {
3195069154d5SJulian Elischer 		NG_FREE_ITEM(item);
319630400f03SJulian Elischer 		TRAP_ERROR
3197069154d5SJulian Elischer 		return(EINVAL);
3198069154d5SJulian Elischer 	}
3199069154d5SJulian Elischer 	/* Fill out the contents */
3200069154d5SJulian Elischer 	item->el_flags = NGQF_MESG;
3201069154d5SJulian Elischer 	item->el_next = NULL;
3202069154d5SJulian Elischer 	item->el_dest = dest;
3203069154d5SJulian Elischer 	item->el_hook = NULL;
3204069154d5SJulian Elischer 	SET_RETADDR;
3205069154d5SJulian Elischer 	return (0);
3206069154d5SJulian Elischer }
3207069154d5SJulian Elischer 
3208069154d5SJulian Elischer /*
3209069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
3210069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
3211069154d5SJulian Elischer  * Useful for removing that hook :-)
3212069154d5SJulian Elischer  */
3213069154d5SJulian Elischer item_p
3214069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
3215069154d5SJulian Elischer {
3216069154d5SJulian Elischer 	item_p item;
32174cf49a43SJulian Elischer 
3218859a4d16SJulian Elischer 	/*
3219859a4d16SJulian Elischer 	 * Find the target node.
3220859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3221859a4d16SJulian Elischer 	 * to the address.
3222859a4d16SJulian Elischer 	 */
3223069154d5SJulian Elischer 	if ((item = ng_getqblk()) == NULL) {
3224069154d5SJulian Elischer 		if ((msg->header.flags & NGF_STATIC) == 0) {
3225069154d5SJulian Elischer 			NG_FREE_MSG(msg);
32264cf49a43SJulian Elischer 		}
3227069154d5SJulian Elischer 		return (NULL);
32284cf49a43SJulian Elischer 	}
32294cf49a43SJulian Elischer 
32304cf49a43SJulian Elischer 	/* Fill out the contents */
3231069154d5SJulian Elischer 	item->el_flags = NGQF_MESG;
3232069154d5SJulian Elischer 	item->el_next = NULL;
3233069154d5SJulian Elischer 	item->el_dest = here;
323430400f03SJulian Elischer 	NG_NODE_REF(here);
3235069154d5SJulian Elischer 	item->el_hook = hook;
3236069154d5SJulian Elischer 	if (hook)
323730400f03SJulian Elischer 		NG_HOOK_REF(hook);
3238069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3239069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3240069154d5SJulian Elischer 	return (item);
32414cf49a43SJulian Elischer }
32424cf49a43SJulian Elischer 
32434cf49a43SJulian Elischer /*
3244069154d5SJulian Elischer  * Set the address, if none given, give the node here.
32454cf49a43SJulian Elischer  */
3246069154d5SJulian Elischer void
3247069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
32484cf49a43SJulian Elischer {
3249069154d5SJulian Elischer 	if (retaddr) {
3250069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3251069154d5SJulian Elischer 	} else {
3252069154d5SJulian Elischer 		/*
3253069154d5SJulian Elischer 		 * The old return address should be ok.
3254069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3255069154d5SJulian Elischer 		 */
3256069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3257069154d5SJulian Elischer 	}
3258069154d5SJulian Elischer }
3259069154d5SJulian Elischer 
3260069154d5SJulian Elischer #define TESTING
3261069154d5SJulian Elischer #ifdef TESTING
3262069154d5SJulian Elischer /* just test all the macros */
3263069154d5SJulian Elischer void
3264069154d5SJulian Elischer ng_macro_test(item_p item);
3265069154d5SJulian Elischer void
3266069154d5SJulian Elischer ng_macro_test(item_p item)
3267069154d5SJulian Elischer {
3268069154d5SJulian Elischer 	node_p node = NULL;
3269069154d5SJulian Elischer 	hook_p hook = NULL;
32704cf49a43SJulian Elischer 	struct mbuf *m;
32714cf49a43SJulian Elischer 	meta_p meta;
32724cf49a43SJulian Elischer 	struct ng_mesg *msg;
3273069154d5SJulian Elischer 	ng_ID_t retaddr;
3274069154d5SJulian Elischer 	int	error;
32754cf49a43SJulian Elischer 
3276069154d5SJulian Elischer 	NGI_GET_M(item, m);
3277069154d5SJulian Elischer 	NGI_GET_META(item, meta);
3278069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3279069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
3280069154d5SJulian Elischer 	NG_SEND_DATA(error, hook, m, meta);
3281069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3282069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
328330400f03SJulian Elischer 	NG_FWD_ITEM_HOOK(error, item, hook);
3284069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3285069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3286069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3287069154d5SJulian Elischer 	NG_QUEUE_MSG(error, node, msg, ".:", retaddr);
3288069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
32894cf49a43SJulian Elischer }
3290069154d5SJulian Elischer #endif /* TESTING */
32914cf49a43SJulian Elischer 
3292