ng_base.c revision 131933
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 131933 2004-07-10 21:45:58Z marcel $
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>
50131933Smarcel#include <sys/kdb.h>
5152419Sjulian#include <sys/kernel.h>
52114216Skan#include <sys/limits.h>
5352419Sjulian#include <sys/malloc.h>
5452419Sjulian#include <sys/syslog.h>
5570700Sjulian#include <sys/sysctl.h>
5652419Sjulian#include <sys/linker.h>
5752419Sjulian#include <sys/queue.h>
5852419Sjulian#include <sys/mbuf.h>
5952843Sphk#include <sys/ctype.h>
6072946Sjulian#include <sys/sysctl.h>
6152419Sjulian
6252419Sjulian#include <net/netisr.h>
6352419Sjulian
6452419Sjulian#include <netgraph/ng_message.h>
6552419Sjulian#include <netgraph/netgraph.h>
6653913Sarchie#include <netgraph/ng_parse.h>
6752419Sjulian
6872053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION);
6959756Speter
7070784Sjulian/* List of all active nodes */
7170700Sjulianstatic LIST_HEAD(, ng_node) ng_nodelist;
7270700Sjulianstatic struct mtx	ng_nodelist_mtx;
7352419Sjulian
74122110Sharti/* Mutex that protects the free queue item list */
75122110Shartistatic struct mtx	ngq_mtx;
76122110Sharti
7770784Sjulian#ifdef	NETGRAPH_DEBUG
7870784Sjulian
7970784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
8070784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8170784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
8270784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8370784Sjulian
8470784Sjulianstatic void ng_dumpitems(void);
8570784Sjulianstatic void ng_dumpnodes(void);
8670784Sjulianstatic void ng_dumphooks(void);
8770784Sjulian
8870784Sjulian#endif	/* NETGRAPH_DEBUG */
8970935Sjulian/*
9070935Sjulian * DEAD versions of the structures.
9170935Sjulian * In order to avoid races, it is sometimes neccesary to point
9270935Sjulian * at SOMETHING even though theoretically, the current entity is
9370935Sjulian * INVALID. Use these to avoid these races.
9470935Sjulian */
9570935Sjulianstruct ng_type ng_deadtype = {
9670935Sjulian	NG_ABI_VERSION,
9770935Sjulian	"dead",
9870935Sjulian	NULL,	/* modevent */
9970935Sjulian	NULL,	/* constructor */
10070935Sjulian	NULL,	/* rcvmsg */
10170935Sjulian	NULL,	/* shutdown */
10270935Sjulian	NULL,	/* newhook */
10370935Sjulian	NULL,	/* findhook */
10470935Sjulian	NULL,	/* connect */
10570935Sjulian	NULL,	/* rcvdata */
10670935Sjulian	NULL,	/* disconnect */
10770935Sjulian	NULL, 	/* cmdlist */
10870935Sjulian};
10970784Sjulian
11070935Sjulianstruct ng_node ng_deadnode = {
11170935Sjulian	"dead",
11270935Sjulian	&ng_deadtype,
11370935Sjulian	NG_INVALID,
11470935Sjulian	1,	/* refs */
11570935Sjulian	0,	/* numhooks */
11670935Sjulian	NULL,	/* private */
11770935Sjulian	0,	/* ID */
11870935Sjulian	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
11970935Sjulian	{},	/* all_nodes list entry */
12070935Sjulian	{},	/* id hashtable list entry */
12170935Sjulian	{},	/* workqueue entry */
12270935Sjulian	{	0,
12370935Sjulian		{}, /* should never use! (should hang) */
12470935Sjulian		NULL,
12570935Sjulian		&ng_deadnode.nd_input_queue.queue,
12670935Sjulian		&ng_deadnode
12770935Sjulian	},
12870935Sjulian#ifdef	NETGRAPH_DEBUG
12970935Sjulian	ND_MAGIC,
13070935Sjulian	__FILE__,
13170935Sjulian	__LINE__,
13270935Sjulian	{NULL}
13370935Sjulian#endif	/* NETGRAPH_DEBUG */
13470935Sjulian};
13570935Sjulian
13670935Sjulianstruct ng_hook ng_deadhook = {
13770935Sjulian	"dead",
13870935Sjulian	NULL,		/* private */
13970935Sjulian	HK_INVALID | HK_DEAD,
14070935Sjulian	1,		/* refs always >= 1 */
14170935Sjulian	&ng_deadhook,	/* Peer is self */
14270935Sjulian	&ng_deadnode,	/* attached to deadnode */
14370935Sjulian	{},		/* hooks list */
14471885Sjulian	NULL,		/* override rcvmsg() */
14571885Sjulian	NULL,		/* override rcvdata() */
14670935Sjulian#ifdef	NETGRAPH_DEBUG
14770935Sjulian	HK_MAGIC,
14870935Sjulian	__FILE__,
14970935Sjulian	__LINE__,
15070935Sjulian	{NULL}
15170935Sjulian#endif	/* NETGRAPH_DEBUG */
15270935Sjulian};
15370935Sjulian
15470935Sjulian/*
15570935Sjulian * END DEAD STRUCTURES
15670935Sjulian */
15770700Sjulian/* List nodes with unallocated work */
15870700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
15971902Sjulianstatic struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
16070700Sjulian
16152419Sjulian/* List of installed types */
16270700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
16370700Sjulianstatic struct mtx	ng_typelist_mtx;
16452419Sjulian
16570700Sjulian/* Hash related definitions */
16671354Sjulian/* XXX Don't need to initialise them because it's a LIST */
16771354Sjulian#define NG_ID_HASH_SIZE 32 /* most systems wont need even this many */
16871354Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
16970700Sjulianstatic struct mtx	ng_idhash_mtx;
17071354Sjulian/* Method to find a node.. used twice so do it here */
17171354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
17271354Sjulian#define NG_IDHASH_FIND(ID, node)					\
17371354Sjulian	do { 								\
174131008Srwatson		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
17571354Sjulian		LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)],	\
17671354Sjulian						nd_idnodes) {		\
17771354Sjulian			if (NG_NODE_IS_VALID(node)			\
17871354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
17971354Sjulian				break;					\
18071354Sjulian			}						\
18171354Sjulian		}							\
18271354Sjulian	} while (0)
18352722Sjulian
18470700Sjulian
18552419Sjulian/* Internal functions */
18652419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
18770700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
18852722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
18952419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
19070700Sjulianstatic void	ng_worklist_remove(node_p node);
19152419Sjulianstatic void	ngintr(void);
19274078Sjulianstatic int	ng_apply_item(node_p node, item_p item);
19370700Sjulianstatic void	ng_flush_input_queue(struct ng_queue * ngq);
19470700Sjulianstatic void	ng_setisr(node_p node);
19570700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
19671047Sjulianstatic int	ng_con_nodes(node_p node, const char *name, node_p node2,
19771047Sjulian							const char *name2);
19871849Sjulianstatic void	ng_con_part2(node_p node, hook_p hook, void *arg1, int arg2);
19971849Sjulianstatic void	ng_con_part3(node_p node, hook_p hook, void *arg1, int arg2);
20071047Sjulianstatic int	ng_mkpeer(node_p node, const char *name,
20171047Sjulian						const char *name2, char *type);
20252419Sjulian
20370784Sjulian/* imported , these used to be externally visible, some may go back */
20470700Sjulianvoid	ng_destroy_hook(hook_p hook);
20570700Sjuliannode_p	ng_name2noderef(node_p node, const char *name);
20670700Sjulianint	ng_path2noderef(node_p here, const char *path,
20770700Sjulian	node_p *dest, hook_p *lasthook);
20870700Sjulianstruct	ng_type *ng_findtype(const char *type);
20970700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
21070700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
21171849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
21270784Sjulianvoid	ng_unname(node_p node);
21370700Sjulian
21470700Sjulian
21552419Sjulian/* Our own netgraph malloc type */
21652419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
21770700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
21870700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
21970700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
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
326131933Smarcel/* Set this to kdb_enter("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									\
338113255Sdes		M_ASSERTPKTHDR(m);					\
33953403Sarchie		for (total = 0, n = (m); n != NULL; n = n->m_next)	\
34053403Sarchie			total += n->m_len;				\
34153403Sarchie		if ((m)->m_pkthdr.len != total) {			\
34253403Sarchie			panic("%s: %d != %d",				\
34387599Sobrien			    __func__, (m)->m_pkthdr.len, total);	\
34453403Sarchie		}							\
34553403Sarchie	} while (0)
34653403Sarchie#else
34753403Sarchie#define CHECK_DATA_MBUF(m)
34853403Sarchie#endif
34952722Sjulian
35053403Sarchie
35152419Sjulian/************************************************************************
35253913Sarchie	Parse type definitions for generic messages
35353913Sarchie************************************************************************/
35453913Sarchie
35553913Sarchie/* Handy structure parse type defining macro */
35653913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
35797685Sarchiestatic const struct ng_parse_struct_field				\
35897685Sarchie	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
35953913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
36053913Sarchie	&ng_parse_struct_type,						\
36197685Sarchie	&ng_ ## lo ## _type_fields					\
36253913Sarchie}
36353913Sarchie
36453913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
36553913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
36653913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
36753913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
36853913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
36953913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
37053913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
37153913Sarchie
37253913Sarchie/* Get length of an array when the length is stored as a 32 bit
37372645Sasmodai   value immediately preceding the array -- as with struct namelist
37453913Sarchie   and struct typelist. */
37553913Sarchiestatic int
37653913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
37753913Sarchie	const u_char *start, const u_char *buf)
37853913Sarchie{
37953913Sarchie	return *((const u_int32_t *)(buf - 4));
38053913Sarchie}
38153913Sarchie
38253913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
38353913Sarchiestatic int
38453913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
38553913Sarchie	const u_char *start, const u_char *buf)
38653913Sarchie{
38753913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
38853913Sarchie
38953913Sarchie	return hl->nodeinfo.hooks;
39053913Sarchie}
39153913Sarchie
39253913Sarchie/* Array type for a variable length array of struct namelist */
39353913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
39453913Sarchie	&ng_generic_nodeinfo_type,
39553913Sarchie	&ng_generic_list_getLength
39653913Sarchie};
39753913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
39853913Sarchie	&ng_parse_array_type,
39953913Sarchie	&ng_nodeinfoarray_type_info
40053913Sarchie};
40153913Sarchie
40253913Sarchie/* Array type for a variable length array of struct typelist */
40353913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
40453913Sarchie	&ng_generic_typeinfo_type,
40553913Sarchie	&ng_generic_list_getLength
40653913Sarchie};
40753913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
40853913Sarchie	&ng_parse_array_type,
40953913Sarchie	&ng_typeinfoarray_type_info
41053913Sarchie};
41153913Sarchie
41253913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
41353913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
41453913Sarchie	&ng_generic_linkinfo_type,
41553913Sarchie	&ng_generic_linkinfo_getLength
41653913Sarchie};
41753913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
41853913Sarchie	&ng_parse_array_type,
41953913Sarchie	&ng_generic_linkinfo_array_type_info
42053913Sarchie};
42153913Sarchie
42253913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
42353913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
42453913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
42553913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
42653913Sarchie	(&ng_generic_nodeinfoarray_type));
42753913Sarchie
42853913Sarchie/* List of commands and how to convert arguments to/from ASCII */
42953913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
43053913Sarchie	{
43153913Sarchie	  NGM_GENERIC_COOKIE,
43253913Sarchie	  NGM_SHUTDOWN,
43353913Sarchie	  "shutdown",
43453913Sarchie	  NULL,
43553913Sarchie	  NULL
43653913Sarchie	},
43753913Sarchie	{
43853913Sarchie	  NGM_GENERIC_COOKIE,
43953913Sarchie	  NGM_MKPEER,
44053913Sarchie	  "mkpeer",
44153913Sarchie	  &ng_generic_mkpeer_type,
44253913Sarchie	  NULL
44353913Sarchie	},
44453913Sarchie	{
44553913Sarchie	  NGM_GENERIC_COOKIE,
44653913Sarchie	  NGM_CONNECT,
44753913Sarchie	  "connect",
44853913Sarchie	  &ng_generic_connect_type,
44953913Sarchie	  NULL
45053913Sarchie	},
45153913Sarchie	{
45253913Sarchie	  NGM_GENERIC_COOKIE,
45353913Sarchie	  NGM_NAME,
45453913Sarchie	  "name",
45553913Sarchie	  &ng_generic_name_type,
45653913Sarchie	  NULL
45753913Sarchie	},
45853913Sarchie	{
45953913Sarchie	  NGM_GENERIC_COOKIE,
46053913Sarchie	  NGM_RMHOOK,
46153913Sarchie	  "rmhook",
46253913Sarchie	  &ng_generic_rmhook_type,
46353913Sarchie	  NULL
46453913Sarchie	},
46553913Sarchie	{
46653913Sarchie	  NGM_GENERIC_COOKIE,
46753913Sarchie	  NGM_NODEINFO,
46853913Sarchie	  "nodeinfo",
46953913Sarchie	  NULL,
47053913Sarchie	  &ng_generic_nodeinfo_type
47153913Sarchie	},
47253913Sarchie	{
47353913Sarchie	  NGM_GENERIC_COOKIE,
47453913Sarchie	  NGM_LISTHOOKS,
47553913Sarchie	  "listhooks",
47653913Sarchie	  NULL,
47753913Sarchie	  &ng_generic_hooklist_type
47853913Sarchie	},
47953913Sarchie	{
48053913Sarchie	  NGM_GENERIC_COOKIE,
48153913Sarchie	  NGM_LISTNAMES,
48253913Sarchie	  "listnames",
48353913Sarchie	  NULL,
48453913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
48553913Sarchie	},
48653913Sarchie	{
48753913Sarchie	  NGM_GENERIC_COOKIE,
48853913Sarchie	  NGM_LISTNODES,
48953913Sarchie	  "listnodes",
49053913Sarchie	  NULL,
49153913Sarchie	  &ng_generic_listnodes_type
49253913Sarchie	},
49353913Sarchie	{
49453913Sarchie	  NGM_GENERIC_COOKIE,
49553913Sarchie	  NGM_LISTTYPES,
49653913Sarchie	  "listtypes",
49753913Sarchie	  NULL,
49853913Sarchie	  &ng_generic_typeinfo_type
49953913Sarchie	},
50053913Sarchie	{
50153913Sarchie	  NGM_GENERIC_COOKIE,
50262471Sphk	  NGM_TEXT_CONFIG,
50362471Sphk	  "textconfig",
50462471Sphk	  NULL,
50562471Sphk	  &ng_parse_string_type
50662471Sphk	},
50762471Sphk	{
50862471Sphk	  NGM_GENERIC_COOKIE,
50953913Sarchie	  NGM_TEXT_STATUS,
51053913Sarchie	  "textstatus",
51153913Sarchie	  NULL,
51253913Sarchie	  &ng_parse_string_type
51353913Sarchie	},
51453913Sarchie	{
51553913Sarchie	  NGM_GENERIC_COOKIE,
51653913Sarchie	  NGM_ASCII2BINARY,
51753913Sarchie	  "ascii2binary",
51853913Sarchie	  &ng_parse_ng_mesg_type,
51953913Sarchie	  &ng_parse_ng_mesg_type
52053913Sarchie	},
52153913Sarchie	{
52253913Sarchie	  NGM_GENERIC_COOKIE,
52353913Sarchie	  NGM_BINARY2ASCII,
52453913Sarchie	  "binary2ascii",
52553913Sarchie	  &ng_parse_ng_mesg_type,
52653913Sarchie	  &ng_parse_ng_mesg_type
52753913Sarchie	},
52853913Sarchie	{ 0 }
52953913Sarchie};
53053913Sarchie
53153913Sarchie/************************************************************************
53252419Sjulian			Node routines
53352419Sjulian************************************************************************/
53452419Sjulian
53552419Sjulian/*
53652419Sjulian * Instantiate a node of the requested type
53752419Sjulian */
53852419Sjulianint
53952419Sjulianng_make_node(const char *typename, node_p *nodepp)
54052419Sjulian{
54152419Sjulian	struct ng_type *type;
54270700Sjulian	int	error;
54352419Sjulian
54452419Sjulian	/* Check that the type makes sense */
54552419Sjulian	if (typename == NULL) {
54671047Sjulian		TRAP_ERROR();
54752419Sjulian		return (EINVAL);
54852419Sjulian	}
54952419Sjulian
55052419Sjulian	/* Locate the node type */
55152419Sjulian	if ((type = ng_findtype(typename)) == NULL) {
552125028Sharti		char filename[NG_TYPESIZ + 3];
55352419Sjulian		linker_file_t lf;
55452419Sjulian		int error;
55552419Sjulian
55652419Sjulian		/* Not found, try to load it as a loadable module */
55769923Sjulian		snprintf(filename, sizeof(filename), "ng_%s", typename);
558101241Smux		error = linker_load_module(NULL, filename, NULL, NULL, &lf);
55952419Sjulian		if (error != 0)
56052419Sjulian			return (error);
56152419Sjulian		lf->userrefs++;		/* pretend loaded by the syscall */
56252419Sjulian
56352419Sjulian		/* Try again, as now the type should have linked itself in */
56452419Sjulian		if ((type = ng_findtype(typename)) == NULL)
56552419Sjulian			return (ENXIO);
56652419Sjulian	}
56752419Sjulian
56870700Sjulian	/*
56970700Sjulian	 * If we have a constructor, then make the node and
57070700Sjulian	 * call the constructor to do type specific initialisation.
57170700Sjulian	 */
57270700Sjulian	if (type->constructor != NULL) {
57370700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
57470700Sjulian			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
57570784Sjulian				NG_NODE_UNREF(*nodepp);
57670700Sjulian			}
57770700Sjulian		}
57870700Sjulian	} else {
57970700Sjulian		/*
58070700Sjulian		 * Node has no constructor. We cannot ask for one
58170700Sjulian		 * to be made. It must be brought into existance by
58270935Sjulian		 * some external agency. The external agency should
58370700Sjulian		 * call ng_make_node_common() directly to get the
58470700Sjulian		 * netgraph part initialised.
58570700Sjulian		 */
58671047Sjulian		TRAP_ERROR();
58770700Sjulian		error = EINVAL;
58870700Sjulian	}
58970700Sjulian	return (error);
59052419Sjulian}
59152419Sjulian
59252419Sjulian/*
59370700Sjulian * Generic node creation. Called by node initialisation for externally
59470700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
59552419Sjulian * The returned node has a reference count of 1.
59652419Sjulian */
59752419Sjulianint
59852419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
59952419Sjulian{
60052419Sjulian	node_p node;
60152419Sjulian
60252419Sjulian	/* Require the node type to have been already installed */
60352419Sjulian	if (ng_findtype(type->name) == NULL) {
60471047Sjulian		TRAP_ERROR();
60552419Sjulian		return (EINVAL);
60652419Sjulian	}
60752419Sjulian
60852419Sjulian	/* Make a node and try attach it to the type */
60970784Sjulian	NG_ALLOC_NODE(node);
61052419Sjulian	if (node == NULL) {
61171047Sjulian		TRAP_ERROR();
61252419Sjulian		return (ENOMEM);
61352419Sjulian	}
61470784Sjulian	node->nd_type = type;
61570784Sjulian	NG_NODE_REF(node);				/* note reference */
61652419Sjulian	type->refs++;
61752419Sjulian
61893818Sjhb	mtx_init(&node->nd_input_queue.q_mtx, "ng_node", NULL, MTX_SPIN);
61970784Sjulian	node->nd_input_queue.queue = NULL;
62070784Sjulian	node->nd_input_queue.last = &node->nd_input_queue.queue;
62170784Sjulian	node->nd_input_queue.q_flags = 0;
62270784Sjulian	node->nd_input_queue.q_node = node;
62352419Sjulian
62452419Sjulian	/* Initialize hook list for new node */
62570784Sjulian	LIST_INIT(&node->nd_hooks);
62652419Sjulian
62770700Sjulian	/* Link us into the node linked list */
62872200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
62970784Sjulian	LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes);
63072200Sbmilekic	mtx_unlock(&ng_nodelist_mtx);
63170700Sjulian
63270700Sjulian
63352722Sjulian	/* get an ID and put us in the hash chain */
63472200Sbmilekic	mtx_lock(&ng_idhash_mtx);
63570784Sjulian	for (;;) { /* wrap protection, even if silly */
63670700Sjulian		node_p node2 = NULL;
63770784Sjulian		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
63871354Sjulian
63970784Sjulian		/* Is there a problem with the new number? */
64071354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
64171354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
64270784Sjulian			break;
64370700Sjulian		}
64470784Sjulian	}
64571354Sjulian	LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
64670784Sjulian							node, nd_idnodes);
64772200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
64852722Sjulian
64952419Sjulian	/* Done */
65052419Sjulian	*nodepp = node;
65152419Sjulian	return (0);
65252419Sjulian}
65352419Sjulian
65452419Sjulian/*
65552419Sjulian * Forceably start the shutdown process on a node. Either call
65652419Sjulian * it's shutdown method, or do the default shutdown if there is
65752419Sjulian * no type-specific method.
65852419Sjulian *
65970700Sjulian * We can only be called form a shutdown message, so we know we have
66070939Sjulian * a writer lock, and therefore exclusive access. It also means
66170939Sjulian * that we should not be on the work queue, but we check anyhow.
66270700Sjulian *
66370700Sjulian * Persistent node types must have a type-specific method which
66470939Sjulian * Allocates a new node in which case, this one is irretrievably going away,
66570939Sjulian * or cleans up anything it needs, and just makes the node valid again,
66670939Sjulian * in which case we allow the node to survive.
66770939Sjulian *
66870939Sjulian * XXX We need to think of how to tell a persistant node that we
66970939Sjulian * REALLY need to go away because the hardware has gone or we
67070939Sjulian * are rebooting.... etc.
67152419Sjulian */
67252419Sjulianvoid
67371849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
67452419Sjulian{
67570939Sjulian	hook_p hook;
67670939Sjulian
67752419Sjulian	/* Check if it's already shutting down */
67870784Sjulian	if ((node->nd_flags & NG_CLOSING) != 0)
67952419Sjulian		return;
68052419Sjulian
68171849Sjulian	if (node == &ng_deadnode) {
68271849Sjulian		printf ("shutdown called on deadnode\n");
68371849Sjulian		return;
68471849Sjulian	}
68571849Sjulian
68652419Sjulian	/* Add an extra reference so it doesn't go away during this */
68770784Sjulian	NG_NODE_REF(node);
68852419Sjulian
68970784Sjulian	/*
69070784Sjulian	 * Mark it invalid so any newcomers know not to try use it
69170784Sjulian	 * Also add our own mark so we can't recurse
69270784Sjulian	 * note that NG_INVALID does not do this as it's also set during
69370784Sjulian	 * creation
69470784Sjulian	 */
69570784Sjulian	node->nd_flags |= NG_INVALID|NG_CLOSING;
69652419Sjulian
697129836Sjulian	/* If node has its pre-shutdown method, then call it first*/
698129836Sjulian	if (node->nd_type && node->nd_type->close)
699129836Sjulian		(*node->nd_type->close)(node);
700129836Sjulian
70170939Sjulian	/* Notify all remaining connected nodes to disconnect */
70270939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
70370939Sjulian		ng_destroy_hook(hook);
70470784Sjulian
70570700Sjulian	/*
70670700Sjulian	 * Drain the input queue forceably.
70770784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
70870784Sjulian	 * Theoretically we came here from a queue entry that was added
70970784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
71071902Sjulian	 * Also removes us from worklist if needed.
71170700Sjulian	 */
71270784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
71370700Sjulian
71452419Sjulian	/* Ask the type if it has anything to do in this case */
71570784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
71670784Sjulian		(*node->nd_type->shutdown)(node);
71771849Sjulian		if (NG_NODE_IS_VALID(node)) {
71871849Sjulian			/*
71971849Sjulian			 * Well, blow me down if the node code hasn't declared
72071849Sjulian			 * that it doesn't want to die.
72171849Sjulian			 * Presumably it is a persistant node.
72271849Sjulian			 * If we REALLY want it to go away,
72371849Sjulian			 *  e.g. hardware going away,
72471849Sjulian			 * Our caller should set NG_REALLY_DIE in nd_flags.
72571849Sjulian			 */
72671849Sjulian			node->nd_flags &= ~(NG_INVALID|NG_CLOSING);
72771849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
72871849Sjulian			return;
72971849Sjulian		}
73070700Sjulian	} else {				/* do the default thing */
73170784Sjulian		NG_NODE_UNREF(node);
73252419Sjulian	}
73352419Sjulian
73470784Sjulian	ng_unname(node); /* basically a NOP these days */
73570784Sjulian
73670784Sjulian	/*
73770784Sjulian	 * Remove extra reference, possibly the last
73870784Sjulian	 * Possible other holders of references may include
73970784Sjulian	 * timeout callouts, but theoretically the node's supposed to
74070784Sjulian	 * have cancelled them. Possibly hardware dependencies may
74170784Sjulian	 * force a driver to 'linger' with a reference.
74270784Sjulian	 */
74370784Sjulian	NG_NODE_UNREF(node);
74452419Sjulian}
74552419Sjulian
74674078Sjulian#ifdef	NETGRAPH_DEBUG
74774078Sjulianvoid
74874078Sjulianng_ref_node(node_p node)
74974078Sjulian{
75074078Sjulian	_NG_NODE_REF(node);
75174078Sjulian}
75274078Sjulian#endif
75374078Sjulian
75452419Sjulian/*
75574078Sjulian * Remove a reference to the node, possibly the last.
75674078Sjulian * deadnode always acts as it it were the last.
75752419Sjulian */
75874078Sjulianint
75970784Sjulianng_unref_node(node_p node)
76052419Sjulian{
76170784Sjulian	int     v;
76271047Sjulian
76371047Sjulian	if (node == &ng_deadnode) {
76474078Sjulian		return (0);
76571047Sjulian	}
76671047Sjulian
76770784Sjulian	do {
76874078Sjulian		v = node->nd_refs - 1;
76974078Sjulian	} while (! atomic_cmpset_int(&node->nd_refs, v + 1, v));
77069519Sjulian
77174078Sjulian	if (v == 0) { /* we were the last */
77270700Sjulian
77372200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
77470784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
77570784Sjulian		LIST_REMOVE(node, nd_nodes);
77672200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
77770700Sjulian
77872200Sbmilekic		mtx_lock(&ng_idhash_mtx);
77970784Sjulian		LIST_REMOVE(node, nd_idnodes);
78072200Sbmilekic		mtx_unlock(&ng_idhash_mtx);
78152419Sjulian
78270791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
78370700Sjulian		NG_FREE_NODE(node);
78452419Sjulian	}
78574078Sjulian	return (v);
78652419Sjulian}
78752419Sjulian
78852419Sjulian/************************************************************************
78952722Sjulian			Node ID handling
79052722Sjulian************************************************************************/
79152722Sjulianstatic node_p
79270700Sjulianng_ID2noderef(ng_ID_t ID)
79352722Sjulian{
79470784Sjulian	node_p node;
79572200Sbmilekic	mtx_lock(&ng_idhash_mtx);
79671354Sjulian	NG_IDHASH_FIND(ID, node);
79770784Sjulian	if(node)
79870784Sjulian		NG_NODE_REF(node);
79972200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
80070784Sjulian	return(node);
80152722Sjulian}
80252722Sjulian
80352722Sjulianng_ID_t
80452722Sjulianng_node2ID(node_p node)
80552722Sjulian{
80670912Sjulian	return (node ? NG_NODE_ID(node) : 0);
80752722Sjulian}
80852722Sjulian
80952722Sjulian/************************************************************************
81052419Sjulian			Node name handling
81152419Sjulian************************************************************************/
81252419Sjulian
81352419Sjulian/*
81452419Sjulian * Assign a node a name. Once assigned, the name cannot be changed.
81552419Sjulian */
81652419Sjulianint
81752419Sjulianng_name_node(node_p node, const char *name)
81852419Sjulian{
81952419Sjulian	int i;
82070700Sjulian	node_p node2;
82152419Sjulian
82252419Sjulian	/* Check the name is valid */
823125028Sharti	for (i = 0; i < NG_NODESIZ; i++) {
82452419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
82552419Sjulian			break;
82652419Sjulian	}
82752419Sjulian	if (i == 0 || name[i] != '\0') {
82871047Sjulian		TRAP_ERROR();
82952419Sjulian		return (EINVAL);
83052419Sjulian	}
83152722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
83271047Sjulian		TRAP_ERROR();
83352419Sjulian		return (EINVAL);
83452419Sjulian	}
83552419Sjulian
83652419Sjulian	/* Check the name isn't already being used */
83770700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
83870784Sjulian		NG_NODE_UNREF(node2);
83971047Sjulian		TRAP_ERROR();
84052419Sjulian		return (EADDRINUSE);
84152419Sjulian	}
84252419Sjulian
84370700Sjulian	/* copy it */
844125028Sharti	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
84552419Sjulian
84652419Sjulian	return (0);
84752419Sjulian}
84852419Sjulian
84952419Sjulian/*
85052419Sjulian * Find a node by absolute name. The name should NOT end with ':'
85152419Sjulian * The name "." means "this node" and "[xxx]" means "the node
85252419Sjulian * with ID (ie, at address) xxx".
85352419Sjulian *
85452419Sjulian * Returns the node if found, else NULL.
85570700Sjulian * Eventually should add something faster than a sequential search.
85670784Sjulian * Note it aquires a reference on the node so you can be sure it's still there.
85752419Sjulian */
85852419Sjuliannode_p
85970700Sjulianng_name2noderef(node_p here, const char *name)
86052419Sjulian{
86152722Sjulian	node_p node;
86252722Sjulian	ng_ID_t temp;
86352419Sjulian
86452419Sjulian	/* "." means "this node" */
86570700Sjulian	if (strcmp(name, ".") == 0) {
86670784Sjulian		NG_NODE_REF(here);
86770700Sjulian		return(here);
86870700Sjulian	}
86952419Sjulian
87052419Sjulian	/* Check for name-by-ID */
87152722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
87270700Sjulian		return (ng_ID2noderef(temp));
87352419Sjulian	}
87452419Sjulian
87552419Sjulian	/* Find node by name */
87672200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
87770784Sjulian	LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
87870912Sjulian		if (NG_NODE_IS_VALID(node)
87970912Sjulian		&& NG_NODE_HAS_NAME(node)
88070912Sjulian		&& (strcmp(NG_NODE_NAME(node), name) == 0)) {
88152419Sjulian			break;
88270912Sjulian		}
88352419Sjulian	}
88470700Sjulian	if (node)
88570784Sjulian		NG_NODE_REF(node);
88672200Sbmilekic	mtx_unlock(&ng_nodelist_mtx);
88752419Sjulian	return (node);
88852419Sjulian}
88952419Sjulian
89052419Sjulian/*
891108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the
89252722Sjulian * string is not valid, otherwise returns the value.
89352419Sjulian */
89452722Sjulianstatic ng_ID_t
89552419Sjulianng_decodeidname(const char *name)
89652419Sjulian{
89752816Sarchie	const int len = strlen(name);
89853648Sarchie	char *eptr;
89952816Sarchie	u_long val;
90052419Sjulian
90152816Sarchie	/* Check for proper length, brackets, no leading junk */
90270912Sjulian	if ((len < 3)
90370912Sjulian	|| (name[0] != '[')
90470912Sjulian	|| (name[len - 1] != ']')
90570912Sjulian	|| (!isxdigit(name[1]))) {
90670912Sjulian		return ((ng_ID_t)0);
90770912Sjulian	}
90852419Sjulian
90952816Sarchie	/* Decode number */
91052816Sarchie	val = strtoul(name + 1, &eptr, 16);
91170912Sjulian	if ((eptr - name != len - 1)
91270912Sjulian	|| (val == ULONG_MAX)
91370912Sjulian	|| (val == 0)) {
91453042Sjulian		return ((ng_ID_t)0);
91570912Sjulian	}
91652816Sarchie	return (ng_ID_t)val;
91752419Sjulian}
91852419Sjulian
91952419Sjulian/*
92052419Sjulian * Remove a name from a node. This should only be called
92152419Sjulian * when shutting down and removing the node.
92271849Sjulian * IF we allow name changing this may be more resurected.
92352419Sjulian */
92452419Sjulianvoid
92552419Sjulianng_unname(node_p node)
92652419Sjulian{
92752419Sjulian}
92852419Sjulian
92952419Sjulian/************************************************************************
93052419Sjulian			Hook routines
93152419Sjulian Names are not optional. Hooks are always connected, except for a
93270939Sjulian brief moment within these routines. On invalidation or during creation
93370939Sjulian they are connected to the 'dead' hook.
93452419Sjulian************************************************************************/
93552419Sjulian
93652419Sjulian/*
93752419Sjulian * Remove a hook reference
93852419Sjulian */
93970784Sjulianvoid
94052419Sjulianng_unref_hook(hook_p hook)
94152419Sjulian{
94270784Sjulian	int     v;
94371047Sjulian
94471047Sjulian	if (hook == &ng_deadhook) {
94571047Sjulian		return;
94671047Sjulian	}
94770784Sjulian	do {
94870784Sjulian		v = hook->hk_refs;
94970784Sjulian	} while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1));
95069519Sjulian
95170784Sjulian	if (v == 1) { /* we were the last */
95271047Sjulian		if (_NG_HOOK_NODE(hook)) { /* it'll probably be ng_deadnode */
95371047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
95470784Sjulian			hook->hk_node = NULL;
95570700Sjulian		}
95670700Sjulian		NG_FREE_HOOK(hook);
95770700Sjulian	}
95852419Sjulian}
95952419Sjulian
96052419Sjulian/*
96152419Sjulian * Add an unconnected hook to a node. Only used internally.
96270939Sjulian * Assumes node is locked. (XXX not yet true )
96352419Sjulian */
96452419Sjulianstatic int
96552419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
96652419Sjulian{
96752419Sjulian	hook_p hook;
96852419Sjulian	int error = 0;
96952419Sjulian
97052419Sjulian	/* Check that the given name is good */
97152419Sjulian	if (name == NULL) {
97271047Sjulian		TRAP_ERROR();
97352419Sjulian		return (EINVAL);
97452419Sjulian	}
97554096Sarchie	if (ng_findhook(node, name) != NULL) {
97671047Sjulian		TRAP_ERROR();
97754096Sarchie		return (EEXIST);
97852419Sjulian	}
97952419Sjulian
98052419Sjulian	/* Allocate the hook and link it up */
98170784Sjulian	NG_ALLOC_HOOK(hook);
98252419Sjulian	if (hook == NULL) {
98371047Sjulian		TRAP_ERROR();
98452419Sjulian		return (ENOMEM);
98552419Sjulian	}
98670939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
98770784Sjulian	hook->hk_flags = HK_INVALID;
98870939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
98970784Sjulian	hook->hk_node = node;
99070784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
99152419Sjulian
99270939Sjulian	/* Set hook name */
993125028Sharti	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
99470939Sjulian
99570939Sjulian	/*
99670939Sjulian	 * Check if the node type code has something to say about it
99770939Sjulian	 * If it fails, the unref of the hook will also unref the node.
99870939Sjulian	 */
99970935Sjulian	if (node->nd_type->newhook != NULL) {
100070935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
100170784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
100270700Sjulian			return (error);
100370700Sjulian		}
100470935Sjulian	}
100552419Sjulian	/*
100652419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
100752419Sjulian	 * We'll ask again later when we actually connect the hooks.
100852419Sjulian	 */
100970784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
101070784Sjulian	node->nd_numhooks++;
101170939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
101252419Sjulian
101352419Sjulian	if (hookp)
101452419Sjulian		*hookp = hook;
101570939Sjulian	return (0);
101652419Sjulian}
101752419Sjulian
101852419Sjulian/*
101954096Sarchie * Find a hook
102054096Sarchie *
102154096Sarchie * Node types may supply their own optimized routines for finding
102254096Sarchie * hooks.  If none is supplied, we just do a linear search.
102370939Sjulian * XXX Possibly we should add a reference to the hook?
102454096Sarchie */
102554096Sarchiehook_p
102654096Sarchieng_findhook(node_p node, const char *name)
102754096Sarchie{
102854096Sarchie	hook_p hook;
102954096Sarchie
103070784Sjulian	if (node->nd_type->findhook != NULL)
103170784Sjulian		return (*node->nd_type->findhook)(node, name);
103270784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
103370912Sjulian		if (NG_HOOK_IS_VALID(hook)
103470917Sarchie		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
103554096Sarchie			return (hook);
103654096Sarchie	}
103754096Sarchie	return (NULL);
103854096Sarchie}
103954096Sarchie
104054096Sarchie/*
104152419Sjulian * Destroy a hook
104252419Sjulian *
104352419Sjulian * As hooks are always attached, this really destroys two hooks.
104452419Sjulian * The one given, and the one attached to it. Disconnect the hooks
104570939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
104670939Sjulian * hook so that it can still exist after we depart. We then
104770939Sjulian * send the peer its own destroy message. This ensures that we only
104870939Sjulian * interact with the peer's structures when it is locked processing that
104970939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
105070939Sjulian * the peer hook and node are still going to exist until
105170939Sjulian * we are finished there as the hook holds a ref on the node.
105270939Sjulian * We run this same code again on the peer hook, but that time it is already
105370939Sjulian * attached to the 'dead' hook.
105471047Sjulian *
105571047Sjulian * This routine is called at all stages of hook creation
105671047Sjulian * on error detection and must be able to handle any such stage.
105752419Sjulian */
105852419Sjulianvoid
105952419Sjulianng_destroy_hook(hook_p hook)
106052419Sjulian{
106170784Sjulian	hook_p peer = NG_HOOK_PEER(hook);
106271047Sjulian	node_p node = NG_HOOK_NODE(hook);
106352419Sjulian
106471047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
106571047Sjulian		printf("ng_destroy_hook called on deadhook\n");
106671047Sjulian		return;
106771047Sjulian	}
106870784Sjulian	hook->hk_flags |= HK_INVALID;		/* as soon as possible */
106970939Sjulian	if (peer && (peer != &ng_deadhook)) {
107070939Sjulian		/*
107170939Sjulian		 * Set the peer to point to ng_deadhook
107270939Sjulian		 * from this moment on we are effectively independent it.
107370939Sjulian		 * send it an rmhook message of it's own.
107470939Sjulian		 */
107570939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
107670939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
107771047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
107871047Sjulian			/*
107971047Sjulian			 * If it's already divorced from a node,
108071047Sjulian			 * just free it.
108171047Sjulian			 */
108271047Sjulian			/* nothing */
108371047Sjulian		} else {
108471047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
108571047Sjulian		}
108670942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
108770942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
108852419Sjulian	}
108952419Sjulian
109052419Sjulian	/*
109152419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
109252419Sjulian	 * in case the disconnection results in node shutdown.
109352419Sjulian	 */
109471047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
109571047Sjulian		return;
109671047Sjulian	}
109770784Sjulian	LIST_REMOVE(hook, hk_hooks);
109870784Sjulian	node->nd_numhooks--;
109970784Sjulian	if (node->nd_type->disconnect) {
110052419Sjulian		/*
110171047Sjulian		 * The type handler may elect to destroy the node so don't
110271047Sjulian		 * trust its existance after this point. (except
110371047Sjulian		 * that we still hold a reference on it. (which we
110471047Sjulian		 * inherrited from the hook we are destroying)
110552419Sjulian		 */
110670784Sjulian		(*node->nd_type->disconnect) (hook);
110752419Sjulian	}
110871047Sjulian
110971047Sjulian	/*
111071047Sjulian	 * Note that because we will point to ng_deadnode, the original node
111171047Sjulian	 * is not decremented automatically so we do that manually.
111271047Sjulian	 */
111371047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
111471047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
111571047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
111652419Sjulian}
111752419Sjulian
111852419Sjulian/*
111952419Sjulian * Take two hooks on a node and merge the connection so that the given node
112052419Sjulian * is effectively bypassed.
112152419Sjulian */
112252419Sjulianint
112352419Sjulianng_bypass(hook_p hook1, hook_p hook2)
112452419Sjulian{
112570784Sjulian	if (hook1->hk_node != hook2->hk_node) {
112671047Sjulian		TRAP_ERROR();
112752419Sjulian		return (EINVAL);
112870784Sjulian	}
112970784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
113070784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
113152419Sjulian
113270939Sjulian	hook1->hk_peer = &ng_deadhook;
113370939Sjulian	hook2->hk_peer = &ng_deadhook;
113470939Sjulian
113552419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
113652419Sjulian	ng_destroy_hook(hook1);
113752419Sjulian	ng_destroy_hook(hook2);
113852419Sjulian	return (0);
113952419Sjulian}
114052419Sjulian
114152419Sjulian/*
114252419Sjulian * Install a new netgraph type
114352419Sjulian */
114452419Sjulianint
114552419Sjulianng_newtype(struct ng_type *tp)
114652419Sjulian{
114752419Sjulian	const size_t namelen = strlen(tp->name);
114852419Sjulian
114952419Sjulian	/* Check version and type name fields */
115070159Sjulian	if ((tp->version != NG_ABI_VERSION)
115170159Sjulian	|| (namelen == 0)
1152125028Sharti	|| (namelen >= NG_TYPESIZ)) {
115371047Sjulian		TRAP_ERROR();
1154131374Sjulian		if (tp->version != NG_ABI_VERSION) {
1155131374Sjulian			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
1156131374Sjulian		}
115752419Sjulian		return (EINVAL);
115852419Sjulian	}
115952419Sjulian
116052419Sjulian	/* Check for name collision */
116152419Sjulian	if (ng_findtype(tp->name) != NULL) {
116271047Sjulian		TRAP_ERROR();
116352419Sjulian		return (EEXIST);
116452419Sjulian	}
116552419Sjulian
116670700Sjulian
116752419Sjulian	/* Link in new type */
116872200Sbmilekic	mtx_lock(&ng_typelist_mtx);
116970700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
117071603Sjulian	tp->refs = 1;	/* first ref is linked list */
117172200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
117252419Sjulian	return (0);
117352419Sjulian}
117452419Sjulian
117552419Sjulian/*
117680222Sjulian * unlink a netgraph type
117780222Sjulian * If no examples exist
117880222Sjulian */
117980222Sjulianint
118080222Sjulianng_rmtype(struct ng_type *tp)
118180222Sjulian{
118280222Sjulian	/* Check for name collision */
118380222Sjulian	if (tp->refs != 1) {
118480222Sjulian		TRAP_ERROR();
118580222Sjulian		return (EBUSY);
118680222Sjulian	}
118780222Sjulian
118880222Sjulian	/* Unlink type */
118980222Sjulian	mtx_lock(&ng_typelist_mtx);
119080222Sjulian	LIST_REMOVE(tp, types);
119180222Sjulian	mtx_unlock(&ng_typelist_mtx);
119280222Sjulian	return (0);
119380222Sjulian}
119480222Sjulian
119580222Sjulian/*
119652419Sjulian * Look for a type of the name given
119752419Sjulian */
119852419Sjulianstruct ng_type *
119952419Sjulianng_findtype(const char *typename)
120052419Sjulian{
120152419Sjulian	struct ng_type *type;
120252419Sjulian
120372200Sbmilekic	mtx_lock(&ng_typelist_mtx);
120470700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
120552419Sjulian		if (strcmp(type->name, typename) == 0)
120652419Sjulian			break;
120752419Sjulian	}
120872200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
120952419Sjulian	return (type);
121052419Sjulian}
121152419Sjulian
121252419Sjulian/************************************************************************
121352419Sjulian			Composite routines
121452419Sjulian************************************************************************/
121552419Sjulian/*
121671047Sjulian * Connect two nodes using the specified hooks, using queued functions.
121752419Sjulian */
121871849Sjulianstatic void
121971047Sjulianng_con_part3(node_p node, hook_p hook, void *arg1, int arg2)
122052419Sjulian{
122152419Sjulian
122271047Sjulian	/*
122371047Sjulian	 * When we run, we know that the node 'node' is locked for us.
122471047Sjulian	 * Our caller has a reference on the hook.
122571047Sjulian	 * Our caller has a reference on the node.
122671047Sjulian	 * (In this case our caller is ng_apply_item() ).
122771047Sjulian	 * The peer hook has a reference on the hook.
122871849Sjulian	 * We are all set up except for the final call to the node, and
122971849Sjulian	 * the clearing of the INVALID flag.
123071047Sjulian	 */
123171047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
123271047Sjulian		/*
123371047Sjulian		 * The node must have been freed again since we last visited
123471047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
123571047Sjulian		 * We should just release our references and
123671047Sjulian		 * free anything we can think of.
123771047Sjulian		 * Since we know it's been destroyed, and it's our caller
123871047Sjulian		 * that holds the references, just return.
123971047Sjulian		 */
124071849Sjulian		return ;
124152419Sjulian	}
124271047Sjulian	if (hook->hk_node->nd_type->connect) {
124371849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
124471047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
124571849Sjulian			printf("failed in ng_con_part3()\n");
124671849Sjulian			return ;
124771047Sjulian		}
124852419Sjulian	}
124971047Sjulian	/*
125071047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
125171047Sjulian	 * to separate out 'create' and 'invalid' flags.
125271047Sjulian	 * should only set flags on hooks we have locked under our node.
125371047Sjulian	 */
125471047Sjulian	hook->hk_flags &= ~HK_INVALID;
125571849Sjulian	return ;
125671047Sjulian}
125752419Sjulian
125871849Sjulianstatic void
125971047Sjulianng_con_part2(node_p node, hook_p hook, void *arg1, int arg2)
126071047Sjulian{
126171047Sjulian
126252419Sjulian	/*
126371047Sjulian	 * When we run, we know that the node 'node' is locked for us.
126471047Sjulian	 * Our caller has a reference on the hook.
126571047Sjulian	 * Our caller has a reference on the node.
126671047Sjulian	 * (In this case our caller is ng_apply_item() ).
126771047Sjulian	 * The peer hook has a reference on the hook.
126871047Sjulian	 * our node pointer points to the 'dead' node.
126971047Sjulian	 * First check the hook name is unique.
127071849Sjulian	 * Should not happen because we checked before queueing this.
127152419Sjulian	 */
127271047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
127371047Sjulian		TRAP_ERROR();
127471047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
127571849Sjulian		printf("failed in ng_con_part2()\n");
127671849Sjulian		return ;
127771047Sjulian	}
127870939Sjulian	/*
127971047Sjulian	 * Check if the node type code has something to say about it
128071047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
128171047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
128271047Sjulian	 * The peer hook will also be destroyed.
128370939Sjulian	 */
128471047Sjulian	if (node->nd_type->newhook != NULL) {
128571849Sjulian		if ((*node->nd_type->newhook)(node, hook, hook->hk_name)) {
128671047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
128771849Sjulian			printf("failed in ng_con_part2()\n");
128871849Sjulian			return ;
128971047Sjulian		}
129071047Sjulian	}
129171047Sjulian
129271047Sjulian	/*
129371047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
129471047Sjulian	 * We'll ask again later when we actually connect the hooks.
129571047Sjulian	 */
129671047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
129771047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
129871047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
129971047Sjulian	node->nd_numhooks++;
130071047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
130171047Sjulian
130271047Sjulian	/*
130371047Sjulian	 * We now have a symetrical situation, where both hooks have been
130474078Sjulian	 * linked to their nodes, the newhook methods have been called
130571047Sjulian	 * And the references are all correct. The hooks are still marked
130671047Sjulian	 * as invalid, as we have not called the 'connect' methods
130771047Sjulian	 * yet.
130871047Sjulian	 * We can call the local one immediatly as we have the
130971047Sjulian	 * node locked, but we need to queue the remote one.
131071047Sjulian	 */
131171047Sjulian	if (hook->hk_node->nd_type->connect) {
131271849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
131371047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
131471849Sjulian			printf("failed in ng_con_part2(A)\n");
131571849Sjulian			return ;
131671047Sjulian		}
131771047Sjulian	}
131871849Sjulian	if (ng_send_fn(hook->hk_peer->hk_node, hook->hk_peer,
131971849Sjulian			&ng_con_part3, arg1, arg2)) {
132071849Sjulian		printf("failed in ng_con_part2(B)");
132171849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
132271849Sjulian		return ;
132371849Sjulian	}
132471047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
132571849Sjulian	return ;
132652419Sjulian}
132752419Sjulian
132852419Sjulian/*
132971047Sjulian * Connect this node with another node. We assume that this node is
133071047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
133152419Sjulian */
133271047Sjulianstatic int
133352419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
133452419Sjulian{
133552419Sjulian	int     error;
133652419Sjulian	hook_p  hook;
133752419Sjulian	hook_p  hook2;
133852419Sjulian
133971849Sjulian	if (ng_findhook(node2, name2) != NULL) {
134071849Sjulian		return(EEXIST);
134171849Sjulian	}
134270939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
134352419Sjulian		return (error);
134471047Sjulian	/* Allocate the other hook and link it up */
134571047Sjulian	NG_ALLOC_HOOK(hook2);
134671047Sjulian	if (hook == NULL) {
134771047Sjulian		TRAP_ERROR();
134871047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
134971047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
135071047Sjulian		return (ENOMEM);
135171047Sjulian	}
135271047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
135371047Sjulian	hook2->hk_flags = HK_INVALID;
135471047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
135571047Sjulian	hook->hk_peer = hook2;
135671047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
135771047Sjulian	NG_HOOK_REF(hook2);
135871047Sjulian	hook2->hk_node = &ng_deadnode;
1359125028Sharti	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
136071047Sjulian
136171047Sjulian	/*
136271047Sjulian	 * Queue the function above.
136371047Sjulian	 * Procesing continues in that function in the lock context of
136471047Sjulian	 * the other node.
136571047Sjulian	 */
136671849Sjulian	ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0);
136771047Sjulian
136871047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
136971047Sjulian	NG_HOOK_UNREF(hook2);
137071849Sjulian	return (0);
137171047Sjulian}
137271047Sjulian
137371047Sjulian/*
137471047Sjulian * Make a peer and connect.
137571047Sjulian * We assume that the local node is locked.
137671047Sjulian * The new node probably doesn't need a lock until
137771047Sjulian * it has a hook, because it cannot really have any work until then,
137871047Sjulian * but we should think about it a bit more.
137971047Sjulian *
138071047Sjulian * The problem may come if the other node also fires up
138171047Sjulian * some hardware or a timer or some other source of activation,
138271047Sjulian * also it may already get a command msg via it's ID.
138371047Sjulian *
138471047Sjulian * We could use the same method as ng_con_nodes() but we'd have
138571047Sjulian * to add ability to remove the node when failing. (Not hard, just
138671047Sjulian * make arg1 point to the node to remove).
138771047Sjulian * Unless of course we just ignore failure to connect and leave
138871047Sjulian * an unconnected node?
138971047Sjulian */
139071047Sjulianstatic int
139171047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
139271047Sjulian{
139371047Sjulian	node_p  node2;
139471047Sjulian	hook_p  hook1;
139571047Sjulian	hook_p  hook2;
139671047Sjulian	int     error;
139771047Sjulian
139871047Sjulian	if ((error = ng_make_node(type, &node2))) {
139952419Sjulian		return (error);
140052419Sjulian	}
140170939Sjulian
140271047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
140371849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
140471047Sjulian		return (error);
140571047Sjulian	}
140671047Sjulian
140771047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
140871849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
140971047Sjulian		ng_destroy_hook(hook1);
141071047Sjulian		NG_HOOK_UNREF(hook1);
141171047Sjulian		return (error);
141271047Sjulian	}
141371047Sjulian
141470939Sjulian	/*
141571047Sjulian	 * Actually link the two hooks together.
141671047Sjulian	 */
141771047Sjulian	hook1->hk_peer = hook2;
141871047Sjulian	hook2->hk_peer = hook1;
141971047Sjulian
142071047Sjulian	/* Each hook is referenced by the other */
142171047Sjulian	NG_HOOK_REF(hook1);
142271047Sjulian	NG_HOOK_REF(hook2);
142371047Sjulian
142471047Sjulian	/* Give each node the opportunity to veto the pending connection */
142571047Sjulian	if (hook1->hk_node->nd_type->connect) {
142671047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
142771047Sjulian	}
142871047Sjulian
142971047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
143071047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
143171047Sjulian
143271047Sjulian	}
143371047Sjulian
143471047Sjulian	/*
143570939Sjulian	 * drop the references we were holding on the two hooks.
143670939Sjulian	 */
143771047Sjulian	if (error) {
143871047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
143971849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
144071047Sjulian	} else {
144171047Sjulian		/* As a last act, allow the hooks to be used */
144271047Sjulian		hook1->hk_flags &= ~HK_INVALID;
144371047Sjulian		hook2->hk_flags &= ~HK_INVALID;
144471047Sjulian	}
144571047Sjulian	NG_HOOK_UNREF(hook1);
144670939Sjulian	NG_HOOK_UNREF(hook2);
144770939Sjulian	return (error);
144852419Sjulian}
144971047Sjulian
145070700Sjulian/************************************************************************
145170700Sjulian		Utility routines to send self messages
145270700Sjulian************************************************************************/
145370700Sjulian
145471849Sjulian/* Shut this node down as soon as everyone is clear of it */
145571849Sjulian/* Should add arg "immediatly" to jump the queue */
145670700Sjulianint
145771849Sjulianng_rmnode_self(node_p node)
145870700Sjulian{
145971849Sjulian	int		error;
146052419Sjulian
146171849Sjulian	if (node == &ng_deadnode)
146271849Sjulian		return (0);
146372053Sjulian	node->nd_flags |= NG_INVALID;
146471849Sjulian	if (node->nd_flags & NG_CLOSING)
146571849Sjulian		return (0);
146670700Sjulian
146771849Sjulian	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
146871849Sjulian	return (error);
146970700Sjulian}
147070700Sjulian
147171849Sjulianstatic void
147271047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
147371047Sjulian{
147471047Sjulian	ng_destroy_hook(hook);
147571849Sjulian	return ;
147671047Sjulian}
147771047Sjulian
147870935Sjulianint
147970935Sjulianng_rmhook_self(hook_p hook)
148070935Sjulian{
148171047Sjulian	int		error;
148270935Sjulian	node_p node = NG_HOOK_NODE(hook);
148370935Sjulian
148471047Sjulian	if (node == &ng_deadnode)
148571047Sjulian		return (0);
148671047Sjulian
148771047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
148871047Sjulian	return (error);
148970935Sjulian}
149070935Sjulian
149170700Sjulian/***********************************************************************
149252419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
149352419Sjulian *
149452419Sjulian * Such a string can refer to a specific node or a specific hook
149552419Sjulian * on a specific node, depending on how you look at it. In the
149652419Sjulian * latter case, the PATH component must not end in a dot.
149752419Sjulian *
149852419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
149952419Sjulian * of hook names separated by dots. This breaks out the original
150052419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
150152419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
150252419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
150352419Sjulian *
150452419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
150570700Sjulian ***********************************************************************/
150652419Sjulianint
150752419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
150852419Sjulian{
150952419Sjulian	char   *node, *path, *hook;
151052419Sjulian	int     k;
151152419Sjulian
151252419Sjulian	/*
151352419Sjulian	 * Extract absolute NODE, if any
151452419Sjulian	 */
151552419Sjulian	for (path = addr; *path && *path != ':'; path++);
151652419Sjulian	if (*path) {
151752419Sjulian		node = addr;	/* Here's the NODE */
151852419Sjulian		*path++ = '\0';	/* Here's the PATH */
151952419Sjulian
152052419Sjulian		/* Node name must not be empty */
152152419Sjulian		if (!*node)
152252419Sjulian			return -1;
152352419Sjulian
152452419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
152552419Sjulian		if (strcmp(node, ".") != 0) {
152652419Sjulian			for (k = 0; node[k]; k++)
152752419Sjulian				if (node[k] == '.')
152852419Sjulian					return -1;
152952419Sjulian		}
153052419Sjulian	} else {
153152419Sjulian		node = NULL;	/* No absolute NODE */
153252419Sjulian		path = addr;	/* Here's the PATH */
153352419Sjulian	}
153452419Sjulian
153552419Sjulian	/* Snoop for illegal characters in PATH */
153652419Sjulian	for (k = 0; path[k]; k++)
153752419Sjulian		if (path[k] == ':')
153852419Sjulian			return -1;
153952419Sjulian
154052419Sjulian	/* Check for no repeated dots in PATH */
154152419Sjulian	for (k = 0; path[k]; k++)
154252419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
154352419Sjulian			return -1;
154452419Sjulian
154552419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
154652419Sjulian	if (path[0] == '.')
154752419Sjulian		path++;
154852419Sjulian	if (*path && path[strlen(path) - 1] == '.')
154952419Sjulian		path[strlen(path) - 1] = 0;
155052419Sjulian
155152419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
155252419Sjulian	if (*path) {
155352419Sjulian		for (hook = path, k = 0; path[k]; k++)
155452419Sjulian			if (path[k] == '.') {
155552419Sjulian				hook = NULL;
155652419Sjulian				break;
155752419Sjulian			}
155852419Sjulian	} else
155952419Sjulian		path = hook = NULL;
156052419Sjulian
156152419Sjulian	/* Done */
156252419Sjulian	if (nodep)
156352419Sjulian		*nodep = node;
156452419Sjulian	if (pathp)
156552419Sjulian		*pathp = path;
156652419Sjulian	if (hookp)
156752419Sjulian		*hookp = hook;
156852419Sjulian	return (0);
156952419Sjulian}
157052419Sjulian
157152419Sjulian/*
157252419Sjulian * Given a path, which may be absolute or relative, and a starting node,
157370700Sjulian * return the destination node.
157452419Sjulian */
157552419Sjulianint
157670700Sjulianng_path2noderef(node_p here, const char *address,
157770700Sjulian				node_p *destp, hook_p *lasthook)
157852419Sjulian{
1579125028Sharti	char    fullpath[NG_PATHSIZ];
158052419Sjulian	char   *nodename, *path, pbuf[2];
158170700Sjulian	node_p  node, oldnode;
158252419Sjulian	char   *cp;
158359728Sjulian	hook_p hook = NULL;
158452419Sjulian
158552419Sjulian	/* Initialize */
158670784Sjulian	if (destp == NULL) {
158771047Sjulian		TRAP_ERROR();
158852419Sjulian		return EINVAL;
158970784Sjulian	}
159052419Sjulian	*destp = NULL;
159152419Sjulian
159252419Sjulian	/* Make a writable copy of address for ng_path_parse() */
159352419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
159452419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
159552419Sjulian
159652419Sjulian	/* Parse out node and sequence of hooks */
159752419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
159871047Sjulian		TRAP_ERROR();
159952419Sjulian		return EINVAL;
160052419Sjulian	}
160152419Sjulian	if (path == NULL) {
160252419Sjulian		pbuf[0] = '.';	/* Needs to be writable */
160352419Sjulian		pbuf[1] = '\0';
160452419Sjulian		path = pbuf;
160552419Sjulian	}
160652419Sjulian
160770700Sjulian	/*
160870700Sjulian	 * For an absolute address, jump to the starting node.
160970700Sjulian	 * Note that this holds a reference on the node for us.
161070700Sjulian	 * Don't forget to drop the reference if we don't need it.
161170700Sjulian	 */
161252419Sjulian	if (nodename) {
161370700Sjulian		node = ng_name2noderef(here, nodename);
161452419Sjulian		if (node == NULL) {
161571047Sjulian			TRAP_ERROR();
161652419Sjulian			return (ENOENT);
161752419Sjulian		}
161870700Sjulian	} else {
161970700Sjulian		if (here == NULL) {
162071047Sjulian			TRAP_ERROR();
162170700Sjulian			return (EINVAL);
162270700Sjulian		}
162352419Sjulian		node = here;
162470784Sjulian		NG_NODE_REF(node);
162570700Sjulian	}
162652419Sjulian
162770700Sjulian	/*
162870700Sjulian	 * Now follow the sequence of hooks
162970700Sjulian	 * XXX
163070700Sjulian	 * We actually cannot guarantee that the sequence
163170700Sjulian	 * is not being demolished as we crawl along it
163270700Sjulian	 * without extra-ordinary locking etc.
163370700Sjulian	 * So this is a bit dodgy to say the least.
163470700Sjulian	 * We can probably hold up some things by holding
163570700Sjulian	 * the nodelist mutex for the time of this
163670700Sjulian	 * crawl if we wanted.. At least that way we wouldn't have to
163770700Sjulian	 * worry about the nodes dissappearing, but the hooks would still
163870700Sjulian	 * be a problem.
163970700Sjulian	 */
164052419Sjulian	for (cp = path; node != NULL && *cp != '\0'; ) {
164152419Sjulian		char *segment;
164252419Sjulian
164352419Sjulian		/*
164452419Sjulian		 * Break out the next path segment. Replace the dot we just
164552419Sjulian		 * found with a NUL; "cp" points to the next segment (or the
164652419Sjulian		 * NUL at the end).
164752419Sjulian		 */
164852419Sjulian		for (segment = cp; *cp != '\0'; cp++) {
164952419Sjulian			if (*cp == '.') {
165052419Sjulian				*cp++ = '\0';
165152419Sjulian				break;
165252419Sjulian			}
165352419Sjulian		}
165452419Sjulian
165552419Sjulian		/* Empty segment */
165652419Sjulian		if (*segment == '\0')
165752419Sjulian			continue;
165852419Sjulian
165952419Sjulian		/* We have a segment, so look for a hook by that name */
166054096Sarchie		hook = ng_findhook(node, segment);
166152419Sjulian
166252419Sjulian		/* Can't get there from here... */
166352419Sjulian		if (hook == NULL
166470784Sjulian		    || NG_HOOK_PEER(hook) == NULL
166570784Sjulian		    || NG_HOOK_NOT_VALID(hook)
166670784Sjulian		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
166771047Sjulian			TRAP_ERROR();
166870784Sjulian			NG_NODE_UNREF(node);
166970784Sjulian#if 0
167070784Sjulian			printf("hooknotvalid %s %s %d %d %d %d ",
167170784Sjulian					path,
167270784Sjulian					segment,
167370784Sjulian					hook == NULL,
167470784Sjulian		     			NG_HOOK_PEER(hook) == NULL,
167570784Sjulian		     			NG_HOOK_NOT_VALID(hook),
167670784Sjulian		     			NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
167770784Sjulian#endif
167852419Sjulian			return (ENOENT);
167952419Sjulian		}
168052419Sjulian
168170700Sjulian		/*
168270700Sjulian		 * Hop on over to the next node
168370700Sjulian		 * XXX
168470700Sjulian		 * Big race conditions here as hooks and nodes go away
168570700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
168670700Sjulian		 * instead of the direct hook in this crawl?
168770700Sjulian		 */
168870700Sjulian		oldnode = node;
168970784Sjulian		if ((node = NG_PEER_NODE(hook)))
169070784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
169170784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
169270784Sjulian		if (NG_NODE_NOT_VALID(node)) {
169370784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
169470700Sjulian			node = NULL;
169570700Sjulian		}
169652419Sjulian	}
169752419Sjulian
169852419Sjulian	/* If node somehow missing, fail here (probably this is not needed) */
169952419Sjulian	if (node == NULL) {
170071047Sjulian		TRAP_ERROR();
170152419Sjulian		return (ENXIO);
170252419Sjulian	}
170352419Sjulian
170452419Sjulian	/* Done */
170552419Sjulian	*destp = node;
170659900Sarchie	if (lasthook != NULL)
170770784Sjulian		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
170852419Sjulian	return (0);
170952419Sjulian}
171052419Sjulian
171170700Sjulian/***************************************************************\
171270700Sjulian* Input queue handling.
171370700Sjulian* All activities are submitted to the node via the input queue
171470700Sjulian* which implements a multiple-reader/single-writer gate.
171570700Sjulian* Items which cannot be handled immeditly are queued.
171670700Sjulian*
171770700Sjulian* read-write queue locking inline functions			*
171870700Sjulian\***************************************************************/
171970700Sjulian
172070700Sjulianstatic __inline item_p ng_dequeue(struct ng_queue * ngq);
172170700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq,
172270700Sjulian					item_p  item);
172370700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq,
172470700Sjulian					item_p  item);
172570700Sjulianstatic __inline void	ng_leave_read(struct ng_queue * ngq);
172670700Sjulianstatic __inline void	ng_leave_write(struct ng_queue * ngq);
172770700Sjulianstatic __inline void	ng_queue_rw(struct ng_queue * ngq,
172870700Sjulian					item_p  item, int rw);
172970700Sjulian
173052419Sjulian/*
173170700Sjulian * Definition of the bits fields in the ng_queue flag word.
173270700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
173370700Sjulian * with them.
173470700Sjulian *
173571902Sjulian * The ordering here may be important! don't shuffle these.
173652419Sjulian */
173770700Sjulian/*-
173870700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
173970700Sjulian                       |
174070700Sjulian                       V
174170700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
174270700Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
174371902Sjulian| |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W|
174471902Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P|
174570700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
174671902Sjulian\___________________________ ____________________________/ | | |
174771902Sjulian                            V                              | | |
174871902Sjulian                  [active reader count]                    | | |
174970700Sjulian                                                           | | |
175071902Sjulian          Read Pending ------------------------------------+ | |
175170700Sjulian                                                             | |
175271902Sjulian          Active Writer -------------------------------------+ |
175370700Sjulian                                                               |
175471902Sjulian          Write Pending ---------------------------------------+
175571902Sjulian
175671902Sjulian
175770700Sjulian*/
175871902Sjulian#define WRITE_PENDING	0x00000001
175971902Sjulian#define WRITER_ACTIVE	0x00000002
176071902Sjulian#define READ_PENDING	0x00000004
176171902Sjulian#define READER_INCREMENT 0x00000008
176271902Sjulian#define READER_MASK	0xfffffff0	/* Not valid if WRITER_ACTIVE is set */
176370700Sjulian#define SAFETY_BARRIER	0x00100000	/* 64K items queued should be enough */
176471902Sjulian
176571902Sjulian/* Defines of more elaborate states on the queue */
176671902Sjulian/* Mask of bits a read cares about */
176771902Sjulian#define NGQ_RMASK	(WRITE_PENDING|WRITER_ACTIVE|READ_PENDING)
176871902Sjulian
176971902Sjulian/* Mask of bits a write cares about */
177071902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
177171902Sjulian
177271902Sjulian/* tests to decide if we could get a read or write off the queue */
177371902Sjulian#define CAN_GET_READ(flag)	((flag & NGQ_RMASK) == READ_PENDING)
177471902Sjulian#define CAN_GET_WRITE(flag)	((flag & NGQ_WMASK) == WRITE_PENDING)
177571902Sjulian
177671902Sjulian/* Is there a chance of getting ANY work off the queue? */
177771902Sjulian#define CAN_GET_WORK(flag)	(CAN_GET_READ(flag) || CAN_GET_WRITE(flag))
177871902Sjulian
177970700Sjulian/*
178070700Sjulian * Taking into account the current state of the queue and node, possibly take
178170700Sjulian * the next entry off the queue and return it. Return NULL if there was
178270700Sjulian * nothing we could return, either because there really was nothing there, or
178370700Sjulian * because the node was in a state where it cannot yet process the next item
178470700Sjulian * on the queue.
178570700Sjulian *
178670700Sjulian * This MUST MUST MUST be called with the mutex held.
178770700Sjulian */
178870700Sjulianstatic __inline item_p
178970700Sjulianng_dequeue(struct ng_queue *ngq)
179070700Sjulian{
179170700Sjulian	item_p item;
179270700Sjulian	u_int		add_arg;
179371902Sjulian
179471902Sjulian	if (CAN_GET_READ(ngq->q_flags)) {
179570700Sjulian		/*
179671902Sjulian		 * Head of queue is a reader and we have no write active.
179771902Sjulian		 * We don't care how many readers are already active.
179871902Sjulian		 * Adjust the flags for the item we are about to dequeue.
179971902Sjulian		 * Add the correct increment for the reader count as well.
180070700Sjulian		 */
180171902Sjulian		add_arg = (READER_INCREMENT - READ_PENDING);
180271902Sjulian	} else if (CAN_GET_WRITE(ngq->q_flags)) {
180370700Sjulian		/*
180470700Sjulian		 * There is a pending write, no readers and no active writer.
180570700Sjulian		 * This means we can go ahead with the pending writer. Note
180670700Sjulian		 * the fact that we now have a writer, ready for when we take
180770700Sjulian		 * it off the queue.
180870700Sjulian		 *
180970700Sjulian		 * We don't need to worry about a possible collision with the
181070700Sjulian		 * fasttrack reader.
181170700Sjulian		 *
181270700Sjulian		 * The fasttrack thread may take a long time to discover that we
181370700Sjulian		 * are running so we would have an inconsistent state in the
181470700Sjulian		 * flags for a while. Since we ignore the reader count
181570700Sjulian		 * entirely when the WRITER_ACTIVE flag is set, this should
181670700Sjulian		 * not matter (in fact it is defined that way). If it tests
181770700Sjulian		 * the flag before this operation, the WRITE_PENDING flag
181870700Sjulian		 * will make it fail, and if it tests it later, the
181971902Sjulian		 * WRITER_ACTIVE flag will do the same. If it is SO slow that
182070700Sjulian		 * we have actually completed the operation, and neither flag
182170700Sjulian		 * is set (nor the READ_PENDING) by the time that it tests
182270700Sjulian		 * the flags, then it is actually ok for it to continue. If
182370700Sjulian		 * it completes and we've finished and the read pending is
182470700Sjulian		 * set it still fails.
182570700Sjulian		 *
182670700Sjulian		 * So we can just ignore it,  as long as we can ensure that the
182770700Sjulian		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
182870700Sjulian		 * state is atomic.
182970700Sjulian		 *
183070700Sjulian		 * After failing, first it will be held back by the mutex, then
183170700Sjulian		 * when it can proceed, it will queue its request, then it
183270700Sjulian		 * would arrive at this function. Usually it will have to
183371902Sjulian		 * leave empty handed because the ACTIVE WRITER bit will be
183470700Sjulian		 * set.
183571902Sjulian		 *
183671902Sjulian		 * Adjust the flags for the item we are about to dequeue
183771902Sjulian		 * and for the new active writer.
183870700Sjulian		 */
183971902Sjulian		add_arg = (WRITER_ACTIVE - WRITE_PENDING);
184070700Sjulian		/*
184170700Sjulian		 * We want to write "active writer, no readers " Now go make
184270700Sjulian		 * it true. In fact there may be a number in the readers
184370700Sjulian		 * count but we know it is not true and will be fixed soon.
184470700Sjulian		 * We will fix the flags for the next pending entry in a
184570700Sjulian		 * moment.
184670700Sjulian		 */
184770700Sjulian	} else {
184870700Sjulian		/*
184970700Sjulian		 * We can't dequeue anything.. return and say so. Probably we
185070700Sjulian		 * have a write pending and the readers count is non zero. If
185170700Sjulian		 * we got here because a reader hit us just at the wrong
185270700Sjulian		 * moment with the fasttrack code, and put us in a strange
185370700Sjulian		 * state, then it will be through in just a moment, (as soon
185470700Sjulian		 * as we release the mutex) and keep things moving.
185571902Sjulian		 * Make sure we remove ourselves from the work queue.
185670700Sjulian		 */
185771902Sjulian		ng_worklist_remove(ngq->q_node);
185870700Sjulian		return (0);
185970700Sjulian	}
186052419Sjulian
186170700Sjulian	/*
186270700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
186370700Sjulian	 * pending flags and the next and last pointers.
186470700Sjulian	 */
186570700Sjulian	item = ngq->queue;
186670700Sjulian	ngq->queue = item->el_next;
186770700Sjulian	if (ngq->last == &(item->el_next)) {
186870700Sjulian		/*
186970700Sjulian		 * that was the last entry in the queue so set the 'last
187070700Sjulian		 * pointer up correctly and make sure the pending flags are
187170700Sjulian		 * clear.
187270700Sjulian		 */
187370700Sjulian		ngq->last = &(ngq->queue);
187470700Sjulian		/*
187571902Sjulian		 * Whatever flag was set will be cleared and
187671902Sjulian		 * the new acive field will be set by the add as well,
187771902Sjulian		 * so we don't need to change add_arg.
187871902Sjulian		 * But we know we don't need to be on the work list.
187970700Sjulian		 */
188071902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
188171902Sjulian		ng_worklist_remove(ngq->q_node);
188270700Sjulian	} else {
188371902Sjulian		/*
188471902Sjulian		 * Since there is something on the queue, note what it is
188571902Sjulian		 * in the flags word.
188671902Sjulian		 */
188771047Sjulian		if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
188870700Sjulian			add_arg += READ_PENDING;
188970700Sjulian		} else {
189070700Sjulian			add_arg += WRITE_PENDING;
189170700Sjulian		}
189271902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
189371902Sjulian		/*
189471902Sjulian		 * If we see more doable work, make sure we are
189571902Sjulian		 * on the work queue.
189671902Sjulian		 */
189771902Sjulian		if (CAN_GET_WORK(ngq->q_flags)) {
189871902Sjulian			ng_setisr(ngq->q_node);
189971902Sjulian		}
190070700Sjulian	}
190170700Sjulian	/*
190270700Sjulian	 * We have successfully cleared the old pending flag, set the new one
190370700Sjulian	 * if it is needed, and incremented the appropriate active field.
190471902Sjulian	 * (all in one atomic addition.. )
190570700Sjulian	 */
190670700Sjulian	return (item);
190770700Sjulian}
190852419Sjulian
190970700Sjulian/*
191070700Sjulian * Queue a packet to be picked up by someone else.
191170700Sjulian * We really don't care who, but we can't or don't want to hang around
191270700Sjulian * to process it ourselves. We are probably an interrupt routine..
191370700Sjulian * 1 = writer, 0 = reader
191470700Sjulian */
191570700Sjulian#define NGQRW_R 0
191670700Sjulian#define NGQRW_W 1
191770700Sjulianstatic __inline void
191870700Sjulianng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
191970700Sjulian{
192070700Sjulian	item->el_next = NULL;	/* maybe not needed */
192170700Sjulian	*ngq->last = item;
192270700Sjulian	/*
192370700Sjulian	 * If it was the first item in the queue then we need to
192470700Sjulian	 * set the last pointer and the type flags.
192570700Sjulian	 */
192670700Sjulian	if (ngq->last == &(ngq->queue)) {
192770700Sjulian		/*
192870700Sjulian		 * When called with constants for rw, the optimiser will
192970700Sjulian		 * remove the unneeded branch below.
193070700Sjulian		 */
193170700Sjulian		if (rw == NGQRW_W) {
193270700Sjulian			atomic_add_long(&ngq->q_flags, WRITE_PENDING);
193370700Sjulian		} else {
193470700Sjulian			atomic_add_long(&ngq->q_flags, READ_PENDING);
193570700Sjulian		}
193670700Sjulian	}
193770700Sjulian	ngq->last = &(item->el_next);
193870700Sjulian}
193952419Sjulian
194070700Sjulian
194152419Sjulian/*
194270700Sjulian * This function 'cheats' in that it first tries to 'grab' the use of the
194370700Sjulian * node, without going through the mutex. We can do this becasue of the
194470700Sjulian * semantics of the lock. The semantics include a clause that says that the
194570700Sjulian * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It
194670700Sjulian * also says that the WRITER_ACTIVE flag cannot be set if the readers count
194770700Sjulian * is not zero. Note that this talks about what is valid to SET the
194870700Sjulian * WRITER_ACTIVE flag, because from the moment it is set, the value if the
194970700Sjulian * reader count is immaterial, and not valid. The two 'pending' flags have a
195070700Sjulian * similar effect, in that If they are orthogonal to the two active fields in
195170700Sjulian * how they are set, but if either is set, the attempted 'grab' need to be
195270700Sjulian * backed out because there is earlier work, and we maintain ordering in the
195370700Sjulian * queue. The result of this is that the reader request can try obtain use of
195470700Sjulian * the node with only a single atomic addition, and without any of the mutex
195570700Sjulian * overhead. If this fails the operation degenerates to the same as for other
195670700Sjulian * cases.
195770700Sjulian *
195852419Sjulian */
195970700Sjulianstatic __inline item_p
196070700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item)
196152419Sjulian{
196252419Sjulian
196370700Sjulian	/* ######### Hack alert ######### */
196470700Sjulian	atomic_add_long(&ngq->q_flags, READER_INCREMENT);
196571902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
196670700Sjulian		/* Successfully grabbed node */
196770700Sjulian		return (item);
196870700Sjulian	}
196970700Sjulian	/* undo the damage if we didn't succeed */
197070700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
197170700Sjulian
197270700Sjulian	/* ######### End Hack alert ######### */
197372200Sbmilekic	mtx_lock_spin((&ngq->q_mtx));
197469922Sjulian	/*
197570700Sjulian	 * Try again. Another processor (or interrupt for that matter) may
197670700Sjulian	 * have removed the last queued item that was stopping us from
197770700Sjulian	 * running, between the previous test, and the moment that we took
197870700Sjulian	 * the mutex. (Or maybe a writer completed.)
197969922Sjulian	 */
198071902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
198170700Sjulian		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
198272200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
198370700Sjulian		return (item);
198470700Sjulian	}
198570700Sjulian
198670700Sjulian	/*
198770700Sjulian	 * and queue the request for later.
198870700Sjulian	 */
198971047Sjulian	item->el_flags |= NGQF_READER;
199070700Sjulian	ng_queue_rw(ngq, item, NGQRW_R);
199170700Sjulian
199270700Sjulian	/*
199370700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
199470700Sjulian	 * see if we can dequeue something to run instead.
199570700Sjulian	 */
199670700Sjulian	item = ng_dequeue(ngq);
199772200Sbmilekic	mtx_unlock_spin(&(ngq->q_mtx));
199870700Sjulian	return (item);
199970700Sjulian}
200070700Sjulian
200170700Sjulianstatic __inline item_p
200270700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item)
200370700Sjulian{
200470700Sjulianrestart:
200572200Sbmilekic	mtx_lock_spin(&(ngq->q_mtx));
200670700Sjulian	/*
200770700Sjulian	 * If there are no readers, no writer, and no pending packets, then
200870700Sjulian	 * we can just go ahead. In all other situations we need to queue the
200970700Sjulian	 * request
201070700Sjulian	 */
201171902Sjulian	if ((ngq->q_flags & NGQ_WMASK) == 0) {
201270700Sjulian		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
201372200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
201470700Sjulian		if (ngq->q_flags & READER_MASK) {
201570700Sjulian			/* Collision with fast-track reader */
201671902Sjulian			atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
201770700Sjulian			goto restart;
201869922Sjulian		}
201970700Sjulian		return (item);
202052419Sjulian	}
202152419Sjulian
202270700Sjulian	/*
202370700Sjulian	 * and queue the request for later.
202470700Sjulian	 */
202571047Sjulian	item->el_flags &= ~NGQF_RW;
202670700Sjulian	ng_queue_rw(ngq, item, NGQRW_W);
202770700Sjulian
202870700Sjulian	/*
202970700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
203070700Sjulian	 * see if we can dequeue something to run instead.
203170700Sjulian	 */
203270700Sjulian	item = ng_dequeue(ngq);
203372200Sbmilekic	mtx_unlock_spin(&(ngq->q_mtx));
203470700Sjulian	return (item);
203570700Sjulian}
203670700Sjulian
203770700Sjulianstatic __inline void
203870700Sjulianng_leave_read(struct ng_queue *ngq)
203970700Sjulian{
204070700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
204170700Sjulian}
204270700Sjulian
204370700Sjulianstatic __inline void
204470700Sjulianng_leave_write(struct ng_queue *ngq)
204570700Sjulian{
204670700Sjulian	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
204770700Sjulian}
204870700Sjulian
204970700Sjulianstatic void
205070700Sjulianng_flush_input_queue(struct ng_queue * ngq)
205170700Sjulian{
205270700Sjulian	item_p item;
205370700Sjulian	u_int		add_arg;
205472200Sbmilekic	mtx_lock_spin(&ngq->q_mtx);
205570700Sjulian	for (;;) {
205670700Sjulian		/* Now take a look at what's on the queue */
205770700Sjulian		if (ngq->q_flags & READ_PENDING) {
205870700Sjulian			add_arg = -READ_PENDING;
205970700Sjulian		} else if (ngq->q_flags & WRITE_PENDING) {
206070700Sjulian			add_arg = -WRITE_PENDING;
206170700Sjulian		} else {
206270700Sjulian			break;
206370700Sjulian		}
206470700Sjulian
206570700Sjulian		item = ngq->queue;
206670700Sjulian		ngq->queue = item->el_next;
206770700Sjulian		if (ngq->last == &(item->el_next)) {
206870700Sjulian			ngq->last = &(ngq->queue);
206970700Sjulian		} else {
207071047Sjulian			if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
207170700Sjulian				add_arg += READ_PENDING;
207270700Sjulian			} else {
207370700Sjulian				add_arg += WRITE_PENDING;
207470700Sjulian			}
207570700Sjulian		}
207670700Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
207770700Sjulian
207872200Sbmilekic		mtx_lock_spin(&ngq->q_mtx);
207970700Sjulian		NG_FREE_ITEM(item);
208072200Sbmilekic		mtx_unlock_spin(&ngq->q_mtx);
208170700Sjulian	}
208271902Sjulian	/*
208371902Sjulian	 * Take us off the work queue if we are there.
208471902Sjulian	 * We definatly have no work to be done.
208571902Sjulian	 */
208671902Sjulian	ng_worklist_remove(ngq->q_node);
208772200Sbmilekic	mtx_unlock_spin(&ngq->q_mtx);
208870700Sjulian}
208970700Sjulian
209070700Sjulian/***********************************************************************
209170700Sjulian* Externally visible method for sending or queueing messages or data.
209270700Sjulian***********************************************************************/
209370700Sjulian
209470700Sjulian/*
209571849Sjulian * The module code should have filled out the item correctly by this stage:
209670700Sjulian * Common:
209770700Sjulian *    reference to destination node.
209870700Sjulian *    Reference to destination rcv hook if relevant.
209970700Sjulian * Data:
210070700Sjulian *    pointer to mbuf
210170700Sjulian * Control_Message:
210270700Sjulian *    pointer to msg.
210370700Sjulian *    ID of original sender node. (return address)
210471849Sjulian * Function:
210571849Sjulian *    Function pointer
210671849Sjulian *    void * argument
210771849Sjulian *    integer argument
210870700Sjulian *
210970700Sjulian * The nodes have several routines and macros to help with this task:
211070700Sjulian */
211170700Sjulian
211270700Sjulianint
211370700Sjulianng_snd_item(item_p item, int queue)
211470700Sjulian{
211571849Sjulian	hook_p hook = NGI_HOOK(item);
211671902Sjulian	node_p node = NGI_NODE(item);
211770700Sjulian	int rw;
211870700Sjulian	int error = 0, ierror;
211970700Sjulian	item_p	oitem;
212071902Sjulian	struct ng_queue * ngq = &node->nd_input_queue;
212170700Sjulian
212270784Sjulian#ifdef	NETGRAPH_DEBUG
212370700Sjulian        _ngi_check(item, __FILE__, __LINE__);
212470700Sjulian#endif
212570700Sjulian
212670700Sjulian	if (item == NULL) {
212771047Sjulian		TRAP_ERROR();
212870700Sjulian		return (EINVAL);	/* failed to get queue element */
212970700Sjulian	}
213071902Sjulian	if (node == NULL) {
213170700Sjulian		NG_FREE_ITEM(item);
213271047Sjulian		TRAP_ERROR();
213370700Sjulian		return (EINVAL);	/* No address */
213470700Sjulian	}
213571047Sjulian	switch(item->el_flags & NGQF_TYPE) {
213671047Sjulian	case NGQF_DATA:
213769922Sjulian		/*
213870700Sjulian		 * DATA MESSAGE
213970700Sjulian		 * Delivered to a node via a non-optional hook.
214070700Sjulian		 * Both should be present in the item even though
214170700Sjulian		 * the node is derivable from the hook.
214270700Sjulian		 * References are held on both by the item.
214369922Sjulian		 */
2144131112Sjulian
2145131112Sjulian		/* Protect nodes from sending NULL pointers
2146131112Sjulian		 * to each other
2147131112Sjulian		 */
2148131123Sjulian		if (NGI_M(item) == NULL)
2149131112Sjulian			return (EINVAL);
2150131112Sjulian
215170700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
215270700Sjulian		if (hook == NULL) {
215370700Sjulian			NG_FREE_ITEM(item);
215471047Sjulian			TRAP_ERROR();
215570700Sjulian			return(EINVAL);
215670700Sjulian		}
215770784Sjulian		if ((NG_HOOK_NOT_VALID(hook))
215870784Sjulian		|| (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) {
215970700Sjulian			NG_FREE_ITEM(item);
216070700Sjulian			return (ENOTCONN);
216169922Sjulian		}
216270784Sjulian		if ((hook->hk_flags & HK_QUEUE)) {
216370700Sjulian			queue = 1;
216470700Sjulian		}
216570700Sjulian		/* By default data is a reader in the locking scheme */
216670700Sjulian		item->el_flags |= NGQF_READER;
216770700Sjulian		rw = NGQRW_R;
216871047Sjulian		break;
216971047Sjulian	case NGQF_MESG:
217070700Sjulian		/*
217170700Sjulian		 * CONTROL MESSAGE
217270700Sjulian		 * Delivered to a node.
217370700Sjulian		 * Hook is optional.
217470700Sjulian		 * References are held by the item on the node and
217570700Sjulian		 * the hook if it is present.
217670700Sjulian		 */
217770784Sjulian		if (hook && (hook->hk_flags & HK_QUEUE)) {
217870700Sjulian			queue = 1;
217970700Sjulian		}
218070700Sjulian		/* Data messages count as writers unles explicitly exempted */
218170700Sjulian		if (NGI_MSG(item)->header.cmd & NGM_READONLY) {
218270700Sjulian			item->el_flags |= NGQF_READER;
218370700Sjulian			rw = NGQRW_R;
218470700Sjulian		} else {
218571047Sjulian			item->el_flags &= ~NGQF_RW;
218670700Sjulian			rw = NGQRW_W;
218770700Sjulian		}
218871047Sjulian		break;
218971047Sjulian	case NGQF_FN:
219071047Sjulian		item->el_flags &= ~NGQF_RW;
219171047Sjulian		rw = NGQRW_W;
219271047Sjulian		break;
219371047Sjulian	default:
219471047Sjulian		NG_FREE_ITEM(item);
219571047Sjulian		TRAP_ERROR();
219671047Sjulian		return (EINVAL);
219769922Sjulian	}
219870700Sjulian	/*
219970700Sjulian	 * If the node specifies single threading, force writer semantics
220070700Sjulian	 * Similarly the node may say one hook always produces writers.
220170700Sjulian	 * These are over-rides.
220270700Sjulian	 */
220371902Sjulian	if ((node->nd_flags & NG_FORCE_WRITER)
220470784Sjulian	|| (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
220570700Sjulian			rw = NGQRW_W;
220671047Sjulian			item->el_flags &= ~NGQF_READER;
220770700Sjulian	}
220870700Sjulian	if (queue) {
220970700Sjulian		/* Put it on the queue for that node*/
221070784Sjulian#ifdef	NETGRAPH_DEBUG
221170700Sjulian        _ngi_check(item, __FILE__, __LINE__);
221270700Sjulian#endif
221372200Sbmilekic		mtx_lock_spin(&(ngq->q_mtx));
221470700Sjulian		ng_queue_rw(ngq, item, rw);
221570700Sjulian		/*
221670700Sjulian		 * If there are active elements then we can rely on
221770700Sjulian		 * them. if not we should not rely on another packet
221870700Sjulian		 * coming here by another path,
221970700Sjulian		 * so it is best to put us in the netisr list.
222071902Sjulian		 * We can take the worklist lock with the node locked
222171902Sjulian		 * BUT NOT THE REVERSE!
222270700Sjulian		 */
222371902Sjulian		if (CAN_GET_WORK(ngq->q_flags)) {
222471902Sjulian			ng_setisr(node);
222570700Sjulian		}
222672200Sbmilekic		mtx_unlock_spin(&(ngq->q_mtx));
222770700Sjulian		return (0);
222870700Sjulian	}
222970700Sjulian	/*
223070700Sjulian	 * Take a queue item and a node and see if we can apply the item to
223170700Sjulian	 * the node. We may end up getting a different item to apply instead.
223270700Sjulian	 * Will allow for a piggyback reply only in the case where
223370700Sjulian	 * there is no queueing.
223470700Sjulian	 */
223569922Sjulian
223670700Sjulian	oitem = item;
223770700Sjulian	/*
223870700Sjulian	 * We already decided how we will be queueud or treated.
223970700Sjulian	 * Try get the appropriate operating permission.
224070700Sjulian	 */
224170700Sjulian 	if (rw == NGQRW_R) {
224270700Sjulian		item = ng_acquire_read(ngq, item);
224370700Sjulian	} else {
224470700Sjulian		item = ng_acquire_write(ngq, item);
224570700Sjulian	}
224652419Sjulian
224770700Sjulian	/*
224870700Sjulian	 * May have come back with a different item.
224970700Sjulian	 * or maybe none at all. The one we started with will
225070700Sjulian	 * have been queued in thises cases.
225170700Sjulian	 */
225270700Sjulian	if (item == NULL) {
225370700Sjulian		return (0);
225470700Sjulian	}
225552419Sjulian
225670784Sjulian#ifdef	NETGRAPH_DEBUG
225770700Sjulian        _ngi_check(item, __FILE__, __LINE__);
225870700Sjulian#endif
225974078Sjulian	/*
226074078Sjulian	 * Take over the reference frm the item.
226174078Sjulian	 * Hold it until the called function returns.
226274078Sjulian	 */
226374078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
226452419Sjulian
226574078Sjulian	ierror = ng_apply_item(node, item); /* drops r/w lock when done */
226674078Sjulian
226770700Sjulian	/* only return an error if it was our initial item.. (compat hack) */
226870700Sjulian	if (oitem == item) {
226970700Sjulian		error = ierror;
227070700Sjulian	}
227170700Sjulian
227252419Sjulian	/*
227374078Sjulian	 * If the node goes away when we remove the reference,
227482058Sbrian	 * whatever we just did caused it.. whatever we do, DO NOT
227574078Sjulian	 * access the node again!
227674078Sjulian	 */
227774078Sjulian	if (NG_NODE_UNREF(node) == 0) {
227874078Sjulian		return (error);
227974078Sjulian	}
228074078Sjulian
228174078Sjulian	/*
228270700Sjulian	 * Now we've handled the packet we brought, (or a friend of it) let's
228370700Sjulian	 * look for any other packets that may have been queued up. We hold
228470700Sjulian	 * no locks, so if someone puts something in the queue after
228570700Sjulian	 * we check that it is empty, it is their problem
228670700Sjulian	 * to ensure it is processed. If we have the netisr thread cme in here
228770700Sjulian	 * while we still say we have stuff to do, we may get a boost
228870700Sjulian	 * in SMP systems. :-)
228952419Sjulian	 */
229070700Sjulian	for (;;) {
229170700Sjulian		/*
229270700Sjulian		 * dequeue acquires and adjusts the input_queue as it dequeues
229370700Sjulian		 * packets. It acquires the rw lock as needed.
229470700Sjulian		 */
229572200Sbmilekic		mtx_lock_spin(&ngq->q_mtx);
229671902Sjulian		item = ng_dequeue(ngq); /* fixes worklist too*/
229770700Sjulian		if (!item) {
229872200Sbmilekic			mtx_unlock_spin(&ngq->q_mtx);
229971849Sjulian			return (error);
230070700Sjulian		}
230172200Sbmilekic		mtx_unlock_spin(&ngq->q_mtx);
230270700Sjulian
230370700Sjulian		/*
230474078Sjulian		 * Take over the reference frm the item.
230574078Sjulian		 * Hold it until the called function returns.
230674078Sjulian		 */
230774078Sjulian
230874078Sjulian		NGI_GET_NODE(item, node); /* zaps stored node */
230974078Sjulian
231074078Sjulian		/*
231170700Sjulian		 * We have the appropriate lock, so run the item.
231270700Sjulian		 * When finished it will drop the lock accordingly
231370700Sjulian		 */
231474078Sjulian		ierror = ng_apply_item(node, item);
231570700Sjulian
231670700Sjulian		/*
231770700Sjulian		 * only return an error if it was our initial
231870700Sjulian		 * item.. (compat hack)
231970700Sjulian		 */
232070700Sjulian		if (oitem == item) {
232170700Sjulian			error = ierror;
232270700Sjulian		}
232374078Sjulian
232474078Sjulian		/*
232574078Sjulian		 * If the node goes away when we remove the reference,
232682058Sbrian		 * whatever we just did caused it.. whatever we do, DO NOT
232774078Sjulian		 * access the node again!
232874078Sjulian		 */
232974078Sjulian		if (NG_NODE_UNREF(node) == 0) {
233074078Sjulian			break;
233174078Sjulian		}
233270700Sjulian	}
233371849Sjulian	return (error);
233452419Sjulian}
233552419Sjulian
233652419Sjulian/*
233770700Sjulian * We have an item that was possibly queued somewhere.
233870700Sjulian * It should contain all the information needed
233970700Sjulian * to run it on the appropriate node/hook.
234052419Sjulian */
234152419Sjulianstatic int
234274078Sjulianng_apply_item(node_p node, item_p item)
234352419Sjulian{
234470700Sjulian	hook_p  hook;
234571849Sjulian	int	was_reader = ((item->el_flags & NGQF_RW));
234671849Sjulian	int	error = 0;
234770700Sjulian	ng_rcvdata_t *rcvdata;
234871885Sjulian	ng_rcvmsg_t *rcvmsg;
234952419Sjulian
235071849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
235170784Sjulian#ifdef	NETGRAPH_DEBUG
235270700Sjulian        _ngi_check(item, __FILE__, __LINE__);
235370700Sjulian#endif
235471047Sjulian	switch (item->el_flags & NGQF_TYPE) {
235570700Sjulian	case NGQF_DATA:
235670700Sjulian		/*
235770700Sjulian		 * Check things are still ok as when we were queued.
235870700Sjulian		 */
235970700Sjulian		if ((hook == NULL)
236070784Sjulian		|| NG_HOOK_NOT_VALID(hook)
236171885Sjulian		|| NG_NODE_NOT_VALID(node) ) {
236270700Sjulian			error = EIO;
236370700Sjulian			NG_FREE_ITEM(item);
236471885Sjulian			break;
236570700Sjulian		}
236671885Sjulian		/*
236771885Sjulian		 * If no receive method, just silently drop it.
236871885Sjulian		 * Give preference to the hook over-ride method
236971885Sjulian		 */
237071885Sjulian		if ((!(rcvdata = hook->hk_rcvdata))
237171885Sjulian		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
237271885Sjulian			error = 0;
237371885Sjulian			NG_FREE_ITEM(item);
237471885Sjulian			break;
237571885Sjulian		}
237671885Sjulian		error = (*rcvdata)(hook, item);
237770700Sjulian		break;
237870700Sjulian	case NGQF_MESG:
237970700Sjulian		if (hook) {
238070784Sjulian			if (NG_HOOK_NOT_VALID(hook)) {
238171849Sjulian				/*
238271849Sjulian				 * The hook has been zapped then we can't
238371849Sjulian				 * use it. Immediatly drop its reference.
238471849Sjulian				 * The message may not need it.
238571849Sjulian				 */
238670784Sjulian				NG_HOOK_UNREF(hook);
238770700Sjulian				hook = NULL;
238870700Sjulian			}
238970700Sjulian		}
239070700Sjulian		/*
239170700Sjulian		 * Similarly, if the node is a zombie there is
239270700Sjulian		 * nothing we can do with it, drop everything.
239370700Sjulian		 */
239470784Sjulian		if (NG_NODE_NOT_VALID(node)) {
239571047Sjulian			TRAP_ERROR();
239670700Sjulian			error = EINVAL;
239770700Sjulian			NG_FREE_ITEM(item);
239870700Sjulian		} else {
239970700Sjulian			/*
240070700Sjulian			 * Call the appropriate message handler for the object.
240170700Sjulian			 * It is up to the message handler to free the message.
240270700Sjulian			 * If it's a generic message, handle it generically,
240370700Sjulian			 * otherwise call the type's message handler
240470700Sjulian			 * (if it exists)
240570700Sjulian			 * XXX (race). Remember that a queued message may
240670700Sjulian			 * reference a node or hook that has just been
240770700Sjulian			 * invalidated. It will exist as the queue code
240870700Sjulian			 * is holding a reference, but..
240970700Sjulian			 */
241070700Sjulian
241170700Sjulian			struct ng_mesg *msg = NGI_MSG(item);
241270700Sjulian
241371885Sjulian			/*
241471885Sjulian			 * check if the generic handler owns it.
241571885Sjulian			 */
241670700Sjulian			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
241770700Sjulian			&& ((msg->header.flags & NGF_RESP) == 0)) {
241870700Sjulian				error = ng_generic_msg(node, item, hook);
241971885Sjulian				break;
242070700Sjulian			}
242171885Sjulian			/*
242271885Sjulian			 * Now see if there is a handler (hook or node specific)
242371885Sjulian			 * in the target node. If none, silently discard.
242471885Sjulian			 */
242571885Sjulian			if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg)))
242671885Sjulian			&& (!(rcvmsg = node->nd_type->rcvmsg))) {
242771885Sjulian				TRAP_ERROR();
242871885Sjulian				error = 0;
242971885Sjulian				NG_FREE_ITEM(item);
243071885Sjulian				break;
243171885Sjulian			}
243271885Sjulian			error = (*rcvmsg)(node, item, hook);
243370700Sjulian		}
243470700Sjulian		break;
243571047Sjulian	case NGQF_FN:
243671047Sjulian		/*
243771047Sjulian		 *  We have to implicitly trust the hook,
243871047Sjulian		 * as some of these are used for system purposes
243971849Sjulian		 * where the hook is invalid. In the case of
244071849Sjulian		 * the shutdown message we allow it to hit
244171849Sjulian		 * even if the node is invalid.
244271047Sjulian		 */
244371849Sjulian		if ((NG_NODE_NOT_VALID(node))
244471849Sjulian		&& (NGI_FN(item) != &ng_rmnode)) {
244571047Sjulian			TRAP_ERROR();
244671047Sjulian			error = EINVAL;
244771047Sjulian			break;
244871047Sjulian		}
244971849Sjulian		(*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item));
245071047Sjulian		NG_FREE_ITEM(item);
245171047Sjulian		break;
245271047Sjulian
245370700Sjulian	}
245470700Sjulian	/*
245570700Sjulian	 * We held references on some of the resources
245670700Sjulian	 * that we took from the item. Now that we have
245770700Sjulian	 * finished doing everything, drop those references.
245870700Sjulian	 */
245970700Sjulian	if (hook) {
246070784Sjulian		NG_HOOK_UNREF(hook);
246170700Sjulian	}
246270700Sjulian
246370700Sjulian	if (was_reader) {
246470784Sjulian		ng_leave_read(&node->nd_input_queue);
246570700Sjulian	} else {
246670784Sjulian		ng_leave_write(&node->nd_input_queue);
246770700Sjulian	}
246870700Sjulian	return (error);
246970700Sjulian}
247070700Sjulian
247170700Sjulian/***********************************************************************
247270700Sjulian * Implement the 'generic' control messages
247370700Sjulian ***********************************************************************/
247470700Sjulianstatic int
247570700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
247670700Sjulian{
247770700Sjulian	int error = 0;
247870700Sjulian	struct ng_mesg *msg;
247970700Sjulian	struct ng_mesg *resp = NULL;
248070700Sjulian
248170700Sjulian	NGI_GET_MSG(item, msg);
248252419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
248371047Sjulian		TRAP_ERROR();
248470700Sjulian		error = EINVAL;
248570700Sjulian		goto out;
248652419Sjulian	}
248752419Sjulian	switch (msg->header.cmd) {
248852419Sjulian	case NGM_SHUTDOWN:
248971849Sjulian		ng_rmnode(here, NULL, NULL, 0);
249052419Sjulian		break;
249152419Sjulian	case NGM_MKPEER:
249252419Sjulian	    {
249352419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
249452419Sjulian
249552419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
249671047Sjulian			TRAP_ERROR();
249770700Sjulian			error = EINVAL;
249870700Sjulian			break;
249952419Sjulian		}
250052419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
250152419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
250252419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
250352419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
250452419Sjulian		break;
250552419Sjulian	    }
250652419Sjulian	case NGM_CONNECT:
250752419Sjulian	    {
250852419Sjulian		struct ngm_connect *const con =
250952419Sjulian			(struct ngm_connect *) msg->data;
251052419Sjulian		node_p node2;
251152419Sjulian
251252419Sjulian		if (msg->header.arglen != sizeof(*con)) {
251371047Sjulian			TRAP_ERROR();
251470700Sjulian			error = EINVAL;
251570700Sjulian			break;
251652419Sjulian		}
251752419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
251852419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
251952419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
252070700Sjulian		/* Don't forget we get a reference.. */
252170700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
252252419Sjulian		if (error)
252352419Sjulian			break;
252452419Sjulian		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
252570784Sjulian		NG_NODE_UNREF(node2);
252652419Sjulian		break;
252752419Sjulian	    }
252852419Sjulian	case NGM_NAME:
252952419Sjulian	    {
253052419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
253152419Sjulian
253252419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
253371047Sjulian			TRAP_ERROR();
253470700Sjulian			error = EINVAL;
253570700Sjulian			break;
253652419Sjulian		}
253752419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
253852419Sjulian		error = ng_name_node(here, nam->name);
253952419Sjulian		break;
254052419Sjulian	    }
254152419Sjulian	case NGM_RMHOOK:
254252419Sjulian	    {
254352419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
254452419Sjulian		hook_p hook;
254552419Sjulian
254652419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
254771047Sjulian			TRAP_ERROR();
254870700Sjulian			error = EINVAL;
254970700Sjulian			break;
255052419Sjulian		}
255152419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
255254096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
255352419Sjulian			ng_destroy_hook(hook);
255452419Sjulian		break;
255552419Sjulian	    }
255652419Sjulian	case NGM_NODEINFO:
255752419Sjulian	    {
255852419Sjulian		struct nodeinfo *ni;
255952419Sjulian
256070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
256152419Sjulian		if (resp == NULL) {
256252419Sjulian			error = ENOMEM;
256352419Sjulian			break;
256452419Sjulian		}
256552419Sjulian
256652419Sjulian		/* Fill in node info */
256770700Sjulian		ni = (struct nodeinfo *) resp->data;
256870784Sjulian		if (NG_NODE_HAS_NAME(here))
2569125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2570125028Sharti		strcpy(ni->type, here->nd_type->name);
257152722Sjulian		ni->id = ng_node2ID(here);
257270784Sjulian		ni->hooks = here->nd_numhooks;
257352419Sjulian		break;
257452419Sjulian	    }
257552419Sjulian	case NGM_LISTHOOKS:
257652419Sjulian	    {
257770784Sjulian		const int nhooks = here->nd_numhooks;
257852419Sjulian		struct hooklist *hl;
257952419Sjulian		struct nodeinfo *ni;
258052419Sjulian		hook_p hook;
258152419Sjulian
258252419Sjulian		/* Get response struct */
258370700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*hl)
258470700Sjulian		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
258552419Sjulian		if (resp == NULL) {
258652419Sjulian			error = ENOMEM;
258752419Sjulian			break;
258852419Sjulian		}
258970700Sjulian		hl = (struct hooklist *) resp->data;
259052419Sjulian		ni = &hl->nodeinfo;
259152419Sjulian
259252419Sjulian		/* Fill in node info */
259370784Sjulian		if (NG_NODE_HAS_NAME(here))
2594125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2595125028Sharti		strcpy(ni->type, here->nd_type->name);
259652722Sjulian		ni->id = ng_node2ID(here);
259752419Sjulian
259852419Sjulian		/* Cycle through the linked list of hooks */
259952419Sjulian		ni->hooks = 0;
260070784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
260152419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
260252419Sjulian
260352419Sjulian			if (ni->hooks >= nhooks) {
260452419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
260587599Sobrien				    __func__, "hooks");
260652419Sjulian				break;
260752419Sjulian			}
260870784Sjulian			if (NG_HOOK_NOT_VALID(hook))
260952419Sjulian				continue;
2610125028Sharti			strcpy(link->ourhook, NG_HOOK_NAME(hook));
2611125028Sharti			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
261270784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
2613125028Sharti				strcpy(link->nodeinfo.name,
2614125028Sharti				    NG_PEER_NODE_NAME(hook));
2615125028Sharti			strcpy(link->nodeinfo.type,
2616125028Sharti			   NG_PEER_NODE(hook)->nd_type->name);
261770784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
261870784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
261952419Sjulian			ni->hooks++;
262052419Sjulian		}
262152419Sjulian		break;
262252419Sjulian	    }
262352419Sjulian
262452419Sjulian	case NGM_LISTNAMES:
262552419Sjulian	case NGM_LISTNODES:
262652419Sjulian	    {
262752419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
262852419Sjulian		struct namelist *nl;
262952419Sjulian		node_p node;
263052419Sjulian		int num = 0;
263152419Sjulian
263272200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
263352419Sjulian		/* Count number of nodes */
263470784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
263570912Sjulian			if (NG_NODE_IS_VALID(node)
263670912Sjulian			&& (unnamed || NG_NODE_HAS_NAME(node))) {
263752419Sjulian				num++;
263870912Sjulian			}
263952419Sjulian		}
264072200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
264152419Sjulian
264252419Sjulian		/* Get response struct */
264370700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*nl)
264470700Sjulian		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
264552419Sjulian		if (resp == NULL) {
264652419Sjulian			error = ENOMEM;
264752419Sjulian			break;
264852419Sjulian		}
264970700Sjulian		nl = (struct namelist *) resp->data;
265052419Sjulian
265152419Sjulian		/* Cycle through the linked list of nodes */
265252419Sjulian		nl->numnames = 0;
265372200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
265470784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
265552419Sjulian			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
265652419Sjulian
265752419Sjulian			if (nl->numnames >= num) {
265852419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
265987599Sobrien				    __func__, "nodes");
266052419Sjulian				break;
266152419Sjulian			}
266270784Sjulian			if (NG_NODE_NOT_VALID(node))
266352419Sjulian				continue;
266470784Sjulian			if (!unnamed && (! NG_NODE_HAS_NAME(node)))
266552419Sjulian				continue;
266670784Sjulian			if (NG_NODE_HAS_NAME(node))
2667125028Sharti				strcpy(np->name, NG_NODE_NAME(node));
2668125028Sharti			strcpy(np->type, node->nd_type->name);
266952722Sjulian			np->id = ng_node2ID(node);
267070784Sjulian			np->hooks = node->nd_numhooks;
267152419Sjulian			nl->numnames++;
267252419Sjulian		}
267372200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
267452419Sjulian		break;
267552419Sjulian	    }
267652419Sjulian
267752419Sjulian	case NGM_LISTTYPES:
267852419Sjulian	    {
267952419Sjulian		struct typelist *tl;
268052419Sjulian		struct ng_type *type;
268152419Sjulian		int num = 0;
268252419Sjulian
268372200Sbmilekic		mtx_lock(&ng_typelist_mtx);
268452419Sjulian		/* Count number of types */
268570912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
268652419Sjulian			num++;
268770912Sjulian		}
268872200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
268952419Sjulian
269052419Sjulian		/* Get response struct */
269170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*tl)
269270700Sjulian		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
269352419Sjulian		if (resp == NULL) {
269452419Sjulian			error = ENOMEM;
269552419Sjulian			break;
269652419Sjulian		}
269770700Sjulian		tl = (struct typelist *) resp->data;
269852419Sjulian
269952419Sjulian		/* Cycle through the linked list of types */
270052419Sjulian		tl->numtypes = 0;
270172200Sbmilekic		mtx_lock(&ng_typelist_mtx);
270270700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
270352419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
270452419Sjulian
270552419Sjulian			if (tl->numtypes >= num) {
270652419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
270787599Sobrien				    __func__, "types");
270852419Sjulian				break;
270952419Sjulian			}
2710125028Sharti			strcpy(tp->type_name, type->name);
271171603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
271252419Sjulian			tl->numtypes++;
271352419Sjulian		}
271472200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
271552419Sjulian		break;
271652419Sjulian	    }
271752419Sjulian
271853913Sarchie	case NGM_BINARY2ASCII:
271953913Sarchie	    {
272064510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
272153913Sarchie		const struct ng_parse_type *argstype;
272253913Sarchie		const struct ng_cmdlist *c;
272370700Sjulian		struct ng_mesg *binary, *ascii;
272453913Sarchie
272553913Sarchie		/* Data area must contain a valid netgraph message */
272653913Sarchie		binary = (struct ng_mesg *)msg->data;
272753913Sarchie		if (msg->header.arglen < sizeof(struct ng_mesg)
272870912Sjulian		    || (msg->header.arglen - sizeof(struct ng_mesg)
272970912Sjulian		      < binary->header.arglen)) {
273071047Sjulian			TRAP_ERROR();
273153913Sarchie			error = EINVAL;
273253913Sarchie			break;
273353913Sarchie		}
273453913Sarchie
273570700Sjulian		/* Get a response message with lots of room */
273670700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
273770159Sjulian		if (resp == NULL) {
273853913Sarchie			error = ENOMEM;
273953913Sarchie			break;
274053913Sarchie		}
274170700Sjulian		ascii = (struct ng_mesg *)resp->data;
274253913Sarchie
274353913Sarchie		/* Copy binary message header to response message payload */
274453913Sarchie		bcopy(binary, ascii, sizeof(*binary));
274553913Sarchie
274653913Sarchie		/* Find command by matching typecookie and command number */
274770784Sjulian		for (c = here->nd_type->cmdlist;
274853913Sarchie		    c != NULL && c->name != NULL; c++) {
274953913Sarchie			if (binary->header.typecookie == c->cookie
275053913Sarchie			    && binary->header.cmd == c->cmd)
275153913Sarchie				break;
275253913Sarchie		}
275353913Sarchie		if (c == NULL || c->name == NULL) {
275453913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
275553913Sarchie				if (binary->header.typecookie == c->cookie
275653913Sarchie				    && binary->header.cmd == c->cmd)
275753913Sarchie					break;
275853913Sarchie			}
275953913Sarchie			if (c->name == NULL) {
276070700Sjulian				NG_FREE_MSG(resp);
276153913Sarchie				error = ENOSYS;
276253913Sarchie				break;
276353913Sarchie			}
276453913Sarchie		}
276553913Sarchie
276653913Sarchie		/* Convert command name to ASCII */
276753913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
276853913Sarchie		    "%s", c->name);
276953913Sarchie
277053913Sarchie		/* Convert command arguments to ASCII */
277153913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
277253913Sarchie		    c->respType : c->mesgType;
277370912Sjulian		if (argstype == NULL) {
277453913Sarchie			*ascii->data = '\0';
277570912Sjulian		} else {
277653913Sarchie			if ((error = ng_unparse(argstype,
277753913Sarchie			    (u_char *)binary->data,
277853913Sarchie			    ascii->data, bufSize)) != 0) {
277970700Sjulian				NG_FREE_MSG(resp);
278053913Sarchie				break;
278153913Sarchie			}
278253913Sarchie		}
278353913Sarchie
278453913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
278553913Sarchie		bufSize = strlen(ascii->data) + 1;
278653913Sarchie		ascii->header.arglen = bufSize;
278770700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
278853913Sarchie		break;
278953913Sarchie	    }
279053913Sarchie
279153913Sarchie	case NGM_ASCII2BINARY:
279253913Sarchie	    {
279353913Sarchie		int bufSize = 2000;	/* XXX hard coded constant */
279453913Sarchie		const struct ng_cmdlist *c;
279553913Sarchie		const struct ng_parse_type *argstype;
279670700Sjulian		struct ng_mesg *ascii, *binary;
279759178Sarchie		int off = 0;
279853913Sarchie
279953913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
280053913Sarchie		ascii = (struct ng_mesg *)msg->data;
280170912Sjulian		if ((msg->header.arglen < sizeof(*ascii) + 1)
280270912Sjulian		    || (ascii->header.arglen < 1)
280370912Sjulian		    || (msg->header.arglen
280470912Sjulian		      < sizeof(*ascii) + ascii->header.arglen)) {
280571047Sjulian			TRAP_ERROR();
280653913Sarchie			error = EINVAL;
280753913Sarchie			break;
280853913Sarchie		}
280953913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
281053913Sarchie
281170700Sjulian		/* Get a response message with lots of room */
281270700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
281370159Sjulian		if (resp == NULL) {
281453913Sarchie			error = ENOMEM;
281553913Sarchie			break;
281653913Sarchie		}
281770700Sjulian		binary = (struct ng_mesg *)resp->data;
281853913Sarchie
281953913Sarchie		/* Copy ASCII message header to response message payload */
282053913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
282153913Sarchie
282253913Sarchie		/* Find command by matching ASCII command string */
282370784Sjulian		for (c = here->nd_type->cmdlist;
282453913Sarchie		    c != NULL && c->name != NULL; c++) {
282553913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
282653913Sarchie				break;
282753913Sarchie		}
282853913Sarchie		if (c == NULL || c->name == NULL) {
282953913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
283053913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
283153913Sarchie					break;
283253913Sarchie			}
283353913Sarchie			if (c->name == NULL) {
283470700Sjulian				NG_FREE_MSG(resp);
283553913Sarchie				error = ENOSYS;
283653913Sarchie				break;
283753913Sarchie			}
283853913Sarchie		}
283953913Sarchie
284053913Sarchie		/* Convert command name to binary */
284153913Sarchie		binary->header.cmd = c->cmd;
284253913Sarchie		binary->header.typecookie = c->cookie;
284353913Sarchie
284453913Sarchie		/* Convert command arguments to binary */
284553913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
284653913Sarchie		    c->respType : c->mesgType;
284770912Sjulian		if (argstype == NULL) {
284853913Sarchie			bufSize = 0;
284970912Sjulian		} else {
285053913Sarchie			if ((error = ng_parse(argstype, ascii->data,
285153913Sarchie			    &off, (u_char *)binary->data, &bufSize)) != 0) {
285270700Sjulian				NG_FREE_MSG(resp);
285353913Sarchie				break;
285453913Sarchie			}
285553913Sarchie		}
285653913Sarchie
285753913Sarchie		/* Return the result */
285853913Sarchie		binary->header.arglen = bufSize;
285970700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
286053913Sarchie		break;
286153913Sarchie	    }
286253913Sarchie
286362471Sphk	case NGM_TEXT_CONFIG:
286452419Sjulian	case NGM_TEXT_STATUS:
286552419Sjulian		/*
286652419Sjulian		 * This one is tricky as it passes the command down to the
286752419Sjulian		 * actual node, even though it is a generic type command.
286870700Sjulian		 * This means we must assume that the item/msg is already freed
286952419Sjulian		 * when control passes back to us.
287052419Sjulian		 */
287170784Sjulian		if (here->nd_type->rcvmsg != NULL) {
287270700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
287370784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
287452419Sjulian		}
287552419Sjulian		/* Fall through if rcvmsg not supported */
287652419Sjulian	default:
287771047Sjulian		TRAP_ERROR();
287852419Sjulian		error = EINVAL;
287952419Sjulian	}
288070700Sjulian	/*
288170700Sjulian	 * Sometimes a generic message may be statically allocated
288270700Sjulian	 * to avoid problems with allocating when in tight memeory situations.
288370700Sjulian	 * Don't free it if it is so.
288470700Sjulian	 * I break them appart here, because erros may cause a free if the item
288570700Sjulian	 * in which case we'd be doing it twice.
288670700Sjulian	 * they are kept together above, to simplify freeing.
288770700Sjulian	 */
288870700Sjulianout:
288970700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
289071849Sjulian	if (msg)
289170700Sjulian		NG_FREE_MSG(msg);
289252419Sjulian	return (error);
289352419Sjulian}
289452419Sjulian
289552419Sjulian/************************************************************************
289652419Sjulian			Module routines
289752419Sjulian************************************************************************/
289852419Sjulian
289952419Sjulian/*
290052419Sjulian * Handle the loading/unloading of a netgraph node type module
290152419Sjulian */
290252419Sjulianint
290352419Sjulianng_mod_event(module_t mod, int event, void *data)
290452419Sjulian{
290552419Sjulian	struct ng_type *const type = data;
290652419Sjulian	int s, error = 0;
290752419Sjulian
290852419Sjulian	switch (event) {
290952419Sjulian	case MOD_LOAD:
291052419Sjulian
291152419Sjulian		/* Register new netgraph node type */
291252419Sjulian		s = splnet();
291352419Sjulian		if ((error = ng_newtype(type)) != 0) {
291452419Sjulian			splx(s);
291552419Sjulian			break;
291652419Sjulian		}
291752419Sjulian
291852419Sjulian		/* Call type specific code */
291952419Sjulian		if (type->mod_event != NULL)
292070700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
292172200Sbmilekic				mtx_lock(&ng_typelist_mtx);
292271603Sjulian				type->refs--;	/* undo it */
292352419Sjulian				LIST_REMOVE(type, types);
292472200Sbmilekic				mtx_unlock(&ng_typelist_mtx);
292570700Sjulian			}
292652419Sjulian		splx(s);
292752419Sjulian		break;
292852419Sjulian
292952419Sjulian	case MOD_UNLOAD:
293052419Sjulian		s = splnet();
293171603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
293252419Sjulian			error = EBUSY;
293371603Sjulian		} else {
293471603Sjulian			if (type->refs == 0) {
293571603Sjulian				/* failed load, nothing to undo */
293671603Sjulian				splx(s);
293771603Sjulian				break;
293871603Sjulian			}
293952419Sjulian			if (type->mod_event != NULL) {	/* check with type */
294052419Sjulian				error = (*type->mod_event)(mod, event, data);
294152419Sjulian				if (error != 0) {	/* type refuses.. */
294252419Sjulian					splx(s);
294352419Sjulian					break;
294452419Sjulian				}
294552419Sjulian			}
294672200Sbmilekic			mtx_lock(&ng_typelist_mtx);
294752419Sjulian			LIST_REMOVE(type, types);
294872200Sbmilekic			mtx_unlock(&ng_typelist_mtx);
294952419Sjulian		}
295052419Sjulian		splx(s);
295152419Sjulian		break;
295252419Sjulian
295352419Sjulian	default:
295452419Sjulian		if (type->mod_event != NULL)
295552419Sjulian			error = (*type->mod_event)(mod, event, data);
295652419Sjulian		else
295752419Sjulian			error = 0;		/* XXX ? */
295852419Sjulian		break;
295952419Sjulian	}
296052419Sjulian	return (error);
296152419Sjulian}
296252419Sjulian
296352419Sjulian/*
296452419Sjulian * Handle loading and unloading for this code.
296552419Sjulian * The only thing we need to link into is the NETISR strucure.
296652419Sjulian */
296752419Sjulianstatic int
296852419Sjulianngb_mod_event(module_t mod, int event, void *data)
296952419Sjulian{
297052419Sjulian	int s, error = 0;
297152419Sjulian
297252419Sjulian	switch (event) {
297352419Sjulian	case MOD_LOAD:
297452419Sjulian		/* Register line discipline */
297593818Sjhb		mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_SPIN);
2976123278Struckman		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
2977123278Struckman		    MTX_DEF);
2978123278Struckman		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
2979123278Struckman		    MTX_DEF);
2980123278Struckman		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
2981123278Struckman		    MTX_DEF);
2982123278Struckman		mtx_init(&ngq_mtx, "netgraph free item list mutex", NULL,
2983123278Struckman		    MTX_DEF);
298452419Sjulian		s = splimp();
2985122320Ssam		/* XXX could use NETISR_MPSAFE but need to verify code */
2986122320Ssam		netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL, 0);
298752419Sjulian		splx(s);
298852419Sjulian		break;
298952419Sjulian	case MOD_UNLOAD:
299052419Sjulian		/* You cant unload it because an interface may be using it.  */
299152419Sjulian		error = EBUSY;
299252419Sjulian		break;
299352419Sjulian	default:
299452419Sjulian		error = EOPNOTSUPP;
299552419Sjulian		break;
299652419Sjulian	}
299752419Sjulian	return (error);
299852419Sjulian}
299952419Sjulian
300052419Sjulianstatic moduledata_t netgraph_mod = {
300152419Sjulian	"netgraph",
300252419Sjulian	ngb_mod_event,
300352419Sjulian	(NULL)
300452419Sjulian};
300552419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
300672946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
300772946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
300872946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
300952419Sjulian
301052419Sjulian/************************************************************************
301170700Sjulian			Queue element get/free routines
301252419Sjulian************************************************************************/
301352419Sjulian
301452419Sjulian
301570700Sjulianstatic int			allocated;	/* number of items malloc'd */
3016111749Sharti
301770700Sjulianstatic int			maxalloc = 128;	/* limit the damage of a leak */
3018111749Shartistatic int			ngqfreemax = 64;/* cache at most this many */
3019111749Sharti
3020111749ShartiTUNABLE_INT("net.graph.maxalloc", &maxalloc);
3021121307SsilbySYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
3022111749Sharti    0, "Maximum number of queue items to allocate");
3023111749Sharti
3024111749ShartiTUNABLE_INT("net.graph.ngqfreemax", &ngqfreemax);
3025121307SsilbySYSCTL_INT(_net_graph, OID_AUTO, ngqfreemax, CTLFLAG_RDTUN, &ngqfreemax,
3026111749Sharti    0, "Maximum number of free queue items to cache");
3027111749Sharti
302870700Sjulianstatic const int		ngqfreelow = 4; /* try malloc if free < this */
302970700Sjulianstatic volatile int		ngqfreesize;	/* number of cached entries */
3030122110Shartistatic volatile item_p		ngqfree;	/* free ones */
3031122110Sharti
303270784Sjulian#ifdef	NETGRAPH_DEBUG
303370700Sjulianstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
303470700Sjulian#endif
303552419Sjulian/*
303652419Sjulian * Get a queue entry
303770700Sjulian * This is usually called when a packet first enters netgraph.
303870700Sjulian * By definition, this is usually from an interrupt, or from a user.
303970700Sjulian * Users are not so important, but try be quick for the times that it's
3040122110Sharti * an interrupt.
304170700Sjulian * XXX If reserve is low, we should try to get 2 from malloc as this
304270700Sjulian * would indicate it often fails.
304352419Sjulian */
304470700Sjulianstatic item_p
304552419Sjulianng_getqblk(void)
304652419Sjulian{
304770700Sjulian	item_p item = NULL;
304852419Sjulian
304970700Sjulian	/*
305070700Sjulian	 * Try get a cached queue block, or else allocate a new one
305170700Sjulian	 * If we are less than our reserve, try malloc. If malloc
305270700Sjulian	 * fails, then that's what the reserve is for...
3053122110Sharti	 * We have our little reserve
305470700Sjulian	 * because we use M_NOWAIT for malloc. This just helps us
305570700Sjulian	 * avoid dropping packets while not increasing the time
305671650Sjulian	 * we take to service the interrupt (on average) (I hope).
305770700Sjulian	 */
3058122110Sharti	mtx_lock(&ngq_mtx);
3059122110Sharti
3060122110Sharti	if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) {
3061122110Sharti		if (allocated < maxalloc) {  /* don't leak forever */
3062122110Sharti			MALLOC(item, item_p ,
3063122110Sharti			    sizeof(*item), M_NETGRAPH_ITEM,
3064122110Sharti			    (M_NOWAIT | M_ZERO));
3065122110Sharti			if (item) {
306670784Sjulian#ifdef	NETGRAPH_DEBUG
3067122110Sharti				TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
306870784Sjulian#endif	/* NETGRAPH_DEBUG */
3069122110Sharti				allocated++;
307070700Sjulian			}
307170700Sjulian		}
3072122110Sharti	}
307352419Sjulian
3074122110Sharti	/*
3075122110Sharti	 * We didn't or couldn't malloc.
3076122110Sharti	 * try get one from our cache.
3077122110Sharti	 */
3078122110Sharti	if (item == NULL && (item = ngqfree) != NULL) {
3079122110Sharti		ngqfree = item->el_next;
3080122110Sharti		ngqfreesize--;
3081122110Sharti		item->el_flags &= ~NGQF_FREE;
308252419Sjulian	}
3083122110Sharti
3084122110Sharti	mtx_unlock(&ngq_mtx);
308570700Sjulian	return (item);
308652419Sjulian}
308752419Sjulian
308852419Sjulian/*
308952419Sjulian * Release a queue entry
309052419Sjulian */
309170700Sjulianvoid
309270700Sjulianng_free_item(item_p item)
309352419Sjulian{
309452419Sjulian
309570700Sjulian	/*
309670700Sjulian	 * The item may hold resources on it's own. We need to free
309770700Sjulian	 * these before we can free the item. What they are depends upon
309870700Sjulian	 * what kind of item it is. it is important that nodes zero
309970700Sjulian	 * out pointers to resources that they remove from the item
310070700Sjulian	 * or we release them again here.
310170700Sjulian	 */
310270700Sjulian	if (item->el_flags & NGQF_FREE) {
310370700Sjulian		panic(" Freeing free queue item");
310452419Sjulian	}
310571047Sjulian	switch (item->el_flags & NGQF_TYPE) {
310670700Sjulian	case NGQF_DATA:
3107131374Sjulian		/* If we have an mbuf still attached.. */
310870700Sjulian		NG_FREE_M(_NGI_M(item));
310970700Sjulian		break;
311070700Sjulian	case NGQF_MESG:
3111102244Sarchie		_NGI_RETADDR(item) = 0;
311270700Sjulian		NG_FREE_MSG(_NGI_MSG(item));
311370700Sjulian		break;
311471047Sjulian	case NGQF_FN:
311571047Sjulian		/* nothing to free really, */
311671047Sjulian		_NGI_FN(item) = NULL;
311771047Sjulian		_NGI_ARG1(item) = NULL;
311871047Sjulian		_NGI_ARG2(item) = 0;
311971047Sjulian	case NGQF_UNDEF:
312097229Speter		break;
312152419Sjulian	}
312271849Sjulian	/* If we still have a node or hook referenced... */
312371849Sjulian	_NGI_CLR_NODE(item);
312471849Sjulian	_NGI_CLR_HOOK(item);
312570700Sjulian	item->el_flags |= NGQF_FREE;
312652419Sjulian
3127122110Sharti	mtx_lock(&ngq_mtx);
3128122110Sharti	if (ngqfreesize < ngqfreemax) {
3129122110Sharti		ngqfreesize++;
3130122110Sharti		item->el_next = ngqfree;
3131122110Sharti		ngqfree = item;
313270700Sjulian	} else {
313370784Sjulian#ifdef	NETGRAPH_DEBUG
313470700Sjulian		TAILQ_REMOVE(&ng_itemlist, item, all);
313570784Sjulian#endif	/* NETGRAPH_DEBUG */
313670700Sjulian		NG_FREE_ITEM_REAL(item);
3137122110Sharti		allocated--;
313870700Sjulian	}
3139122110Sharti	mtx_unlock(&ngq_mtx);
314070700Sjulian}
314152419Sjulian
314270784Sjulian#ifdef	NETGRAPH_DEBUG
314370700Sjulianvoid
314470784Sjuliandumphook (hook_p hook, char *file, int line)
314570784Sjulian{
314670784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
314770784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
314870784Sjulian	printf("	Last active @ %s, line %d\n",
314970784Sjulian		hook->lastfile, hook->lastline);
315070784Sjulian	if (line) {
315170784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
315270784Sjulian	}
315370784Sjulian}
315470784Sjulian
315570784Sjulianvoid
315670784Sjuliandumpnode(node_p node, char *file, int line)
315770784Sjulian{
315870784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
315971047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
316070784Sjulian		node->nd_numhooks, node->nd_flags,
316170784Sjulian		node->nd_refs, node->nd_name);
316270784Sjulian	printf("	Last active @ %s, line %d\n",
316370784Sjulian		node->lastfile, node->lastline);
316470784Sjulian	if (line) {
316570784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
316670784Sjulian	}
316770784Sjulian}
316870784Sjulian
316970784Sjulianvoid
317070700Sjuliandumpitem(item_p item, char *file, int line)
317170700Sjulian{
317270700Sjulian	if (item->el_flags & NGQF_FREE) {
317370700Sjulian		printf(" Free item, freed at %s, line %d\n",
317470700Sjulian			item->lastfile, item->lastline);
317552419Sjulian	} else {
317670700Sjulian		printf(" ACTIVE item, last used at %s, line %d",
317770700Sjulian			item->lastfile, item->lastline);
317871047Sjulian		switch(item->el_flags & NGQF_TYPE) {
317971047Sjulian		case NGQF_DATA:
318071047Sjulian			printf(" - [data]\n");
318171047Sjulian			break;
318271047Sjulian		case NGQF_MESG:
318370700Sjulian			printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
318471047Sjulian			break;
318571047Sjulian		case NGQF_FN:
318671047Sjulian			printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
318771047Sjulian				item->body.fn.fn_fn,
318871849Sjulian				NGI_NODE(item),
318971849Sjulian				NGI_HOOK(item),
319071047Sjulian				item->body.fn.fn_arg1,
319171047Sjulian				item->body.fn.fn_arg2,
319271047Sjulian				item->body.fn.fn_arg2);
319371047Sjulian			break;
319471047Sjulian		case NGQF_UNDEF:
319571047Sjulian			printf(" - UNDEFINED!\n");
319670700Sjulian		}
319752419Sjulian	}
319870784Sjulian	if (line) {
319970784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
320071849Sjulian		if (NGI_NODE(item)) {
320170784Sjulian			printf("node %p ([%x])\n",
320271849Sjulian				NGI_NODE(item), ng_node2ID(NGI_NODE(item)));
320370784Sjulian		}
320470784Sjulian	}
320570700Sjulian}
320652419Sjulian
320770784Sjulianstatic void
320870784Sjulianng_dumpitems(void)
320970784Sjulian{
321070784Sjulian	item_p item;
321170784Sjulian	int i = 1;
321270784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
321370784Sjulian		printf("[%d] ", i++);
321470784Sjulian		dumpitem(item, NULL, 0);
321570784Sjulian	}
321670784Sjulian}
321770784Sjulian
321870784Sjulianstatic void
321970784Sjulianng_dumpnodes(void)
322070784Sjulian{
322170784Sjulian	node_p node;
322270784Sjulian	int i = 1;
3223131008Srwatson	mtx_lock(&ng_nodelist_mtx);
322470784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
322570784Sjulian		printf("[%d] ", i++);
322670784Sjulian		dumpnode(node, NULL, 0);
322770784Sjulian	}
3228131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
322970784Sjulian}
323070784Sjulian
323170784Sjulianstatic void
323270784Sjulianng_dumphooks(void)
323370784Sjulian{
323470784Sjulian	hook_p hook;
323570784Sjulian	int i = 1;
3236131008Srwatson	mtx_lock(&ng_nodelist_mtx);
323770784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
323870784Sjulian		printf("[%d] ", i++);
323970784Sjulian		dumphook(hook, NULL, 0);
324070784Sjulian	}
3241131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
324270784Sjulian}
324370784Sjulian
324470700Sjulianstatic int
324570700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
324670700Sjulian{
324770700Sjulian	int error;
324870700Sjulian	int val;
324970700Sjulian	int i;
325070700Sjulian
325170700Sjulian	val = allocated;
325270700Sjulian	i = 1;
325370700Sjulian	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
325471047Sjulian	if (error != 0 || req->newptr == NULL)
325571047Sjulian		return (error);
325671047Sjulian	if (val == 42) {
325770784Sjulian		ng_dumpitems();
325870784Sjulian		ng_dumpnodes();
325970784Sjulian		ng_dumphooks();
326070700Sjulian	}
326171047Sjulian	return (0);
326252419Sjulian}
326352419Sjulian
326471047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
326571047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
326670784Sjulian#endif	/* NETGRAPH_DEBUG */
326770700Sjulian
326870700Sjulian
326970700Sjulian/***********************************************************************
327070700Sjulian* Worklist routines
327170700Sjulian**********************************************************************/
327270700Sjulian/* NETISR thread enters here */
327352419Sjulian/*
327470700Sjulian * Pick a node off the list of nodes with work,
327570700Sjulian * try get an item to process off it.
327670700Sjulian * If there are no more, remove the node from the list.
327752419Sjulian */
327870700Sjulianstatic void
327970700Sjulianngintr(void)
328052419Sjulian{
328170700Sjulian	item_p item;
328270700Sjulian	node_p  node = NULL;
328352419Sjulian
328470700Sjulian	for (;;) {
328572200Sbmilekic		mtx_lock_spin(&ng_worklist_mtx);
328670700Sjulian		node = TAILQ_FIRST(&ng_worklist);
328770700Sjulian		if (!node) {
328872200Sbmilekic			mtx_unlock_spin(&ng_worklist_mtx);
328970700Sjulian			break;
329069922Sjulian		}
329171902Sjulian		node->nd_flags &= ~NG_WORKQ;
329270784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
329372200Sbmilekic		mtx_unlock_spin(&ng_worklist_mtx);
329470700Sjulian		/*
329570700Sjulian		 * We have the node. We also take over the reference
329670700Sjulian		 * that the list had on it.
329770700Sjulian		 * Now process as much as you can, until it won't
329870700Sjulian		 * let you have another item off the queue.
329970700Sjulian		 * All this time, keep the reference
330070700Sjulian		 * that lets us be sure that the node still exists.
330170700Sjulian		 * Let the reference go at the last minute.
330271902Sjulian		 * ng_dequeue will put us back on the worklist
330371902Sjulian		 * if there is more too do. This may be of use if there
330471902Sjulian		 * are Multiple Processors and multiple Net threads in the
330571902Sjulian		 * future.
330670700Sjulian		 */
330770700Sjulian		for (;;) {
330872200Sbmilekic			mtx_lock_spin(&node->nd_input_queue.q_mtx);
330970784Sjulian			item = ng_dequeue(&node->nd_input_queue);
331070700Sjulian			if (item == NULL) {
331172200Sbmilekic				mtx_unlock_spin(&node->nd_input_queue.q_mtx);
331270700Sjulian				break; /* go look for another node */
331370700Sjulian			} else {
331472200Sbmilekic				mtx_unlock_spin(&node->nd_input_queue.q_mtx);
331574078Sjulian				NGI_GET_NODE(item, node); /* zaps stored node */
331674078Sjulian				ng_apply_item(node, item);
331774078Sjulian				NG_NODE_UNREF(node);
331870700Sjulian			}
331970700Sjulian		}
332073238Sjulian		NG_NODE_UNREF(node);
332152419Sjulian	}
332270700Sjulian}
332369922Sjulian
332470700Sjulianstatic void
332570700Sjulianng_worklist_remove(node_p node)
332670700Sjulian{
332772200Sbmilekic	mtx_lock_spin(&ng_worklist_mtx);
332870784Sjulian	if (node->nd_flags & NG_WORKQ) {
332972979Sjulian		node->nd_flags &= ~NG_WORKQ;
333070784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
333172979Sjulian		mtx_unlock_spin(&ng_worklist_mtx);
333270784Sjulian		NG_NODE_UNREF(node);
333372979Sjulian	} else {
333472979Sjulian		mtx_unlock_spin(&ng_worklist_mtx);
333570700Sjulian	}
333670700Sjulian}
333770700Sjulian
333872979Sjulian/*
333972979Sjulian * XXX
334072979Sjulian * It's posible that a debugging NG_NODE_REF may need
334172979Sjulian * to be outside the mutex zone
334272979Sjulian */
334370700Sjulianstatic void
334470700Sjulianng_setisr(node_p node)
334570700Sjulian{
334672200Sbmilekic	mtx_lock_spin(&ng_worklist_mtx);
334770784Sjulian	if ((node->nd_flags & NG_WORKQ) == 0) {
334869922Sjulian		/*
334970700Sjulian		 * If we are not already on the work queue,
335070700Sjulian		 * then put us on.
335169922Sjulian		 */
335270784Sjulian		node->nd_flags |= NG_WORKQ;
335370784Sjulian		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
335472979Sjulian		NG_NODE_REF(node); /* XXX fafe in mutex? */
335570700Sjulian	}
335672200Sbmilekic	mtx_unlock_spin(&ng_worklist_mtx);
335770700Sjulian	schednetisr(NETISR_NETGRAPH);
335870700Sjulian}
335970700Sjulian
336070700Sjulian
336170700Sjulian/***********************************************************************
336270700Sjulian* Externally useable functions to set up a queue item ready for sending
336370700Sjulian***********************************************************************/
336470700Sjulian
336570784Sjulian#ifdef	NETGRAPH_DEBUG
336670784Sjulian#define	ITEM_DEBUG_CHECKS						\
336770700Sjulian	do {								\
336871849Sjulian		if (NGI_NODE(item) ) {					\
336970700Sjulian			printf("item already has node");		\
3370131933Smarcel			kdb_enter("has node");				\
337171849Sjulian			NGI_CLR_NODE(item);				\
337270700Sjulian		}							\
337371849Sjulian		if (NGI_HOOK(item) ) {					\
337470700Sjulian			printf("item already has hook");		\
3375131933Smarcel			kdb_enter("has hook");				\
337671849Sjulian			NGI_CLR_HOOK(item);				\
337770700Sjulian		}							\
337870700Sjulian	} while (0)
337970700Sjulian#else
338070784Sjulian#define ITEM_DEBUG_CHECKS
338170700Sjulian#endif
338270700Sjulian
338370700Sjulian/*
3384131374Sjulian * Put mbuf into the item.
338570700Sjulian * Hook and node references will be removed when the item is dequeued.
338670700Sjulian * (or equivalent)
338770700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
338870700Sjulian * remote node might go away in this timescale.
338970700Sjulian * We know the hooks can't go away because that would require getting
339070700Sjulian * a writer item on both nodes and we must have at least a  reader
339170700Sjulian * here to eb able to do this.
339270700Sjulian * Note that the hook loaded is the REMOTE hook.
339370700Sjulian *
339470700Sjulian * This is possibly in the critical path for new data.
339570700Sjulian */
339670700Sjulianitem_p
3397131374Sjulianng_package_data(struct mbuf *m, void *dummy)
339870700Sjulian{
339970700Sjulian	item_p item;
340070700Sjulian
340170700Sjulian	if ((item = ng_getqblk()) == NULL) {
340270700Sjulian		NG_FREE_M(m);
340370700Sjulian		return (NULL);
340470700Sjulian	}
340570784Sjulian	ITEM_DEBUG_CHECKS;
340670700Sjulian	item->el_flags = NGQF_DATA;
340770700Sjulian	item->el_next = NULL;
340870700Sjulian	NGI_M(item) = m;
340970700Sjulian	return (item);
341070700Sjulian}
341170700Sjulian
341270700Sjulian/*
341370700Sjulian * Allocate a queue item and put items into it..
341470700Sjulian * Evaluate the address as this will be needed to queue it and
341570700Sjulian * to work out what some of the fields should be.
341670700Sjulian * Hook and node references will be removed when the item is dequeued.
341770700Sjulian * (or equivalent)
341870700Sjulian */
341970700Sjulianitem_p
342070700Sjulianng_package_msg(struct ng_mesg *msg)
342170700Sjulian{
342270700Sjulian	item_p item;
342370700Sjulian
342470700Sjulian	if ((item = ng_getqblk()) == NULL) {
342571849Sjulian		NG_FREE_MSG(msg);
342670700Sjulian		return (NULL);
342769922Sjulian	}
342870784Sjulian	ITEM_DEBUG_CHECKS;
342970700Sjulian	item->el_flags = NGQF_MESG;
343070700Sjulian	item->el_next = NULL;
343170700Sjulian	/*
343270700Sjulian	 * Set the current lasthook into the queue item
343370700Sjulian	 */
343470700Sjulian	NGI_MSG(item) = msg;
3435102244Sarchie	NGI_RETADDR(item) = 0;
343670700Sjulian	return (item);
343770700Sjulian}
343869922Sjulian
343970700Sjulian
344070700Sjulian
344171849Sjulian#define SET_RETADDR(item, here, retaddr)				\
344271047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
344371047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
344470700Sjulian			if (retaddr) {					\
344570700Sjulian				NGI_RETADDR(item) = retaddr;		\
344670700Sjulian			} else {					\
344770700Sjulian				/*					\
344870700Sjulian				 * The old return address should be ok.	\
344970700Sjulian				 * If there isn't one, use the address	\
345070700Sjulian				 * here.				\
345170700Sjulian				 */					\
345270700Sjulian				if (NGI_RETADDR(item) == 0) {		\
345370700Sjulian					NGI_RETADDR(item)		\
345470700Sjulian						= ng_node2ID(here);	\
345570700Sjulian				}					\
345670700Sjulian			}						\
345770700Sjulian		}							\
345870700Sjulian	} while (0)
345970700Sjulian
346070700Sjulianint
346170700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
346270700Sjulian{
346371849Sjulian	hook_p peer;
346471849Sjulian	node_p peernode;
346570784Sjulian	ITEM_DEBUG_CHECKS;
346670700Sjulian	/*
346770700Sjulian	 * Quick sanity check..
346870784Sjulian	 * Since a hook holds a reference on it's node, once we know
346970784Sjulian	 * that the peer is still connected (even if invalid,) we know
347070784Sjulian	 * that the peer node is present, though maybe invalid.
347170700Sjulian	 */
347270700Sjulian	if ((hook == NULL)
347370784Sjulian	|| NG_HOOK_NOT_VALID(hook)
347470784Sjulian	|| (NG_HOOK_PEER(hook) == NULL)
347570784Sjulian	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
347670784Sjulian	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
347770700Sjulian		NG_FREE_ITEM(item);
347871047Sjulian		TRAP_ERROR();
347973083Sjulian		return (ENETDOWN);
348052419Sjulian	}
348152419Sjulian
348270700Sjulian	/*
348370700Sjulian	 * Transfer our interest to the other (peer) end.
348470700Sjulian	 */
348571849Sjulian	peer = NG_HOOK_PEER(hook);
348671849Sjulian	NG_HOOK_REF(peer);
348771849Sjulian	NGI_SET_HOOK(item, peer);
348871849Sjulian	peernode = NG_PEER_NODE(hook);
348971849Sjulian	NG_NODE_REF(peernode);
349071849Sjulian	NGI_SET_NODE(item, peernode);
349179706Sjulian	SET_RETADDR(item, here, retaddr);
349270700Sjulian	return (0);
349370700Sjulian}
349452419Sjulian
349570700Sjulianint
349670700Sjulianng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
349770700Sjulian{
349870700Sjulian	node_p  dest = NULL;
349970700Sjulian	hook_p	hook = NULL;
350070700Sjulian	int     error;
350170700Sjulian
350270784Sjulian	ITEM_DEBUG_CHECKS;
350370700Sjulian	/*
350470700Sjulian	 * Note that ng_path2noderef increments the reference count
350570700Sjulian	 * on the node for us if it finds one. So we don't have to.
350670700Sjulian	 */
350770700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
350870700Sjulian	if (error) {
350970700Sjulian		NG_FREE_ITEM(item);
351070784Sjulian		return (error);
351152419Sjulian	}
351271849Sjulian	NGI_SET_NODE(item, dest);
351371849Sjulian	if ( hook) {
351470784Sjulian		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
351571849Sjulian		NGI_SET_HOOK(item, hook);
351671849Sjulian	}
351771849Sjulian	SET_RETADDR(item, here, retaddr);
351870700Sjulian	return (0);
351970700Sjulian}
352052419Sjulian
352170700Sjulianint
352270700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
352370700Sjulian{
352470700Sjulian	node_p dest;
352570700Sjulian
352670784Sjulian	ITEM_DEBUG_CHECKS;
352770700Sjulian	/*
352870700Sjulian	 * Find the target node.
352970700Sjulian	 */
353070700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
353170700Sjulian	if (dest == NULL) {
353270700Sjulian		NG_FREE_ITEM(item);
353371047Sjulian		TRAP_ERROR();
353470700Sjulian		return(EINVAL);
353570700Sjulian	}
353670700Sjulian	/* Fill out the contents */
353770700Sjulian	item->el_flags = NGQF_MESG;
353870700Sjulian	item->el_next = NULL;
353971849Sjulian	NGI_SET_NODE(item, dest);
354071849Sjulian	NGI_CLR_HOOK(item);
354171849Sjulian	SET_RETADDR(item, here, retaddr);
354252419Sjulian	return (0);
354352419Sjulian}
354452419Sjulian
354552419Sjulian/*
354670700Sjulian * special case to send a message to self (e.g. destroy node)
354770700Sjulian * Possibly indicate an arrival hook too.
354870700Sjulian * Useful for removing that hook :-)
354952419Sjulian */
355070700Sjulianitem_p
355170700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
355252419Sjulian{
355370700Sjulian	item_p item;
355452419Sjulian
355570700Sjulian	/*
355670700Sjulian	 * Find the target node.
355770700Sjulian	 * If there is a HOOK argument, then use that in preference
355870700Sjulian	 * to the address.
355970700Sjulian	 */
356070700Sjulian	if ((item = ng_getqblk()) == NULL) {
356171849Sjulian		NG_FREE_MSG(msg);
356270700Sjulian		return (NULL);
356352419Sjulian	}
356470700Sjulian
356570700Sjulian	/* Fill out the contents */
356670700Sjulian	item->el_flags = NGQF_MESG;
356770700Sjulian	item->el_next = NULL;
356870784Sjulian	NG_NODE_REF(here);
356971849Sjulian	NGI_SET_NODE(item, here);
357071849Sjulian	if (hook) {
357170784Sjulian		NG_HOOK_REF(hook);
357271849Sjulian		NGI_SET_HOOK(item, hook);
357371849Sjulian	}
357470700Sjulian	NGI_MSG(item) = msg;
357570700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
357670700Sjulian	return (item);
357752419Sjulian}
357852419Sjulian
357971047Sjulianint
358071047Sjulianng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
358171047Sjulian{
358271047Sjulian	item_p item;
358371047Sjulian
358471047Sjulian	if ((item = ng_getqblk()) == NULL) {
358571047Sjulian		return (ENOMEM);
358671047Sjulian	}
358771047Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
358873238Sjulian	NG_NODE_REF(node); /* and one for the item */
358971849Sjulian	NGI_SET_NODE(item, node);
359071849Sjulian	if (hook) {
359171047Sjulian		NG_HOOK_REF(hook);
359271849Sjulian		NGI_SET_HOOK(item, hook);
359371047Sjulian	}
359471047Sjulian	NGI_FN(item) = fn;
359571047Sjulian	NGI_ARG1(item) = arg1;
359671047Sjulian	NGI_ARG2(item) = arg2;
359774078Sjulian	return(ng_snd_item(item, 0));
359871047Sjulian}
359971047Sjulian
360091711Sjulian/*
360191711Sjulian * Official timeout routines for Netgraph nodes.
360291711Sjulian */
360391711Sjulianstatic void
360491711Sjulianng_timeout_trapoline(void *arg)
360591711Sjulian{
360691711Sjulian	item_p item = arg;
360791711Sjulian
360891711Sjulian	ng_snd_item(item, 0);
360991711Sjulian}
361091711Sjulian
361191711Sjulian
361291711Sjulianstruct callout_handle
361391711Sjulianng_timeout(node_p node, hook_p hook, int ticks,
361491711Sjulian    ng_item_fn *fn, void * arg1, int arg2)
361591711Sjulian{
361691711Sjulian	item_p item;
361791711Sjulian
361891711Sjulian	if ((item = ng_getqblk()) == NULL) {
361991711Sjulian		struct callout_handle handle;
362091711Sjulian		handle.callout = NULL;
362191711Sjulian		return (handle);
362291711Sjulian	}
362391711Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
362491711Sjulian	NG_NODE_REF(node);		/* and one for the item */
362591711Sjulian	NGI_SET_NODE(item, node);
362691711Sjulian	if (hook) {
362791711Sjulian		NG_HOOK_REF(hook);
362891711Sjulian		NGI_SET_HOOK(item, hook);
362991711Sjulian	}
363091711Sjulian	NGI_FN(item) = fn;
363191711Sjulian	NGI_ARG1(item) = arg1;
363291711Sjulian	NGI_ARG2(item) = arg2;
363391711Sjulian	return (timeout(&ng_timeout_trapoline, item, ticks));
363491711Sjulian}
363591711Sjulian
363691711Sjulian/* A special modified version of untimeout() */
363791711Sjulianint
363891711Sjulianng_untimeout(struct callout_handle handle, node_p node)
363991711Sjulian{
364091711Sjulian	item_p item;
364191711Sjulian
364291711Sjulian	if (handle.callout == NULL)
364391711Sjulian		return (0);
364491711Sjulian	mtx_lock_spin(&callout_lock);
364591711Sjulian	item = handle.callout->c_arg; /* should be an official way to do this */
364691711Sjulian	if ((handle.callout->c_func == &ng_timeout_trapoline) &&
364791711Sjulian	    (NGI_NODE(item) == node) &&
364891711Sjulian	    (callout_stop(handle.callout))) {
364991711Sjulian		/*
365091711Sjulian		 * We successfully removed it from the queue before it ran
365191711Sjulian		 * So now we need to unreference everything that was
365291711Sjulian		 * given extra references. (NG_FREE_ITEM does this).
365391711Sjulian		 */
365491711Sjulian		mtx_unlock_spin(&callout_lock);
365591711Sjulian		NG_FREE_ITEM(item);
365691711Sjulian		return (1);
365791711Sjulian	}
365891711Sjulian	mtx_unlock_spin(&callout_lock);
365991711Sjulian	return (0);
366091711Sjulian}
366191711Sjulian
366270700Sjulian/*
366370700Sjulian * Set the address, if none given, give the node here.
366470700Sjulian */
366570700Sjulianvoid
366670700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
366770700Sjulian{
366870700Sjulian	if (retaddr) {
366970700Sjulian		NGI_RETADDR(item) = retaddr;
367070700Sjulian	} else {
367170700Sjulian		/*
367270700Sjulian		 * The old return address should be ok.
367370700Sjulian		 * If there isn't one, use the address here.
367470700Sjulian		 */
367570700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
367670700Sjulian	}
367770700Sjulian}
367852419Sjulian
367970700Sjulian#define TESTING
368070700Sjulian#ifdef TESTING
368170700Sjulian/* just test all the macros */
368270700Sjulianvoid
368370700Sjulianng_macro_test(item_p item);
368470700Sjulianvoid
368570700Sjulianng_macro_test(item_p item)
368670700Sjulian{
368770700Sjulian	node_p node = NULL;
368870700Sjulian	hook_p hook = NULL;
368970700Sjulian	struct mbuf *m;
369070700Sjulian	struct ng_mesg *msg;
369170700Sjulian	ng_ID_t retaddr;
369270700Sjulian	int	error;
369370700Sjulian
369470700Sjulian	NGI_GET_M(item, m);
369570700Sjulian	NGI_GET_MSG(item, msg);
369670700Sjulian	retaddr = NGI_RETADDR(item);
3697131374Sjulian	NG_SEND_DATA(error, hook, m, NULL);
369870700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
369970700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
370070784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
370170700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
370270700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
370370700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
370470700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
370570700Sjulian}
370670700Sjulian#endif /* TESTING */
370770700Sjulian
3708