ng_base.c revision 80222
152419Sjulian/*
252419Sjulian * ng_base.c
352419Sjulian *
452419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
552419Sjulian * All rights reserved.
670700Sjulian *
752419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
852419Sjulian * redistribution of this software, in source or object code forms, with or
952419Sjulian * without modifications are expressly permitted by Whistle Communications;
1052419Sjulian * provided, however, that:
1152419Sjulian * 1. Any and all reproductions of the source or object code must include the
1252419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1352419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1452419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1552419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1652419Sjulian *    such appears in the above copyright notice or in the software.
1770700Sjulian *
1852419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
1952419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2052419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2152419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2252419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2352419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2452419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2552419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2652419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2752419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2852419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2952419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3052419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3152419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3252419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3352419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3452419Sjulian * OF SUCH DAMAGE.
3552419Sjulian *
3667506Sjulian * Authors: Julian Elischer <julian@freebsd.org>
3767506Sjulian *          Archie Cobbs <archie@freebsd.org>
3852419Sjulian *
3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 80222 2001-07-23 21:14:57Z julian $
4052419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
4152419Sjulian */
4252419Sjulian
4352419Sjulian/*
4452419Sjulian * This file implements the base netgraph code.
4552419Sjulian */
4652419Sjulian
4752419Sjulian#include <sys/param.h>
4852419Sjulian#include <sys/systm.h>
4952419Sjulian#include <sys/errno.h>
5052419Sjulian#include <sys/kernel.h>
5152419Sjulian#include <sys/malloc.h>
5252419Sjulian#include <sys/syslog.h>
5370700Sjulian#include <sys/sysctl.h>
5452419Sjulian#include <sys/linker.h>
5552419Sjulian#include <sys/queue.h>
5652419Sjulian#include <sys/mbuf.h>
5752843Sphk#include <sys/ctype.h>
5872946Sjulian#include <sys/sysctl.h>
5952816Sarchie#include <machine/limits.h>
6052419Sjulian
6152419Sjulian#include <net/netisr.h>
6252419Sjulian
6352419Sjulian#include <netgraph/ng_message.h>
6452419Sjulian#include <netgraph/netgraph.h>
6553913Sarchie#include <netgraph/ng_parse.h>
6652419Sjulian
6772053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION);
6859756Speter
6970784Sjulian/* List of all active nodes */
7070700Sjulianstatic LIST_HEAD(, ng_node) ng_nodelist;
7170700Sjulianstatic struct mtx	ng_nodelist_mtx;
7252419Sjulian
7370784Sjulian#ifdef	NETGRAPH_DEBUG
7470784Sjulian
7570784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
7670784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
7770784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
7870784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
7970784Sjulian
8070784Sjulianstatic void ng_dumpitems(void);
8170784Sjulianstatic void ng_dumpnodes(void);
8270784Sjulianstatic void ng_dumphooks(void);
8370784Sjulian
8470784Sjulian#endif	/* NETGRAPH_DEBUG */
8570935Sjulian/*
8670935Sjulian * DEAD versions of the structures.
8770935Sjulian * In order to avoid races, it is sometimes neccesary to point
8870935Sjulian * at SOMETHING even though theoretically, the current entity is
8970935Sjulian * INVALID. Use these to avoid these races.
9070935Sjulian */
9170935Sjulianstruct ng_type ng_deadtype = {
9270935Sjulian	NG_ABI_VERSION,
9370935Sjulian	"dead",
9470935Sjulian	NULL,	/* modevent */
9570935Sjulian	NULL,	/* constructor */
9670935Sjulian	NULL,	/* rcvmsg */
9770935Sjulian	NULL,	/* shutdown */
9870935Sjulian	NULL,	/* newhook */
9970935Sjulian	NULL,	/* findhook */
10070935Sjulian	NULL,	/* connect */
10170935Sjulian	NULL,	/* rcvdata */
10270935Sjulian	NULL,	/* disconnect */
10370935Sjulian	NULL, 	/* cmdlist */
10470935Sjulian};
10570784Sjulian
10670935Sjulianstruct ng_node ng_deadnode = {
10770935Sjulian	"dead",
10870935Sjulian	&ng_deadtype,
10970935Sjulian	NG_INVALID,
11070935Sjulian	1,	/* refs */
11170935Sjulian	0,	/* numhooks */
11270935Sjulian	NULL,	/* private */
11370935Sjulian	0,	/* ID */
11470935Sjulian	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
11570935Sjulian	{},	/* all_nodes list entry */
11670935Sjulian	{},	/* id hashtable list entry */
11770935Sjulian	{},	/* workqueue entry */
11870935Sjulian	{	0,
11970935Sjulian		{}, /* should never use! (should hang) */
12070935Sjulian		NULL,
12170935Sjulian		&ng_deadnode.nd_input_queue.queue,
12270935Sjulian		&ng_deadnode
12370935Sjulian	},
12470935Sjulian#ifdef	NETGRAPH_DEBUG
12570935Sjulian	ND_MAGIC,
12670935Sjulian	__FILE__,
12770935Sjulian	__LINE__,
12870935Sjulian	{NULL}
12970935Sjulian#endif	/* NETGRAPH_DEBUG */
13070935Sjulian};
13170935Sjulian
13270935Sjulianstruct ng_hook ng_deadhook = {
13370935Sjulian	"dead",
13470935Sjulian	NULL,		/* private */
13570935Sjulian	HK_INVALID | HK_DEAD,
13670935Sjulian	1,		/* refs always >= 1 */
13770935Sjulian	&ng_deadhook,	/* Peer is self */
13870935Sjulian	&ng_deadnode,	/* attached to deadnode */
13970935Sjulian	{},		/* hooks list */
14071885Sjulian	NULL,		/* override rcvmsg() */
14171885Sjulian	NULL,		/* override rcvdata() */
14270935Sjulian#ifdef	NETGRAPH_DEBUG
14370935Sjulian	HK_MAGIC,
14470935Sjulian	__FILE__,
14570935Sjulian	__LINE__,
14670935Sjulian	{NULL}
14770935Sjulian#endif	/* NETGRAPH_DEBUG */
14870935Sjulian};
14970935Sjulian
15070935Sjulian/*
15170935Sjulian * END DEAD STRUCTURES
15270935Sjulian */
15370700Sjulian/* List nodes with unallocated work */
15470700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
15571902Sjulianstatic struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
15670700Sjulian
15752419Sjulian/* List of installed types */
15870700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
15970700Sjulianstatic struct mtx	ng_typelist_mtx;
16052419Sjulian
16170700Sjulian/* Hash related definitions */
16271354Sjulian/* XXX Don't need to initialise them because it's a LIST */
16371354Sjulian#define NG_ID_HASH_SIZE 32 /* most systems wont need even this many */
16471354Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
16570700Sjulianstatic struct mtx	ng_idhash_mtx;
16671354Sjulian/* Method to find a node.. used twice so do it here */
16771354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
16871354Sjulian#define NG_IDHASH_FIND(ID, node)					\
16971354Sjulian	do { 								\
17071354Sjulian		LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)],	\
17171354Sjulian						nd_idnodes) {		\
17271354Sjulian			if (NG_NODE_IS_VALID(node)			\
17371354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
17471354Sjulian				break;					\
17571354Sjulian			}						\
17671354Sjulian		}							\
17771354Sjulian	} while (0)
17852722Sjulian
17970700Sjulian/* Mutex that protects the free queue item list */
18070700Sjulianstatic volatile item_p		ngqfree;	/* free ones */
18170700Sjulianstatic struct mtx	ngq_mtx;
18270700Sjulian
18352419Sjulian/* Internal functions */
18452419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
18570700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
18652722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
18752419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
18870700Sjulianstatic void	ng_worklist_remove(node_p node);
18952419Sjulianstatic void	ngintr(void);
19074078Sjulianstatic int	ng_apply_item(node_p node, item_p item);
19170700Sjulianstatic void	ng_flush_input_queue(struct ng_queue * ngq);
19270700Sjulianstatic void	ng_setisr(node_p node);
19370700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
19471047Sjulianstatic int	ng_con_nodes(node_p node, const char *name, node_p node2,
19571047Sjulian							const char *name2);
19671849Sjulianstatic void	ng_con_part2(node_p node, hook_p hook, void *arg1, int arg2);
19771849Sjulianstatic void	ng_con_part3(node_p node, hook_p hook, void *arg1, int arg2);
19871047Sjulianstatic int	ng_mkpeer(node_p node, const char *name,
19971047Sjulian						const char *name2, char *type);
20052419Sjulian
20170784Sjulian/* imported , these used to be externally visible, some may go back */
20270700Sjulianint	ng_bypass(hook_p hook1, hook_p hook2);
20370700Sjulianvoid	ng_destroy_hook(hook_p hook);
20470700Sjuliannode_p	ng_name2noderef(node_p node, const char *name);
20570700Sjulianint	ng_path2noderef(node_p here, const char *path,
20670700Sjulian	node_p *dest, hook_p *lasthook);
20770700Sjulianstruct	ng_type *ng_findtype(const char *type);
20870700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
20970700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
21071849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
21170784Sjulianvoid	ng_unname(node_p node);
21270700Sjulian
21370700Sjulian
21452419Sjulian/* Our own netgraph malloc type */
21552419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
21670700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
21770700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
21870700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
21970700SjulianMALLOC_DEFINE(M_NETGRAPH_META, "netgraph_meta", "netgraph name storage");
22070700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
22152419Sjulian
22270700Sjulian/* Should not be visible outside this file */
22370784Sjulian
22470784Sjulian#define _NG_ALLOC_HOOK(hook) \
22570784Sjulian	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
22670784Sjulian#define _NG_ALLOC_NODE(node) \
22770784Sjulian	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
22870784Sjulian
22970784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
23070784Sjulian/*
23170784Sjulian * In debug mode:
23270784Sjulian * In an attempt to help track reference count screwups
23370784Sjulian * we do not free objects back to the malloc system, but keep them
23470784Sjulian * in a local cache where we can examine them and keep information safely
23570784Sjulian * after they have been freed.
23670784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
23770784Sjulian */
23870784Sjulianstatic __inline hook_p
23970784Sjulianng_alloc_hook(void)
24070784Sjulian{
24170784Sjulian	hook_p hook;
24270784Sjulian	SLIST_ENTRY(ng_hook) temp;
24372200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
24470784Sjulian	hook = LIST_FIRST(&ng_freehooks);
24570784Sjulian	if (hook) {
24670784Sjulian		LIST_REMOVE(hook, hk_hooks);
24770784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
24870784Sjulian		bzero(hook, sizeof(struct ng_hook));
24970784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
25072200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
25170784Sjulian		hook->hk_magic = HK_MAGIC;
25270784Sjulian	} else {
25372200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
25470784Sjulian		_NG_ALLOC_HOOK(hook);
25570784Sjulian		if (hook) {
25670784Sjulian			hook->hk_magic = HK_MAGIC;
25772200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
25870784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
25972200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
26070784Sjulian		}
26170784Sjulian	}
26270784Sjulian	return (hook);
26370784Sjulian}
26470784Sjulian
26570784Sjulianstatic __inline node_p
26670784Sjulianng_alloc_node(void)
26770784Sjulian{
26870784Sjulian	node_p node;
26970784Sjulian	SLIST_ENTRY(ng_node) temp;
27072200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
27170784Sjulian	node = LIST_FIRST(&ng_freenodes);
27270784Sjulian	if (node) {
27370784Sjulian		LIST_REMOVE(node, nd_nodes);
27470784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
27570784Sjulian		bzero(node, sizeof(struct ng_node));
27670784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
27772200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
27870784Sjulian		node->nd_magic = ND_MAGIC;
27970784Sjulian	} else {
28072200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
28170784Sjulian		_NG_ALLOC_NODE(node);
28270784Sjulian		if (node) {
28370784Sjulian			node->nd_magic = ND_MAGIC;
28472200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
28570784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
28672200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
28770784Sjulian		}
28870784Sjulian	}
28970784Sjulian	return (node);
29070784Sjulian}
29170784Sjulian
29270784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
29370784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
29470784Sjulian
29570784Sjulian
29670784Sjulian#define NG_FREE_HOOK(hook)						\
29770784Sjulian	do {								\
29872200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
29970784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
30070784Sjulian		hook->hk_magic = 0;					\
30172200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
30270784Sjulian	} while (0)
30370784Sjulian
30470784Sjulian#define NG_FREE_NODE(node)						\
30570784Sjulian	do {								\
30672200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
30770784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
30870784Sjulian		node->nd_magic = 0;					\
30972200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
31070784Sjulian	} while (0)
31170784Sjulian
31270784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
31370784Sjulian
31470784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
31570784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
31670784Sjulian
31770700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
31870700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
31970784Sjulian
32070784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
32170784Sjulian
32270700Sjulian/* Warning: Generally use NG_FREE_ITEM() instead */
32370700Sjulian#define NG_FREE_ITEM_REAL(item) do { FREE((item), M_NETGRAPH_ITEM); } while (0)
32470700Sjulian
32570700Sjulian
32652419Sjulian/* Set this to Debugger("X") to catch all errors as they occur */
32752419Sjulian#ifndef TRAP_ERROR
32871047Sjulian#define TRAP_ERROR()
32952419Sjulian#endif
33052419Sjulian
33152722Sjulianstatic	ng_ID_t nextID = 1;
33252722Sjulian
33353403Sarchie#ifdef INVARIANTS
33453403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
33553403Sarchie		struct mbuf *n;						\
33653403Sarchie		int total;						\
33753403Sarchie									\
33853403Sarchie		if (((m)->m_flags & M_PKTHDR) == 0)			\
33953403Sarchie			panic("%s: !PKTHDR", __FUNCTION__);		\
34053403Sarchie		for (total = 0, n = (m); n != NULL; n = n->m_next)	\
34153403Sarchie			total += n->m_len;				\
34253403Sarchie		if ((m)->m_pkthdr.len != total) {			\
34353403Sarchie			panic("%s: %d != %d",				\
34453403Sarchie			    __FUNCTION__, (m)->m_pkthdr.len, total);	\
34553403Sarchie		}							\
34653403Sarchie	} while (0)
34753403Sarchie#else
34853403Sarchie#define CHECK_DATA_MBUF(m)
34953403Sarchie#endif
35052722Sjulian
35153403Sarchie
35252419Sjulian/************************************************************************
35353913Sarchie	Parse type definitions for generic messages
35453913Sarchie************************************************************************/
35553913Sarchie
35653913Sarchie/* Handy structure parse type defining macro */
35753913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
35853913Sarchiestatic const struct ng_parse_struct_info				\
35953913Sarchie	ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args;	\
36053913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
36153913Sarchie	&ng_parse_struct_type,						\
36253913Sarchie	&ng_ ## lo ## _type_info					\
36353913Sarchie}
36453913Sarchie
36553913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
36653913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
36753913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
36853913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
36953913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
37053913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
37153913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
37253913Sarchie
37353913Sarchie/* Get length of an array when the length is stored as a 32 bit
37472645Sasmodai   value immediately preceding the array -- as with struct namelist
37553913Sarchie   and struct typelist. */
37653913Sarchiestatic int
37753913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
37853913Sarchie	const u_char *start, const u_char *buf)
37953913Sarchie{
38053913Sarchie	return *((const u_int32_t *)(buf - 4));
38153913Sarchie}
38253913Sarchie
38353913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
38453913Sarchiestatic int
38553913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
38653913Sarchie	const u_char *start, const u_char *buf)
38753913Sarchie{
38853913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
38953913Sarchie
39053913Sarchie	return hl->nodeinfo.hooks;
39153913Sarchie}
39253913Sarchie
39353913Sarchie/* Array type for a variable length array of struct namelist */
39453913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
39553913Sarchie	&ng_generic_nodeinfo_type,
39653913Sarchie	&ng_generic_list_getLength
39753913Sarchie};
39853913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
39953913Sarchie	&ng_parse_array_type,
40053913Sarchie	&ng_nodeinfoarray_type_info
40153913Sarchie};
40253913Sarchie
40353913Sarchie/* Array type for a variable length array of struct typelist */
40453913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
40553913Sarchie	&ng_generic_typeinfo_type,
40653913Sarchie	&ng_generic_list_getLength
40753913Sarchie};
40853913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
40953913Sarchie	&ng_parse_array_type,
41053913Sarchie	&ng_typeinfoarray_type_info
41153913Sarchie};
41253913Sarchie
41353913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
41453913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
41553913Sarchie	&ng_generic_linkinfo_type,
41653913Sarchie	&ng_generic_linkinfo_getLength
41753913Sarchie};
41853913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
41953913Sarchie	&ng_parse_array_type,
42053913Sarchie	&ng_generic_linkinfo_array_type_info
42153913Sarchie};
42253913Sarchie
42353913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
42453913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
42553913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
42653913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
42753913Sarchie	(&ng_generic_nodeinfoarray_type));
42853913Sarchie
42953913Sarchie/* List of commands and how to convert arguments to/from ASCII */
43053913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
43153913Sarchie	{
43253913Sarchie	  NGM_GENERIC_COOKIE,
43353913Sarchie	  NGM_SHUTDOWN,
43453913Sarchie	  "shutdown",
43553913Sarchie	  NULL,
43653913Sarchie	  NULL
43753913Sarchie	},
43853913Sarchie	{
43953913Sarchie	  NGM_GENERIC_COOKIE,
44053913Sarchie	  NGM_MKPEER,
44153913Sarchie	  "mkpeer",
44253913Sarchie	  &ng_generic_mkpeer_type,
44353913Sarchie	  NULL
44453913Sarchie	},
44553913Sarchie	{
44653913Sarchie	  NGM_GENERIC_COOKIE,
44753913Sarchie	  NGM_CONNECT,
44853913Sarchie	  "connect",
44953913Sarchie	  &ng_generic_connect_type,
45053913Sarchie	  NULL
45153913Sarchie	},
45253913Sarchie	{
45353913Sarchie	  NGM_GENERIC_COOKIE,
45453913Sarchie	  NGM_NAME,
45553913Sarchie	  "name",
45653913Sarchie	  &ng_generic_name_type,
45753913Sarchie	  NULL
45853913Sarchie	},
45953913Sarchie	{
46053913Sarchie	  NGM_GENERIC_COOKIE,
46153913Sarchie	  NGM_RMHOOK,
46253913Sarchie	  "rmhook",
46353913Sarchie	  &ng_generic_rmhook_type,
46453913Sarchie	  NULL
46553913Sarchie	},
46653913Sarchie	{
46753913Sarchie	  NGM_GENERIC_COOKIE,
46853913Sarchie	  NGM_NODEINFO,
46953913Sarchie	  "nodeinfo",
47053913Sarchie	  NULL,
47153913Sarchie	  &ng_generic_nodeinfo_type
47253913Sarchie	},
47353913Sarchie	{
47453913Sarchie	  NGM_GENERIC_COOKIE,
47553913Sarchie	  NGM_LISTHOOKS,
47653913Sarchie	  "listhooks",
47753913Sarchie	  NULL,
47853913Sarchie	  &ng_generic_hooklist_type
47953913Sarchie	},
48053913Sarchie	{
48153913Sarchie	  NGM_GENERIC_COOKIE,
48253913Sarchie	  NGM_LISTNAMES,
48353913Sarchie	  "listnames",
48453913Sarchie	  NULL,
48553913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
48653913Sarchie	},
48753913Sarchie	{
48853913Sarchie	  NGM_GENERIC_COOKIE,
48953913Sarchie	  NGM_LISTNODES,
49053913Sarchie	  "listnodes",
49153913Sarchie	  NULL,
49253913Sarchie	  &ng_generic_listnodes_type
49353913Sarchie	},
49453913Sarchie	{
49553913Sarchie	  NGM_GENERIC_COOKIE,
49653913Sarchie	  NGM_LISTTYPES,
49753913Sarchie	  "listtypes",
49853913Sarchie	  NULL,
49953913Sarchie	  &ng_generic_typeinfo_type
50053913Sarchie	},
50153913Sarchie	{
50253913Sarchie	  NGM_GENERIC_COOKIE,
50362471Sphk	  NGM_TEXT_CONFIG,
50462471Sphk	  "textconfig",
50562471Sphk	  NULL,
50662471Sphk	  &ng_parse_string_type
50762471Sphk	},
50862471Sphk	{
50962471Sphk	  NGM_GENERIC_COOKIE,
51053913Sarchie	  NGM_TEXT_STATUS,
51153913Sarchie	  "textstatus",
51253913Sarchie	  NULL,
51353913Sarchie	  &ng_parse_string_type
51453913Sarchie	},
51553913Sarchie	{
51653913Sarchie	  NGM_GENERIC_COOKIE,
51753913Sarchie	  NGM_ASCII2BINARY,
51853913Sarchie	  "ascii2binary",
51953913Sarchie	  &ng_parse_ng_mesg_type,
52053913Sarchie	  &ng_parse_ng_mesg_type
52153913Sarchie	},
52253913Sarchie	{
52353913Sarchie	  NGM_GENERIC_COOKIE,
52453913Sarchie	  NGM_BINARY2ASCII,
52553913Sarchie	  "binary2ascii",
52653913Sarchie	  &ng_parse_ng_mesg_type,
52753913Sarchie	  &ng_parse_ng_mesg_type
52853913Sarchie	},
52953913Sarchie	{ 0 }
53053913Sarchie};
53153913Sarchie
53253913Sarchie/************************************************************************
53352419Sjulian			Node routines
53452419Sjulian************************************************************************/
53552419Sjulian
53652419Sjulian/*
53752419Sjulian * Instantiate a node of the requested type
53852419Sjulian */
53952419Sjulianint
54052419Sjulianng_make_node(const char *typename, node_p *nodepp)
54152419Sjulian{
54252419Sjulian	struct ng_type *type;
54370700Sjulian	int	error;
54452419Sjulian
54552419Sjulian	/* Check that the type makes sense */
54652419Sjulian	if (typename == NULL) {
54771047Sjulian		TRAP_ERROR();
54852419Sjulian		return (EINVAL);
54952419Sjulian	}
55052419Sjulian
55152419Sjulian	/* Locate the node type */
55252419Sjulian	if ((type = ng_findtype(typename)) == NULL) {
55359875Speter		char filename[NG_TYPELEN + 4];
55452419Sjulian		linker_file_t lf;
55552419Sjulian		int error;
55652419Sjulian
55752419Sjulian		/* Not found, try to load it as a loadable module */
55869923Sjulian		snprintf(filename, sizeof(filename), "ng_%s", typename);
55959875Speter		error = linker_load_file(filename, &lf);
56052419Sjulian		if (error != 0)
56152419Sjulian			return (error);
56252419Sjulian		lf->userrefs++;		/* pretend loaded by the syscall */
56352419Sjulian
56452419Sjulian		/* Try again, as now the type should have linked itself in */
56552419Sjulian		if ((type = ng_findtype(typename)) == NULL)
56652419Sjulian			return (ENXIO);
56752419Sjulian	}
56852419Sjulian
56970700Sjulian	/*
57070700Sjulian	 * If we have a constructor, then make the node and
57170700Sjulian	 * call the constructor to do type specific initialisation.
57270700Sjulian	 */
57370700Sjulian	if (type->constructor != NULL) {
57470700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
57570700Sjulian			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
57670784Sjulian				NG_NODE_UNREF(*nodepp);
57770700Sjulian			}
57870700Sjulian		}
57970700Sjulian	} else {
58070700Sjulian		/*
58170700Sjulian		 * Node has no constructor. We cannot ask for one
58270700Sjulian		 * to be made. It must be brought into existance by
58370935Sjulian		 * some external agency. The external agency should
58470700Sjulian		 * call ng_make_node_common() directly to get the
58570700Sjulian		 * netgraph part initialised.
58670700Sjulian		 */
58771047Sjulian		TRAP_ERROR();
58870700Sjulian		error = EINVAL;
58970700Sjulian	}
59070700Sjulian	return (error);
59152419Sjulian}
59252419Sjulian
59352419Sjulian/*
59470700Sjulian * Generic node creation. Called by node initialisation for externally
59570700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
59652419Sjulian * The returned node has a reference count of 1.
59752419Sjulian */
59852419Sjulianint
59952419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
60052419Sjulian{
60152419Sjulian	node_p node;
60252419Sjulian
60352419Sjulian	/* Require the node type to have been already installed */
60452419Sjulian	if (ng_findtype(type->name) == NULL) {
60571047Sjulian		TRAP_ERROR();
60652419Sjulian		return (EINVAL);
60752419Sjulian	}
60852419Sjulian
60952419Sjulian	/* Make a node and try attach it to the type */
61070784Sjulian	NG_ALLOC_NODE(node);
61152419Sjulian	if (node == NULL) {
61271047Sjulian		TRAP_ERROR();
61352419Sjulian		return (ENOMEM);
61452419Sjulian	}
61570784Sjulian	node->nd_type = type;
61670784Sjulian	NG_NODE_REF(node);				/* note reference */
61752419Sjulian	type->refs++;
61852419Sjulian
61972979Sjulian	mtx_init(&node->nd_input_queue.q_mtx, "ng_node", MTX_SPIN);
62070784Sjulian	node->nd_input_queue.queue = NULL;
62170784Sjulian	node->nd_input_queue.last = &node->nd_input_queue.queue;
62270784Sjulian	node->nd_input_queue.q_flags = 0;
62370784Sjulian	node->nd_input_queue.q_node = node;
62452419Sjulian
62552419Sjulian	/* Initialize hook list for new node */
62670784Sjulian	LIST_INIT(&node->nd_hooks);
62752419Sjulian
62870700Sjulian	/* Link us into the node linked list */
62972200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
63070784Sjulian	LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes);
63172200Sbmilekic	mtx_unlock(&ng_nodelist_mtx);
63270700Sjulian
63370700Sjulian
63452722Sjulian	/* get an ID and put us in the hash chain */
63572200Sbmilekic	mtx_lock(&ng_idhash_mtx);
63670784Sjulian	for (;;) { /* wrap protection, even if silly */
63770700Sjulian		node_p node2 = NULL;
63870784Sjulian		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
63971354Sjulian
64070784Sjulian		/* Is there a problem with the new number? */
64171354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
64271354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
64370784Sjulian			break;
64470700Sjulian		}
64570784Sjulian	}
64671354Sjulian	LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
64770784Sjulian							node, nd_idnodes);
64872200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
64952722Sjulian
65052419Sjulian	/* Done */
65152419Sjulian	*nodepp = node;
65252419Sjulian	return (0);
65352419Sjulian}
65452419Sjulian
65552419Sjulian/*
65652419Sjulian * Forceably start the shutdown process on a node. Either call
65752419Sjulian * it's shutdown method, or do the default shutdown if there is
65852419Sjulian * no type-specific method.
65952419Sjulian *
66070700Sjulian * We can only be called form a shutdown message, so we know we have
66170939Sjulian * a writer lock, and therefore exclusive access. It also means
66270939Sjulian * that we should not be on the work queue, but we check anyhow.
66370700Sjulian *
66470700Sjulian * Persistent node types must have a type-specific method which
66570939Sjulian * Allocates a new node in which case, this one is irretrievably going away,
66670939Sjulian * or cleans up anything it needs, and just makes the node valid again,
66770939Sjulian * in which case we allow the node to survive.
66870939Sjulian *
66970939Sjulian * XXX We need to think of how to tell a persistant node that we
67070939Sjulian * REALLY need to go away because the hardware has gone or we
67170939Sjulian * are rebooting.... etc.
67252419Sjulian */
67352419Sjulianvoid
67471849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
67552419Sjulian{
67670939Sjulian	hook_p hook;
67770939Sjulian
67852419Sjulian	/* Check if it's already shutting down */
67970784Sjulian	if ((node->nd_flags & NG_CLOSING) != 0)
68052419Sjulian		return;
68152419Sjulian
68271849Sjulian	if (node == &ng_deadnode) {
68371849Sjulian		printf ("shutdown called on deadnode\n");
68471849Sjulian		return;
68571849Sjulian	}
68671849Sjulian
68752419Sjulian	/* Add an extra reference so it doesn't go away during this */
68870784Sjulian	NG_NODE_REF(node);
68952419Sjulian
69070784Sjulian	/*
69170784Sjulian	 * Mark it invalid so any newcomers know not to try use it
69270784Sjulian	 * Also add our own mark so we can't recurse
69370784Sjulian	 * note that NG_INVALID does not do this as it's also set during
69470784Sjulian	 * creation
69570784Sjulian	 */
69670784Sjulian	node->nd_flags |= NG_INVALID|NG_CLOSING;
69752419Sjulian
69870939Sjulian	/* Notify all remaining connected nodes to disconnect */
69970939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
70070939Sjulian		ng_destroy_hook(hook);
70170784Sjulian
70270700Sjulian	/*
70370700Sjulian	 * Drain the input queue forceably.
70470784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
70570784Sjulian	 * Theoretically we came here from a queue entry that was added
70670784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
70771902Sjulian	 * Also removes us from worklist if needed.
70870700Sjulian	 */
70970784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
71070700Sjulian
71152419Sjulian	/* Ask the type if it has anything to do in this case */
71270784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
71370784Sjulian		(*node->nd_type->shutdown)(node);
71471849Sjulian		if (NG_NODE_IS_VALID(node)) {
71571849Sjulian			/*
71671849Sjulian			 * Well, blow me down if the node code hasn't declared
71771849Sjulian			 * that it doesn't want to die.
71871849Sjulian			 * Presumably it is a persistant node.
71971849Sjulian			 * If we REALLY want it to go away,
72071849Sjulian			 *  e.g. hardware going away,
72171849Sjulian			 * Our caller should set NG_REALLY_DIE in nd_flags.
72271849Sjulian			 */
72371849Sjulian			node->nd_flags &= ~(NG_INVALID|NG_CLOSING);
72471849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
72571849Sjulian			return;
72671849Sjulian		}
72770700Sjulian	} else {				/* do the default thing */
72870784Sjulian		NG_NODE_UNREF(node);
72952419Sjulian	}
73052419Sjulian
73170784Sjulian	ng_unname(node); /* basically a NOP these days */
73270784Sjulian
73370784Sjulian	/*
73470784Sjulian	 * Remove extra reference, possibly the last
73570784Sjulian	 * Possible other holders of references may include
73670784Sjulian	 * timeout callouts, but theoretically the node's supposed to
73770784Sjulian	 * have cancelled them. Possibly hardware dependencies may
73870784Sjulian	 * force a driver to 'linger' with a reference.
73970784Sjulian	 */
74070784Sjulian	NG_NODE_UNREF(node);
74152419Sjulian}
74252419Sjulian
74374078Sjulian#ifdef	NETGRAPH_DEBUG
74474078Sjulianvoid
74574078Sjulianng_ref_node(node_p node)
74674078Sjulian{
74774078Sjulian	_NG_NODE_REF(node);
74874078Sjulian}
74974078Sjulian#endif
75074078Sjulian
75152419Sjulian/*
75274078Sjulian * Remove a reference to the node, possibly the last.
75374078Sjulian * deadnode always acts as it it were the last.
75452419Sjulian */
75574078Sjulianint
75670784Sjulianng_unref_node(node_p node)
75752419Sjulian{
75870784Sjulian	int     v;
75971047Sjulian
76071047Sjulian	if (node == &ng_deadnode) {
76174078Sjulian		return (0);
76271047Sjulian	}
76371047Sjulian
76470784Sjulian	do {
76574078Sjulian		v = node->nd_refs - 1;
76674078Sjulian	} while (! atomic_cmpset_int(&node->nd_refs, v + 1, v));
76769519Sjulian
76874078Sjulian	if (v == 0) { /* we were the last */
76970700Sjulian
77072200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
77170784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
77270784Sjulian		LIST_REMOVE(node, nd_nodes);
77372200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
77470700Sjulian
77572200Sbmilekic		mtx_lock(&ng_idhash_mtx);
77670784Sjulian		LIST_REMOVE(node, nd_idnodes);
77772200Sbmilekic		mtx_unlock(&ng_idhash_mtx);
77852419Sjulian
77970791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
78070700Sjulian		NG_FREE_NODE(node);
78152419Sjulian	}
78274078Sjulian	return (v);
78352419Sjulian}
78452419Sjulian
78552419Sjulian/************************************************************************
78652722Sjulian			Node ID handling
78752722Sjulian************************************************************************/
78852722Sjulianstatic node_p
78970700Sjulianng_ID2noderef(ng_ID_t ID)
79052722Sjulian{
79170784Sjulian	node_p node;
79272200Sbmilekic	mtx_lock(&ng_idhash_mtx);
79371354Sjulian	NG_IDHASH_FIND(ID, node);
79470784Sjulian	if(node)
79570784Sjulian		NG_NODE_REF(node);
79672200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
79770784Sjulian	return(node);
79852722Sjulian}
79952722Sjulian
80052722Sjulianng_ID_t
80152722Sjulianng_node2ID(node_p node)
80252722Sjulian{
80370912Sjulian	return (node ? NG_NODE_ID(node) : 0);
80452722Sjulian}
80552722Sjulian
80652722Sjulian/************************************************************************
80752419Sjulian			Node name handling
80852419Sjulian************************************************************************/
80952419Sjulian
81052419Sjulian/*
81152419Sjulian * Assign a node a name. Once assigned, the name cannot be changed.
81252419Sjulian */
81352419Sjulianint
81452419Sjulianng_name_node(node_p node, const char *name)
81552419Sjulian{
81652419Sjulian	int i;
81770700Sjulian	node_p node2;
81852419Sjulian
81952419Sjulian	/* Check the name is valid */
82052419Sjulian	for (i = 0; i < NG_NODELEN + 1; i++) {
82152419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
82252419Sjulian			break;
82352419Sjulian	}
82452419Sjulian	if (i == 0 || name[i] != '\0') {
82571047Sjulian		TRAP_ERROR();
82652419Sjulian		return (EINVAL);
82752419Sjulian	}
82852722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
82971047Sjulian		TRAP_ERROR();
83052419Sjulian		return (EINVAL);
83152419Sjulian	}
83252419Sjulian
83352419Sjulian	/* Check the name isn't already being used */
83470700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
83570784Sjulian		NG_NODE_UNREF(node2);
83671047Sjulian		TRAP_ERROR();
83752419Sjulian		return (EADDRINUSE);
83852419Sjulian	}
83952419Sjulian
84070700Sjulian	/* copy it */
84170784Sjulian	strncpy(NG_NODE_NAME(node), name, NG_NODELEN);
84252419Sjulian
84352419Sjulian	return (0);
84452419Sjulian}
84552419Sjulian
84652419Sjulian/*
84752419Sjulian * Find a node by absolute name. The name should NOT end with ':'
84852419Sjulian * The name "." means "this node" and "[xxx]" means "the node
84952419Sjulian * with ID (ie, at address) xxx".
85052419Sjulian *
85152419Sjulian * Returns the node if found, else NULL.
85270700Sjulian * Eventually should add something faster than a sequential search.
85370784Sjulian * Note it aquires a reference on the node so you can be sure it's still there.
85452419Sjulian */
85552419Sjuliannode_p
85670700Sjulianng_name2noderef(node_p here, const char *name)
85752419Sjulian{
85852722Sjulian	node_p node;
85952722Sjulian	ng_ID_t temp;
86052419Sjulian
86152419Sjulian	/* "." means "this node" */
86270700Sjulian	if (strcmp(name, ".") == 0) {
86370784Sjulian		NG_NODE_REF(here);
86470700Sjulian		return(here);
86570700Sjulian	}
86652419Sjulian
86752419Sjulian	/* Check for name-by-ID */
86852722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
86970700Sjulian		return (ng_ID2noderef(temp));
87052419Sjulian	}
87152419Sjulian
87252419Sjulian	/* Find node by name */
87372200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
87470784Sjulian	LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
87570912Sjulian		if (NG_NODE_IS_VALID(node)
87670912Sjulian		&& NG_NODE_HAS_NAME(node)
87770912Sjulian		&& (strcmp(NG_NODE_NAME(node), name) == 0)) {
87852419Sjulian			break;
87970912Sjulian		}
88052419Sjulian	}
88170700Sjulian	if (node)
88270784Sjulian		NG_NODE_REF(node);
88372200Sbmilekic	mtx_unlock(&ng_nodelist_mtx);
88452419Sjulian	return (node);
88552419Sjulian}
88652419Sjulian
88752419Sjulian/*
88852722Sjulian * Decode a ID name, eg. "[f03034de]". Returns 0 if the
88952722Sjulian * string is not valid, otherwise returns the value.
89052419Sjulian */
89152722Sjulianstatic ng_ID_t
89252419Sjulianng_decodeidname(const char *name)
89352419Sjulian{
89452816Sarchie	const int len = strlen(name);
89553648Sarchie	char *eptr;
89652816Sarchie	u_long val;
89752419Sjulian
89852816Sarchie	/* Check for proper length, brackets, no leading junk */
89970912Sjulian	if ((len < 3)
90070912Sjulian	|| (name[0] != '[')
90170912Sjulian	|| (name[len - 1] != ']')
90270912Sjulian	|| (!isxdigit(name[1]))) {
90370912Sjulian		return ((ng_ID_t)0);
90470912Sjulian	}
90552419Sjulian
90652816Sarchie	/* Decode number */
90752816Sarchie	val = strtoul(name + 1, &eptr, 16);
90870912Sjulian	if ((eptr - name != len - 1)
90970912Sjulian	|| (val == ULONG_MAX)
91070912Sjulian	|| (val == 0)) {
91153042Sjulian		return ((ng_ID_t)0);
91270912Sjulian	}
91352816Sarchie	return (ng_ID_t)val;
91452419Sjulian}
91552419Sjulian
91652419Sjulian/*
91752419Sjulian * Remove a name from a node. This should only be called
91852419Sjulian * when shutting down and removing the node.
91971849Sjulian * IF we allow name changing this may be more resurected.
92052419Sjulian */
92152419Sjulianvoid
92252419Sjulianng_unname(node_p node)
92352419Sjulian{
92452419Sjulian}
92552419Sjulian
92652419Sjulian/************************************************************************
92752419Sjulian			Hook routines
92852419Sjulian Names are not optional. Hooks are always connected, except for a
92970939Sjulian brief moment within these routines. On invalidation or during creation
93070939Sjulian they are connected to the 'dead' hook.
93152419Sjulian************************************************************************/
93252419Sjulian
93352419Sjulian/*
93452419Sjulian * Remove a hook reference
93552419Sjulian */
93670784Sjulianvoid
93752419Sjulianng_unref_hook(hook_p hook)
93852419Sjulian{
93970784Sjulian	int     v;
94071047Sjulian
94171047Sjulian	if (hook == &ng_deadhook) {
94271047Sjulian		return;
94371047Sjulian	}
94470784Sjulian	do {
94570784Sjulian		v = hook->hk_refs;
94670784Sjulian	} while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1));
94769519Sjulian
94870784Sjulian	if (v == 1) { /* we were the last */
94971047Sjulian		if (_NG_HOOK_NODE(hook)) { /* it'll probably be ng_deadnode */
95071047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
95170784Sjulian			hook->hk_node = NULL;
95270700Sjulian		}
95370700Sjulian		NG_FREE_HOOK(hook);
95470700Sjulian	}
95552419Sjulian}
95652419Sjulian
95752419Sjulian/*
95852419Sjulian * Add an unconnected hook to a node. Only used internally.
95970939Sjulian * Assumes node is locked. (XXX not yet true )
96052419Sjulian */
96152419Sjulianstatic int
96252419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
96352419Sjulian{
96452419Sjulian	hook_p hook;
96552419Sjulian	int error = 0;
96652419Sjulian
96752419Sjulian	/* Check that the given name is good */
96852419Sjulian	if (name == NULL) {
96971047Sjulian		TRAP_ERROR();
97052419Sjulian		return (EINVAL);
97152419Sjulian	}
97254096Sarchie	if (ng_findhook(node, name) != NULL) {
97371047Sjulian		TRAP_ERROR();
97454096Sarchie		return (EEXIST);
97552419Sjulian	}
97652419Sjulian
97752419Sjulian	/* Allocate the hook and link it up */
97870784Sjulian	NG_ALLOC_HOOK(hook);
97952419Sjulian	if (hook == NULL) {
98071047Sjulian		TRAP_ERROR();
98152419Sjulian		return (ENOMEM);
98252419Sjulian	}
98370939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
98470784Sjulian	hook->hk_flags = HK_INVALID;
98570939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
98670784Sjulian	hook->hk_node = node;
98770784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
98852419Sjulian
98970939Sjulian	/* Set hook name */
99070939Sjulian	strncpy(NG_HOOK_NAME(hook), name, NG_HOOKLEN);
99170939Sjulian
99270939Sjulian	/*
99370939Sjulian	 * Check if the node type code has something to say about it
99470939Sjulian	 * If it fails, the unref of the hook will also unref the node.
99570939Sjulian	 */
99670935Sjulian	if (node->nd_type->newhook != NULL) {
99770935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
99870784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
99970700Sjulian			return (error);
100070700Sjulian		}
100170935Sjulian	}
100252419Sjulian	/*
100352419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
100452419Sjulian	 * We'll ask again later when we actually connect the hooks.
100552419Sjulian	 */
100670784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
100770784Sjulian	node->nd_numhooks++;
100870939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
100952419Sjulian
101052419Sjulian	if (hookp)
101152419Sjulian		*hookp = hook;
101270939Sjulian	return (0);
101352419Sjulian}
101452419Sjulian
101552419Sjulian/*
101654096Sarchie * Find a hook
101754096Sarchie *
101854096Sarchie * Node types may supply their own optimized routines for finding
101954096Sarchie * hooks.  If none is supplied, we just do a linear search.
102070939Sjulian * XXX Possibly we should add a reference to the hook?
102154096Sarchie */
102254096Sarchiehook_p
102354096Sarchieng_findhook(node_p node, const char *name)
102454096Sarchie{
102554096Sarchie	hook_p hook;
102654096Sarchie
102770784Sjulian	if (node->nd_type->findhook != NULL)
102870784Sjulian		return (*node->nd_type->findhook)(node, name);
102970784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
103070912Sjulian		if (NG_HOOK_IS_VALID(hook)
103170917Sarchie		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
103254096Sarchie			return (hook);
103354096Sarchie	}
103454096Sarchie	return (NULL);
103554096Sarchie}
103654096Sarchie
103754096Sarchie/*
103852419Sjulian * Destroy a hook
103952419Sjulian *
104052419Sjulian * As hooks are always attached, this really destroys two hooks.
104152419Sjulian * The one given, and the one attached to it. Disconnect the hooks
104270939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
104370939Sjulian * hook so that it can still exist after we depart. We then
104470939Sjulian * send the peer its own destroy message. This ensures that we only
104570939Sjulian * interact with the peer's structures when it is locked processing that
104670939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
104770939Sjulian * the peer hook and node are still going to exist until
104870939Sjulian * we are finished there as the hook holds a ref on the node.
104970939Sjulian * We run this same code again on the peer hook, but that time it is already
105070939Sjulian * attached to the 'dead' hook.
105171047Sjulian *
105271047Sjulian * This routine is called at all stages of hook creation
105371047Sjulian * on error detection and must be able to handle any such stage.
105452419Sjulian */
105552419Sjulianvoid
105652419Sjulianng_destroy_hook(hook_p hook)
105752419Sjulian{
105870784Sjulian	hook_p peer = NG_HOOK_PEER(hook);
105971047Sjulian	node_p node = NG_HOOK_NODE(hook);
106052419Sjulian
106171047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
106271047Sjulian		printf("ng_destroy_hook called on deadhook\n");
106371047Sjulian		return;
106471047Sjulian	}
106570784Sjulian	hook->hk_flags |= HK_INVALID;		/* as soon as possible */
106670939Sjulian	if (peer && (peer != &ng_deadhook)) {
106770939Sjulian		/*
106870939Sjulian		 * Set the peer to point to ng_deadhook
106970939Sjulian		 * from this moment on we are effectively independent it.
107070939Sjulian		 * send it an rmhook message of it's own.
107170939Sjulian		 */
107270939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
107370939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
107471047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
107571047Sjulian			/*
107671047Sjulian			 * If it's already divorced from a node,
107771047Sjulian			 * just free it.
107871047Sjulian			 */
107971047Sjulian			/* nothing */
108071047Sjulian		} else {
108171047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
108271047Sjulian		}
108370942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
108470942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
108552419Sjulian	}
108652419Sjulian
108752419Sjulian	/*
108852419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
108952419Sjulian	 * in case the disconnection results in node shutdown.
109052419Sjulian	 */
109171047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
109271047Sjulian		return;
109371047Sjulian	}
109470784Sjulian	LIST_REMOVE(hook, hk_hooks);
109570784Sjulian	node->nd_numhooks--;
109670784Sjulian	if (node->nd_type->disconnect) {
109752419Sjulian		/*
109871047Sjulian		 * The type handler may elect to destroy the node so don't
109971047Sjulian		 * trust its existance after this point. (except
110071047Sjulian		 * that we still hold a reference on it. (which we
110171047Sjulian		 * inherrited from the hook we are destroying)
110252419Sjulian		 */
110370784Sjulian		(*node->nd_type->disconnect) (hook);
110452419Sjulian	}
110571047Sjulian
110671047Sjulian	/*
110771047Sjulian	 * Note that because we will point to ng_deadnode, the original node
110871047Sjulian	 * is not decremented automatically so we do that manually.
110971047Sjulian	 */
111071047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
111171047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
111271047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
111352419Sjulian}
111452419Sjulian
111552419Sjulian/*
111652419Sjulian * Take two hooks on a node and merge the connection so that the given node
111752419Sjulian * is effectively bypassed.
111852419Sjulian */
111952419Sjulianint
112052419Sjulianng_bypass(hook_p hook1, hook_p hook2)
112152419Sjulian{
112270784Sjulian	if (hook1->hk_node != hook2->hk_node) {
112371047Sjulian		TRAP_ERROR();
112452419Sjulian		return (EINVAL);
112570784Sjulian	}
112670784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
112770784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
112852419Sjulian
112970939Sjulian	hook1->hk_peer = &ng_deadhook;
113070939Sjulian	hook2->hk_peer = &ng_deadhook;
113170939Sjulian
113252419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
113352419Sjulian	ng_destroy_hook(hook1);
113452419Sjulian	ng_destroy_hook(hook2);
113552419Sjulian	return (0);
113652419Sjulian}
113752419Sjulian
113852419Sjulian/*
113952419Sjulian * Install a new netgraph type
114052419Sjulian */
114152419Sjulianint
114252419Sjulianng_newtype(struct ng_type *tp)
114352419Sjulian{
114452419Sjulian	const size_t namelen = strlen(tp->name);
114552419Sjulian
114652419Sjulian	/* Check version and type name fields */
114770159Sjulian	if ((tp->version != NG_ABI_VERSION)
114870159Sjulian	|| (namelen == 0)
114970159Sjulian	|| (namelen > NG_TYPELEN)) {
115071047Sjulian		TRAP_ERROR();
115152419Sjulian		return (EINVAL);
115252419Sjulian	}
115352419Sjulian
115452419Sjulian	/* Check for name collision */
115552419Sjulian	if (ng_findtype(tp->name) != NULL) {
115671047Sjulian		TRAP_ERROR();
115752419Sjulian		return (EEXIST);
115852419Sjulian	}
115952419Sjulian
116070700Sjulian
116152419Sjulian	/* Link in new type */
116272200Sbmilekic	mtx_lock(&ng_typelist_mtx);
116370700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
116471603Sjulian	tp->refs = 1;	/* first ref is linked list */
116572200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
116652419Sjulian	return (0);
116752419Sjulian}
116852419Sjulian
116952419Sjulian/*
117080222Sjulian * unlink a netgraph type
117180222Sjulian * If no examples exist
117280222Sjulian */
117380222Sjulianint
117480222Sjulianng_rmtype(struct ng_type *tp)
117580222Sjulian{
117680222Sjulian	/* Check for name collision */
117780222Sjulian	if (tp->refs != 1) {
117880222Sjulian		TRAP_ERROR();
117980222Sjulian		return (EBUSY);
118080222Sjulian	}
118180222Sjulian
118280222Sjulian	/* Unlink type */
118380222Sjulian	mtx_lock(&ng_typelist_mtx);
118480222Sjulian	LIST_REMOVE(tp, types);
118580222Sjulian	mtx_unlock(&ng_typelist_mtx);
118680222Sjulian	return (0);
118780222Sjulian}
118880222Sjulian
118980222Sjulian/*
119052419Sjulian * Look for a type of the name given
119152419Sjulian */
119252419Sjulianstruct ng_type *
119352419Sjulianng_findtype(const char *typename)
119452419Sjulian{
119552419Sjulian	struct ng_type *type;
119652419Sjulian
119772200Sbmilekic	mtx_lock(&ng_typelist_mtx);
119870700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
119952419Sjulian		if (strcmp(type->name, typename) == 0)
120052419Sjulian			break;
120152419Sjulian	}
120272200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
120352419Sjulian	return (type);
120452419Sjulian}
120552419Sjulian
120652419Sjulian/************************************************************************
120752419Sjulian			Composite routines
120852419Sjulian************************************************************************/
120952419Sjulian/*
121071047Sjulian * Connect two nodes using the specified hooks, using queued functions.
121152419Sjulian */
121271849Sjulianstatic void
121371047Sjulianng_con_part3(node_p node, hook_p hook, void *arg1, int arg2)
121452419Sjulian{
121552419Sjulian
121671047Sjulian	/*
121771047Sjulian	 * When we run, we know that the node 'node' is locked for us.
121871047Sjulian	 * Our caller has a reference on the hook.
121971047Sjulian	 * Our caller has a reference on the node.
122071047Sjulian	 * (In this case our caller is ng_apply_item() ).
122171047Sjulian	 * The peer hook has a reference on the hook.
122271849Sjulian	 * We are all set up except for the final call to the node, and
122371849Sjulian	 * the clearing of the INVALID flag.
122471047Sjulian	 */
122571047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
122671047Sjulian		/*
122771047Sjulian		 * The node must have been freed again since we last visited
122871047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
122971047Sjulian		 * We should just release our references and
123071047Sjulian		 * free anything we can think of.
123171047Sjulian		 * Since we know it's been destroyed, and it's our caller
123271047Sjulian		 * that holds the references, just return.
123371047Sjulian		 */
123471849Sjulian		return ;
123552419Sjulian	}
123671047Sjulian	if (hook->hk_node->nd_type->connect) {
123771849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
123871047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
123971849Sjulian			printf("failed in ng_con_part3()\n");
124071849Sjulian			return ;
124171047Sjulian		}
124252419Sjulian	}
124371047Sjulian	/*
124471047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
124571047Sjulian	 * to separate out 'create' and 'invalid' flags.
124671047Sjulian	 * should only set flags on hooks we have locked under our node.
124771047Sjulian	 */
124871047Sjulian	hook->hk_flags &= ~HK_INVALID;
124971849Sjulian	return ;
125071047Sjulian}
125152419Sjulian
125271849Sjulianstatic void
125371047Sjulianng_con_part2(node_p node, hook_p hook, void *arg1, int arg2)
125471047Sjulian{
125571047Sjulian
125652419Sjulian	/*
125771047Sjulian	 * When we run, we know that the node 'node' is locked for us.
125871047Sjulian	 * Our caller has a reference on the hook.
125971047Sjulian	 * Our caller has a reference on the node.
126071047Sjulian	 * (In this case our caller is ng_apply_item() ).
126171047Sjulian	 * The peer hook has a reference on the hook.
126271047Sjulian	 * our node pointer points to the 'dead' node.
126371047Sjulian	 * First check the hook name is unique.
126471849Sjulian	 * Should not happen because we checked before queueing this.
126552419Sjulian	 */
126671047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
126771047Sjulian		TRAP_ERROR();
126871047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
126971849Sjulian		printf("failed in ng_con_part2()\n");
127071849Sjulian		return ;
127171047Sjulian	}
127270939Sjulian	/*
127371047Sjulian	 * Check if the node type code has something to say about it
127471047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
127571047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
127671047Sjulian	 * The peer hook will also be destroyed.
127770939Sjulian	 */
127871047Sjulian	if (node->nd_type->newhook != NULL) {
127971849Sjulian		if ((*node->nd_type->newhook)(node, hook, hook->hk_name)) {
128071047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
128171849Sjulian			printf("failed in ng_con_part2()\n");
128271849Sjulian			return ;
128371047Sjulian		}
128471047Sjulian	}
128571047Sjulian
128671047Sjulian	/*
128771047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
128871047Sjulian	 * We'll ask again later when we actually connect the hooks.
128971047Sjulian	 */
129071047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
129171047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
129271047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
129371047Sjulian	node->nd_numhooks++;
129471047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
129571047Sjulian
129671047Sjulian	/*
129771047Sjulian	 * We now have a symetrical situation, where both hooks have been
129874078Sjulian	 * linked to their nodes, the newhook methods have been called
129971047Sjulian	 * And the references are all correct. The hooks are still marked
130071047Sjulian	 * as invalid, as we have not called the 'connect' methods
130171047Sjulian	 * yet.
130271047Sjulian	 * We can call the local one immediatly as we have the
130371047Sjulian	 * node locked, but we need to queue the remote one.
130471047Sjulian	 */
130571047Sjulian	if (hook->hk_node->nd_type->connect) {
130671849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
130771047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
130871849Sjulian			printf("failed in ng_con_part2(A)\n");
130971849Sjulian			return ;
131071047Sjulian		}
131171047Sjulian	}
131271849Sjulian	if (ng_send_fn(hook->hk_peer->hk_node, hook->hk_peer,
131371849Sjulian			&ng_con_part3, arg1, arg2)) {
131471849Sjulian		printf("failed in ng_con_part2(B)");
131571849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
131671849Sjulian		return ;
131771849Sjulian	}
131871047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
131971849Sjulian	return ;
132052419Sjulian}
132152419Sjulian
132252419Sjulian/*
132371047Sjulian * Connect this node with another node. We assume that this node is
132471047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
132552419Sjulian */
132671047Sjulianstatic int
132752419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
132852419Sjulian{
132952419Sjulian	int     error;
133052419Sjulian	hook_p  hook;
133152419Sjulian	hook_p  hook2;
133252419Sjulian
133371849Sjulian	if (ng_findhook(node2, name2) != NULL) {
133471849Sjulian		return(EEXIST);
133571849Sjulian	}
133670939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
133752419Sjulian		return (error);
133871047Sjulian	/* Allocate the other hook and link it up */
133971047Sjulian	NG_ALLOC_HOOK(hook2);
134071047Sjulian	if (hook == NULL) {
134171047Sjulian		TRAP_ERROR();
134271047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
134371047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
134471047Sjulian		return (ENOMEM);
134571047Sjulian	}
134671047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
134771047Sjulian	hook2->hk_flags = HK_INVALID;
134871047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
134971047Sjulian	hook->hk_peer = hook2;
135071047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
135171047Sjulian	NG_HOOK_REF(hook2);
135271047Sjulian	hook2->hk_node = &ng_deadnode;
135371047Sjulian	strncpy(NG_HOOK_NAME(hook2), name2, NG_HOOKLEN);
135471047Sjulian
135571047Sjulian	/*
135671047Sjulian	 * Queue the function above.
135771047Sjulian	 * Procesing continues in that function in the lock context of
135871047Sjulian	 * the other node.
135971047Sjulian	 */
136071849Sjulian	ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0);
136171047Sjulian
136271047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
136371047Sjulian	NG_HOOK_UNREF(hook2);
136471849Sjulian	return (0);
136571047Sjulian}
136671047Sjulian
136771047Sjulian/*
136871047Sjulian * Make a peer and connect.
136971047Sjulian * We assume that the local node is locked.
137071047Sjulian * The new node probably doesn't need a lock until
137171047Sjulian * it has a hook, because it cannot really have any work until then,
137271047Sjulian * but we should think about it a bit more.
137371047Sjulian *
137471047Sjulian * The problem may come if the other node also fires up
137571047Sjulian * some hardware or a timer or some other source of activation,
137671047Sjulian * also it may already get a command msg via it's ID.
137771047Sjulian *
137871047Sjulian * We could use the same method as ng_con_nodes() but we'd have
137971047Sjulian * to add ability to remove the node when failing. (Not hard, just
138071047Sjulian * make arg1 point to the node to remove).
138171047Sjulian * Unless of course we just ignore failure to connect and leave
138271047Sjulian * an unconnected node?
138371047Sjulian */
138471047Sjulianstatic int
138571047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
138671047Sjulian{
138771047Sjulian	node_p  node2;
138871047Sjulian	hook_p  hook1;
138971047Sjulian	hook_p  hook2;
139071047Sjulian	int     error;
139171047Sjulian
139271047Sjulian	if ((error = ng_make_node(type, &node2))) {
139352419Sjulian		return (error);
139452419Sjulian	}
139570939Sjulian
139671047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
139771849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
139871047Sjulian		return (error);
139971047Sjulian	}
140071047Sjulian
140171047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
140271849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
140371047Sjulian		ng_destroy_hook(hook1);
140471047Sjulian		NG_HOOK_UNREF(hook1);
140571047Sjulian		return (error);
140671047Sjulian	}
140771047Sjulian
140870939Sjulian	/*
140971047Sjulian	 * Actually link the two hooks together.
141071047Sjulian	 */
141171047Sjulian	hook1->hk_peer = hook2;
141271047Sjulian	hook2->hk_peer = hook1;
141371047Sjulian
141471047Sjulian	/* Each hook is referenced by the other */
141571047Sjulian	NG_HOOK_REF(hook1);
141671047Sjulian	NG_HOOK_REF(hook2);
141771047Sjulian
141871047Sjulian	/* Give each node the opportunity to veto the pending connection */
141971047Sjulian	if (hook1->hk_node->nd_type->connect) {
142071047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
142171047Sjulian	}
142271047Sjulian
142371047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
142471047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
142571047Sjulian
142671047Sjulian	}
142771047Sjulian
142871047Sjulian	/*
142970939Sjulian	 * drop the references we were holding on the two hooks.
143070939Sjulian	 */
143171047Sjulian	if (error) {
143271047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
143371849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
143471047Sjulian	} else {
143571047Sjulian		/* As a last act, allow the hooks to be used */
143671047Sjulian		hook1->hk_flags &= ~HK_INVALID;
143771047Sjulian		hook2->hk_flags &= ~HK_INVALID;
143871047Sjulian	}
143971047Sjulian	NG_HOOK_UNREF(hook1);
144070939Sjulian	NG_HOOK_UNREF(hook2);
144170939Sjulian	return (error);
144252419Sjulian}
144371047Sjulian
144470700Sjulian/************************************************************************
144570700Sjulian		Utility routines to send self messages
144670700Sjulian************************************************************************/
144770700Sjulian
144871849Sjulian/* Shut this node down as soon as everyone is clear of it */
144971849Sjulian/* Should add arg "immediatly" to jump the queue */
145070700Sjulianint
145171849Sjulianng_rmnode_self(node_p node)
145270700Sjulian{
145371849Sjulian	int		error;
145452419Sjulian
145571849Sjulian	if (node == &ng_deadnode)
145671849Sjulian		return (0);
145772053Sjulian	node->nd_flags |= NG_INVALID;
145871849Sjulian	if (node->nd_flags & NG_CLOSING)
145971849Sjulian		return (0);
146070700Sjulian
146171849Sjulian	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
146271849Sjulian	return (error);
146370700Sjulian}
146470700Sjulian
146571849Sjulianstatic void
146671047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
146771047Sjulian{
146871047Sjulian	ng_destroy_hook(hook);
146971849Sjulian	return ;
147071047Sjulian}
147171047Sjulian
147270935Sjulianint
147370935Sjulianng_rmhook_self(hook_p hook)
147470935Sjulian{
147571047Sjulian	int		error;
147670935Sjulian	node_p node = NG_HOOK_NODE(hook);
147770935Sjulian
147871047Sjulian	if (node == &ng_deadnode)
147971047Sjulian		return (0);
148071047Sjulian
148171047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
148271047Sjulian	return (error);
148370935Sjulian}
148470935Sjulian
148570700Sjulian/***********************************************************************
148652419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
148752419Sjulian *
148852419Sjulian * Such a string can refer to a specific node or a specific hook
148952419Sjulian * on a specific node, depending on how you look at it. In the
149052419Sjulian * latter case, the PATH component must not end in a dot.
149152419Sjulian *
149252419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
149352419Sjulian * of hook names separated by dots. This breaks out the original
149452419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
149552419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
149652419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
149752419Sjulian *
149852419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
149970700Sjulian ***********************************************************************/
150052419Sjulianint
150152419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
150252419Sjulian{
150352419Sjulian	char   *node, *path, *hook;
150452419Sjulian	int     k;
150552419Sjulian
150652419Sjulian	/*
150752419Sjulian	 * Extract absolute NODE, if any
150852419Sjulian	 */
150952419Sjulian	for (path = addr; *path && *path != ':'; path++);
151052419Sjulian	if (*path) {
151152419Sjulian		node = addr;	/* Here's the NODE */
151252419Sjulian		*path++ = '\0';	/* Here's the PATH */
151352419Sjulian
151452419Sjulian		/* Node name must not be empty */
151552419Sjulian		if (!*node)
151652419Sjulian			return -1;
151752419Sjulian
151852419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
151952419Sjulian		if (strcmp(node, ".") != 0) {
152052419Sjulian			for (k = 0; node[k]; k++)
152152419Sjulian				if (node[k] == '.')
152252419Sjulian					return -1;
152352419Sjulian		}
152452419Sjulian	} else {
152552419Sjulian		node = NULL;	/* No absolute NODE */
152652419Sjulian		path = addr;	/* Here's the PATH */
152752419Sjulian	}
152852419Sjulian
152952419Sjulian	/* Snoop for illegal characters in PATH */
153052419Sjulian	for (k = 0; path[k]; k++)
153152419Sjulian		if (path[k] == ':')
153252419Sjulian			return -1;
153352419Sjulian
153452419Sjulian	/* Check for no repeated dots in PATH */
153552419Sjulian	for (k = 0; path[k]; k++)
153652419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
153752419Sjulian			return -1;
153852419Sjulian
153952419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
154052419Sjulian	if (path[0] == '.')
154152419Sjulian		path++;
154252419Sjulian	if (*path && path[strlen(path) - 1] == '.')
154352419Sjulian		path[strlen(path) - 1] = 0;
154452419Sjulian
154552419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
154652419Sjulian	if (*path) {
154752419Sjulian		for (hook = path, k = 0; path[k]; k++)
154852419Sjulian			if (path[k] == '.') {
154952419Sjulian				hook = NULL;
155052419Sjulian				break;
155152419Sjulian			}
155252419Sjulian	} else
155352419Sjulian		path = hook = NULL;
155452419Sjulian
155552419Sjulian	/* Done */
155652419Sjulian	if (nodep)
155752419Sjulian		*nodep = node;
155852419Sjulian	if (pathp)
155952419Sjulian		*pathp = path;
156052419Sjulian	if (hookp)
156152419Sjulian		*hookp = hook;
156252419Sjulian	return (0);
156352419Sjulian}
156452419Sjulian
156552419Sjulian/*
156652419Sjulian * Given a path, which may be absolute or relative, and a starting node,
156770700Sjulian * return the destination node.
156852419Sjulian */
156952419Sjulianint
157070700Sjulianng_path2noderef(node_p here, const char *address,
157170700Sjulian				node_p *destp, hook_p *lasthook)
157252419Sjulian{
157352419Sjulian	char    fullpath[NG_PATHLEN + 1];
157452419Sjulian	char   *nodename, *path, pbuf[2];
157570700Sjulian	node_p  node, oldnode;
157652419Sjulian	char   *cp;
157759728Sjulian	hook_p hook = NULL;
157852419Sjulian
157952419Sjulian	/* Initialize */
158070784Sjulian	if (destp == NULL) {
158171047Sjulian		TRAP_ERROR();
158252419Sjulian		return EINVAL;
158370784Sjulian	}
158452419Sjulian	*destp = NULL;
158552419Sjulian
158652419Sjulian	/* Make a writable copy of address for ng_path_parse() */
158752419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
158852419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
158952419Sjulian
159052419Sjulian	/* Parse out node and sequence of hooks */
159152419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
159271047Sjulian		TRAP_ERROR();
159352419Sjulian		return EINVAL;
159452419Sjulian	}
159552419Sjulian	if (path == NULL) {
159652419Sjulian		pbuf[0] = '.';	/* Needs to be writable */
159752419Sjulian		pbuf[1] = '\0';
159852419Sjulian		path = pbuf;
159952419Sjulian	}
160052419Sjulian
160170700Sjulian	/*
160270700Sjulian	 * For an absolute address, jump to the starting node.
160370700Sjulian	 * Note that this holds a reference on the node for us.
160470700Sjulian	 * Don't forget to drop the reference if we don't need it.
160570700Sjulian	 */
160652419Sjulian	if (nodename) {
160770700Sjulian		node = ng_name2noderef(here, nodename);
160852419Sjulian		if (node == NULL) {
160971047Sjulian			TRAP_ERROR();
161052419Sjulian			return (ENOENT);
161152419Sjulian		}
161270700Sjulian	} else {
161370700Sjulian		if (here == NULL) {
161471047Sjulian			TRAP_ERROR();
161570700Sjulian			return (EINVAL);
161670700Sjulian		}
161752419Sjulian		node = here;
161870784Sjulian		NG_NODE_REF(node);
161970700Sjulian	}
162052419Sjulian
162170700Sjulian	/*
162270700Sjulian	 * Now follow the sequence of hooks
162370700Sjulian	 * XXX
162470700Sjulian	 * We actually cannot guarantee that the sequence
162570700Sjulian	 * is not being demolished as we crawl along it
162670700Sjulian	 * without extra-ordinary locking etc.
162770700Sjulian	 * So this is a bit dodgy to say the least.
162870700Sjulian	 * We can probably hold up some things by holding
162970700Sjulian	 * the nodelist mutex for the time of this
163070700Sjulian	 * crawl if we wanted.. At least that way we wouldn't have to
163170700Sjulian	 * worry about the nodes dissappearing, but the hooks would still
163270700Sjulian	 * be a problem.
163370700Sjulian	 */
163452419Sjulian	for (cp = path; node != NULL && *cp != '\0'; ) {
163552419Sjulian		char *segment;
163652419Sjulian
163752419Sjulian		/*
163852419Sjulian		 * Break out the next path segment. Replace the dot we just
163952419Sjulian		 * found with a NUL; "cp" points to the next segment (or the
164052419Sjulian		 * NUL at the end).
164152419Sjulian		 */
164252419Sjulian		for (segment = cp; *cp != '\0'; cp++) {
164352419Sjulian			if (*cp == '.') {
164452419Sjulian				*cp++ = '\0';
164552419Sjulian				break;
164652419Sjulian			}
164752419Sjulian		}
164852419Sjulian
164952419Sjulian		/* Empty segment */
165052419Sjulian		if (*segment == '\0')
165152419Sjulian			continue;
165252419Sjulian
165352419Sjulian		/* We have a segment, so look for a hook by that name */
165454096Sarchie		hook = ng_findhook(node, segment);
165552419Sjulian
165652419Sjulian		/* Can't get there from here... */
165752419Sjulian		if (hook == NULL
165870784Sjulian		    || NG_HOOK_PEER(hook) == NULL
165970784Sjulian		    || NG_HOOK_NOT_VALID(hook)
166070784Sjulian		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
166171047Sjulian			TRAP_ERROR();
166270784Sjulian			NG_NODE_UNREF(node);
166370784Sjulian#if 0
166470784Sjulian			printf("hooknotvalid %s %s %d %d %d %d ",
166570784Sjulian					path,
166670784Sjulian					segment,
166770784Sjulian					hook == NULL,
166870784Sjulian		     			NG_HOOK_PEER(hook) == NULL,
166970784Sjulian		     			NG_HOOK_NOT_VALID(hook),
167070784Sjulian		     			NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
167170784Sjulian#endif
167252419Sjulian			return (ENOENT);
167352419Sjulian		}
167452419Sjulian
167570700Sjulian		/*
167670700Sjulian		 * Hop on over to the next node
167770700Sjulian		 * XXX
167870700Sjulian		 * Big race conditions here as hooks and nodes go away
167970700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
168070700Sjulian		 * instead of the direct hook in this crawl?
168170700Sjulian		 */
168270700Sjulian		oldnode = node;
168370784Sjulian		if ((node = NG_PEER_NODE(hook)))
168470784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
168570784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
168670784Sjulian		if (NG_NODE_NOT_VALID(node)) {
168770784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
168870700Sjulian			node = NULL;
168970700Sjulian		}
169052419Sjulian	}
169152419Sjulian
169252419Sjulian	/* If node somehow missing, fail here (probably this is not needed) */
169352419Sjulian	if (node == NULL) {
169471047Sjulian		TRAP_ERROR();
169552419Sjulian		return (ENXIO);
169652419Sjulian	}
169752419Sjulian
169852419Sjulian	/* Done */
169952419Sjulian	*destp = node;
170059900Sarchie	if (lasthook != NULL)
170170784Sjulian		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
170252419Sjulian	return (0);
170352419Sjulian}
170452419Sjulian
170570700Sjulian/***************************************************************\
170670700Sjulian* Input queue handling.
170770700Sjulian* All activities are submitted to the node via the input queue
170870700Sjulian* which implements a multiple-reader/single-writer gate.
170970700Sjulian* Items which cannot be handled immeditly are queued.
171070700Sjulian*
171170700Sjulian* read-write queue locking inline functions			*
171270700Sjulian\***************************************************************/
171370700Sjulian
171470700Sjulianstatic __inline item_p ng_dequeue(struct ng_queue * ngq);
171570700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq,
171670700Sjulian					item_p  item);
171770700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq,
171870700Sjulian					item_p  item);
171970700Sjulianstatic __inline void	ng_leave_read(struct ng_queue * ngq);
172070700Sjulianstatic __inline void	ng_leave_write(struct ng_queue * ngq);
172170700Sjulianstatic __inline void	ng_queue_rw(struct ng_queue * ngq,
172270700Sjulian					item_p  item, int rw);
172370700Sjulian
172452419Sjulian/*
172570700Sjulian * Definition of the bits fields in the ng_queue flag word.
172670700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
172770700Sjulian * with them.
172870700Sjulian *
172971902Sjulian * The ordering here may be important! don't shuffle these.
173052419Sjulian */
173170700Sjulian/*-
173270700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
173370700Sjulian                       |
173470700Sjulian                       V
173570700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
173670700Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
173771902Sjulian| |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W|
173871902Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P|
173970700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
174071902Sjulian\___________________________ ____________________________/ | | |
174171902Sjulian                            V                              | | |
174271902Sjulian                  [active reader count]                    | | |
174370700Sjulian                                                           | | |
174471902Sjulian          Read Pending ------------------------------------+ | |
174570700Sjulian                                                             | |
174671902Sjulian          Active Writer -------------------------------------+ |
174770700Sjulian                                                               |
174871902Sjulian          Write Pending ---------------------------------------+
174971902Sjulian
175071902Sjulian
175170700Sjulian*/
175271902Sjulian#define WRITE_PENDING	0x00000001
175371902Sjulian#define WRITER_ACTIVE	0x00000002
175471902Sjulian#define READ_PENDING	0x00000004
175571902Sjulian#define READER_INCREMENT 0x00000008
175671902Sjulian#define READER_MASK	0xfffffff0	/* Not valid if WRITER_ACTIVE is set */
175770700Sjulian#define SAFETY_BARRIER	0x00100000	/* 64K items queued should be enough */
175871902Sjulian
175971902Sjulian/* Defines of more elaborate states on the queue */
176071902Sjulian/* Mask of bits a read cares about */
176171902Sjulian#define NGQ_RMASK	(WRITE_PENDING|WRITER_ACTIVE|READ_PENDING)
176271902Sjulian
176371902Sjulian/* Mask of bits a write cares about */
176471902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
176571902Sjulian
176671902Sjulian/* tests to decide if we could get a read or write off the queue */
176771902Sjulian#define CAN_GET_READ(flag)	((flag & NGQ_RMASK) == READ_PENDING)
176871902Sjulian#define CAN_GET_WRITE(flag)	((flag & NGQ_WMASK) == WRITE_PENDING)
176971902Sjulian
177071902Sjulian/* Is there a chance of getting ANY work off the queue? */
177171902Sjulian#define CAN_GET_WORK(flag)	(CAN_GET_READ(flag) || CAN_GET_WRITE(flag))
177271902Sjulian
177370700Sjulian/*
177470700Sjulian * Taking into account the current state of the queue and node, possibly take
177570700Sjulian * the next entry off the queue and return it. Return NULL if there was
177670700Sjulian * nothing we could return, either because there really was nothing there, or
177770700Sjulian * because the node was in a state where it cannot yet process the next item
177870700Sjulian * on the queue.
177970700Sjulian *
178070700Sjulian * This MUST MUST MUST be called with the mutex held.
178170700Sjulian */
178270700Sjulianstatic __inline item_p
178370700Sjulianng_dequeue(struct ng_queue *ngq)
178470700Sjulian{
178570700Sjulian	item_p item;
178670700Sjulian	u_int		add_arg;
178771902Sjulian
178871902Sjulian	if (CAN_GET_READ(ngq->q_flags)) {
178970700Sjulian		/*
179071902Sjulian		 * Head of queue is a reader and we have no write active.
179171902Sjulian		 * We don't care how many readers are already active.
179271902Sjulian		 * Adjust the flags for the item we are about to dequeue.
179371902Sjulian		 * Add the correct increment for the reader count as well.
179470700Sjulian		 */
179571902Sjulian		add_arg = (READER_INCREMENT - READ_PENDING);
179671902Sjulian	} else if (CAN_GET_WRITE(ngq->q_flags)) {
179770700Sjulian		/*
179870700Sjulian		 * There is a pending write, no readers and no active writer.
179970700Sjulian		 * This means we can go ahead with the pending writer. Note
180070700Sjulian		 * the fact that we now have a writer, ready for when we take
180170700Sjulian		 * it off the queue.
180270700Sjulian		 *
180370700Sjulian		 * We don't need to worry about a possible collision with the
180470700Sjulian		 * fasttrack reader.
180570700Sjulian		 *
180670700Sjulian		 * The fasttrack thread may take a long time to discover that we
180770700Sjulian		 * are running so we would have an inconsistent state in the
180870700Sjulian		 * flags for a while. Since we ignore the reader count
180970700Sjulian		 * entirely when the WRITER_ACTIVE flag is set, this should
181070700Sjulian		 * not matter (in fact it is defined that way). If it tests
181170700Sjulian		 * the flag before this operation, the WRITE_PENDING flag
181270700Sjulian		 * will make it fail, and if it tests it later, the
181371902Sjulian		 * WRITER_ACTIVE flag will do the same. If it is SO slow that
181470700Sjulian		 * we have actually completed the operation, and neither flag
181570700Sjulian		 * is set (nor the READ_PENDING) by the time that it tests
181670700Sjulian		 * the flags, then it is actually ok for it to continue. If
181770700Sjulian		 * it completes and we've finished and the read pending is
181870700Sjulian		 * set it still fails.
181970700Sjulian		 *
182070700Sjulian		 * So we can just ignore it,  as long as we can ensure that the
182170700Sjulian		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
182270700Sjulian		 * state is atomic.
182370700Sjulian		 *
182470700Sjulian		 * After failing, first it will be held back by the mutex, then
182570700Sjulian		 * when it can proceed, it will queue its request, then it
182670700Sjulian		 * would arrive at this function. Usually it will have to
182771902Sjulian		 * leave empty handed because the ACTIVE WRITER bit will be
182870700Sjulian		 * set.
182971902Sjulian		 *
183071902Sjulian		 * Adjust the flags for the item we are about to dequeue
183171902Sjulian		 * and for the new active writer.
183270700Sjulian		 */
183371902Sjulian		add_arg = (WRITER_ACTIVE - WRITE_PENDING);
183470700Sjulian		/*
183570700Sjulian		 * We want to write "active writer, no readers " Now go make
183670700Sjulian		 * it true. In fact there may be a number in the readers
183770700Sjulian		 * count but we know it is not true and will be fixed soon.
183870700Sjulian		 * We will fix the flags for the next pending entry in a
183970700Sjulian		 * moment.
184070700Sjulian		 */
184170700Sjulian	} else {
184270700Sjulian		/*
184370700Sjulian		 * We can't dequeue anything.. return and say so. Probably we
184470700Sjulian		 * have a write pending and the readers count is non zero. If
184570700Sjulian		 * we got here because a reader hit us just at the wrong
184670700Sjulian		 * moment with the fasttrack code, and put us in a strange
184770700Sjulian		 * state, then it will be through in just a moment, (as soon
184870700Sjulian		 * as we release the mutex) and keep things moving.
184971902Sjulian		 * Make sure we remove ourselves from the work queue.
185070700Sjulian		 */
185171902Sjulian		ng_worklist_remove(ngq->q_node);
185270700Sjulian		return (0);
185370700Sjulian	}
185452419Sjulian
185570700Sjulian	/*
185670700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
185770700Sjulian	 * pending flags and the next and last pointers.
185870700Sjulian	 */
185970700Sjulian	item = ngq->queue;
186070700Sjulian	ngq->queue = item->el_next;
186170700Sjulian	if (ngq->last == &(item->el_next)) {
186270700Sjulian		/*
186370700Sjulian		 * that was the last entry in the queue so set the 'last
186470700Sjulian		 * pointer up correctly and make sure the pending flags are
186570700Sjulian		 * clear.
186670700Sjulian		 */
186770700Sjulian		ngq->last = &(ngq->queue);
186870700Sjulian		/*
186971902Sjulian		 * Whatever flag was set will be cleared and
187071902Sjulian		 * the new acive field will be set by the add as well,
187171902Sjulian		 * so we don't need to change add_arg.
187271902Sjulian		 * But we know we don't need to be on the work list.
187370700Sjulian		 */
187471902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
187571902Sjulian		ng_worklist_remove(ngq->q_node);
187670700Sjulian	} else {
187771902Sjulian		/*
187871902Sjulian		 * Since there is something on the queue, note what it is
187971902Sjulian		 * in the flags word.
188071902Sjulian		 */
188171047Sjulian		if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
188270700Sjulian			add_arg += READ_PENDING;
188370700Sjulian		} else {
188470700Sjulian			add_arg += WRITE_PENDING;
188570700Sjulian		}
188671902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
188771902Sjulian		/*
188871902Sjulian		 * If we see more doable work, make sure we are
188971902Sjulian		 * on the work queue.
189071902Sjulian		 */
189171902Sjulian		if (CAN_GET_WORK(ngq->q_flags)) {
189271902Sjulian			ng_setisr(ngq->q_node);
189371902Sjulian		}
189470700Sjulian	}
189570700Sjulian	/*
189670700Sjulian	 * We have successfully cleared the old pending flag, set the new one
189770700Sjulian	 * if it is needed, and incremented the appropriate active field.
189871902Sjulian	 * (all in one atomic addition.. )
189970700Sjulian	 */
190070700Sjulian	return (item);
190170700Sjulian}
190252419Sjulian
190370700Sjulian/*
190470700Sjulian * Queue a packet to be picked up by someone else.
190570700Sjulian * We really don't care who, but we can't or don't want to hang around
190670700Sjulian * to process it ourselves. We are probably an interrupt routine..
190770700Sjulian * 1 = writer, 0 = reader
190870700Sjulian */
190970700Sjulian#define NGQRW_R 0
191070700Sjulian#define NGQRW_W 1
191170700Sjulianstatic __inline void
191270700Sjulianng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
191370700Sjulian{
191470700Sjulian	item->el_next = NULL;	/* maybe not needed */
191570700Sjulian	*ngq->last = item;
191670700Sjulian	/*
191770700Sjulian	 * If it was the first item in the queue then we need to
191870700Sjulian	 * set the last pointer and the type flags.
191970700Sjulian	 */
192070700Sjulian	if (ngq->last == &(ngq->queue)) {
192170700Sjulian		/*
192270700Sjulian		 * When called with constants for rw, the optimiser will
192370700Sjulian		 * remove the unneeded branch below.
192470700Sjulian		 */
192570700Sjulian		if (rw == NGQRW_W) {
192670700Sjulian			atomic_add_long(&ngq->q_flags, WRITE_PENDING);
192770700Sjulian		} else {
192870700Sjulian			atomic_add_long(&ngq->q_flags, READ_PENDING);
192970700Sjulian		}
193070700Sjulian	}
193170700Sjulian	ngq->last = &(item->el_next);
193270700Sjulian}
193352419Sjulian
193470700Sjulian
193552419Sjulian/*
193670700Sjulian * This function 'cheats' in that it first tries to 'grab' the use of the
193770700Sjulian * node, without going through the mutex. We can do this becasue of the
193870700Sjulian * semantics of the lock. The semantics include a clause that says that the
193970700Sjulian * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It
194070700Sjulian * also says that the WRITER_ACTIVE flag cannot be set if the readers count
194170700Sjulian * is not zero. Note that this talks about what is valid to SET the
194270700Sjulian * WRITER_ACTIVE flag, because from the moment it is set, the value if the
194370700Sjulian * reader count is immaterial, and not valid. The two 'pending' flags have a
194470700Sjulian * similar effect, in that If they are orthogonal to the two active fields in
194570700Sjulian * how they are set, but if either is set, the attempted 'grab' need to be
194670700Sjulian * backed out because there is earlier work, and we maintain ordering in the
194770700Sjulian * queue. The result of this is that the reader request can try obtain use of
194870700Sjulian * the node with only a single atomic addition, and without any of the mutex
194970700Sjulian * overhead. If this fails the operation degenerates to the same as for other
195070700Sjulian * cases.
195170700Sjulian *
195252419Sjulian */
195370700Sjulianstatic __inline item_p
195470700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item)
195552419Sjulian{
195652419Sjulian
195770700Sjulian	/* ######### Hack alert ######### */
195870700Sjulian	atomic_add_long(&ngq->q_flags, READER_INCREMENT);
195971902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
196070700Sjulian		/* Successfully grabbed node */
196170700Sjulian		return (item);
196270700Sjulian	}
196370700Sjulian	/* undo the damage if we didn't succeed */
196470700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
196570700Sjulian
196670700Sjulian	/* ######### End Hack alert ######### */
196772200Sbmilekic	mtx_lock_spin((&ngq->q_mtx));
196869922Sjulian	/*
196970700Sjulian	 * Try again. Another processor (or interrupt for that matter) may
197070700Sjulian	 * have removed the last queued item that was stopping us from
197170700Sjulian	 * running, between the previous test, and the moment that we took
197270700Sjulian	 * the mutex. (Or maybe a writer completed.)
197369922Sjulian	 */
197471902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
197570700Sjulian		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
197672200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
197770700Sjulian		return (item);
197870700Sjulian	}
197970700Sjulian
198070700Sjulian	/*
198170700Sjulian	 * and queue the request for later.
198270700Sjulian	 */
198371047Sjulian	item->el_flags |= NGQF_READER;
198470700Sjulian	ng_queue_rw(ngq, item, NGQRW_R);
198570700Sjulian
198670700Sjulian	/*
198770700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
198870700Sjulian	 * see if we can dequeue something to run instead.
198970700Sjulian	 */
199070700Sjulian	item = ng_dequeue(ngq);
199172200Sbmilekic	mtx_unlock_spin(&(ngq->q_mtx));
199270700Sjulian	return (item);
199370700Sjulian}
199470700Sjulian
199570700Sjulianstatic __inline item_p
199670700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item)
199770700Sjulian{
199870700Sjulianrestart:
199972200Sbmilekic	mtx_lock_spin(&(ngq->q_mtx));
200070700Sjulian	/*
200170700Sjulian	 * If there are no readers, no writer, and no pending packets, then
200270700Sjulian	 * we can just go ahead. In all other situations we need to queue the
200370700Sjulian	 * request
200470700Sjulian	 */
200571902Sjulian	if ((ngq->q_flags & NGQ_WMASK) == 0) {
200670700Sjulian		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
200772200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
200870700Sjulian		if (ngq->q_flags & READER_MASK) {
200970700Sjulian			/* Collision with fast-track reader */
201071902Sjulian			atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
201170700Sjulian			goto restart;
201269922Sjulian		}
201370700Sjulian		return (item);
201452419Sjulian	}
201552419Sjulian
201670700Sjulian	/*
201770700Sjulian	 * and queue the request for later.
201870700Sjulian	 */
201971047Sjulian	item->el_flags &= ~NGQF_RW;
202070700Sjulian	ng_queue_rw(ngq, item, NGQRW_W);
202170700Sjulian
202270700Sjulian	/*
202370700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
202470700Sjulian	 * see if we can dequeue something to run instead.
202570700Sjulian	 */
202670700Sjulian	item = ng_dequeue(ngq);
202772200Sbmilekic	mtx_unlock_spin(&(ngq->q_mtx));
202870700Sjulian	return (item);
202970700Sjulian}
203070700Sjulian
203170700Sjulianstatic __inline void
203270700Sjulianng_leave_read(struct ng_queue *ngq)
203370700Sjulian{
203470700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
203570700Sjulian}
203670700Sjulian
203770700Sjulianstatic __inline void
203870700Sjulianng_leave_write(struct ng_queue *ngq)
203970700Sjulian{
204070700Sjulian	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
204170700Sjulian}
204270700Sjulian
204370700Sjulianstatic void
204470700Sjulianng_flush_input_queue(struct ng_queue * ngq)
204570700Sjulian{
204670700Sjulian	item_p item;
204770700Sjulian	u_int		add_arg;
204872200Sbmilekic	mtx_lock_spin(&ngq->q_mtx);
204970700Sjulian	for (;;) {
205070700Sjulian		/* Now take a look at what's on the queue */
205170700Sjulian		if (ngq->q_flags & READ_PENDING) {
205270700Sjulian			add_arg = -READ_PENDING;
205370700Sjulian		} else if (ngq->q_flags & WRITE_PENDING) {
205470700Sjulian			add_arg = -WRITE_PENDING;
205570700Sjulian		} else {
205670700Sjulian			break;
205770700Sjulian		}
205870700Sjulian
205970700Sjulian		item = ngq->queue;
206070700Sjulian		ngq->queue = item->el_next;
206170700Sjulian		if (ngq->last == &(item->el_next)) {
206270700Sjulian			ngq->last = &(ngq->queue);
206370700Sjulian		} else {
206471047Sjulian			if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
206570700Sjulian				add_arg += READ_PENDING;
206670700Sjulian			} else {
206770700Sjulian				add_arg += WRITE_PENDING;
206870700Sjulian			}
206970700Sjulian		}
207070700Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
207170700Sjulian
207272200Sbmilekic		mtx_lock_spin(&ngq->q_mtx);
207370700Sjulian		NG_FREE_ITEM(item);
207472200Sbmilekic		mtx_unlock_spin(&ngq->q_mtx);
207570700Sjulian	}
207671902Sjulian	/*
207771902Sjulian	 * Take us off the work queue if we are there.
207871902Sjulian	 * We definatly have no work to be done.
207971902Sjulian	 */
208071902Sjulian	ng_worklist_remove(ngq->q_node);
208172200Sbmilekic	mtx_unlock_spin(&ngq->q_mtx);
208270700Sjulian}
208370700Sjulian
208470700Sjulian/***********************************************************************
208570700Sjulian* Externally visible method for sending or queueing messages or data.
208670700Sjulian***********************************************************************/
208770700Sjulian
208870700Sjulian/*
208971849Sjulian * The module code should have filled out the item correctly by this stage:
209070700Sjulian * Common:
209170700Sjulian *    reference to destination node.
209270700Sjulian *    Reference to destination rcv hook if relevant.
209370700Sjulian * Data:
209470700Sjulian *    pointer to mbuf
209570700Sjulian *    pointer to metadata
209670700Sjulian * Control_Message:
209770700Sjulian *    pointer to msg.
209870700Sjulian *    ID of original sender node. (return address)
209971849Sjulian * Function:
210071849Sjulian *    Function pointer
210171849Sjulian *    void * argument
210271849Sjulian *    integer argument
210370700Sjulian *
210470700Sjulian * The nodes have several routines and macros to help with this task:
210570700Sjulian */
210670700Sjulian
210770700Sjulianint
210870700Sjulianng_snd_item(item_p item, int queue)
210970700Sjulian{
211071849Sjulian	hook_p hook = NGI_HOOK(item);
211171902Sjulian	node_p node = NGI_NODE(item);
211270700Sjulian	int rw;
211370700Sjulian	int error = 0, ierror;
211470700Sjulian	item_p	oitem;
211571902Sjulian	struct ng_queue * ngq = &node->nd_input_queue;
211670700Sjulian
211770784Sjulian#ifdef	NETGRAPH_DEBUG
211870700Sjulian        _ngi_check(item, __FILE__, __LINE__);
211970700Sjulian#endif
212070700Sjulian
212170700Sjulian	if (item == NULL) {
212271047Sjulian		TRAP_ERROR();
212370700Sjulian		return (EINVAL);	/* failed to get queue element */
212470700Sjulian	}
212571902Sjulian	if (node == NULL) {
212670700Sjulian		NG_FREE_ITEM(item);
212771047Sjulian		TRAP_ERROR();
212870700Sjulian		return (EINVAL);	/* No address */
212970700Sjulian	}
213071047Sjulian	switch(item->el_flags & NGQF_TYPE) {
213171047Sjulian	case NGQF_DATA:
213269922Sjulian		/*
213370700Sjulian		 * DATA MESSAGE
213470700Sjulian		 * Delivered to a node via a non-optional hook.
213570700Sjulian		 * Both should be present in the item even though
213670700Sjulian		 * the node is derivable from the hook.
213770700Sjulian		 * References are held on both by the item.
213869922Sjulian		 */
213970700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
214070700Sjulian		if (hook == NULL) {
214170700Sjulian			NG_FREE_ITEM(item);
214271047Sjulian			TRAP_ERROR();
214370700Sjulian			return(EINVAL);
214470700Sjulian		}
214570784Sjulian		if ((NG_HOOK_NOT_VALID(hook))
214670784Sjulian		|| (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) {
214770700Sjulian			NG_FREE_ITEM(item);
214870700Sjulian			return (ENOTCONN);
214969922Sjulian		}
215070784Sjulian		if ((hook->hk_flags & HK_QUEUE)) {
215170700Sjulian			queue = 1;
215270700Sjulian		}
215370700Sjulian		/* By default data is a reader in the locking scheme */
215470700Sjulian		item->el_flags |= NGQF_READER;
215570700Sjulian		rw = NGQRW_R;
215671047Sjulian		break;
215771047Sjulian	case NGQF_MESG:
215870700Sjulian		/*
215970700Sjulian		 * CONTROL MESSAGE
216070700Sjulian		 * Delivered to a node.
216170700Sjulian		 * Hook is optional.
216270700Sjulian		 * References are held by the item on the node and
216370700Sjulian		 * the hook if it is present.
216470700Sjulian		 */
216570784Sjulian		if (hook && (hook->hk_flags & HK_QUEUE)) {
216670700Sjulian			queue = 1;
216770700Sjulian		}
216870700Sjulian		/* Data messages count as writers unles explicitly exempted */
216970700Sjulian		if (NGI_MSG(item)->header.cmd & NGM_READONLY) {
217070700Sjulian			item->el_flags |= NGQF_READER;
217170700Sjulian			rw = NGQRW_R;
217270700Sjulian		} else {
217371047Sjulian			item->el_flags &= ~NGQF_RW;
217470700Sjulian			rw = NGQRW_W;
217570700Sjulian		}
217671047Sjulian		break;
217771047Sjulian	case NGQF_FN:
217871047Sjulian		item->el_flags &= ~NGQF_RW;
217971047Sjulian		rw = NGQRW_W;
218071047Sjulian		break;
218171047Sjulian	default:
218271047Sjulian		NG_FREE_ITEM(item);
218371047Sjulian		TRAP_ERROR();
218471047Sjulian		return (EINVAL);
218569922Sjulian	}
218670700Sjulian	/*
218770700Sjulian	 * If the node specifies single threading, force writer semantics
218870700Sjulian	 * Similarly the node may say one hook always produces writers.
218970700Sjulian	 * These are over-rides.
219070700Sjulian	 */
219171902Sjulian	if ((node->nd_flags & NG_FORCE_WRITER)
219270784Sjulian	|| (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
219370700Sjulian			rw = NGQRW_W;
219471047Sjulian			item->el_flags &= ~NGQF_READER;
219570700Sjulian	}
219670700Sjulian	if (queue) {
219770700Sjulian		/* Put it on the queue for that node*/
219870784Sjulian#ifdef	NETGRAPH_DEBUG
219970700Sjulian        _ngi_check(item, __FILE__, __LINE__);
220070700Sjulian#endif
220172200Sbmilekic		mtx_lock_spin(&(ngq->q_mtx));
220270700Sjulian		ng_queue_rw(ngq, item, rw);
220370700Sjulian		/*
220470700Sjulian		 * If there are active elements then we can rely on
220570700Sjulian		 * them. if not we should not rely on another packet
220670700Sjulian		 * coming here by another path,
220770700Sjulian		 * so it is best to put us in the netisr list.
220871902Sjulian		 * We can take the worklist lock with the node locked
220971902Sjulian		 * BUT NOT THE REVERSE!
221070700Sjulian		 */
221171902Sjulian		if (CAN_GET_WORK(ngq->q_flags)) {
221271902Sjulian			ng_setisr(node);
221370700Sjulian		}
221472200Sbmilekic		mtx_unlock_spin(&(ngq->q_mtx));
221570700Sjulian		return (0);
221670700Sjulian	}
221770700Sjulian	/*
221870700Sjulian	 * Take a queue item and a node and see if we can apply the item to
221970700Sjulian	 * the node. We may end up getting a different item to apply instead.
222070700Sjulian	 * Will allow for a piggyback reply only in the case where
222170700Sjulian	 * there is no queueing.
222270700Sjulian	 */
222369922Sjulian
222470700Sjulian	oitem = item;
222570700Sjulian	/*
222670700Sjulian	 * We already decided how we will be queueud or treated.
222770700Sjulian	 * Try get the appropriate operating permission.
222870700Sjulian	 */
222970700Sjulian 	if (rw == NGQRW_R) {
223070700Sjulian		item = ng_acquire_read(ngq, item);
223170700Sjulian	} else {
223270700Sjulian		item = ng_acquire_write(ngq, item);
223370700Sjulian	}
223452419Sjulian
223570700Sjulian	/*
223670700Sjulian	 * May have come back with a different item.
223770700Sjulian	 * or maybe none at all. The one we started with will
223870700Sjulian	 * have been queued in thises cases.
223970700Sjulian	 */
224070700Sjulian	if (item == NULL) {
224170700Sjulian		return (0);
224270700Sjulian	}
224352419Sjulian
224470784Sjulian#ifdef	NETGRAPH_DEBUG
224570700Sjulian        _ngi_check(item, __FILE__, __LINE__);
224670700Sjulian#endif
224774078Sjulian	/*
224874078Sjulian	 * Take over the reference frm the item.
224974078Sjulian	 * Hold it until the called function returns.
225074078Sjulian	 */
225174078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
225252419Sjulian
225374078Sjulian	ierror = ng_apply_item(node, item); /* drops r/w lock when done */
225474078Sjulian
225570700Sjulian	/* only return an error if it was our initial item.. (compat hack) */
225670700Sjulian	if (oitem == item) {
225770700Sjulian		error = ierror;
225870700Sjulian	}
225970700Sjulian
226052419Sjulian	/*
226174078Sjulian	 * If the node goes away when we remove the reference,
226274078Sjulian	 * whatever we just did caused it.. hwatever we do, DO NOT
226374078Sjulian	 * access the node again!
226474078Sjulian	 */
226574078Sjulian	if (NG_NODE_UNREF(node) == 0) {
226674078Sjulian		return (error);
226774078Sjulian	}
226874078Sjulian
226974078Sjulian	/*
227070700Sjulian	 * Now we've handled the packet we brought, (or a friend of it) let's
227170700Sjulian	 * look for any other packets that may have been queued up. We hold
227270700Sjulian	 * no locks, so if someone puts something in the queue after
227370700Sjulian	 * we check that it is empty, it is their problem
227470700Sjulian	 * to ensure it is processed. If we have the netisr thread cme in here
227570700Sjulian	 * while we still say we have stuff to do, we may get a boost
227670700Sjulian	 * in SMP systems. :-)
227752419Sjulian	 */
227870700Sjulian	for (;;) {
227970700Sjulian		/*
228070700Sjulian		 * dequeue acquires and adjusts the input_queue as it dequeues
228170700Sjulian		 * packets. It acquires the rw lock as needed.
228270700Sjulian		 */
228372200Sbmilekic		mtx_lock_spin(&ngq->q_mtx);
228471902Sjulian		item = ng_dequeue(ngq); /* fixes worklist too*/
228570700Sjulian		if (!item) {
228672200Sbmilekic			mtx_unlock_spin(&ngq->q_mtx);
228771849Sjulian			return (error);
228870700Sjulian		}
228972200Sbmilekic		mtx_unlock_spin(&ngq->q_mtx);
229070700Sjulian
229170700Sjulian		/*
229274078Sjulian		 * Take over the reference frm the item.
229374078Sjulian		 * Hold it until the called function returns.
229474078Sjulian		 */
229574078Sjulian
229674078Sjulian		NGI_GET_NODE(item, node); /* zaps stored node */
229774078Sjulian
229874078Sjulian		/*
229970700Sjulian		 * We have the appropriate lock, so run the item.
230070700Sjulian		 * When finished it will drop the lock accordingly
230170700Sjulian		 */
230274078Sjulian		ierror = ng_apply_item(node, item);
230370700Sjulian
230470700Sjulian		/*
230570700Sjulian		 * only return an error if it was our initial
230670700Sjulian		 * item.. (compat hack)
230770700Sjulian		 */
230870700Sjulian		if (oitem == item) {
230970700Sjulian			error = ierror;
231070700Sjulian		}
231174078Sjulian
231274078Sjulian		/*
231374078Sjulian		 * If the node goes away when we remove the reference,
231474078Sjulian		 * whatever we just did caused it.. hwatever we do, DO NOT
231574078Sjulian		 * access the node again!
231674078Sjulian		 */
231774078Sjulian		if (NG_NODE_UNREF(node) == 0) {
231874078Sjulian			break;
231974078Sjulian		}
232070700Sjulian	}
232171849Sjulian	return (error);
232252419Sjulian}
232352419Sjulian
232452419Sjulian/*
232570700Sjulian * We have an item that was possibly queued somewhere.
232670700Sjulian * It should contain all the information needed
232770700Sjulian * to run it on the appropriate node/hook.
232852419Sjulian */
232952419Sjulianstatic int
233074078Sjulianng_apply_item(node_p node, item_p item)
233152419Sjulian{
233270700Sjulian	hook_p  hook;
233371849Sjulian	int	was_reader = ((item->el_flags & NGQF_RW));
233471849Sjulian	int	error = 0;
233570700Sjulian	ng_rcvdata_t *rcvdata;
233671885Sjulian	ng_rcvmsg_t *rcvmsg;
233752419Sjulian
233871849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
233970784Sjulian#ifdef	NETGRAPH_DEBUG
234070700Sjulian        _ngi_check(item, __FILE__, __LINE__);
234170700Sjulian#endif
234271047Sjulian	switch (item->el_flags & NGQF_TYPE) {
234370700Sjulian	case NGQF_DATA:
234470700Sjulian		/*
234570700Sjulian		 * Check things are still ok as when we were queued.
234670700Sjulian		 */
234770700Sjulian		if ((hook == NULL)
234870784Sjulian		|| NG_HOOK_NOT_VALID(hook)
234971885Sjulian		|| NG_NODE_NOT_VALID(node) ) {
235070700Sjulian			error = EIO;
235170700Sjulian			NG_FREE_ITEM(item);
235271885Sjulian			break;
235370700Sjulian		}
235471885Sjulian		/*
235571885Sjulian		 * If no receive method, just silently drop it.
235671885Sjulian		 * Give preference to the hook over-ride method
235771885Sjulian		 */
235871885Sjulian		if ((!(rcvdata = hook->hk_rcvdata))
235971885Sjulian		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
236071885Sjulian			error = 0;
236171885Sjulian			NG_FREE_ITEM(item);
236271885Sjulian			break;
236371885Sjulian		}
236471885Sjulian		error = (*rcvdata)(hook, item);
236570700Sjulian		break;
236670700Sjulian	case NGQF_MESG:
236770700Sjulian		if (hook) {
236870784Sjulian			if (NG_HOOK_NOT_VALID(hook)) {
236971849Sjulian				/*
237071849Sjulian				 * The hook has been zapped then we can't
237171849Sjulian				 * use it. Immediatly drop its reference.
237271849Sjulian				 * The message may not need it.
237371849Sjulian				 */
237470784Sjulian				NG_HOOK_UNREF(hook);
237570700Sjulian				hook = NULL;
237670700Sjulian			}
237770700Sjulian		}
237870700Sjulian		/*
237970700Sjulian		 * Similarly, if the node is a zombie there is
238070700Sjulian		 * nothing we can do with it, drop everything.
238170700Sjulian		 */
238270784Sjulian		if (NG_NODE_NOT_VALID(node)) {
238371047Sjulian			TRAP_ERROR();
238470700Sjulian			error = EINVAL;
238570700Sjulian			NG_FREE_ITEM(item);
238670700Sjulian		} else {
238770700Sjulian			/*
238870700Sjulian			 * Call the appropriate message handler for the object.
238970700Sjulian			 * It is up to the message handler to free the message.
239070700Sjulian			 * If it's a generic message, handle it generically,
239170700Sjulian			 * otherwise call the type's message handler
239270700Sjulian			 * (if it exists)
239370700Sjulian			 * XXX (race). Remember that a queued message may
239470700Sjulian			 * reference a node or hook that has just been
239570700Sjulian			 * invalidated. It will exist as the queue code
239670700Sjulian			 * is holding a reference, but..
239770700Sjulian			 */
239870700Sjulian
239970700Sjulian			struct ng_mesg *msg = NGI_MSG(item);
240070700Sjulian
240171885Sjulian			/*
240271885Sjulian			 * check if the generic handler owns it.
240371885Sjulian			 */
240470700Sjulian			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
240570700Sjulian			&& ((msg->header.flags & NGF_RESP) == 0)) {
240670700Sjulian				error = ng_generic_msg(node, item, hook);
240771885Sjulian				break;
240870700Sjulian			}
240971885Sjulian			/*
241071885Sjulian			 * Now see if there is a handler (hook or node specific)
241171885Sjulian			 * in the target node. If none, silently discard.
241271885Sjulian			 */
241371885Sjulian			if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg)))
241471885Sjulian			&& (!(rcvmsg = node->nd_type->rcvmsg))) {
241571885Sjulian				TRAP_ERROR();
241671885Sjulian				error = 0;
241771885Sjulian				NG_FREE_ITEM(item);
241871885Sjulian				break;
241971885Sjulian			}
242071885Sjulian			error = (*rcvmsg)(node, item, hook);
242170700Sjulian		}
242270700Sjulian		break;
242371047Sjulian	case NGQF_FN:
242471047Sjulian		/*
242571047Sjulian		 *  We have to implicitly trust the hook,
242671047Sjulian		 * as some of these are used for system purposes
242771849Sjulian		 * where the hook is invalid. In the case of
242871849Sjulian		 * the shutdown message we allow it to hit
242971849Sjulian		 * even if the node is invalid.
243071047Sjulian		 */
243171849Sjulian		if ((NG_NODE_NOT_VALID(node))
243271849Sjulian		&& (NGI_FN(item) != &ng_rmnode)) {
243371047Sjulian			TRAP_ERROR();
243471047Sjulian			error = EINVAL;
243571047Sjulian			break;
243671047Sjulian		}
243771849Sjulian		(*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item));
243871047Sjulian		NG_FREE_ITEM(item);
243971047Sjulian		break;
244071047Sjulian
244170700Sjulian	}
244270700Sjulian	/*
244370700Sjulian	 * We held references on some of the resources
244470700Sjulian	 * that we took from the item. Now that we have
244570700Sjulian	 * finished doing everything, drop those references.
244670700Sjulian	 */
244770700Sjulian	if (hook) {
244870784Sjulian		NG_HOOK_UNREF(hook);
244970700Sjulian	}
245070700Sjulian
245170700Sjulian	if (was_reader) {
245270784Sjulian		ng_leave_read(&node->nd_input_queue);
245370700Sjulian	} else {
245470784Sjulian		ng_leave_write(&node->nd_input_queue);
245570700Sjulian	}
245670700Sjulian	return (error);
245770700Sjulian}
245870700Sjulian
245970700Sjulian/***********************************************************************
246070700Sjulian * Implement the 'generic' control messages
246170700Sjulian ***********************************************************************/
246270700Sjulianstatic int
246370700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
246470700Sjulian{
246570700Sjulian	int error = 0;
246670700Sjulian	struct ng_mesg *msg;
246770700Sjulian	struct ng_mesg *resp = NULL;
246870700Sjulian
246970700Sjulian	NGI_GET_MSG(item, msg);
247052419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
247171047Sjulian		TRAP_ERROR();
247270700Sjulian		error = EINVAL;
247370700Sjulian		goto out;
247452419Sjulian	}
247552419Sjulian	switch (msg->header.cmd) {
247652419Sjulian	case NGM_SHUTDOWN:
247771849Sjulian		ng_rmnode(here, NULL, NULL, 0);
247852419Sjulian		break;
247952419Sjulian	case NGM_MKPEER:
248052419Sjulian	    {
248152419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
248252419Sjulian
248352419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
248471047Sjulian			TRAP_ERROR();
248570700Sjulian			error = EINVAL;
248670700Sjulian			break;
248752419Sjulian		}
248852419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
248952419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
249052419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
249152419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
249252419Sjulian		break;
249352419Sjulian	    }
249452419Sjulian	case NGM_CONNECT:
249552419Sjulian	    {
249652419Sjulian		struct ngm_connect *const con =
249752419Sjulian			(struct ngm_connect *) msg->data;
249852419Sjulian		node_p node2;
249952419Sjulian
250052419Sjulian		if (msg->header.arglen != sizeof(*con)) {
250171047Sjulian			TRAP_ERROR();
250270700Sjulian			error = EINVAL;
250370700Sjulian			break;
250452419Sjulian		}
250552419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
250652419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
250752419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
250870700Sjulian		/* Don't forget we get a reference.. */
250970700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
251052419Sjulian		if (error)
251152419Sjulian			break;
251252419Sjulian		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
251370784Sjulian		NG_NODE_UNREF(node2);
251452419Sjulian		break;
251552419Sjulian	    }
251652419Sjulian	case NGM_NAME:
251752419Sjulian	    {
251852419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
251952419Sjulian
252052419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
252171047Sjulian			TRAP_ERROR();
252270700Sjulian			error = EINVAL;
252370700Sjulian			break;
252452419Sjulian		}
252552419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
252652419Sjulian		error = ng_name_node(here, nam->name);
252752419Sjulian		break;
252852419Sjulian	    }
252952419Sjulian	case NGM_RMHOOK:
253052419Sjulian	    {
253152419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
253252419Sjulian		hook_p hook;
253352419Sjulian
253452419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
253571047Sjulian			TRAP_ERROR();
253670700Sjulian			error = EINVAL;
253770700Sjulian			break;
253852419Sjulian		}
253952419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
254054096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
254152419Sjulian			ng_destroy_hook(hook);
254252419Sjulian		break;
254352419Sjulian	    }
254452419Sjulian	case NGM_NODEINFO:
254552419Sjulian	    {
254652419Sjulian		struct nodeinfo *ni;
254752419Sjulian
254870700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
254952419Sjulian		if (resp == NULL) {
255052419Sjulian			error = ENOMEM;
255152419Sjulian			break;
255252419Sjulian		}
255352419Sjulian
255452419Sjulian		/* Fill in node info */
255570700Sjulian		ni = (struct nodeinfo *) resp->data;
255670784Sjulian		if (NG_NODE_HAS_NAME(here))
255770784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
255870784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
255952722Sjulian		ni->id = ng_node2ID(here);
256070784Sjulian		ni->hooks = here->nd_numhooks;
256152419Sjulian		break;
256252419Sjulian	    }
256352419Sjulian	case NGM_LISTHOOKS:
256452419Sjulian	    {
256570784Sjulian		const int nhooks = here->nd_numhooks;
256652419Sjulian		struct hooklist *hl;
256752419Sjulian		struct nodeinfo *ni;
256852419Sjulian		hook_p hook;
256952419Sjulian
257052419Sjulian		/* Get response struct */
257170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*hl)
257270700Sjulian		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
257352419Sjulian		if (resp == NULL) {
257452419Sjulian			error = ENOMEM;
257552419Sjulian			break;
257652419Sjulian		}
257770700Sjulian		hl = (struct hooklist *) resp->data;
257852419Sjulian		ni = &hl->nodeinfo;
257952419Sjulian
258052419Sjulian		/* Fill in node info */
258170784Sjulian		if (NG_NODE_HAS_NAME(here))
258270784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
258370784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
258452722Sjulian		ni->id = ng_node2ID(here);
258552419Sjulian
258652419Sjulian		/* Cycle through the linked list of hooks */
258752419Sjulian		ni->hooks = 0;
258870784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
258952419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
259052419Sjulian
259152419Sjulian			if (ni->hooks >= nhooks) {
259252419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
259352419Sjulian				    __FUNCTION__, "hooks");
259452419Sjulian				break;
259552419Sjulian			}
259670784Sjulian			if (NG_HOOK_NOT_VALID(hook))
259752419Sjulian				continue;
259870784Sjulian			strncpy(link->ourhook, NG_HOOK_NAME(hook), NG_HOOKLEN);
259970784Sjulian			strncpy(link->peerhook,
260070784Sjulian				NG_PEER_HOOK_NAME(hook), NG_HOOKLEN);
260170784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
260252419Sjulian				strncpy(link->nodeinfo.name,
260370784Sjulian				    NG_PEER_NODE_NAME(hook), NG_NODELEN);
260452419Sjulian			strncpy(link->nodeinfo.type,
260570784Sjulian			   NG_PEER_NODE(hook)->nd_type->name, NG_TYPELEN);
260670784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
260770784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
260852419Sjulian			ni->hooks++;
260952419Sjulian		}
261052419Sjulian		break;
261152419Sjulian	    }
261252419Sjulian
261352419Sjulian	case NGM_LISTNAMES:
261452419Sjulian	case NGM_LISTNODES:
261552419Sjulian	    {
261652419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
261752419Sjulian		struct namelist *nl;
261852419Sjulian		node_p node;
261952419Sjulian		int num = 0;
262052419Sjulian
262172200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
262252419Sjulian		/* Count number of nodes */
262370784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
262470912Sjulian			if (NG_NODE_IS_VALID(node)
262570912Sjulian			&& (unnamed || NG_NODE_HAS_NAME(node))) {
262652419Sjulian				num++;
262770912Sjulian			}
262852419Sjulian		}
262972200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
263052419Sjulian
263152419Sjulian		/* Get response struct */
263270700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*nl)
263370700Sjulian		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
263452419Sjulian		if (resp == NULL) {
263552419Sjulian			error = ENOMEM;
263652419Sjulian			break;
263752419Sjulian		}
263870700Sjulian		nl = (struct namelist *) resp->data;
263952419Sjulian
264052419Sjulian		/* Cycle through the linked list of nodes */
264152419Sjulian		nl->numnames = 0;
264272200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
264370784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
264452419Sjulian			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
264552419Sjulian
264652419Sjulian			if (nl->numnames >= num) {
264752419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
264852419Sjulian				    __FUNCTION__, "nodes");
264952419Sjulian				break;
265052419Sjulian			}
265170784Sjulian			if (NG_NODE_NOT_VALID(node))
265252419Sjulian				continue;
265370784Sjulian			if (!unnamed && (! NG_NODE_HAS_NAME(node)))
265452419Sjulian				continue;
265570784Sjulian			if (NG_NODE_HAS_NAME(node))
265670784Sjulian				strncpy(np->name, NG_NODE_NAME(node), NG_NODELEN);
265770784Sjulian			strncpy(np->type, node->nd_type->name, NG_TYPELEN);
265852722Sjulian			np->id = ng_node2ID(node);
265970784Sjulian			np->hooks = node->nd_numhooks;
266052419Sjulian			nl->numnames++;
266152419Sjulian		}
266272200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
266352419Sjulian		break;
266452419Sjulian	    }
266552419Sjulian
266652419Sjulian	case NGM_LISTTYPES:
266752419Sjulian	    {
266852419Sjulian		struct typelist *tl;
266952419Sjulian		struct ng_type *type;
267052419Sjulian		int num = 0;
267152419Sjulian
267272200Sbmilekic		mtx_lock(&ng_typelist_mtx);
267352419Sjulian		/* Count number of types */
267470912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
267552419Sjulian			num++;
267670912Sjulian		}
267772200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
267852419Sjulian
267952419Sjulian		/* Get response struct */
268070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*tl)
268170700Sjulian		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
268252419Sjulian		if (resp == NULL) {
268352419Sjulian			error = ENOMEM;
268452419Sjulian			break;
268552419Sjulian		}
268670700Sjulian		tl = (struct typelist *) resp->data;
268752419Sjulian
268852419Sjulian		/* Cycle through the linked list of types */
268952419Sjulian		tl->numtypes = 0;
269072200Sbmilekic		mtx_lock(&ng_typelist_mtx);
269170700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
269252419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
269352419Sjulian
269452419Sjulian			if (tl->numtypes >= num) {
269552419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
269652419Sjulian				    __FUNCTION__, "types");
269752419Sjulian				break;
269852419Sjulian			}
269959879Sarchie			strncpy(tp->type_name, type->name, NG_TYPELEN);
270071603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
270152419Sjulian			tl->numtypes++;
270252419Sjulian		}
270372200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
270452419Sjulian		break;
270552419Sjulian	    }
270652419Sjulian
270753913Sarchie	case NGM_BINARY2ASCII:
270853913Sarchie	    {
270964510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
271053913Sarchie		const struct ng_parse_type *argstype;
271153913Sarchie		const struct ng_cmdlist *c;
271270700Sjulian		struct ng_mesg *binary, *ascii;
271353913Sarchie
271453913Sarchie		/* Data area must contain a valid netgraph message */
271553913Sarchie		binary = (struct ng_mesg *)msg->data;
271653913Sarchie		if (msg->header.arglen < sizeof(struct ng_mesg)
271770912Sjulian		    || (msg->header.arglen - sizeof(struct ng_mesg)
271870912Sjulian		      < binary->header.arglen)) {
271971047Sjulian			TRAP_ERROR();
272053913Sarchie			error = EINVAL;
272153913Sarchie			break;
272253913Sarchie		}
272353913Sarchie
272470700Sjulian		/* Get a response message with lots of room */
272570700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
272670159Sjulian		if (resp == NULL) {
272753913Sarchie			error = ENOMEM;
272853913Sarchie			break;
272953913Sarchie		}
273070700Sjulian		ascii = (struct ng_mesg *)resp->data;
273153913Sarchie
273253913Sarchie		/* Copy binary message header to response message payload */
273353913Sarchie		bcopy(binary, ascii, sizeof(*binary));
273453913Sarchie
273553913Sarchie		/* Find command by matching typecookie and command number */
273670784Sjulian		for (c = here->nd_type->cmdlist;
273753913Sarchie		    c != NULL && c->name != NULL; c++) {
273853913Sarchie			if (binary->header.typecookie == c->cookie
273953913Sarchie			    && binary->header.cmd == c->cmd)
274053913Sarchie				break;
274153913Sarchie		}
274253913Sarchie		if (c == NULL || c->name == NULL) {
274353913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
274453913Sarchie				if (binary->header.typecookie == c->cookie
274553913Sarchie				    && binary->header.cmd == c->cmd)
274653913Sarchie					break;
274753913Sarchie			}
274853913Sarchie			if (c->name == NULL) {
274970700Sjulian				NG_FREE_MSG(resp);
275053913Sarchie				error = ENOSYS;
275153913Sarchie				break;
275253913Sarchie			}
275353913Sarchie		}
275453913Sarchie
275553913Sarchie		/* Convert command name to ASCII */
275653913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
275753913Sarchie		    "%s", c->name);
275853913Sarchie
275953913Sarchie		/* Convert command arguments to ASCII */
276053913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
276153913Sarchie		    c->respType : c->mesgType;
276270912Sjulian		if (argstype == NULL) {
276353913Sarchie			*ascii->data = '\0';
276470912Sjulian		} else {
276553913Sarchie			if ((error = ng_unparse(argstype,
276653913Sarchie			    (u_char *)binary->data,
276753913Sarchie			    ascii->data, bufSize)) != 0) {
276870700Sjulian				NG_FREE_MSG(resp);
276953913Sarchie				break;
277053913Sarchie			}
277153913Sarchie		}
277253913Sarchie
277353913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
277453913Sarchie		bufSize = strlen(ascii->data) + 1;
277553913Sarchie		ascii->header.arglen = bufSize;
277670700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
277753913Sarchie		break;
277853913Sarchie	    }
277953913Sarchie
278053913Sarchie	case NGM_ASCII2BINARY:
278153913Sarchie	    {
278253913Sarchie		int bufSize = 2000;	/* XXX hard coded constant */
278353913Sarchie		const struct ng_cmdlist *c;
278453913Sarchie		const struct ng_parse_type *argstype;
278570700Sjulian		struct ng_mesg *ascii, *binary;
278659178Sarchie		int off = 0;
278753913Sarchie
278853913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
278953913Sarchie		ascii = (struct ng_mesg *)msg->data;
279070912Sjulian		if ((msg->header.arglen < sizeof(*ascii) + 1)
279170912Sjulian		    || (ascii->header.arglen < 1)
279270912Sjulian		    || (msg->header.arglen
279370912Sjulian		      < sizeof(*ascii) + ascii->header.arglen)) {
279471047Sjulian			TRAP_ERROR();
279553913Sarchie			error = EINVAL;
279653913Sarchie			break;
279753913Sarchie		}
279853913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
279953913Sarchie
280070700Sjulian		/* Get a response message with lots of room */
280170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
280270159Sjulian		if (resp == NULL) {
280353913Sarchie			error = ENOMEM;
280453913Sarchie			break;
280553913Sarchie		}
280670700Sjulian		binary = (struct ng_mesg *)resp->data;
280753913Sarchie
280853913Sarchie		/* Copy ASCII message header to response message payload */
280953913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
281053913Sarchie
281153913Sarchie		/* Find command by matching ASCII command string */
281270784Sjulian		for (c = here->nd_type->cmdlist;
281353913Sarchie		    c != NULL && c->name != NULL; c++) {
281453913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
281553913Sarchie				break;
281653913Sarchie		}
281753913Sarchie		if (c == NULL || c->name == NULL) {
281853913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
281953913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
282053913Sarchie					break;
282153913Sarchie			}
282253913Sarchie			if (c->name == NULL) {
282370700Sjulian				NG_FREE_MSG(resp);
282453913Sarchie				error = ENOSYS;
282553913Sarchie				break;
282653913Sarchie			}
282753913Sarchie		}
282853913Sarchie
282953913Sarchie		/* Convert command name to binary */
283053913Sarchie		binary->header.cmd = c->cmd;
283153913Sarchie		binary->header.typecookie = c->cookie;
283253913Sarchie
283353913Sarchie		/* Convert command arguments to binary */
283453913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
283553913Sarchie		    c->respType : c->mesgType;
283670912Sjulian		if (argstype == NULL) {
283753913Sarchie			bufSize = 0;
283870912Sjulian		} else {
283953913Sarchie			if ((error = ng_parse(argstype, ascii->data,
284053913Sarchie			    &off, (u_char *)binary->data, &bufSize)) != 0) {
284170700Sjulian				NG_FREE_MSG(resp);
284253913Sarchie				break;
284353913Sarchie			}
284453913Sarchie		}
284553913Sarchie
284653913Sarchie		/* Return the result */
284753913Sarchie		binary->header.arglen = bufSize;
284870700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
284953913Sarchie		break;
285053913Sarchie	    }
285153913Sarchie
285262471Sphk	case NGM_TEXT_CONFIG:
285352419Sjulian	case NGM_TEXT_STATUS:
285452419Sjulian		/*
285552419Sjulian		 * This one is tricky as it passes the command down to the
285652419Sjulian		 * actual node, even though it is a generic type command.
285770700Sjulian		 * This means we must assume that the item/msg is already freed
285852419Sjulian		 * when control passes back to us.
285952419Sjulian		 */
286070784Sjulian		if (here->nd_type->rcvmsg != NULL) {
286170700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
286270784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
286352419Sjulian		}
286452419Sjulian		/* Fall through if rcvmsg not supported */
286552419Sjulian	default:
286671047Sjulian		TRAP_ERROR();
286752419Sjulian		error = EINVAL;
286852419Sjulian	}
286970700Sjulian	/*
287070700Sjulian	 * Sometimes a generic message may be statically allocated
287170700Sjulian	 * to avoid problems with allocating when in tight memeory situations.
287270700Sjulian	 * Don't free it if it is so.
287370700Sjulian	 * I break them appart here, because erros may cause a free if the item
287470700Sjulian	 * in which case we'd be doing it twice.
287570700Sjulian	 * they are kept together above, to simplify freeing.
287670700Sjulian	 */
287770700Sjulianout:
287870700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
287971849Sjulian	if (msg)
288070700Sjulian		NG_FREE_MSG(msg);
288152419Sjulian	return (error);
288252419Sjulian}
288352419Sjulian
288452419Sjulian/*
288559879Sarchie * Copy a 'meta'.
288659879Sarchie *
288759879Sarchie * Returns new meta, or NULL if original meta is NULL or ENOMEM.
288859879Sarchie */
288959879Sarchiemeta_p
289059879Sarchieng_copy_meta(meta_p meta)
289159879Sarchie{
289259879Sarchie	meta_p meta2;
289359879Sarchie
289459879Sarchie	if (meta == NULL)
289559879Sarchie		return (NULL);
289670700Sjulian	MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH_META, M_NOWAIT);
289759879Sarchie	if (meta2 == NULL)
289859879Sarchie		return (NULL);
289959879Sarchie	meta2->allocated_len = meta->used_len;
290059879Sarchie	bcopy(meta, meta2, meta->used_len);
290159879Sarchie	return (meta2);
290259879Sarchie}
290359879Sarchie
290452419Sjulian/************************************************************************
290552419Sjulian			Module routines
290652419Sjulian************************************************************************/
290752419Sjulian
290852419Sjulian/*
290952419Sjulian * Handle the loading/unloading of a netgraph node type module
291052419Sjulian */
291152419Sjulianint
291252419Sjulianng_mod_event(module_t mod, int event, void *data)
291352419Sjulian{
291452419Sjulian	struct ng_type *const type = data;
291552419Sjulian	int s, error = 0;
291652419Sjulian
291752419Sjulian	switch (event) {
291852419Sjulian	case MOD_LOAD:
291952419Sjulian
292052419Sjulian		/* Register new netgraph node type */
292152419Sjulian		s = splnet();
292252419Sjulian		if ((error = ng_newtype(type)) != 0) {
292352419Sjulian			splx(s);
292452419Sjulian			break;
292552419Sjulian		}
292652419Sjulian
292752419Sjulian		/* Call type specific code */
292852419Sjulian		if (type->mod_event != NULL)
292970700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
293072200Sbmilekic				mtx_lock(&ng_typelist_mtx);
293171603Sjulian				type->refs--;	/* undo it */
293252419Sjulian				LIST_REMOVE(type, types);
293372200Sbmilekic				mtx_unlock(&ng_typelist_mtx);
293470700Sjulian			}
293552419Sjulian		splx(s);
293652419Sjulian		break;
293752419Sjulian
293852419Sjulian	case MOD_UNLOAD:
293952419Sjulian		s = splnet();
294071603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
294152419Sjulian			error = EBUSY;
294271603Sjulian		} else {
294371603Sjulian			if (type->refs == 0) {
294471603Sjulian				/* failed load, nothing to undo */
294571603Sjulian				splx(s);
294671603Sjulian				break;
294771603Sjulian			}
294852419Sjulian			if (type->mod_event != NULL) {	/* check with type */
294952419Sjulian				error = (*type->mod_event)(mod, event, data);
295052419Sjulian				if (error != 0) {	/* type refuses.. */
295152419Sjulian					splx(s);
295252419Sjulian					break;
295352419Sjulian				}
295452419Sjulian			}
295572200Sbmilekic			mtx_lock(&ng_typelist_mtx);
295652419Sjulian			LIST_REMOVE(type, types);
295772200Sbmilekic			mtx_unlock(&ng_typelist_mtx);
295852419Sjulian		}
295952419Sjulian		splx(s);
296052419Sjulian		break;
296152419Sjulian
296252419Sjulian	default:
296352419Sjulian		if (type->mod_event != NULL)
296452419Sjulian			error = (*type->mod_event)(mod, event, data);
296552419Sjulian		else
296652419Sjulian			error = 0;		/* XXX ? */
296752419Sjulian		break;
296852419Sjulian	}
296952419Sjulian	return (error);
297052419Sjulian}
297152419Sjulian
297252419Sjulian/*
297352419Sjulian * Handle loading and unloading for this code.
297452419Sjulian * The only thing we need to link into is the NETISR strucure.
297552419Sjulian */
297652419Sjulianstatic int
297752419Sjulianngb_mod_event(module_t mod, int event, void *data)
297852419Sjulian{
297952419Sjulian	int s, error = 0;
298052419Sjulian
298152419Sjulian	switch (event) {
298252419Sjulian	case MOD_LOAD:
298352419Sjulian		/* Register line discipline */
298472979Sjulian		mtx_init(&ng_worklist_mtx, "ng_worklist", MTX_SPIN);
298570700Sjulian		mtx_init(&ng_typelist_mtx, "netgraph types mutex", 0);
298670700Sjulian		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", 0);
298770700Sjulian		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", 0);
298870700Sjulian		mtx_init(&ngq_mtx, "netgraph netisr mutex", 0);
298952419Sjulian		s = splimp();
299052419Sjulian		error = register_netisr(NETISR_NETGRAPH, ngintr);
299152419Sjulian		splx(s);
299252419Sjulian		break;
299352419Sjulian	case MOD_UNLOAD:
299452419Sjulian		/* You cant unload it because an interface may be using it.  */
299552419Sjulian		error = EBUSY;
299652419Sjulian		break;
299752419Sjulian	default:
299852419Sjulian		error = EOPNOTSUPP;
299952419Sjulian		break;
300052419Sjulian	}
300152419Sjulian	return (error);
300252419Sjulian}
300352419Sjulian
300452419Sjulianstatic moduledata_t netgraph_mod = {
300552419Sjulian	"netgraph",
300652419Sjulian	ngb_mod_event,
300752419Sjulian	(NULL)
300852419Sjulian};
300952419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
301072946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
301172946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
301272946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
301352419Sjulian
301452419Sjulian/************************************************************************
301570700Sjulian			Queue element get/free routines
301652419Sjulian************************************************************************/
301752419Sjulian
301852419Sjulian
301970700Sjulianstatic int			allocated;	/* number of items malloc'd */
302070700Sjulianstatic int			maxalloc = 128;	/* limit the damage of a leak */
302170700Sjulianstatic const int		ngqfreemax = 64;/* cache at most this many */
302270700Sjulianstatic const int		ngqfreelow = 4; /* try malloc if free < this */
302370700Sjulianstatic volatile int		ngqfreesize;	/* number of cached entries */
302470784Sjulian#ifdef	NETGRAPH_DEBUG
302570700Sjulianstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
302670700Sjulian#endif
302752419Sjulian/*
302852419Sjulian * Get a queue entry
302970700Sjulian * This is usually called when a packet first enters netgraph.
303070700Sjulian * By definition, this is usually from an interrupt, or from a user.
303170700Sjulian * Users are not so important, but try be quick for the times that it's
303270700Sjulian * an interrupt. Use atomic operations to cope with collisions
303370700Sjulian * with interrupts and other processors. Assumes MALLOC is SMP safe.
303470700Sjulian * XXX If reserve is low, we should try to get 2 from malloc as this
303570700Sjulian * would indicate it often fails.
303652419Sjulian */
303770700Sjulianstatic item_p
303852419Sjulianng_getqblk(void)
303952419Sjulian{
304070700Sjulian	item_p item = NULL;
304152419Sjulian
304270700Sjulian	/*
304370700Sjulian	 * Try get a cached queue block, or else allocate a new one
304470700Sjulian	 * If we are less than our reserve, try malloc. If malloc
304570700Sjulian	 * fails, then that's what the reserve is for...
304670700Sjulian	 * Don't completely trust ngqfreesize, as it is subject
304770700Sjulian	 * to races.. (it'll eventually catch up but may be out by one or two
304870700Sjulian	 * for brief moments(under SMP or interrupts).
304970700Sjulian	 * ngqfree is the final arbiter. We have our little reserve
305070700Sjulian	 * because we use M_NOWAIT for malloc. This just helps us
305170700Sjulian	 * avoid dropping packets while not increasing the time
305271650Sjulian	 * we take to service the interrupt (on average) (I hope).
305370700Sjulian	 */
305470700Sjulian	for (;;) {
305570700Sjulian		if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) {
305670700Sjulian			if (allocated < maxalloc) {  /* don't leak forever */
305770700Sjulian				MALLOC(item, item_p ,
305870700Sjulian				    sizeof(*item), M_NETGRAPH_ITEM,
305970700Sjulian				    (M_NOWAIT | M_ZERO));
306070700Sjulian				if (item) {
306170784Sjulian#ifdef	NETGRAPH_DEBUG
306270700Sjulian					TAILQ_INSERT_TAIL(&ng_itemlist,
306370700Sjulian								item, all);
306470784Sjulian#endif	/* NETGRAPH_DEBUG */
306570700Sjulian					atomic_add_int(&allocated, 1);
306670700Sjulian					break;
306770700Sjulian				}
306870700Sjulian			}
306970700Sjulian		}
307052419Sjulian
307170700Sjulian		/*
307270700Sjulian		 * We didn't or couldn't malloc.
307370700Sjulian		 * try get one from our cache.
307470700Sjulian		 * item must be NULL to get here.
307570700Sjulian		 */
307670700Sjulian		if ((item = ngqfree) != NULL) {
307770700Sjulian			/*
307870700Sjulian			 * Atomically try grab the first item
307970700Sjulian			 * and put it's successor in its place.
308070700Sjulian			 * If we fail, just try again.. someone else
308170700Sjulian			 * beat us to this one or freed one.
308270700Sjulian			 * Don't worry about races with ngqfreesize.
308370700Sjulian			 * Close enough is good enough..
308470700Sjulian			 */
308570700Sjulian			if (atomic_cmpset_ptr(&ngqfree, item, item->el_next)) {
308670700Sjulian				atomic_subtract_int(&ngqfreesize, 1);
308771650Sjulian				item->el_flags &= ~NGQF_FREE;
308870700Sjulian				break;
308970700Sjulian			}
309071650Sjulian			/*
309171650Sjulian			 * something got there before we did.. try again
309271650Sjulian			 * (go around the loop again)
309371650Sjulian			 */
309470700Sjulian			item = NULL;
309570700Sjulian		} else {
309670700Sjulian			/* We really ran out */
309770700Sjulian			break;
309852419Sjulian		}
309952419Sjulian	}
310070700Sjulian	return (item);
310152419Sjulian}
310252419Sjulian
310352419Sjulian/*
310452419Sjulian * Release a queue entry
310552419Sjulian */
310670700Sjulianvoid
310770700Sjulianng_free_item(item_p item)
310852419Sjulian{
310952419Sjulian
311070700Sjulian	/*
311170700Sjulian	 * The item may hold resources on it's own. We need to free
311270700Sjulian	 * these before we can free the item. What they are depends upon
311370700Sjulian	 * what kind of item it is. it is important that nodes zero
311470700Sjulian	 * out pointers to resources that they remove from the item
311570700Sjulian	 * or we release them again here.
311670700Sjulian	 */
311770700Sjulian	if (item->el_flags & NGQF_FREE) {
311870700Sjulian		panic(" Freeing free queue item");
311952419Sjulian	}
312071047Sjulian	switch (item->el_flags & NGQF_TYPE) {
312170700Sjulian	case NGQF_DATA:
312270700Sjulian		/* If we have an mbuf and metadata still attached.. */
312370700Sjulian		NG_FREE_M(_NGI_M(item));
312470700Sjulian		NG_FREE_META(_NGI_META(item));
312570700Sjulian		break;
312670700Sjulian	case NGQF_MESG:
312770700Sjulian		_NGI_RETADDR(item) = NULL;
312870700Sjulian		NG_FREE_MSG(_NGI_MSG(item));
312970700Sjulian		break;
313071047Sjulian	case NGQF_FN:
313171047Sjulian		/* nothing to free really, */
313271047Sjulian		_NGI_FN(item) = NULL;
313371047Sjulian		_NGI_ARG1(item) = NULL;
313471047Sjulian		_NGI_ARG2(item) = 0;
313571047Sjulian	case NGQF_UNDEF:
313652419Sjulian	}
313771849Sjulian	/* If we still have a node or hook referenced... */
313871849Sjulian	_NGI_CLR_NODE(item);
313971849Sjulian	_NGI_CLR_HOOK(item);
314070700Sjulian	item->el_flags |= NGQF_FREE;
314152419Sjulian
314270700Sjulian	/*
314370700Sjulian	 * We have freed any resources held by the item.
314470700Sjulian	 * now we can free the item itself.
314570700Sjulian	 */
314670700Sjulian	if (ngqfreesize < ngqfreemax) { /* don't worry about races */
314770700Sjulian		for (;;) {
314870700Sjulian			item->el_next = ngqfree;
314970700Sjulian			if (atomic_cmpset_ptr(&ngqfree, item->el_next, item)) {
315070700Sjulian				break;
315170700Sjulian			}
315270700Sjulian		}
315370700Sjulian		atomic_add_int(&ngqfreesize, 1);
315470700Sjulian	} else {
315570700Sjulian		/* This is the only place that should use this Macro */
315670784Sjulian#ifdef	NETGRAPH_DEBUG
315770700Sjulian		TAILQ_REMOVE(&ng_itemlist, item, all);
315870784Sjulian#endif	/* NETGRAPH_DEBUG */
315970700Sjulian		NG_FREE_ITEM_REAL(item);
316070700Sjulian		atomic_subtract_int(&allocated, 1);
316170700Sjulian	}
316270700Sjulian}
316352419Sjulian
316470784Sjulian#ifdef	NETGRAPH_DEBUG
316570700Sjulianvoid
316670784Sjuliandumphook (hook_p hook, char *file, int line)
316770784Sjulian{
316870784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
316970784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
317070784Sjulian	printf("	Last active @ %s, line %d\n",
317170784Sjulian		hook->lastfile, hook->lastline);
317270784Sjulian	if (line) {
317370784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
317470784Sjulian	}
317570784Sjulian}
317670784Sjulian
317770784Sjulianvoid
317870784Sjuliandumpnode(node_p node, char *file, int line)
317970784Sjulian{
318070784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
318171047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
318270784Sjulian		node->nd_numhooks, node->nd_flags,
318370784Sjulian		node->nd_refs, node->nd_name);
318470784Sjulian	printf("	Last active @ %s, line %d\n",
318570784Sjulian		node->lastfile, node->lastline);
318670784Sjulian	if (line) {
318770784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
318870784Sjulian	}
318970784Sjulian}
319070784Sjulian
319170784Sjulianvoid
319270700Sjuliandumpitem(item_p item, char *file, int line)
319370700Sjulian{
319470700Sjulian	if (item->el_flags & NGQF_FREE) {
319570700Sjulian		printf(" Free item, freed at %s, line %d\n",
319670700Sjulian			item->lastfile, item->lastline);
319752419Sjulian	} else {
319870700Sjulian		printf(" ACTIVE item, last used at %s, line %d",
319970700Sjulian			item->lastfile, item->lastline);
320071047Sjulian		switch(item->el_flags & NGQF_TYPE) {
320171047Sjulian		case NGQF_DATA:
320271047Sjulian			printf(" - [data]\n");
320371047Sjulian			break;
320471047Sjulian		case NGQF_MESG:
320570700Sjulian			printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
320671047Sjulian			break;
320771047Sjulian		case NGQF_FN:
320871047Sjulian			printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
320971047Sjulian				item->body.fn.fn_fn,
321071849Sjulian				NGI_NODE(item),
321171849Sjulian				NGI_HOOK(item),
321271047Sjulian				item->body.fn.fn_arg1,
321371047Sjulian				item->body.fn.fn_arg2,
321471047Sjulian				item->body.fn.fn_arg2);
321571047Sjulian			break;
321671047Sjulian		case NGQF_UNDEF:
321771047Sjulian			printf(" - UNDEFINED!\n");
321870700Sjulian		}
321952419Sjulian	}
322070784Sjulian	if (line) {
322170784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
322271849Sjulian		if (NGI_NODE(item)) {
322370784Sjulian			printf("node %p ([%x])\n",
322471849Sjulian				NGI_NODE(item), ng_node2ID(NGI_NODE(item)));
322570784Sjulian		}
322670784Sjulian	}
322770700Sjulian}
322852419Sjulian
322970784Sjulianstatic void
323070784Sjulianng_dumpitems(void)
323170784Sjulian{
323270784Sjulian	item_p item;
323370784Sjulian	int i = 1;
323470784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
323570784Sjulian		printf("[%d] ", i++);
323670784Sjulian		dumpitem(item, NULL, 0);
323770784Sjulian	}
323870784Sjulian}
323970784Sjulian
324070784Sjulianstatic void
324170784Sjulianng_dumpnodes(void)
324270784Sjulian{
324370784Sjulian	node_p node;
324470784Sjulian	int i = 1;
324570784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
324670784Sjulian		printf("[%d] ", i++);
324770784Sjulian		dumpnode(node, NULL, 0);
324870784Sjulian	}
324970784Sjulian}
325070784Sjulian
325170784Sjulianstatic void
325270784Sjulianng_dumphooks(void)
325370784Sjulian{
325470784Sjulian	hook_p hook;
325570784Sjulian	int i = 1;
325670784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
325770784Sjulian		printf("[%d] ", i++);
325870784Sjulian		dumphook(hook, NULL, 0);
325970784Sjulian	}
326070784Sjulian}
326170784Sjulian
326270700Sjulianstatic int
326370700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
326470700Sjulian{
326570700Sjulian	int error;
326670700Sjulian	int val;
326770700Sjulian	int i;
326870700Sjulian
326970700Sjulian	val = allocated;
327070700Sjulian	i = 1;
327170700Sjulian	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
327271047Sjulian	if (error != 0 || req->newptr == NULL)
327371047Sjulian		return (error);
327471047Sjulian	if (val == 42) {
327570784Sjulian		ng_dumpitems();
327670784Sjulian		ng_dumpnodes();
327770784Sjulian		ng_dumphooks();
327870700Sjulian	}
327971047Sjulian	return (0);
328052419Sjulian}
328152419Sjulian
328271047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
328371047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
328470784Sjulian#endif	/* NETGRAPH_DEBUG */
328570700Sjulian
328670700Sjulian
328770700Sjulian/***********************************************************************
328870700Sjulian* Worklist routines
328970700Sjulian**********************************************************************/
329070700Sjulian/* NETISR thread enters here */
329152419Sjulian/*
329270700Sjulian * Pick a node off the list of nodes with work,
329370700Sjulian * try get an item to process off it.
329470700Sjulian * If there are no more, remove the node from the list.
329552419Sjulian */
329670700Sjulianstatic void
329770700Sjulianngintr(void)
329852419Sjulian{
329970700Sjulian	item_p item;
330070700Sjulian	node_p  node = NULL;
330152419Sjulian
330270700Sjulian	for (;;) {
330372200Sbmilekic		mtx_lock_spin(&ng_worklist_mtx);
330470700Sjulian		node = TAILQ_FIRST(&ng_worklist);
330570700Sjulian		if (!node) {
330672200Sbmilekic			mtx_unlock_spin(&ng_worklist_mtx);
330770700Sjulian			break;
330869922Sjulian		}
330971902Sjulian		node->nd_flags &= ~NG_WORKQ;
331070784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
331172200Sbmilekic		mtx_unlock_spin(&ng_worklist_mtx);
331270700Sjulian		/*
331370700Sjulian		 * We have the node. We also take over the reference
331470700Sjulian		 * that the list had on it.
331570700Sjulian		 * Now process as much as you can, until it won't
331670700Sjulian		 * let you have another item off the queue.
331770700Sjulian		 * All this time, keep the reference
331870700Sjulian		 * that lets us be sure that the node still exists.
331970700Sjulian		 * Let the reference go at the last minute.
332071902Sjulian		 * ng_dequeue will put us back on the worklist
332171902Sjulian		 * if there is more too do. This may be of use if there
332271902Sjulian		 * are Multiple Processors and multiple Net threads in the
332371902Sjulian		 * future.
332470700Sjulian		 */
332570700Sjulian		for (;;) {
332672200Sbmilekic			mtx_lock_spin(&node->nd_input_queue.q_mtx);
332770784Sjulian			item = ng_dequeue(&node->nd_input_queue);
332870700Sjulian			if (item == NULL) {
332972200Sbmilekic				mtx_unlock_spin(&node->nd_input_queue.q_mtx);
333070700Sjulian				break; /* go look for another node */
333170700Sjulian			} else {
333272200Sbmilekic				mtx_unlock_spin(&node->nd_input_queue.q_mtx);
333374078Sjulian				NGI_GET_NODE(item, node); /* zaps stored node */
333474078Sjulian				ng_apply_item(node, item);
333574078Sjulian				NG_NODE_UNREF(node);
333670700Sjulian			}
333770700Sjulian		}
333873238Sjulian		NG_NODE_UNREF(node);
333952419Sjulian	}
334070700Sjulian}
334169922Sjulian
334270700Sjulianstatic void
334370700Sjulianng_worklist_remove(node_p node)
334470700Sjulian{
334572200Sbmilekic	mtx_lock_spin(&ng_worklist_mtx);
334670784Sjulian	if (node->nd_flags & NG_WORKQ) {
334772979Sjulian		node->nd_flags &= ~NG_WORKQ;
334870784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
334972979Sjulian		mtx_unlock_spin(&ng_worklist_mtx);
335070784Sjulian		NG_NODE_UNREF(node);
335172979Sjulian	} else {
335272979Sjulian		mtx_unlock_spin(&ng_worklist_mtx);
335370700Sjulian	}
335470700Sjulian}
335570700Sjulian
335672979Sjulian/*
335772979Sjulian * XXX
335872979Sjulian * It's posible that a debugging NG_NODE_REF may need
335972979Sjulian * to be outside the mutex zone
336072979Sjulian */
336170700Sjulianstatic void
336270700Sjulianng_setisr(node_p node)
336370700Sjulian{
336472200Sbmilekic	mtx_lock_spin(&ng_worklist_mtx);
336570784Sjulian	if ((node->nd_flags & NG_WORKQ) == 0) {
336669922Sjulian		/*
336770700Sjulian		 * If we are not already on the work queue,
336870700Sjulian		 * then put us on.
336969922Sjulian		 */
337070784Sjulian		node->nd_flags |= NG_WORKQ;
337170784Sjulian		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
337272979Sjulian		NG_NODE_REF(node); /* XXX fafe in mutex? */
337370700Sjulian	}
337472200Sbmilekic	mtx_unlock_spin(&ng_worklist_mtx);
337570700Sjulian	schednetisr(NETISR_NETGRAPH);
337670700Sjulian}
337770700Sjulian
337870700Sjulian
337970700Sjulian/***********************************************************************
338070700Sjulian* Externally useable functions to set up a queue item ready for sending
338170700Sjulian***********************************************************************/
338270700Sjulian
338370784Sjulian#ifdef	NETGRAPH_DEBUG
338470784Sjulian#define	ITEM_DEBUG_CHECKS						\
338570700Sjulian	do {								\
338671849Sjulian		if (NGI_NODE(item) ) {					\
338770700Sjulian			printf("item already has node");		\
338870700Sjulian			Debugger("has node");				\
338971849Sjulian			NGI_CLR_NODE(item);				\
339070700Sjulian		}							\
339171849Sjulian		if (NGI_HOOK(item) ) {					\
339270700Sjulian			printf("item already has hook");		\
339370700Sjulian			Debugger("has hook");				\
339471849Sjulian			NGI_CLR_HOOK(item);				\
339570700Sjulian		}							\
339670700Sjulian	} while (0)
339770700Sjulian#else
339870784Sjulian#define ITEM_DEBUG_CHECKS
339970700Sjulian#endif
340070700Sjulian
340170700Sjulian/*
340270700Sjulian * Put elements into the item.
340370700Sjulian * Hook and node references will be removed when the item is dequeued.
340470700Sjulian * (or equivalent)
340570700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
340670700Sjulian * remote node might go away in this timescale.
340770700Sjulian * We know the hooks can't go away because that would require getting
340870700Sjulian * a writer item on both nodes and we must have at least a  reader
340970700Sjulian * here to eb able to do this.
341070700Sjulian * Note that the hook loaded is the REMOTE hook.
341170700Sjulian *
341270700Sjulian * This is possibly in the critical path for new data.
341370700Sjulian */
341470700Sjulianitem_p
341570700Sjulianng_package_data(struct mbuf *m, meta_p meta)
341670700Sjulian{
341770700Sjulian	item_p item;
341870700Sjulian
341970700Sjulian	if ((item = ng_getqblk()) == NULL) {
342070700Sjulian		NG_FREE_M(m);
342170700Sjulian		NG_FREE_META(meta);
342270700Sjulian		return (NULL);
342370700Sjulian	}
342470784Sjulian	ITEM_DEBUG_CHECKS;
342570700Sjulian	item->el_flags = NGQF_DATA;
342670700Sjulian	item->el_next = NULL;
342770700Sjulian	NGI_M(item) = m;
342870700Sjulian	NGI_META(item) = meta;
342970700Sjulian	return (item);
343070700Sjulian}
343170700Sjulian
343270700Sjulian/*
343370700Sjulian * Allocate a queue item and put items into it..
343470700Sjulian * Evaluate the address as this will be needed to queue it and
343570700Sjulian * to work out what some of the fields should be.
343670700Sjulian * Hook and node references will be removed when the item is dequeued.
343770700Sjulian * (or equivalent)
343870700Sjulian */
343970700Sjulianitem_p
344070700Sjulianng_package_msg(struct ng_mesg *msg)
344170700Sjulian{
344270700Sjulian	item_p item;
344370700Sjulian
344470700Sjulian	if ((item = ng_getqblk()) == NULL) {
344571849Sjulian		NG_FREE_MSG(msg);
344670700Sjulian		return (NULL);
344769922Sjulian	}
344870784Sjulian	ITEM_DEBUG_CHECKS;
344970700Sjulian	item->el_flags = NGQF_MESG;
345070700Sjulian	item->el_next = NULL;
345170700Sjulian	/*
345270700Sjulian	 * Set the current lasthook into the queue item
345370700Sjulian	 */
345470700Sjulian	NGI_MSG(item) = msg;
345570700Sjulian	NGI_RETADDR(item) = NULL;
345670700Sjulian	return (item);
345770700Sjulian}
345869922Sjulian
345970700Sjulian
346070700Sjulian
346171849Sjulian#define SET_RETADDR(item, here, retaddr)				\
346271047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
346371047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
346470700Sjulian			if (retaddr) {					\
346570700Sjulian				NGI_RETADDR(item) = retaddr;		\
346670700Sjulian			} else {					\
346770700Sjulian				/*					\
346870700Sjulian				 * The old return address should be ok.	\
346970700Sjulian				 * If there isn't one, use the address	\
347070700Sjulian				 * here.				\
347170700Sjulian				 */					\
347270700Sjulian				if (NGI_RETADDR(item) == 0) {		\
347370700Sjulian					NGI_RETADDR(item)		\
347470700Sjulian						= ng_node2ID(here);	\
347570700Sjulian				}					\
347670700Sjulian			}						\
347770700Sjulian		}							\
347870700Sjulian	} while (0)
347970700Sjulian
348070700Sjulianint
348170700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
348270700Sjulian{
348371849Sjulian	hook_p peer;
348471849Sjulian	node_p peernode;
348570784Sjulian	ITEM_DEBUG_CHECKS;
348670700Sjulian	/*
348770700Sjulian	 * Quick sanity check..
348870784Sjulian	 * Since a hook holds a reference on it's node, once we know
348970784Sjulian	 * that the peer is still connected (even if invalid,) we know
349070784Sjulian	 * that the peer node is present, though maybe invalid.
349170700Sjulian	 */
349270700Sjulian	if ((hook == NULL)
349370784Sjulian	|| NG_HOOK_NOT_VALID(hook)
349470784Sjulian	|| (NG_HOOK_PEER(hook) == NULL)
349570784Sjulian	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
349670784Sjulian	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
349770700Sjulian		NG_FREE_ITEM(item);
349871047Sjulian		TRAP_ERROR();
349973083Sjulian		return (ENETDOWN);
350052419Sjulian	}
350152419Sjulian
350270700Sjulian	/*
350370700Sjulian	 * Transfer our interest to the other (peer) end.
350470700Sjulian	 */
350571849Sjulian	peer = NG_HOOK_PEER(hook);
350671849Sjulian	NG_HOOK_REF(peer);
350771849Sjulian	NGI_SET_HOOK(item, peer);
350871849Sjulian	peernode = NG_PEER_NODE(hook);
350971849Sjulian	NG_NODE_REF(peernode);
351071849Sjulian	NGI_SET_NODE(item, peernode);
351179706Sjulian	SET_RETADDR(item, here, retaddr);
351270700Sjulian	return (0);
351370700Sjulian}
351452419Sjulian
351570700Sjulianint
351670700Sjulianng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
351770700Sjulian{
351870700Sjulian	node_p  dest = NULL;
351970700Sjulian	hook_p	hook = NULL;
352070700Sjulian	int     error;
352170700Sjulian
352270784Sjulian	ITEM_DEBUG_CHECKS;
352370700Sjulian	/*
352470700Sjulian	 * Note that ng_path2noderef increments the reference count
352570700Sjulian	 * on the node for us if it finds one. So we don't have to.
352670700Sjulian	 */
352770700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
352870700Sjulian	if (error) {
352970700Sjulian		NG_FREE_ITEM(item);
353070784Sjulian		return (error);
353152419Sjulian	}
353271849Sjulian	NGI_SET_NODE(item, dest);
353371849Sjulian	if ( hook) {
353470784Sjulian		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
353571849Sjulian		NGI_SET_HOOK(item, hook);
353671849Sjulian	}
353771849Sjulian	SET_RETADDR(item, here, retaddr);
353870700Sjulian	return (0);
353970700Sjulian}
354052419Sjulian
354170700Sjulianint
354270700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
354370700Sjulian{
354470700Sjulian	node_p dest;
354570700Sjulian
354670784Sjulian	ITEM_DEBUG_CHECKS;
354770700Sjulian	/*
354870700Sjulian	 * Find the target node.
354970700Sjulian	 */
355070700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
355170700Sjulian	if (dest == NULL) {
355270700Sjulian		NG_FREE_ITEM(item);
355371047Sjulian		TRAP_ERROR();
355470700Sjulian		return(EINVAL);
355570700Sjulian	}
355670700Sjulian	/* Fill out the contents */
355770700Sjulian	item->el_flags = NGQF_MESG;
355870700Sjulian	item->el_next = NULL;
355971849Sjulian	NGI_SET_NODE(item, dest);
356071849Sjulian	NGI_CLR_HOOK(item);
356171849Sjulian	SET_RETADDR(item, here, retaddr);
356252419Sjulian	return (0);
356352419Sjulian}
356452419Sjulian
356552419Sjulian/*
356670700Sjulian * special case to send a message to self (e.g. destroy node)
356770700Sjulian * Possibly indicate an arrival hook too.
356870700Sjulian * Useful for removing that hook :-)
356952419Sjulian */
357070700Sjulianitem_p
357170700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
357252419Sjulian{
357370700Sjulian	item_p item;
357452419Sjulian
357570700Sjulian	/*
357670700Sjulian	 * Find the target node.
357770700Sjulian	 * If there is a HOOK argument, then use that in preference
357870700Sjulian	 * to the address.
357970700Sjulian	 */
358070700Sjulian	if ((item = ng_getqblk()) == NULL) {
358171849Sjulian		NG_FREE_MSG(msg);
358270700Sjulian		return (NULL);
358352419Sjulian	}
358470700Sjulian
358570700Sjulian	/* Fill out the contents */
358670700Sjulian	item->el_flags = NGQF_MESG;
358770700Sjulian	item->el_next = NULL;
358870784Sjulian	NG_NODE_REF(here);
358971849Sjulian	NGI_SET_NODE(item, here);
359071849Sjulian	if (hook) {
359170784Sjulian		NG_HOOK_REF(hook);
359271849Sjulian		NGI_SET_HOOK(item, hook);
359371849Sjulian	}
359470700Sjulian	NGI_MSG(item) = msg;
359570700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
359670700Sjulian	return (item);
359752419Sjulian}
359852419Sjulian
359971047Sjulianint
360071047Sjulianng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
360171047Sjulian{
360271047Sjulian	item_p item;
360371047Sjulian
360471047Sjulian	if ((item = ng_getqblk()) == NULL) {
360571047Sjulian		return (ENOMEM);
360671047Sjulian	}
360771047Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
360873238Sjulian	NG_NODE_REF(node); /* and one for the item */
360971849Sjulian	NGI_SET_NODE(item, node);
361071849Sjulian	if (hook) {
361171047Sjulian		NG_HOOK_REF(hook);
361271849Sjulian		NGI_SET_HOOK(item, hook);
361371047Sjulian	}
361471047Sjulian	NGI_FN(item) = fn;
361571047Sjulian	NGI_ARG1(item) = arg1;
361671047Sjulian	NGI_ARG2(item) = arg2;
361774078Sjulian	return(ng_snd_item(item, 0));
361871047Sjulian}
361971047Sjulian
362070700Sjulian/*
362170700Sjulian * Set the address, if none given, give the node here.
362270700Sjulian */
362370700Sjulianvoid
362470700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
362570700Sjulian{
362670700Sjulian	if (retaddr) {
362770700Sjulian		NGI_RETADDR(item) = retaddr;
362870700Sjulian	} else {
362970700Sjulian		/*
363070700Sjulian		 * The old return address should be ok.
363170700Sjulian		 * If there isn't one, use the address here.
363270700Sjulian		 */
363370700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
363470700Sjulian	}
363570700Sjulian}
363652419Sjulian
363770700Sjulian#define TESTING
363870700Sjulian#ifdef TESTING
363970700Sjulian/* just test all the macros */
364070700Sjulianvoid
364170700Sjulianng_macro_test(item_p item);
364270700Sjulianvoid
364370700Sjulianng_macro_test(item_p item)
364470700Sjulian{
364570700Sjulian	node_p node = NULL;
364670700Sjulian	hook_p hook = NULL;
364770700Sjulian	struct mbuf *m;
364870700Sjulian	meta_p meta;
364970700Sjulian	struct ng_mesg *msg;
365070700Sjulian	ng_ID_t retaddr;
365170700Sjulian	int	error;
365270700Sjulian
365370700Sjulian	NGI_GET_M(item, m);
365470700Sjulian	NGI_GET_META(item, meta);
365570700Sjulian	NGI_GET_MSG(item, msg);
365670700Sjulian	retaddr = NGI_RETADDR(item);
365770700Sjulian	NG_SEND_DATA(error, hook, m, meta);
365870700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
365970700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
366070784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
366170700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
366270700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
366370700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
366470700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
366570700Sjulian}
366670700Sjulian#endif /* TESTING */
366770700Sjulian
3668