ng_base.c revision 122110
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 122110 2003-11-05 10:32:21Z harti $
4052419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
4152419Sjulian */
4252419Sjulian
4352419Sjulian/*
4452419Sjulian * This file implements the base netgraph code.
4552419Sjulian */
4652419Sjulian
4752419Sjulian#include <sys/param.h>
4852419Sjulian#include <sys/systm.h>
4952419Sjulian#include <sys/errno.h>
5052419Sjulian#include <sys/kernel.h>
51114216Skan#include <sys/limits.h>
5252419Sjulian#include <sys/malloc.h>
5352419Sjulian#include <sys/syslog.h>
5470700Sjulian#include <sys/sysctl.h>
5552419Sjulian#include <sys/linker.h>
5652419Sjulian#include <sys/queue.h>
5752419Sjulian#include <sys/mbuf.h>
5852843Sphk#include <sys/ctype.h>
5972946Sjulian#include <sys/sysctl.h>
6052419Sjulian
6152419Sjulian#include <net/netisr.h>
6252419Sjulian
6352419Sjulian#include <netgraph/ng_message.h>
6452419Sjulian#include <netgraph/netgraph.h>
6553913Sarchie#include <netgraph/ng_parse.h>
6652419Sjulian
6772053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION);
6859756Speter
6970784Sjulian/* List of all active nodes */
7070700Sjulianstatic LIST_HEAD(, ng_node) ng_nodelist;
7170700Sjulianstatic struct mtx	ng_nodelist_mtx;
7252419Sjulian
73122110Sharti/* Mutex that protects the free queue item list */
74122110Shartistatic struct mtx	ngq_mtx;
75122110Sharti
7670784Sjulian#ifdef	NETGRAPH_DEBUG
7770784Sjulian
7870784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
7970784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8070784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
8170784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8270784Sjulian
8370784Sjulianstatic void ng_dumpitems(void);
8470784Sjulianstatic void ng_dumpnodes(void);
8570784Sjulianstatic void ng_dumphooks(void);
8670784Sjulian
8770784Sjulian#endif	/* NETGRAPH_DEBUG */
8870935Sjulian/*
8970935Sjulian * DEAD versions of the structures.
9070935Sjulian * In order to avoid races, it is sometimes neccesary to point
9170935Sjulian * at SOMETHING even though theoretically, the current entity is
9270935Sjulian * INVALID. Use these to avoid these races.
9370935Sjulian */
9470935Sjulianstruct ng_type ng_deadtype = {
9570935Sjulian	NG_ABI_VERSION,
9670935Sjulian	"dead",
9770935Sjulian	NULL,	/* modevent */
9870935Sjulian	NULL,	/* constructor */
9970935Sjulian	NULL,	/* rcvmsg */
10070935Sjulian	NULL,	/* shutdown */
10170935Sjulian	NULL,	/* newhook */
10270935Sjulian	NULL,	/* findhook */
10370935Sjulian	NULL,	/* connect */
10470935Sjulian	NULL,	/* rcvdata */
10570935Sjulian	NULL,	/* disconnect */
10670935Sjulian	NULL, 	/* cmdlist */
10770935Sjulian};
10870784Sjulian
10970935Sjulianstruct ng_node ng_deadnode = {
11070935Sjulian	"dead",
11170935Sjulian	&ng_deadtype,
11270935Sjulian	NG_INVALID,
11370935Sjulian	1,	/* refs */
11470935Sjulian	0,	/* numhooks */
11570935Sjulian	NULL,	/* private */
11670935Sjulian	0,	/* ID */
11770935Sjulian	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
11870935Sjulian	{},	/* all_nodes list entry */
11970935Sjulian	{},	/* id hashtable list entry */
12070935Sjulian	{},	/* workqueue entry */
12170935Sjulian	{	0,
12270935Sjulian		{}, /* should never use! (should hang) */
12370935Sjulian		NULL,
12470935Sjulian		&ng_deadnode.nd_input_queue.queue,
12570935Sjulian		&ng_deadnode
12670935Sjulian	},
12770935Sjulian#ifdef	NETGRAPH_DEBUG
12870935Sjulian	ND_MAGIC,
12970935Sjulian	__FILE__,
13070935Sjulian	__LINE__,
13170935Sjulian	{NULL}
13270935Sjulian#endif	/* NETGRAPH_DEBUG */
13370935Sjulian};
13470935Sjulian
13570935Sjulianstruct ng_hook ng_deadhook = {
13670935Sjulian	"dead",
13770935Sjulian	NULL,		/* private */
13870935Sjulian	HK_INVALID | HK_DEAD,
13970935Sjulian	1,		/* refs always >= 1 */
14070935Sjulian	&ng_deadhook,	/* Peer is self */
14170935Sjulian	&ng_deadnode,	/* attached to deadnode */
14270935Sjulian	{},		/* hooks list */
14371885Sjulian	NULL,		/* override rcvmsg() */
14471885Sjulian	NULL,		/* override rcvdata() */
14570935Sjulian#ifdef	NETGRAPH_DEBUG
14670935Sjulian	HK_MAGIC,
14770935Sjulian	__FILE__,
14870935Sjulian	__LINE__,
14970935Sjulian	{NULL}
15070935Sjulian#endif	/* NETGRAPH_DEBUG */
15170935Sjulian};
15270935Sjulian
15370935Sjulian/*
15470935Sjulian * END DEAD STRUCTURES
15570935Sjulian */
15670700Sjulian/* List nodes with unallocated work */
15770700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
15871902Sjulianstatic struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
15970700Sjulian
16052419Sjulian/* List of installed types */
16170700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
16270700Sjulianstatic struct mtx	ng_typelist_mtx;
16352419Sjulian
16470700Sjulian/* Hash related definitions */
16571354Sjulian/* XXX Don't need to initialise them because it's a LIST */
16671354Sjulian#define NG_ID_HASH_SIZE 32 /* most systems wont need even this many */
16771354Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
16870700Sjulianstatic struct mtx	ng_idhash_mtx;
16971354Sjulian/* Method to find a node.. used twice so do it here */
17071354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
17171354Sjulian#define NG_IDHASH_FIND(ID, node)					\
17271354Sjulian	do { 								\
17371354Sjulian		LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)],	\
17471354Sjulian						nd_idnodes) {		\
17571354Sjulian			if (NG_NODE_IS_VALID(node)			\
17671354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
17771354Sjulian				break;					\
17871354Sjulian			}						\
17971354Sjulian		}							\
18071354Sjulian	} while (0)
18152722Sjulian
18270700Sjulian
18352419Sjulian/* Internal functions */
18452419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
18570700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
18652722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
18752419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
18870700Sjulianstatic void	ng_worklist_remove(node_p node);
18952419Sjulianstatic void	ngintr(void);
19074078Sjulianstatic int	ng_apply_item(node_p node, item_p item);
19170700Sjulianstatic void	ng_flush_input_queue(struct ng_queue * ngq);
19270700Sjulianstatic void	ng_setisr(node_p node);
19370700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
19471047Sjulianstatic int	ng_con_nodes(node_p node, const char *name, node_p node2,
19571047Sjulian							const char *name2);
19671849Sjulianstatic void	ng_con_part2(node_p node, hook_p hook, void *arg1, int arg2);
19771849Sjulianstatic void	ng_con_part3(node_p node, hook_p hook, void *arg1, int arg2);
19871047Sjulianstatic int	ng_mkpeer(node_p node, const char *name,
19971047Sjulian						const char *name2, char *type);
20052419Sjulian
20170784Sjulian/* imported , these used to be externally visible, some may go back */
20270700Sjulianint	ng_bypass(hook_p hook1, hook_p hook2);
20370700Sjulianvoid	ng_destroy_hook(hook_p hook);
20470700Sjuliannode_p	ng_name2noderef(node_p node, const char *name);
20570700Sjulianint	ng_path2noderef(node_p here, const char *path,
20670700Sjulian	node_p *dest, hook_p *lasthook);
20770700Sjulianstruct	ng_type *ng_findtype(const char *type);
20870700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
20970700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
21071849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
21170784Sjulianvoid	ng_unname(node_p node);
21270700Sjulian
21370700Sjulian
21452419Sjulian/* Our own netgraph malloc type */
21552419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
21670700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
21770700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
21870700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
21970700SjulianMALLOC_DEFINE(M_NETGRAPH_META, "netgraph_meta", "netgraph name storage");
22070700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
22152419Sjulian
22270700Sjulian/* Should not be visible outside this file */
22370784Sjulian
22470784Sjulian#define _NG_ALLOC_HOOK(hook) \
22570784Sjulian	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
22670784Sjulian#define _NG_ALLOC_NODE(node) \
22770784Sjulian	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
22870784Sjulian
22970784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
23070784Sjulian/*
23170784Sjulian * In debug mode:
23270784Sjulian * In an attempt to help track reference count screwups
23370784Sjulian * we do not free objects back to the malloc system, but keep them
23470784Sjulian * in a local cache where we can examine them and keep information safely
23570784Sjulian * after they have been freed.
23670784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
23770784Sjulian */
23870784Sjulianstatic __inline hook_p
23970784Sjulianng_alloc_hook(void)
24070784Sjulian{
24170784Sjulian	hook_p hook;
24270784Sjulian	SLIST_ENTRY(ng_hook) temp;
24372200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
24470784Sjulian	hook = LIST_FIRST(&ng_freehooks);
24570784Sjulian	if (hook) {
24670784Sjulian		LIST_REMOVE(hook, hk_hooks);
24770784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
24870784Sjulian		bzero(hook, sizeof(struct ng_hook));
24970784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
25072200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
25170784Sjulian		hook->hk_magic = HK_MAGIC;
25270784Sjulian	} else {
25372200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
25470784Sjulian		_NG_ALLOC_HOOK(hook);
25570784Sjulian		if (hook) {
25670784Sjulian			hook->hk_magic = HK_MAGIC;
25772200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
25870784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
25972200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
26070784Sjulian		}
26170784Sjulian	}
26270784Sjulian	return (hook);
26370784Sjulian}
26470784Sjulian
26570784Sjulianstatic __inline node_p
26670784Sjulianng_alloc_node(void)
26770784Sjulian{
26870784Sjulian	node_p node;
26970784Sjulian	SLIST_ENTRY(ng_node) temp;
27072200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
27170784Sjulian	node = LIST_FIRST(&ng_freenodes);
27270784Sjulian	if (node) {
27370784Sjulian		LIST_REMOVE(node, nd_nodes);
27470784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
27570784Sjulian		bzero(node, sizeof(struct ng_node));
27670784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
27772200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
27870784Sjulian		node->nd_magic = ND_MAGIC;
27970784Sjulian	} else {
28072200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
28170784Sjulian		_NG_ALLOC_NODE(node);
28270784Sjulian		if (node) {
28370784Sjulian			node->nd_magic = ND_MAGIC;
28472200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
28570784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
28672200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
28770784Sjulian		}
28870784Sjulian	}
28970784Sjulian	return (node);
29070784Sjulian}
29170784Sjulian
29270784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
29370784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
29470784Sjulian
29570784Sjulian
29670784Sjulian#define NG_FREE_HOOK(hook)						\
29770784Sjulian	do {								\
29872200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
29970784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
30070784Sjulian		hook->hk_magic = 0;					\
30172200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
30270784Sjulian	} while (0)
30370784Sjulian
30470784Sjulian#define NG_FREE_NODE(node)						\
30570784Sjulian	do {								\
30672200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
30770784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
30870784Sjulian		node->nd_magic = 0;					\
30972200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
31070784Sjulian	} while (0)
31170784Sjulian
31270784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
31370784Sjulian
31470784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
31570784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
31670784Sjulian
31770700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
31870700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
31970784Sjulian
32070784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
32170784Sjulian
32270700Sjulian/* Warning: Generally use NG_FREE_ITEM() instead */
32370700Sjulian#define NG_FREE_ITEM_REAL(item) do { FREE((item), M_NETGRAPH_ITEM); } while (0)
32470700Sjulian
32570700Sjulian
32652419Sjulian/* Set this to Debugger("X") to catch all errors as they occur */
32752419Sjulian#ifndef TRAP_ERROR
32871047Sjulian#define TRAP_ERROR()
32952419Sjulian#endif
33052419Sjulian
33152722Sjulianstatic	ng_ID_t nextID = 1;
33252722Sjulian
33353403Sarchie#ifdef INVARIANTS
33453403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
33553403Sarchie		struct mbuf *n;						\
33653403Sarchie		int total;						\
33753403Sarchie									\
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) {
55259875Speter		char filename[NG_TYPELEN + 4];
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
69770939Sjulian	/* Notify all remaining connected nodes to disconnect */
69870939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
69970939Sjulian		ng_destroy_hook(hook);
70070784Sjulian
70170700Sjulian	/*
70270700Sjulian	 * Drain the input queue forceably.
70370784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
70470784Sjulian	 * Theoretically we came here from a queue entry that was added
70570784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
70671902Sjulian	 * Also removes us from worklist if needed.
70770700Sjulian	 */
70870784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
70970700Sjulian
71052419Sjulian	/* Ask the type if it has anything to do in this case */
71170784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
71270784Sjulian		(*node->nd_type->shutdown)(node);
71371849Sjulian		if (NG_NODE_IS_VALID(node)) {
71471849Sjulian			/*
71571849Sjulian			 * Well, blow me down if the node code hasn't declared
71671849Sjulian			 * that it doesn't want to die.
71771849Sjulian			 * Presumably it is a persistant node.
71871849Sjulian			 * If we REALLY want it to go away,
71971849Sjulian			 *  e.g. hardware going away,
72071849Sjulian			 * Our caller should set NG_REALLY_DIE in nd_flags.
72171849Sjulian			 */
72271849Sjulian			node->nd_flags &= ~(NG_INVALID|NG_CLOSING);
72371849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
72471849Sjulian			return;
72571849Sjulian		}
72670700Sjulian	} else {				/* do the default thing */
72770784Sjulian		NG_NODE_UNREF(node);
72852419Sjulian	}
72952419Sjulian
73070784Sjulian	ng_unname(node); /* basically a NOP these days */
73170784Sjulian
73270784Sjulian	/*
73370784Sjulian	 * Remove extra reference, possibly the last
73470784Sjulian	 * Possible other holders of references may include
73570784Sjulian	 * timeout callouts, but theoretically the node's supposed to
73670784Sjulian	 * have cancelled them. Possibly hardware dependencies may
73770784Sjulian	 * force a driver to 'linger' with a reference.
73870784Sjulian	 */
73970784Sjulian	NG_NODE_UNREF(node);
74052419Sjulian}
74152419Sjulian
74274078Sjulian#ifdef	NETGRAPH_DEBUG
74374078Sjulianvoid
74474078Sjulianng_ref_node(node_p node)
74574078Sjulian{
74674078Sjulian	_NG_NODE_REF(node);
74774078Sjulian}
74874078Sjulian#endif
74974078Sjulian
75052419Sjulian/*
75174078Sjulian * Remove a reference to the node, possibly the last.
75274078Sjulian * deadnode always acts as it it were the last.
75352419Sjulian */
75474078Sjulianint
75570784Sjulianng_unref_node(node_p node)
75652419Sjulian{
75770784Sjulian	int     v;
75871047Sjulian
75971047Sjulian	if (node == &ng_deadnode) {
76074078Sjulian		return (0);
76171047Sjulian	}
76271047Sjulian
76370784Sjulian	do {
76474078Sjulian		v = node->nd_refs - 1;
76574078Sjulian	} while (! atomic_cmpset_int(&node->nd_refs, v + 1, v));
76669519Sjulian
76774078Sjulian	if (v == 0) { /* we were the last */
76870700Sjulian
76972200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
77070784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
77170784Sjulian		LIST_REMOVE(node, nd_nodes);
77272200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
77370700Sjulian
77472200Sbmilekic		mtx_lock(&ng_idhash_mtx);
77570784Sjulian		LIST_REMOVE(node, nd_idnodes);
77672200Sbmilekic		mtx_unlock(&ng_idhash_mtx);
77752419Sjulian
77870791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
77970700Sjulian		NG_FREE_NODE(node);
78052419Sjulian	}
78174078Sjulian	return (v);
78252419Sjulian}
78352419Sjulian
78452419Sjulian/************************************************************************
78552722Sjulian			Node ID handling
78652722Sjulian************************************************************************/
78752722Sjulianstatic node_p
78870700Sjulianng_ID2noderef(ng_ID_t ID)
78952722Sjulian{
79070784Sjulian	node_p node;
79172200Sbmilekic	mtx_lock(&ng_idhash_mtx);
79271354Sjulian	NG_IDHASH_FIND(ID, node);
79370784Sjulian	if(node)
79470784Sjulian		NG_NODE_REF(node);
79572200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
79670784Sjulian	return(node);
79752722Sjulian}
79852722Sjulian
79952722Sjulianng_ID_t
80052722Sjulianng_node2ID(node_p node)
80152722Sjulian{
80270912Sjulian	return (node ? NG_NODE_ID(node) : 0);
80352722Sjulian}
80452722Sjulian
80552722Sjulian/************************************************************************
80652419Sjulian			Node name handling
80752419Sjulian************************************************************************/
80852419Sjulian
80952419Sjulian/*
81052419Sjulian * Assign a node a name. Once assigned, the name cannot be changed.
81152419Sjulian */
81252419Sjulianint
81352419Sjulianng_name_node(node_p node, const char *name)
81452419Sjulian{
81552419Sjulian	int i;
81670700Sjulian	node_p node2;
81752419Sjulian
81852419Sjulian	/* Check the name is valid */
81952419Sjulian	for (i = 0; i < NG_NODELEN + 1; i++) {
82052419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
82152419Sjulian			break;
82252419Sjulian	}
82352419Sjulian	if (i == 0 || name[i] != '\0') {
82471047Sjulian		TRAP_ERROR();
82552419Sjulian		return (EINVAL);
82652419Sjulian	}
82752722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
82871047Sjulian		TRAP_ERROR();
82952419Sjulian		return (EINVAL);
83052419Sjulian	}
83152419Sjulian
83252419Sjulian	/* Check the name isn't already being used */
83370700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
83470784Sjulian		NG_NODE_UNREF(node2);
83571047Sjulian		TRAP_ERROR();
83652419Sjulian		return (EADDRINUSE);
83752419Sjulian	}
83852419Sjulian
83970700Sjulian	/* copy it */
84070784Sjulian	strncpy(NG_NODE_NAME(node), name, NG_NODELEN);
84152419Sjulian
84252419Sjulian	return (0);
84352419Sjulian}
84452419Sjulian
84552419Sjulian/*
84652419Sjulian * Find a node by absolute name. The name should NOT end with ':'
84752419Sjulian * The name "." means "this node" and "[xxx]" means "the node
84852419Sjulian * with ID (ie, at address) xxx".
84952419Sjulian *
85052419Sjulian * Returns the node if found, else NULL.
85170700Sjulian * Eventually should add something faster than a sequential search.
85270784Sjulian * Note it aquires a reference on the node so you can be sure it's still there.
85352419Sjulian */
85452419Sjuliannode_p
85570700Sjulianng_name2noderef(node_p here, const char *name)
85652419Sjulian{
85752722Sjulian	node_p node;
85852722Sjulian	ng_ID_t temp;
85952419Sjulian
86052419Sjulian	/* "." means "this node" */
86170700Sjulian	if (strcmp(name, ".") == 0) {
86270784Sjulian		NG_NODE_REF(here);
86370700Sjulian		return(here);
86470700Sjulian	}
86552419Sjulian
86652419Sjulian	/* Check for name-by-ID */
86752722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
86870700Sjulian		return (ng_ID2noderef(temp));
86952419Sjulian	}
87052419Sjulian
87152419Sjulian	/* Find node by name */
87272200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
87370784Sjulian	LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
87470912Sjulian		if (NG_NODE_IS_VALID(node)
87570912Sjulian		&& NG_NODE_HAS_NAME(node)
87670912Sjulian		&& (strcmp(NG_NODE_NAME(node), name) == 0)) {
87752419Sjulian			break;
87870912Sjulian		}
87952419Sjulian	}
88070700Sjulian	if (node)
88170784Sjulian		NG_NODE_REF(node);
88272200Sbmilekic	mtx_unlock(&ng_nodelist_mtx);
88352419Sjulian	return (node);
88452419Sjulian}
88552419Sjulian
88652419Sjulian/*
887108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the
88852722Sjulian * string is not valid, otherwise returns the value.
88952419Sjulian */
89052722Sjulianstatic ng_ID_t
89152419Sjulianng_decodeidname(const char *name)
89252419Sjulian{
89352816Sarchie	const int len = strlen(name);
89453648Sarchie	char *eptr;
89552816Sarchie	u_long val;
89652419Sjulian
89752816Sarchie	/* Check for proper length, brackets, no leading junk */
89870912Sjulian	if ((len < 3)
89970912Sjulian	|| (name[0] != '[')
90070912Sjulian	|| (name[len - 1] != ']')
90170912Sjulian	|| (!isxdigit(name[1]))) {
90270912Sjulian		return ((ng_ID_t)0);
90370912Sjulian	}
90452419Sjulian
90552816Sarchie	/* Decode number */
90652816Sarchie	val = strtoul(name + 1, &eptr, 16);
90770912Sjulian	if ((eptr - name != len - 1)
90870912Sjulian	|| (val == ULONG_MAX)
90970912Sjulian	|| (val == 0)) {
91053042Sjulian		return ((ng_ID_t)0);
91170912Sjulian	}
91252816Sarchie	return (ng_ID_t)val;
91352419Sjulian}
91452419Sjulian
91552419Sjulian/*
91652419Sjulian * Remove a name from a node. This should only be called
91752419Sjulian * when shutting down and removing the node.
91871849Sjulian * IF we allow name changing this may be more resurected.
91952419Sjulian */
92052419Sjulianvoid
92152419Sjulianng_unname(node_p node)
92252419Sjulian{
92352419Sjulian}
92452419Sjulian
92552419Sjulian/************************************************************************
92652419Sjulian			Hook routines
92752419Sjulian Names are not optional. Hooks are always connected, except for a
92870939Sjulian brief moment within these routines. On invalidation or during creation
92970939Sjulian they are connected to the 'dead' hook.
93052419Sjulian************************************************************************/
93152419Sjulian
93252419Sjulian/*
93352419Sjulian * Remove a hook reference
93452419Sjulian */
93570784Sjulianvoid
93652419Sjulianng_unref_hook(hook_p hook)
93752419Sjulian{
93870784Sjulian	int     v;
93971047Sjulian
94071047Sjulian	if (hook == &ng_deadhook) {
94171047Sjulian		return;
94271047Sjulian	}
94370784Sjulian	do {
94470784Sjulian		v = hook->hk_refs;
94570784Sjulian	} while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1));
94669519Sjulian
94770784Sjulian	if (v == 1) { /* we were the last */
94871047Sjulian		if (_NG_HOOK_NODE(hook)) { /* it'll probably be ng_deadnode */
94971047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
95070784Sjulian			hook->hk_node = NULL;
95170700Sjulian		}
95270700Sjulian		NG_FREE_HOOK(hook);
95370700Sjulian	}
95452419Sjulian}
95552419Sjulian
95652419Sjulian/*
95752419Sjulian * Add an unconnected hook to a node. Only used internally.
95870939Sjulian * Assumes node is locked. (XXX not yet true )
95952419Sjulian */
96052419Sjulianstatic int
96152419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
96252419Sjulian{
96352419Sjulian	hook_p hook;
96452419Sjulian	int error = 0;
96552419Sjulian
96652419Sjulian	/* Check that the given name is good */
96752419Sjulian	if (name == NULL) {
96871047Sjulian		TRAP_ERROR();
96952419Sjulian		return (EINVAL);
97052419Sjulian	}
97154096Sarchie	if (ng_findhook(node, name) != NULL) {
97271047Sjulian		TRAP_ERROR();
97354096Sarchie		return (EEXIST);
97452419Sjulian	}
97552419Sjulian
97652419Sjulian	/* Allocate the hook and link it up */
97770784Sjulian	NG_ALLOC_HOOK(hook);
97852419Sjulian	if (hook == NULL) {
97971047Sjulian		TRAP_ERROR();
98052419Sjulian		return (ENOMEM);
98152419Sjulian	}
98270939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
98370784Sjulian	hook->hk_flags = HK_INVALID;
98470939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
98570784Sjulian	hook->hk_node = node;
98670784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
98752419Sjulian
98870939Sjulian	/* Set hook name */
98970939Sjulian	strncpy(NG_HOOK_NAME(hook), name, NG_HOOKLEN);
99070939Sjulian
99170939Sjulian	/*
99270939Sjulian	 * Check if the node type code has something to say about it
99370939Sjulian	 * If it fails, the unref of the hook will also unref the node.
99470939Sjulian	 */
99570935Sjulian	if (node->nd_type->newhook != NULL) {
99670935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
99770784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
99870700Sjulian			return (error);
99970700Sjulian		}
100070935Sjulian	}
100152419Sjulian	/*
100252419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
100352419Sjulian	 * We'll ask again later when we actually connect the hooks.
100452419Sjulian	 */
100570784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
100670784Sjulian	node->nd_numhooks++;
100770939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
100852419Sjulian
100952419Sjulian	if (hookp)
101052419Sjulian		*hookp = hook;
101170939Sjulian	return (0);
101252419Sjulian}
101352419Sjulian
101452419Sjulian/*
101554096Sarchie * Find a hook
101654096Sarchie *
101754096Sarchie * Node types may supply their own optimized routines for finding
101854096Sarchie * hooks.  If none is supplied, we just do a linear search.
101970939Sjulian * XXX Possibly we should add a reference to the hook?
102054096Sarchie */
102154096Sarchiehook_p
102254096Sarchieng_findhook(node_p node, const char *name)
102354096Sarchie{
102454096Sarchie	hook_p hook;
102554096Sarchie
102670784Sjulian	if (node->nd_type->findhook != NULL)
102770784Sjulian		return (*node->nd_type->findhook)(node, name);
102870784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
102970912Sjulian		if (NG_HOOK_IS_VALID(hook)
103070917Sarchie		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
103154096Sarchie			return (hook);
103254096Sarchie	}
103354096Sarchie	return (NULL);
103454096Sarchie}
103554096Sarchie
103654096Sarchie/*
103752419Sjulian * Destroy a hook
103852419Sjulian *
103952419Sjulian * As hooks are always attached, this really destroys two hooks.
104052419Sjulian * The one given, and the one attached to it. Disconnect the hooks
104170939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
104270939Sjulian * hook so that it can still exist after we depart. We then
104370939Sjulian * send the peer its own destroy message. This ensures that we only
104470939Sjulian * interact with the peer's structures when it is locked processing that
104570939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
104670939Sjulian * the peer hook and node are still going to exist until
104770939Sjulian * we are finished there as the hook holds a ref on the node.
104870939Sjulian * We run this same code again on the peer hook, but that time it is already
104970939Sjulian * attached to the 'dead' hook.
105071047Sjulian *
105171047Sjulian * This routine is called at all stages of hook creation
105271047Sjulian * on error detection and must be able to handle any such stage.
105352419Sjulian */
105452419Sjulianvoid
105552419Sjulianng_destroy_hook(hook_p hook)
105652419Sjulian{
105770784Sjulian	hook_p peer = NG_HOOK_PEER(hook);
105871047Sjulian	node_p node = NG_HOOK_NODE(hook);
105952419Sjulian
106071047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
106171047Sjulian		printf("ng_destroy_hook called on deadhook\n");
106271047Sjulian		return;
106371047Sjulian	}
106470784Sjulian	hook->hk_flags |= HK_INVALID;		/* as soon as possible */
106570939Sjulian	if (peer && (peer != &ng_deadhook)) {
106670939Sjulian		/*
106770939Sjulian		 * Set the peer to point to ng_deadhook
106870939Sjulian		 * from this moment on we are effectively independent it.
106970939Sjulian		 * send it an rmhook message of it's own.
107070939Sjulian		 */
107170939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
107270939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
107371047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
107471047Sjulian			/*
107571047Sjulian			 * If it's already divorced from a node,
107671047Sjulian			 * just free it.
107771047Sjulian			 */
107871047Sjulian			/* nothing */
107971047Sjulian		} else {
108071047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
108171047Sjulian		}
108270942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
108370942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
108452419Sjulian	}
108552419Sjulian
108652419Sjulian	/*
108752419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
108852419Sjulian	 * in case the disconnection results in node shutdown.
108952419Sjulian	 */
109071047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
109171047Sjulian		return;
109271047Sjulian	}
109370784Sjulian	LIST_REMOVE(hook, hk_hooks);
109470784Sjulian	node->nd_numhooks--;
109570784Sjulian	if (node->nd_type->disconnect) {
109652419Sjulian		/*
109771047Sjulian		 * The type handler may elect to destroy the node so don't
109871047Sjulian		 * trust its existance after this point. (except
109971047Sjulian		 * that we still hold a reference on it. (which we
110071047Sjulian		 * inherrited from the hook we are destroying)
110152419Sjulian		 */
110270784Sjulian		(*node->nd_type->disconnect) (hook);
110352419Sjulian	}
110471047Sjulian
110571047Sjulian	/*
110671047Sjulian	 * Note that because we will point to ng_deadnode, the original node
110771047Sjulian	 * is not decremented automatically so we do that manually.
110871047Sjulian	 */
110971047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
111071047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
111171047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
111252419Sjulian}
111352419Sjulian
111452419Sjulian/*
111552419Sjulian * Take two hooks on a node and merge the connection so that the given node
111652419Sjulian * is effectively bypassed.
111752419Sjulian */
111852419Sjulianint
111952419Sjulianng_bypass(hook_p hook1, hook_p hook2)
112052419Sjulian{
112170784Sjulian	if (hook1->hk_node != hook2->hk_node) {
112271047Sjulian		TRAP_ERROR();
112352419Sjulian		return (EINVAL);
112470784Sjulian	}
112570784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
112670784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
112752419Sjulian
112870939Sjulian	hook1->hk_peer = &ng_deadhook;
112970939Sjulian	hook2->hk_peer = &ng_deadhook;
113070939Sjulian
113152419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
113252419Sjulian	ng_destroy_hook(hook1);
113352419Sjulian	ng_destroy_hook(hook2);
113452419Sjulian	return (0);
113552419Sjulian}
113652419Sjulian
113752419Sjulian/*
113852419Sjulian * Install a new netgraph type
113952419Sjulian */
114052419Sjulianint
114152419Sjulianng_newtype(struct ng_type *tp)
114252419Sjulian{
114352419Sjulian	const size_t namelen = strlen(tp->name);
114452419Sjulian
114552419Sjulian	/* Check version and type name fields */
114670159Sjulian	if ((tp->version != NG_ABI_VERSION)
114770159Sjulian	|| (namelen == 0)
114870159Sjulian	|| (namelen > NG_TYPELEN)) {
114971047Sjulian		TRAP_ERROR();
115052419Sjulian		return (EINVAL);
115152419Sjulian	}
115252419Sjulian
115352419Sjulian	/* Check for name collision */
115452419Sjulian	if (ng_findtype(tp->name) != NULL) {
115571047Sjulian		TRAP_ERROR();
115652419Sjulian		return (EEXIST);
115752419Sjulian	}
115852419Sjulian
115970700Sjulian
116052419Sjulian	/* Link in new type */
116172200Sbmilekic	mtx_lock(&ng_typelist_mtx);
116270700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
116371603Sjulian	tp->refs = 1;	/* first ref is linked list */
116472200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
116552419Sjulian	return (0);
116652419Sjulian}
116752419Sjulian
116852419Sjulian/*
116980222Sjulian * unlink a netgraph type
117080222Sjulian * If no examples exist
117180222Sjulian */
117280222Sjulianint
117380222Sjulianng_rmtype(struct ng_type *tp)
117480222Sjulian{
117580222Sjulian	/* Check for name collision */
117680222Sjulian	if (tp->refs != 1) {
117780222Sjulian		TRAP_ERROR();
117880222Sjulian		return (EBUSY);
117980222Sjulian	}
118080222Sjulian
118180222Sjulian	/* Unlink type */
118280222Sjulian	mtx_lock(&ng_typelist_mtx);
118380222Sjulian	LIST_REMOVE(tp, types);
118480222Sjulian	mtx_unlock(&ng_typelist_mtx);
118580222Sjulian	return (0);
118680222Sjulian}
118780222Sjulian
118880222Sjulian/*
118952419Sjulian * Look for a type of the name given
119052419Sjulian */
119152419Sjulianstruct ng_type *
119252419Sjulianng_findtype(const char *typename)
119352419Sjulian{
119452419Sjulian	struct ng_type *type;
119552419Sjulian
119672200Sbmilekic	mtx_lock(&ng_typelist_mtx);
119770700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
119852419Sjulian		if (strcmp(type->name, typename) == 0)
119952419Sjulian			break;
120052419Sjulian	}
120172200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
120252419Sjulian	return (type);
120352419Sjulian}
120452419Sjulian
120552419Sjulian/************************************************************************
120652419Sjulian			Composite routines
120752419Sjulian************************************************************************/
120852419Sjulian/*
120971047Sjulian * Connect two nodes using the specified hooks, using queued functions.
121052419Sjulian */
121171849Sjulianstatic void
121271047Sjulianng_con_part3(node_p node, hook_p hook, void *arg1, int arg2)
121352419Sjulian{
121452419Sjulian
121571047Sjulian	/*
121671047Sjulian	 * When we run, we know that the node 'node' is locked for us.
121771047Sjulian	 * Our caller has a reference on the hook.
121871047Sjulian	 * Our caller has a reference on the node.
121971047Sjulian	 * (In this case our caller is ng_apply_item() ).
122071047Sjulian	 * The peer hook has a reference on the hook.
122171849Sjulian	 * We are all set up except for the final call to the node, and
122271849Sjulian	 * the clearing of the INVALID flag.
122371047Sjulian	 */
122471047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
122571047Sjulian		/*
122671047Sjulian		 * The node must have been freed again since we last visited
122771047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
122871047Sjulian		 * We should just release our references and
122971047Sjulian		 * free anything we can think of.
123071047Sjulian		 * Since we know it's been destroyed, and it's our caller
123171047Sjulian		 * that holds the references, just return.
123271047Sjulian		 */
123371849Sjulian		return ;
123452419Sjulian	}
123571047Sjulian	if (hook->hk_node->nd_type->connect) {
123671849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
123771047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
123871849Sjulian			printf("failed in ng_con_part3()\n");
123971849Sjulian			return ;
124071047Sjulian		}
124152419Sjulian	}
124271047Sjulian	/*
124371047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
124471047Sjulian	 * to separate out 'create' and 'invalid' flags.
124571047Sjulian	 * should only set flags on hooks we have locked under our node.
124671047Sjulian	 */
124771047Sjulian	hook->hk_flags &= ~HK_INVALID;
124871849Sjulian	return ;
124971047Sjulian}
125052419Sjulian
125171849Sjulianstatic void
125271047Sjulianng_con_part2(node_p node, hook_p hook, void *arg1, int arg2)
125371047Sjulian{
125471047Sjulian
125552419Sjulian	/*
125671047Sjulian	 * When we run, we know that the node 'node' is locked for us.
125771047Sjulian	 * Our caller has a reference on the hook.
125871047Sjulian	 * Our caller has a reference on the node.
125971047Sjulian	 * (In this case our caller is ng_apply_item() ).
126071047Sjulian	 * The peer hook has a reference on the hook.
126171047Sjulian	 * our node pointer points to the 'dead' node.
126271047Sjulian	 * First check the hook name is unique.
126371849Sjulian	 * Should not happen because we checked before queueing this.
126452419Sjulian	 */
126571047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
126671047Sjulian		TRAP_ERROR();
126771047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
126871849Sjulian		printf("failed in ng_con_part2()\n");
126971849Sjulian		return ;
127071047Sjulian	}
127170939Sjulian	/*
127271047Sjulian	 * Check if the node type code has something to say about it
127371047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
127471047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
127571047Sjulian	 * The peer hook will also be destroyed.
127670939Sjulian	 */
127771047Sjulian	if (node->nd_type->newhook != NULL) {
127871849Sjulian		if ((*node->nd_type->newhook)(node, hook, hook->hk_name)) {
127971047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
128071849Sjulian			printf("failed in ng_con_part2()\n");
128171849Sjulian			return ;
128271047Sjulian		}
128371047Sjulian	}
128471047Sjulian
128571047Sjulian	/*
128671047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
128771047Sjulian	 * We'll ask again later when we actually connect the hooks.
128871047Sjulian	 */
128971047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
129071047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
129171047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
129271047Sjulian	node->nd_numhooks++;
129371047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
129471047Sjulian
129571047Sjulian	/*
129671047Sjulian	 * We now have a symetrical situation, where both hooks have been
129774078Sjulian	 * linked to their nodes, the newhook methods have been called
129871047Sjulian	 * And the references are all correct. The hooks are still marked
129971047Sjulian	 * as invalid, as we have not called the 'connect' methods
130071047Sjulian	 * yet.
130171047Sjulian	 * We can call the local one immediatly as we have the
130271047Sjulian	 * node locked, but we need to queue the remote one.
130371047Sjulian	 */
130471047Sjulian	if (hook->hk_node->nd_type->connect) {
130571849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
130671047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
130771849Sjulian			printf("failed in ng_con_part2(A)\n");
130871849Sjulian			return ;
130971047Sjulian		}
131071047Sjulian	}
131171849Sjulian	if (ng_send_fn(hook->hk_peer->hk_node, hook->hk_peer,
131271849Sjulian			&ng_con_part3, arg1, arg2)) {
131371849Sjulian		printf("failed in ng_con_part2(B)");
131471849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
131571849Sjulian		return ;
131671849Sjulian	}
131771047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
131871849Sjulian	return ;
131952419Sjulian}
132052419Sjulian
132152419Sjulian/*
132271047Sjulian * Connect this node with another node. We assume that this node is
132371047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
132452419Sjulian */
132571047Sjulianstatic int
132652419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
132752419Sjulian{
132852419Sjulian	int     error;
132952419Sjulian	hook_p  hook;
133052419Sjulian	hook_p  hook2;
133152419Sjulian
133271849Sjulian	if (ng_findhook(node2, name2) != NULL) {
133371849Sjulian		return(EEXIST);
133471849Sjulian	}
133570939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
133652419Sjulian		return (error);
133771047Sjulian	/* Allocate the other hook and link it up */
133871047Sjulian	NG_ALLOC_HOOK(hook2);
133971047Sjulian	if (hook == NULL) {
134071047Sjulian		TRAP_ERROR();
134171047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
134271047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
134371047Sjulian		return (ENOMEM);
134471047Sjulian	}
134571047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
134671047Sjulian	hook2->hk_flags = HK_INVALID;
134771047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
134871047Sjulian	hook->hk_peer = hook2;
134971047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
135071047Sjulian	NG_HOOK_REF(hook2);
135171047Sjulian	hook2->hk_node = &ng_deadnode;
135271047Sjulian	strncpy(NG_HOOK_NAME(hook2), name2, NG_HOOKLEN);
135371047Sjulian
135471047Sjulian	/*
135571047Sjulian	 * Queue the function above.
135671047Sjulian	 * Procesing continues in that function in the lock context of
135771047Sjulian	 * the other node.
135871047Sjulian	 */
135971849Sjulian	ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0);
136071047Sjulian
136171047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
136271047Sjulian	NG_HOOK_UNREF(hook2);
136371849Sjulian	return (0);
136471047Sjulian}
136571047Sjulian
136671047Sjulian/*
136771047Sjulian * Make a peer and connect.
136871047Sjulian * We assume that the local node is locked.
136971047Sjulian * The new node probably doesn't need a lock until
137071047Sjulian * it has a hook, because it cannot really have any work until then,
137171047Sjulian * but we should think about it a bit more.
137271047Sjulian *
137371047Sjulian * The problem may come if the other node also fires up
137471047Sjulian * some hardware or a timer or some other source of activation,
137571047Sjulian * also it may already get a command msg via it's ID.
137671047Sjulian *
137771047Sjulian * We could use the same method as ng_con_nodes() but we'd have
137871047Sjulian * to add ability to remove the node when failing. (Not hard, just
137971047Sjulian * make arg1 point to the node to remove).
138071047Sjulian * Unless of course we just ignore failure to connect and leave
138171047Sjulian * an unconnected node?
138271047Sjulian */
138371047Sjulianstatic int
138471047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
138571047Sjulian{
138671047Sjulian	node_p  node2;
138771047Sjulian	hook_p  hook1;
138871047Sjulian	hook_p  hook2;
138971047Sjulian	int     error;
139071047Sjulian
139171047Sjulian	if ((error = ng_make_node(type, &node2))) {
139252419Sjulian		return (error);
139352419Sjulian	}
139470939Sjulian
139571047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
139671849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
139771047Sjulian		return (error);
139871047Sjulian	}
139971047Sjulian
140071047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
140171849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
140271047Sjulian		ng_destroy_hook(hook1);
140371047Sjulian		NG_HOOK_UNREF(hook1);
140471047Sjulian		return (error);
140571047Sjulian	}
140671047Sjulian
140770939Sjulian	/*
140871047Sjulian	 * Actually link the two hooks together.
140971047Sjulian	 */
141071047Sjulian	hook1->hk_peer = hook2;
141171047Sjulian	hook2->hk_peer = hook1;
141271047Sjulian
141371047Sjulian	/* Each hook is referenced by the other */
141471047Sjulian	NG_HOOK_REF(hook1);
141571047Sjulian	NG_HOOK_REF(hook2);
141671047Sjulian
141771047Sjulian	/* Give each node the opportunity to veto the pending connection */
141871047Sjulian	if (hook1->hk_node->nd_type->connect) {
141971047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
142071047Sjulian	}
142171047Sjulian
142271047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
142371047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
142471047Sjulian
142571047Sjulian	}
142671047Sjulian
142771047Sjulian	/*
142870939Sjulian	 * drop the references we were holding on the two hooks.
142970939Sjulian	 */
143071047Sjulian	if (error) {
143171047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
143271849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
143371047Sjulian	} else {
143471047Sjulian		/* As a last act, allow the hooks to be used */
143571047Sjulian		hook1->hk_flags &= ~HK_INVALID;
143671047Sjulian		hook2->hk_flags &= ~HK_INVALID;
143771047Sjulian	}
143871047Sjulian	NG_HOOK_UNREF(hook1);
143970939Sjulian	NG_HOOK_UNREF(hook2);
144070939Sjulian	return (error);
144152419Sjulian}
144271047Sjulian
144370700Sjulian/************************************************************************
144470700Sjulian		Utility routines to send self messages
144570700Sjulian************************************************************************/
144670700Sjulian
144771849Sjulian/* Shut this node down as soon as everyone is clear of it */
144871849Sjulian/* Should add arg "immediatly" to jump the queue */
144970700Sjulianint
145071849Sjulianng_rmnode_self(node_p node)
145170700Sjulian{
145271849Sjulian	int		error;
145352419Sjulian
145471849Sjulian	if (node == &ng_deadnode)
145571849Sjulian		return (0);
145672053Sjulian	node->nd_flags |= NG_INVALID;
145771849Sjulian	if (node->nd_flags & NG_CLOSING)
145871849Sjulian		return (0);
145970700Sjulian
146071849Sjulian	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
146171849Sjulian	return (error);
146270700Sjulian}
146370700Sjulian
146471849Sjulianstatic void
146571047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
146671047Sjulian{
146771047Sjulian	ng_destroy_hook(hook);
146871849Sjulian	return ;
146971047Sjulian}
147071047Sjulian
147170935Sjulianint
147270935Sjulianng_rmhook_self(hook_p hook)
147370935Sjulian{
147471047Sjulian	int		error;
147570935Sjulian	node_p node = NG_HOOK_NODE(hook);
147670935Sjulian
147771047Sjulian	if (node == &ng_deadnode)
147871047Sjulian		return (0);
147971047Sjulian
148071047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
148171047Sjulian	return (error);
148270935Sjulian}
148370935Sjulian
148470700Sjulian/***********************************************************************
148552419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
148652419Sjulian *
148752419Sjulian * Such a string can refer to a specific node or a specific hook
148852419Sjulian * on a specific node, depending on how you look at it. In the
148952419Sjulian * latter case, the PATH component must not end in a dot.
149052419Sjulian *
149152419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
149252419Sjulian * of hook names separated by dots. This breaks out the original
149352419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
149452419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
149552419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
149652419Sjulian *
149752419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
149870700Sjulian ***********************************************************************/
149952419Sjulianint
150052419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
150152419Sjulian{
150252419Sjulian	char   *node, *path, *hook;
150352419Sjulian	int     k;
150452419Sjulian
150552419Sjulian	/*
150652419Sjulian	 * Extract absolute NODE, if any
150752419Sjulian	 */
150852419Sjulian	for (path = addr; *path && *path != ':'; path++);
150952419Sjulian	if (*path) {
151052419Sjulian		node = addr;	/* Here's the NODE */
151152419Sjulian		*path++ = '\0';	/* Here's the PATH */
151252419Sjulian
151352419Sjulian		/* Node name must not be empty */
151452419Sjulian		if (!*node)
151552419Sjulian			return -1;
151652419Sjulian
151752419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
151852419Sjulian		if (strcmp(node, ".") != 0) {
151952419Sjulian			for (k = 0; node[k]; k++)
152052419Sjulian				if (node[k] == '.')
152152419Sjulian					return -1;
152252419Sjulian		}
152352419Sjulian	} else {
152452419Sjulian		node = NULL;	/* No absolute NODE */
152552419Sjulian		path = addr;	/* Here's the PATH */
152652419Sjulian	}
152752419Sjulian
152852419Sjulian	/* Snoop for illegal characters in PATH */
152952419Sjulian	for (k = 0; path[k]; k++)
153052419Sjulian		if (path[k] == ':')
153152419Sjulian			return -1;
153252419Sjulian
153352419Sjulian	/* Check for no repeated dots in PATH */
153452419Sjulian	for (k = 0; path[k]; k++)
153552419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
153652419Sjulian			return -1;
153752419Sjulian
153852419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
153952419Sjulian	if (path[0] == '.')
154052419Sjulian		path++;
154152419Sjulian	if (*path && path[strlen(path) - 1] == '.')
154252419Sjulian		path[strlen(path) - 1] = 0;
154352419Sjulian
154452419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
154552419Sjulian	if (*path) {
154652419Sjulian		for (hook = path, k = 0; path[k]; k++)
154752419Sjulian			if (path[k] == '.') {
154852419Sjulian				hook = NULL;
154952419Sjulian				break;
155052419Sjulian			}
155152419Sjulian	} else
155252419Sjulian		path = hook = NULL;
155352419Sjulian
155452419Sjulian	/* Done */
155552419Sjulian	if (nodep)
155652419Sjulian		*nodep = node;
155752419Sjulian	if (pathp)
155852419Sjulian		*pathp = path;
155952419Sjulian	if (hookp)
156052419Sjulian		*hookp = hook;
156152419Sjulian	return (0);
156252419Sjulian}
156352419Sjulian
156452419Sjulian/*
156552419Sjulian * Given a path, which may be absolute or relative, and a starting node,
156670700Sjulian * return the destination node.
156752419Sjulian */
156852419Sjulianint
156970700Sjulianng_path2noderef(node_p here, const char *address,
157070700Sjulian				node_p *destp, hook_p *lasthook)
157152419Sjulian{
157252419Sjulian	char    fullpath[NG_PATHLEN + 1];
157352419Sjulian	char   *nodename, *path, pbuf[2];
157470700Sjulian	node_p  node, oldnode;
157552419Sjulian	char   *cp;
157659728Sjulian	hook_p hook = NULL;
157752419Sjulian
157852419Sjulian	/* Initialize */
157970784Sjulian	if (destp == NULL) {
158071047Sjulian		TRAP_ERROR();
158152419Sjulian		return EINVAL;
158270784Sjulian	}
158352419Sjulian	*destp = NULL;
158452419Sjulian
158552419Sjulian	/* Make a writable copy of address for ng_path_parse() */
158652419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
158752419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
158852419Sjulian
158952419Sjulian	/* Parse out node and sequence of hooks */
159052419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
159171047Sjulian		TRAP_ERROR();
159252419Sjulian		return EINVAL;
159352419Sjulian	}
159452419Sjulian	if (path == NULL) {
159552419Sjulian		pbuf[0] = '.';	/* Needs to be writable */
159652419Sjulian		pbuf[1] = '\0';
159752419Sjulian		path = pbuf;
159852419Sjulian	}
159952419Sjulian
160070700Sjulian	/*
160170700Sjulian	 * For an absolute address, jump to the starting node.
160270700Sjulian	 * Note that this holds a reference on the node for us.
160370700Sjulian	 * Don't forget to drop the reference if we don't need it.
160470700Sjulian	 */
160552419Sjulian	if (nodename) {
160670700Sjulian		node = ng_name2noderef(here, nodename);
160752419Sjulian		if (node == NULL) {
160871047Sjulian			TRAP_ERROR();
160952419Sjulian			return (ENOENT);
161052419Sjulian		}
161170700Sjulian	} else {
161270700Sjulian		if (here == NULL) {
161371047Sjulian			TRAP_ERROR();
161470700Sjulian			return (EINVAL);
161570700Sjulian		}
161652419Sjulian		node = here;
161770784Sjulian		NG_NODE_REF(node);
161870700Sjulian	}
161952419Sjulian
162070700Sjulian	/*
162170700Sjulian	 * Now follow the sequence of hooks
162270700Sjulian	 * XXX
162370700Sjulian	 * We actually cannot guarantee that the sequence
162470700Sjulian	 * is not being demolished as we crawl along it
162570700Sjulian	 * without extra-ordinary locking etc.
162670700Sjulian	 * So this is a bit dodgy to say the least.
162770700Sjulian	 * We can probably hold up some things by holding
162870700Sjulian	 * the nodelist mutex for the time of this
162970700Sjulian	 * crawl if we wanted.. At least that way we wouldn't have to
163070700Sjulian	 * worry about the nodes dissappearing, but the hooks would still
163170700Sjulian	 * be a problem.
163270700Sjulian	 */
163352419Sjulian	for (cp = path; node != NULL && *cp != '\0'; ) {
163452419Sjulian		char *segment;
163552419Sjulian
163652419Sjulian		/*
163752419Sjulian		 * Break out the next path segment. Replace the dot we just
163852419Sjulian		 * found with a NUL; "cp" points to the next segment (or the
163952419Sjulian		 * NUL at the end).
164052419Sjulian		 */
164152419Sjulian		for (segment = cp; *cp != '\0'; cp++) {
164252419Sjulian			if (*cp == '.') {
164352419Sjulian				*cp++ = '\0';
164452419Sjulian				break;
164552419Sjulian			}
164652419Sjulian		}
164752419Sjulian
164852419Sjulian		/* Empty segment */
164952419Sjulian		if (*segment == '\0')
165052419Sjulian			continue;
165152419Sjulian
165252419Sjulian		/* We have a segment, so look for a hook by that name */
165354096Sarchie		hook = ng_findhook(node, segment);
165452419Sjulian
165552419Sjulian		/* Can't get there from here... */
165652419Sjulian		if (hook == NULL
165770784Sjulian		    || NG_HOOK_PEER(hook) == NULL
165870784Sjulian		    || NG_HOOK_NOT_VALID(hook)
165970784Sjulian		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
166071047Sjulian			TRAP_ERROR();
166170784Sjulian			NG_NODE_UNREF(node);
166270784Sjulian#if 0
166370784Sjulian			printf("hooknotvalid %s %s %d %d %d %d ",
166470784Sjulian					path,
166570784Sjulian					segment,
166670784Sjulian					hook == NULL,
166770784Sjulian		     			NG_HOOK_PEER(hook) == NULL,
166870784Sjulian		     			NG_HOOK_NOT_VALID(hook),
166970784Sjulian		     			NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
167070784Sjulian#endif
167152419Sjulian			return (ENOENT);
167252419Sjulian		}
167352419Sjulian
167470700Sjulian		/*
167570700Sjulian		 * Hop on over to the next node
167670700Sjulian		 * XXX
167770700Sjulian		 * Big race conditions here as hooks and nodes go away
167870700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
167970700Sjulian		 * instead of the direct hook in this crawl?
168070700Sjulian		 */
168170700Sjulian		oldnode = node;
168270784Sjulian		if ((node = NG_PEER_NODE(hook)))
168370784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
168470784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
168570784Sjulian		if (NG_NODE_NOT_VALID(node)) {
168670784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
168770700Sjulian			node = NULL;
168870700Sjulian		}
168952419Sjulian	}
169052419Sjulian
169152419Sjulian	/* If node somehow missing, fail here (probably this is not needed) */
169252419Sjulian	if (node == NULL) {
169371047Sjulian		TRAP_ERROR();
169452419Sjulian		return (ENXIO);
169552419Sjulian	}
169652419Sjulian
169752419Sjulian	/* Done */
169852419Sjulian	*destp = node;
169959900Sarchie	if (lasthook != NULL)
170070784Sjulian		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
170152419Sjulian	return (0);
170252419Sjulian}
170352419Sjulian
170470700Sjulian/***************************************************************\
170570700Sjulian* Input queue handling.
170670700Sjulian* All activities are submitted to the node via the input queue
170770700Sjulian* which implements a multiple-reader/single-writer gate.
170870700Sjulian* Items which cannot be handled immeditly are queued.
170970700Sjulian*
171070700Sjulian* read-write queue locking inline functions			*
171170700Sjulian\***************************************************************/
171270700Sjulian
171370700Sjulianstatic __inline item_p ng_dequeue(struct ng_queue * ngq);
171470700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq,
171570700Sjulian					item_p  item);
171670700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq,
171770700Sjulian					item_p  item);
171870700Sjulianstatic __inline void	ng_leave_read(struct ng_queue * ngq);
171970700Sjulianstatic __inline void	ng_leave_write(struct ng_queue * ngq);
172070700Sjulianstatic __inline void	ng_queue_rw(struct ng_queue * ngq,
172170700Sjulian					item_p  item, int rw);
172270700Sjulian
172352419Sjulian/*
172470700Sjulian * Definition of the bits fields in the ng_queue flag word.
172570700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
172670700Sjulian * with them.
172770700Sjulian *
172871902Sjulian * The ordering here may be important! don't shuffle these.
172952419Sjulian */
173070700Sjulian/*-
173170700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
173270700Sjulian                       |
173370700Sjulian                       V
173470700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
173570700Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
173671902Sjulian| |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W|
173771902Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P|
173870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
173971902Sjulian\___________________________ ____________________________/ | | |
174071902Sjulian                            V                              | | |
174171902Sjulian                  [active reader count]                    | | |
174270700Sjulian                                                           | | |
174371902Sjulian          Read Pending ------------------------------------+ | |
174470700Sjulian                                                             | |
174571902Sjulian          Active Writer -------------------------------------+ |
174670700Sjulian                                                               |
174771902Sjulian          Write Pending ---------------------------------------+
174871902Sjulian
174971902Sjulian
175070700Sjulian*/
175171902Sjulian#define WRITE_PENDING	0x00000001
175271902Sjulian#define WRITER_ACTIVE	0x00000002
175371902Sjulian#define READ_PENDING	0x00000004
175471902Sjulian#define READER_INCREMENT 0x00000008
175571902Sjulian#define READER_MASK	0xfffffff0	/* Not valid if WRITER_ACTIVE is set */
175670700Sjulian#define SAFETY_BARRIER	0x00100000	/* 64K items queued should be enough */
175771902Sjulian
175871902Sjulian/* Defines of more elaborate states on the queue */
175971902Sjulian/* Mask of bits a read cares about */
176071902Sjulian#define NGQ_RMASK	(WRITE_PENDING|WRITER_ACTIVE|READ_PENDING)
176171902Sjulian
176271902Sjulian/* Mask of bits a write cares about */
176371902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
176471902Sjulian
176571902Sjulian/* tests to decide if we could get a read or write off the queue */
176671902Sjulian#define CAN_GET_READ(flag)	((flag & NGQ_RMASK) == READ_PENDING)
176771902Sjulian#define CAN_GET_WRITE(flag)	((flag & NGQ_WMASK) == WRITE_PENDING)
176871902Sjulian
176971902Sjulian/* Is there a chance of getting ANY work off the queue? */
177071902Sjulian#define CAN_GET_WORK(flag)	(CAN_GET_READ(flag) || CAN_GET_WRITE(flag))
177171902Sjulian
177270700Sjulian/*
177370700Sjulian * Taking into account the current state of the queue and node, possibly take
177470700Sjulian * the next entry off the queue and return it. Return NULL if there was
177570700Sjulian * nothing we could return, either because there really was nothing there, or
177670700Sjulian * because the node was in a state where it cannot yet process the next item
177770700Sjulian * on the queue.
177870700Sjulian *
177970700Sjulian * This MUST MUST MUST be called with the mutex held.
178070700Sjulian */
178170700Sjulianstatic __inline item_p
178270700Sjulianng_dequeue(struct ng_queue *ngq)
178370700Sjulian{
178470700Sjulian	item_p item;
178570700Sjulian	u_int		add_arg;
178671902Sjulian
178771902Sjulian	if (CAN_GET_READ(ngq->q_flags)) {
178870700Sjulian		/*
178971902Sjulian		 * Head of queue is a reader and we have no write active.
179071902Sjulian		 * We don't care how many readers are already active.
179171902Sjulian		 * Adjust the flags for the item we are about to dequeue.
179271902Sjulian		 * Add the correct increment for the reader count as well.
179370700Sjulian		 */
179471902Sjulian		add_arg = (READER_INCREMENT - READ_PENDING);
179571902Sjulian	} else if (CAN_GET_WRITE(ngq->q_flags)) {
179670700Sjulian		/*
179770700Sjulian		 * There is a pending write, no readers and no active writer.
179870700Sjulian		 * This means we can go ahead with the pending writer. Note
179970700Sjulian		 * the fact that we now have a writer, ready for when we take
180070700Sjulian		 * it off the queue.
180170700Sjulian		 *
180270700Sjulian		 * We don't need to worry about a possible collision with the
180370700Sjulian		 * fasttrack reader.
180470700Sjulian		 *
180570700Sjulian		 * The fasttrack thread may take a long time to discover that we
180670700Sjulian		 * are running so we would have an inconsistent state in the
180770700Sjulian		 * flags for a while. Since we ignore the reader count
180870700Sjulian		 * entirely when the WRITER_ACTIVE flag is set, this should
180970700Sjulian		 * not matter (in fact it is defined that way). If it tests
181070700Sjulian		 * the flag before this operation, the WRITE_PENDING flag
181170700Sjulian		 * will make it fail, and if it tests it later, the
181271902Sjulian		 * WRITER_ACTIVE flag will do the same. If it is SO slow that
181370700Sjulian		 * we have actually completed the operation, and neither flag
181470700Sjulian		 * is set (nor the READ_PENDING) by the time that it tests
181570700Sjulian		 * the flags, then it is actually ok for it to continue. If
181670700Sjulian		 * it completes and we've finished and the read pending is
181770700Sjulian		 * set it still fails.
181870700Sjulian		 *
181970700Sjulian		 * So we can just ignore it,  as long as we can ensure that the
182070700Sjulian		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
182170700Sjulian		 * state is atomic.
182270700Sjulian		 *
182370700Sjulian		 * After failing, first it will be held back by the mutex, then
182470700Sjulian		 * when it can proceed, it will queue its request, then it
182570700Sjulian		 * would arrive at this function. Usually it will have to
182671902Sjulian		 * leave empty handed because the ACTIVE WRITER bit will be
182770700Sjulian		 * set.
182871902Sjulian		 *
182971902Sjulian		 * Adjust the flags for the item we are about to dequeue
183071902Sjulian		 * and for the new active writer.
183170700Sjulian		 */
183271902Sjulian		add_arg = (WRITER_ACTIVE - WRITE_PENDING);
183370700Sjulian		/*
183470700Sjulian		 * We want to write "active writer, no readers " Now go make
183570700Sjulian		 * it true. In fact there may be a number in the readers
183670700Sjulian		 * count but we know it is not true and will be fixed soon.
183770700Sjulian		 * We will fix the flags for the next pending entry in a
183870700Sjulian		 * moment.
183970700Sjulian		 */
184070700Sjulian	} else {
184170700Sjulian		/*
184270700Sjulian		 * We can't dequeue anything.. return and say so. Probably we
184370700Sjulian		 * have a write pending and the readers count is non zero. If
184470700Sjulian		 * we got here because a reader hit us just at the wrong
184570700Sjulian		 * moment with the fasttrack code, and put us in a strange
184670700Sjulian		 * state, then it will be through in just a moment, (as soon
184770700Sjulian		 * as we release the mutex) and keep things moving.
184871902Sjulian		 * Make sure we remove ourselves from the work queue.
184970700Sjulian		 */
185071902Sjulian		ng_worklist_remove(ngq->q_node);
185170700Sjulian		return (0);
185270700Sjulian	}
185352419Sjulian
185470700Sjulian	/*
185570700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
185670700Sjulian	 * pending flags and the next and last pointers.
185770700Sjulian	 */
185870700Sjulian	item = ngq->queue;
185970700Sjulian	ngq->queue = item->el_next;
186070700Sjulian	if (ngq->last == &(item->el_next)) {
186170700Sjulian		/*
186270700Sjulian		 * that was the last entry in the queue so set the 'last
186370700Sjulian		 * pointer up correctly and make sure the pending flags are
186470700Sjulian		 * clear.
186570700Sjulian		 */
186670700Sjulian		ngq->last = &(ngq->queue);
186770700Sjulian		/*
186871902Sjulian		 * Whatever flag was set will be cleared and
186971902Sjulian		 * the new acive field will be set by the add as well,
187071902Sjulian		 * so we don't need to change add_arg.
187171902Sjulian		 * But we know we don't need to be on the work list.
187270700Sjulian		 */
187371902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
187471902Sjulian		ng_worklist_remove(ngq->q_node);
187570700Sjulian	} else {
187671902Sjulian		/*
187771902Sjulian		 * Since there is something on the queue, note what it is
187871902Sjulian		 * in the flags word.
187971902Sjulian		 */
188071047Sjulian		if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
188170700Sjulian			add_arg += READ_PENDING;
188270700Sjulian		} else {
188370700Sjulian			add_arg += WRITE_PENDING;
188470700Sjulian		}
188571902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
188671902Sjulian		/*
188771902Sjulian		 * If we see more doable work, make sure we are
188871902Sjulian		 * on the work queue.
188971902Sjulian		 */
189071902Sjulian		if (CAN_GET_WORK(ngq->q_flags)) {
189171902Sjulian			ng_setisr(ngq->q_node);
189271902Sjulian		}
189370700Sjulian	}
189470700Sjulian	/*
189570700Sjulian	 * We have successfully cleared the old pending flag, set the new one
189670700Sjulian	 * if it is needed, and incremented the appropriate active field.
189771902Sjulian	 * (all in one atomic addition.. )
189870700Sjulian	 */
189970700Sjulian	return (item);
190070700Sjulian}
190152419Sjulian
190270700Sjulian/*
190370700Sjulian * Queue a packet to be picked up by someone else.
190470700Sjulian * We really don't care who, but we can't or don't want to hang around
190570700Sjulian * to process it ourselves. We are probably an interrupt routine..
190670700Sjulian * 1 = writer, 0 = reader
190770700Sjulian */
190870700Sjulian#define NGQRW_R 0
190970700Sjulian#define NGQRW_W 1
191070700Sjulianstatic __inline void
191170700Sjulianng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
191270700Sjulian{
191370700Sjulian	item->el_next = NULL;	/* maybe not needed */
191470700Sjulian	*ngq->last = item;
191570700Sjulian	/*
191670700Sjulian	 * If it was the first item in the queue then we need to
191770700Sjulian	 * set the last pointer and the type flags.
191870700Sjulian	 */
191970700Sjulian	if (ngq->last == &(ngq->queue)) {
192070700Sjulian		/*
192170700Sjulian		 * When called with constants for rw, the optimiser will
192270700Sjulian		 * remove the unneeded branch below.
192370700Sjulian		 */
192470700Sjulian		if (rw == NGQRW_W) {
192570700Sjulian			atomic_add_long(&ngq->q_flags, WRITE_PENDING);
192670700Sjulian		} else {
192770700Sjulian			atomic_add_long(&ngq->q_flags, READ_PENDING);
192870700Sjulian		}
192970700Sjulian	}
193070700Sjulian	ngq->last = &(item->el_next);
193170700Sjulian}
193252419Sjulian
193370700Sjulian
193452419Sjulian/*
193570700Sjulian * This function 'cheats' in that it first tries to 'grab' the use of the
193670700Sjulian * node, without going through the mutex. We can do this becasue of the
193770700Sjulian * semantics of the lock. The semantics include a clause that says that the
193870700Sjulian * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It
193970700Sjulian * also says that the WRITER_ACTIVE flag cannot be set if the readers count
194070700Sjulian * is not zero. Note that this talks about what is valid to SET the
194170700Sjulian * WRITER_ACTIVE flag, because from the moment it is set, the value if the
194270700Sjulian * reader count is immaterial, and not valid. The two 'pending' flags have a
194370700Sjulian * similar effect, in that If they are orthogonal to the two active fields in
194470700Sjulian * how they are set, but if either is set, the attempted 'grab' need to be
194570700Sjulian * backed out because there is earlier work, and we maintain ordering in the
194670700Sjulian * queue. The result of this is that the reader request can try obtain use of
194770700Sjulian * the node with only a single atomic addition, and without any of the mutex
194870700Sjulian * overhead. If this fails the operation degenerates to the same as for other
194970700Sjulian * cases.
195070700Sjulian *
195152419Sjulian */
195270700Sjulianstatic __inline item_p
195370700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item)
195452419Sjulian{
195552419Sjulian
195670700Sjulian	/* ######### Hack alert ######### */
195770700Sjulian	atomic_add_long(&ngq->q_flags, READER_INCREMENT);
195871902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
195970700Sjulian		/* Successfully grabbed node */
196070700Sjulian		return (item);
196170700Sjulian	}
196270700Sjulian	/* undo the damage if we didn't succeed */
196370700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
196470700Sjulian
196570700Sjulian	/* ######### End Hack alert ######### */
196672200Sbmilekic	mtx_lock_spin((&ngq->q_mtx));
196769922Sjulian	/*
196870700Sjulian	 * Try again. Another processor (or interrupt for that matter) may
196970700Sjulian	 * have removed the last queued item that was stopping us from
197070700Sjulian	 * running, between the previous test, and the moment that we took
197170700Sjulian	 * the mutex. (Or maybe a writer completed.)
197269922Sjulian	 */
197371902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
197470700Sjulian		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
197572200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
197670700Sjulian		return (item);
197770700Sjulian	}
197870700Sjulian
197970700Sjulian	/*
198070700Sjulian	 * and queue the request for later.
198170700Sjulian	 */
198271047Sjulian	item->el_flags |= NGQF_READER;
198370700Sjulian	ng_queue_rw(ngq, item, NGQRW_R);
198470700Sjulian
198570700Sjulian	/*
198670700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
198770700Sjulian	 * see if we can dequeue something to run instead.
198870700Sjulian	 */
198970700Sjulian	item = ng_dequeue(ngq);
199072200Sbmilekic	mtx_unlock_spin(&(ngq->q_mtx));
199170700Sjulian	return (item);
199270700Sjulian}
199370700Sjulian
199470700Sjulianstatic __inline item_p
199570700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item)
199670700Sjulian{
199770700Sjulianrestart:
199872200Sbmilekic	mtx_lock_spin(&(ngq->q_mtx));
199970700Sjulian	/*
200070700Sjulian	 * If there are no readers, no writer, and no pending packets, then
200170700Sjulian	 * we can just go ahead. In all other situations we need to queue the
200270700Sjulian	 * request
200370700Sjulian	 */
200471902Sjulian	if ((ngq->q_flags & NGQ_WMASK) == 0) {
200570700Sjulian		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
200672200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
200770700Sjulian		if (ngq->q_flags & READER_MASK) {
200870700Sjulian			/* Collision with fast-track reader */
200971902Sjulian			atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
201070700Sjulian			goto restart;
201169922Sjulian		}
201270700Sjulian		return (item);
201352419Sjulian	}
201452419Sjulian
201570700Sjulian	/*
201670700Sjulian	 * and queue the request for later.
201770700Sjulian	 */
201871047Sjulian	item->el_flags &= ~NGQF_RW;
201970700Sjulian	ng_queue_rw(ngq, item, NGQRW_W);
202070700Sjulian
202170700Sjulian	/*
202270700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
202370700Sjulian	 * see if we can dequeue something to run instead.
202470700Sjulian	 */
202570700Sjulian	item = ng_dequeue(ngq);
202672200Sbmilekic	mtx_unlock_spin(&(ngq->q_mtx));
202770700Sjulian	return (item);
202870700Sjulian}
202970700Sjulian
203070700Sjulianstatic __inline void
203170700Sjulianng_leave_read(struct ng_queue *ngq)
203270700Sjulian{
203370700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
203470700Sjulian}
203570700Sjulian
203670700Sjulianstatic __inline void
203770700Sjulianng_leave_write(struct ng_queue *ngq)
203870700Sjulian{
203970700Sjulian	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
204070700Sjulian}
204170700Sjulian
204270700Sjulianstatic void
204370700Sjulianng_flush_input_queue(struct ng_queue * ngq)
204470700Sjulian{
204570700Sjulian	item_p item;
204670700Sjulian	u_int		add_arg;
204772200Sbmilekic	mtx_lock_spin(&ngq->q_mtx);
204870700Sjulian	for (;;) {
204970700Sjulian		/* Now take a look at what's on the queue */
205070700Sjulian		if (ngq->q_flags & READ_PENDING) {
205170700Sjulian			add_arg = -READ_PENDING;
205270700Sjulian		} else if (ngq->q_flags & WRITE_PENDING) {
205370700Sjulian			add_arg = -WRITE_PENDING;
205470700Sjulian		} else {
205570700Sjulian			break;
205670700Sjulian		}
205770700Sjulian
205870700Sjulian		item = ngq->queue;
205970700Sjulian		ngq->queue = item->el_next;
206070700Sjulian		if (ngq->last == &(item->el_next)) {
206170700Sjulian			ngq->last = &(ngq->queue);
206270700Sjulian		} else {
206371047Sjulian			if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
206470700Sjulian				add_arg += READ_PENDING;
206570700Sjulian			} else {
206670700Sjulian				add_arg += WRITE_PENDING;
206770700Sjulian			}
206870700Sjulian		}
206970700Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
207070700Sjulian
207172200Sbmilekic		mtx_lock_spin(&ngq->q_mtx);
207270700Sjulian		NG_FREE_ITEM(item);
207372200Sbmilekic		mtx_unlock_spin(&ngq->q_mtx);
207470700Sjulian	}
207571902Sjulian	/*
207671902Sjulian	 * Take us off the work queue if we are there.
207771902Sjulian	 * We definatly have no work to be done.
207871902Sjulian	 */
207971902Sjulian	ng_worklist_remove(ngq->q_node);
208072200Sbmilekic	mtx_unlock_spin(&ngq->q_mtx);
208170700Sjulian}
208270700Sjulian
208370700Sjulian/***********************************************************************
208470700Sjulian* Externally visible method for sending or queueing messages or data.
208570700Sjulian***********************************************************************/
208670700Sjulian
208770700Sjulian/*
208871849Sjulian * The module code should have filled out the item correctly by this stage:
208970700Sjulian * Common:
209070700Sjulian *    reference to destination node.
209170700Sjulian *    Reference to destination rcv hook if relevant.
209270700Sjulian * Data:
209370700Sjulian *    pointer to mbuf
209470700Sjulian *    pointer to metadata
209570700Sjulian * Control_Message:
209670700Sjulian *    pointer to msg.
209770700Sjulian *    ID of original sender node. (return address)
209871849Sjulian * Function:
209971849Sjulian *    Function pointer
210071849Sjulian *    void * argument
210171849Sjulian *    integer argument
210270700Sjulian *
210370700Sjulian * The nodes have several routines and macros to help with this task:
210470700Sjulian */
210570700Sjulian
210670700Sjulianint
210770700Sjulianng_snd_item(item_p item, int queue)
210870700Sjulian{
210971849Sjulian	hook_p hook = NGI_HOOK(item);
211071902Sjulian	node_p node = NGI_NODE(item);
211170700Sjulian	int rw;
211270700Sjulian	int error = 0, ierror;
211370700Sjulian	item_p	oitem;
211471902Sjulian	struct ng_queue * ngq = &node->nd_input_queue;
211570700Sjulian
211670784Sjulian#ifdef	NETGRAPH_DEBUG
211770700Sjulian        _ngi_check(item, __FILE__, __LINE__);
211870700Sjulian#endif
211970700Sjulian
212070700Sjulian	if (item == NULL) {
212171047Sjulian		TRAP_ERROR();
212270700Sjulian		return (EINVAL);	/* failed to get queue element */
212370700Sjulian	}
212471902Sjulian	if (node == NULL) {
212570700Sjulian		NG_FREE_ITEM(item);
212671047Sjulian		TRAP_ERROR();
212770700Sjulian		return (EINVAL);	/* No address */
212870700Sjulian	}
212971047Sjulian	switch(item->el_flags & NGQF_TYPE) {
213071047Sjulian	case NGQF_DATA:
213169922Sjulian		/*
213270700Sjulian		 * DATA MESSAGE
213370700Sjulian		 * Delivered to a node via a non-optional hook.
213470700Sjulian		 * Both should be present in the item even though
213570700Sjulian		 * the node is derivable from the hook.
213670700Sjulian		 * References are held on both by the item.
213769922Sjulian		 */
213870700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
213970700Sjulian		if (hook == NULL) {
214070700Sjulian			NG_FREE_ITEM(item);
214171047Sjulian			TRAP_ERROR();
214270700Sjulian			return(EINVAL);
214370700Sjulian		}
214470784Sjulian		if ((NG_HOOK_NOT_VALID(hook))
214570784Sjulian		|| (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) {
214670700Sjulian			NG_FREE_ITEM(item);
214770700Sjulian			return (ENOTCONN);
214869922Sjulian		}
214970784Sjulian		if ((hook->hk_flags & HK_QUEUE)) {
215070700Sjulian			queue = 1;
215170700Sjulian		}
215270700Sjulian		/* By default data is a reader in the locking scheme */
215370700Sjulian		item->el_flags |= NGQF_READER;
215470700Sjulian		rw = NGQRW_R;
215571047Sjulian		break;
215671047Sjulian	case NGQF_MESG:
215770700Sjulian		/*
215870700Sjulian		 * CONTROL MESSAGE
215970700Sjulian		 * Delivered to a node.
216070700Sjulian		 * Hook is optional.
216170700Sjulian		 * References are held by the item on the node and
216270700Sjulian		 * the hook if it is present.
216370700Sjulian		 */
216470784Sjulian		if (hook && (hook->hk_flags & HK_QUEUE)) {
216570700Sjulian			queue = 1;
216670700Sjulian		}
216770700Sjulian		/* Data messages count as writers unles explicitly exempted */
216870700Sjulian		if (NGI_MSG(item)->header.cmd & NGM_READONLY) {
216970700Sjulian			item->el_flags |= NGQF_READER;
217070700Sjulian			rw = NGQRW_R;
217170700Sjulian		} else {
217271047Sjulian			item->el_flags &= ~NGQF_RW;
217370700Sjulian			rw = NGQRW_W;
217470700Sjulian		}
217571047Sjulian		break;
217671047Sjulian	case NGQF_FN:
217771047Sjulian		item->el_flags &= ~NGQF_RW;
217871047Sjulian		rw = NGQRW_W;
217971047Sjulian		break;
218071047Sjulian	default:
218171047Sjulian		NG_FREE_ITEM(item);
218271047Sjulian		TRAP_ERROR();
218371047Sjulian		return (EINVAL);
218469922Sjulian	}
218570700Sjulian	/*
218670700Sjulian	 * If the node specifies single threading, force writer semantics
218770700Sjulian	 * Similarly the node may say one hook always produces writers.
218870700Sjulian	 * These are over-rides.
218970700Sjulian	 */
219071902Sjulian	if ((node->nd_flags & NG_FORCE_WRITER)
219170784Sjulian	|| (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
219270700Sjulian			rw = NGQRW_W;
219371047Sjulian			item->el_flags &= ~NGQF_READER;
219470700Sjulian	}
219570700Sjulian	if (queue) {
219670700Sjulian		/* Put it on the queue for that node*/
219770784Sjulian#ifdef	NETGRAPH_DEBUG
219870700Sjulian        _ngi_check(item, __FILE__, __LINE__);
219970700Sjulian#endif
220072200Sbmilekic		mtx_lock_spin(&(ngq->q_mtx));
220170700Sjulian		ng_queue_rw(ngq, item, rw);
220270700Sjulian		/*
220370700Sjulian		 * If there are active elements then we can rely on
220470700Sjulian		 * them. if not we should not rely on another packet
220570700Sjulian		 * coming here by another path,
220670700Sjulian		 * so it is best to put us in the netisr list.
220771902Sjulian		 * We can take the worklist lock with the node locked
220871902Sjulian		 * BUT NOT THE REVERSE!
220970700Sjulian		 */
221071902Sjulian		if (CAN_GET_WORK(ngq->q_flags)) {
221171902Sjulian			ng_setisr(node);
221270700Sjulian		}
221372200Sbmilekic		mtx_unlock_spin(&(ngq->q_mtx));
221470700Sjulian		return (0);
221570700Sjulian	}
221670700Sjulian	/*
221770700Sjulian	 * Take a queue item and a node and see if we can apply the item to
221870700Sjulian	 * the node. We may end up getting a different item to apply instead.
221970700Sjulian	 * Will allow for a piggyback reply only in the case where
222070700Sjulian	 * there is no queueing.
222170700Sjulian	 */
222269922Sjulian
222370700Sjulian	oitem = item;
222470700Sjulian	/*
222570700Sjulian	 * We already decided how we will be queueud or treated.
222670700Sjulian	 * Try get the appropriate operating permission.
222770700Sjulian	 */
222870700Sjulian 	if (rw == NGQRW_R) {
222970700Sjulian		item = ng_acquire_read(ngq, item);
223070700Sjulian	} else {
223170700Sjulian		item = ng_acquire_write(ngq, item);
223270700Sjulian	}
223352419Sjulian
223470700Sjulian	/*
223570700Sjulian	 * May have come back with a different item.
223670700Sjulian	 * or maybe none at all. The one we started with will
223770700Sjulian	 * have been queued in thises cases.
223870700Sjulian	 */
223970700Sjulian	if (item == NULL) {
224070700Sjulian		return (0);
224170700Sjulian	}
224252419Sjulian
224370784Sjulian#ifdef	NETGRAPH_DEBUG
224470700Sjulian        _ngi_check(item, __FILE__, __LINE__);
224570700Sjulian#endif
224674078Sjulian	/*
224774078Sjulian	 * Take over the reference frm the item.
224874078Sjulian	 * Hold it until the called function returns.
224974078Sjulian	 */
225074078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
225152419Sjulian
225274078Sjulian	ierror = ng_apply_item(node, item); /* drops r/w lock when done */
225374078Sjulian
225470700Sjulian	/* only return an error if it was our initial item.. (compat hack) */
225570700Sjulian	if (oitem == item) {
225670700Sjulian		error = ierror;
225770700Sjulian	}
225870700Sjulian
225952419Sjulian	/*
226074078Sjulian	 * If the node goes away when we remove the reference,
226182058Sbrian	 * whatever we just did caused it.. whatever we do, DO NOT
226274078Sjulian	 * access the node again!
226374078Sjulian	 */
226474078Sjulian	if (NG_NODE_UNREF(node) == 0) {
226574078Sjulian		return (error);
226674078Sjulian	}
226774078Sjulian
226874078Sjulian	/*
226970700Sjulian	 * Now we've handled the packet we brought, (or a friend of it) let's
227070700Sjulian	 * look for any other packets that may have been queued up. We hold
227170700Sjulian	 * no locks, so if someone puts something in the queue after
227270700Sjulian	 * we check that it is empty, it is their problem
227370700Sjulian	 * to ensure it is processed. If we have the netisr thread cme in here
227470700Sjulian	 * while we still say we have stuff to do, we may get a boost
227570700Sjulian	 * in SMP systems. :-)
227652419Sjulian	 */
227770700Sjulian	for (;;) {
227870700Sjulian		/*
227970700Sjulian		 * dequeue acquires and adjusts the input_queue as it dequeues
228070700Sjulian		 * packets. It acquires the rw lock as needed.
228170700Sjulian		 */
228272200Sbmilekic		mtx_lock_spin(&ngq->q_mtx);
228371902Sjulian		item = ng_dequeue(ngq); /* fixes worklist too*/
228470700Sjulian		if (!item) {
228572200Sbmilekic			mtx_unlock_spin(&ngq->q_mtx);
228671849Sjulian			return (error);
228770700Sjulian		}
228872200Sbmilekic		mtx_unlock_spin(&ngq->q_mtx);
228970700Sjulian
229070700Sjulian		/*
229174078Sjulian		 * Take over the reference frm the item.
229274078Sjulian		 * Hold it until the called function returns.
229374078Sjulian		 */
229474078Sjulian
229574078Sjulian		NGI_GET_NODE(item, node); /* zaps stored node */
229674078Sjulian
229774078Sjulian		/*
229870700Sjulian		 * We have the appropriate lock, so run the item.
229970700Sjulian		 * When finished it will drop the lock accordingly
230070700Sjulian		 */
230174078Sjulian		ierror = ng_apply_item(node, item);
230270700Sjulian
230370700Sjulian		/*
230470700Sjulian		 * only return an error if it was our initial
230570700Sjulian		 * item.. (compat hack)
230670700Sjulian		 */
230770700Sjulian		if (oitem == item) {
230870700Sjulian			error = ierror;
230970700Sjulian		}
231074078Sjulian
231174078Sjulian		/*
231274078Sjulian		 * If the node goes away when we remove the reference,
231382058Sbrian		 * whatever we just did caused it.. whatever we do, DO NOT
231474078Sjulian		 * access the node again!
231574078Sjulian		 */
231674078Sjulian		if (NG_NODE_UNREF(node) == 0) {
231774078Sjulian			break;
231874078Sjulian		}
231970700Sjulian	}
232071849Sjulian	return (error);
232152419Sjulian}
232252419Sjulian
232352419Sjulian/*
232470700Sjulian * We have an item that was possibly queued somewhere.
232570700Sjulian * It should contain all the information needed
232670700Sjulian * to run it on the appropriate node/hook.
232752419Sjulian */
232852419Sjulianstatic int
232974078Sjulianng_apply_item(node_p node, item_p item)
233052419Sjulian{
233170700Sjulian	hook_p  hook;
233271849Sjulian	int	was_reader = ((item->el_flags & NGQF_RW));
233371849Sjulian	int	error = 0;
233470700Sjulian	ng_rcvdata_t *rcvdata;
233571885Sjulian	ng_rcvmsg_t *rcvmsg;
233652419Sjulian
233771849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
233870784Sjulian#ifdef	NETGRAPH_DEBUG
233970700Sjulian        _ngi_check(item, __FILE__, __LINE__);
234070700Sjulian#endif
234171047Sjulian	switch (item->el_flags & NGQF_TYPE) {
234270700Sjulian	case NGQF_DATA:
234370700Sjulian		/*
234470700Sjulian		 * Check things are still ok as when we were queued.
234570700Sjulian		 */
234670700Sjulian		if ((hook == NULL)
234770784Sjulian		|| NG_HOOK_NOT_VALID(hook)
234871885Sjulian		|| NG_NODE_NOT_VALID(node) ) {
234970700Sjulian			error = EIO;
235070700Sjulian			NG_FREE_ITEM(item);
235171885Sjulian			break;
235270700Sjulian		}
235371885Sjulian		/*
235471885Sjulian		 * If no receive method, just silently drop it.
235571885Sjulian		 * Give preference to the hook over-ride method
235671885Sjulian		 */
235771885Sjulian		if ((!(rcvdata = hook->hk_rcvdata))
235871885Sjulian		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
235971885Sjulian			error = 0;
236071885Sjulian			NG_FREE_ITEM(item);
236171885Sjulian			break;
236271885Sjulian		}
236371885Sjulian		error = (*rcvdata)(hook, item);
236470700Sjulian		break;
236570700Sjulian	case NGQF_MESG:
236670700Sjulian		if (hook) {
236770784Sjulian			if (NG_HOOK_NOT_VALID(hook)) {
236871849Sjulian				/*
236971849Sjulian				 * The hook has been zapped then we can't
237071849Sjulian				 * use it. Immediatly drop its reference.
237171849Sjulian				 * The message may not need it.
237271849Sjulian				 */
237370784Sjulian				NG_HOOK_UNREF(hook);
237470700Sjulian				hook = NULL;
237570700Sjulian			}
237670700Sjulian		}
237770700Sjulian		/*
237870700Sjulian		 * Similarly, if the node is a zombie there is
237970700Sjulian		 * nothing we can do with it, drop everything.
238070700Sjulian		 */
238170784Sjulian		if (NG_NODE_NOT_VALID(node)) {
238271047Sjulian			TRAP_ERROR();
238370700Sjulian			error = EINVAL;
238470700Sjulian			NG_FREE_ITEM(item);
238570700Sjulian		} else {
238670700Sjulian			/*
238770700Sjulian			 * Call the appropriate message handler for the object.
238870700Sjulian			 * It is up to the message handler to free the message.
238970700Sjulian			 * If it's a generic message, handle it generically,
239070700Sjulian			 * otherwise call the type's message handler
239170700Sjulian			 * (if it exists)
239270700Sjulian			 * XXX (race). Remember that a queued message may
239370700Sjulian			 * reference a node or hook that has just been
239470700Sjulian			 * invalidated. It will exist as the queue code
239570700Sjulian			 * is holding a reference, but..
239670700Sjulian			 */
239770700Sjulian
239870700Sjulian			struct ng_mesg *msg = NGI_MSG(item);
239970700Sjulian
240071885Sjulian			/*
240171885Sjulian			 * check if the generic handler owns it.
240271885Sjulian			 */
240370700Sjulian			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
240470700Sjulian			&& ((msg->header.flags & NGF_RESP) == 0)) {
240570700Sjulian				error = ng_generic_msg(node, item, hook);
240671885Sjulian				break;
240770700Sjulian			}
240871885Sjulian			/*
240971885Sjulian			 * Now see if there is a handler (hook or node specific)
241071885Sjulian			 * in the target node. If none, silently discard.
241171885Sjulian			 */
241271885Sjulian			if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg)))
241371885Sjulian			&& (!(rcvmsg = node->nd_type->rcvmsg))) {
241471885Sjulian				TRAP_ERROR();
241571885Sjulian				error = 0;
241671885Sjulian				NG_FREE_ITEM(item);
241771885Sjulian				break;
241871885Sjulian			}
241971885Sjulian			error = (*rcvmsg)(node, item, hook);
242070700Sjulian		}
242170700Sjulian		break;
242271047Sjulian	case NGQF_FN:
242371047Sjulian		/*
242471047Sjulian		 *  We have to implicitly trust the hook,
242571047Sjulian		 * as some of these are used for system purposes
242671849Sjulian		 * where the hook is invalid. In the case of
242771849Sjulian		 * the shutdown message we allow it to hit
242871849Sjulian		 * even if the node is invalid.
242971047Sjulian		 */
243071849Sjulian		if ((NG_NODE_NOT_VALID(node))
243171849Sjulian		&& (NGI_FN(item) != &ng_rmnode)) {
243271047Sjulian			TRAP_ERROR();
243371047Sjulian			error = EINVAL;
243471047Sjulian			break;
243571047Sjulian		}
243671849Sjulian		(*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item));
243771047Sjulian		NG_FREE_ITEM(item);
243871047Sjulian		break;
243971047Sjulian
244070700Sjulian	}
244170700Sjulian	/*
244270700Sjulian	 * We held references on some of the resources
244370700Sjulian	 * that we took from the item. Now that we have
244470700Sjulian	 * finished doing everything, drop those references.
244570700Sjulian	 */
244670700Sjulian	if (hook) {
244770784Sjulian		NG_HOOK_UNREF(hook);
244870700Sjulian	}
244970700Sjulian
245070700Sjulian	if (was_reader) {
245170784Sjulian		ng_leave_read(&node->nd_input_queue);
245270700Sjulian	} else {
245370784Sjulian		ng_leave_write(&node->nd_input_queue);
245470700Sjulian	}
245570700Sjulian	return (error);
245670700Sjulian}
245770700Sjulian
245870700Sjulian/***********************************************************************
245970700Sjulian * Implement the 'generic' control messages
246070700Sjulian ***********************************************************************/
246170700Sjulianstatic int
246270700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
246370700Sjulian{
246470700Sjulian	int error = 0;
246570700Sjulian	struct ng_mesg *msg;
246670700Sjulian	struct ng_mesg *resp = NULL;
246770700Sjulian
246870700Sjulian	NGI_GET_MSG(item, msg);
246952419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
247071047Sjulian		TRAP_ERROR();
247170700Sjulian		error = EINVAL;
247270700Sjulian		goto out;
247352419Sjulian	}
247452419Sjulian	switch (msg->header.cmd) {
247552419Sjulian	case NGM_SHUTDOWN:
247671849Sjulian		ng_rmnode(here, NULL, NULL, 0);
247752419Sjulian		break;
247852419Sjulian	case NGM_MKPEER:
247952419Sjulian	    {
248052419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
248152419Sjulian
248252419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
248371047Sjulian			TRAP_ERROR();
248470700Sjulian			error = EINVAL;
248570700Sjulian			break;
248652419Sjulian		}
248752419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
248852419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
248952419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
249052419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
249152419Sjulian		break;
249252419Sjulian	    }
249352419Sjulian	case NGM_CONNECT:
249452419Sjulian	    {
249552419Sjulian		struct ngm_connect *const con =
249652419Sjulian			(struct ngm_connect *) msg->data;
249752419Sjulian		node_p node2;
249852419Sjulian
249952419Sjulian		if (msg->header.arglen != sizeof(*con)) {
250071047Sjulian			TRAP_ERROR();
250170700Sjulian			error = EINVAL;
250270700Sjulian			break;
250352419Sjulian		}
250452419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
250552419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
250652419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
250770700Sjulian		/* Don't forget we get a reference.. */
250870700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
250952419Sjulian		if (error)
251052419Sjulian			break;
251152419Sjulian		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
251270784Sjulian		NG_NODE_UNREF(node2);
251352419Sjulian		break;
251452419Sjulian	    }
251552419Sjulian	case NGM_NAME:
251652419Sjulian	    {
251752419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
251852419Sjulian
251952419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
252071047Sjulian			TRAP_ERROR();
252170700Sjulian			error = EINVAL;
252270700Sjulian			break;
252352419Sjulian		}
252452419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
252552419Sjulian		error = ng_name_node(here, nam->name);
252652419Sjulian		break;
252752419Sjulian	    }
252852419Sjulian	case NGM_RMHOOK:
252952419Sjulian	    {
253052419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
253152419Sjulian		hook_p hook;
253252419Sjulian
253352419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
253471047Sjulian			TRAP_ERROR();
253570700Sjulian			error = EINVAL;
253670700Sjulian			break;
253752419Sjulian		}
253852419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
253954096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
254052419Sjulian			ng_destroy_hook(hook);
254152419Sjulian		break;
254252419Sjulian	    }
254352419Sjulian	case NGM_NODEINFO:
254452419Sjulian	    {
254552419Sjulian		struct nodeinfo *ni;
254652419Sjulian
254770700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
254852419Sjulian		if (resp == NULL) {
254952419Sjulian			error = ENOMEM;
255052419Sjulian			break;
255152419Sjulian		}
255252419Sjulian
255352419Sjulian		/* Fill in node info */
255470700Sjulian		ni = (struct nodeinfo *) resp->data;
255570784Sjulian		if (NG_NODE_HAS_NAME(here))
255670784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
255770784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
255852722Sjulian		ni->id = ng_node2ID(here);
255970784Sjulian		ni->hooks = here->nd_numhooks;
256052419Sjulian		break;
256152419Sjulian	    }
256252419Sjulian	case NGM_LISTHOOKS:
256352419Sjulian	    {
256470784Sjulian		const int nhooks = here->nd_numhooks;
256552419Sjulian		struct hooklist *hl;
256652419Sjulian		struct nodeinfo *ni;
256752419Sjulian		hook_p hook;
256852419Sjulian
256952419Sjulian		/* Get response struct */
257070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*hl)
257170700Sjulian		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
257252419Sjulian		if (resp == NULL) {
257352419Sjulian			error = ENOMEM;
257452419Sjulian			break;
257552419Sjulian		}
257670700Sjulian		hl = (struct hooklist *) resp->data;
257752419Sjulian		ni = &hl->nodeinfo;
257852419Sjulian
257952419Sjulian		/* Fill in node info */
258070784Sjulian		if (NG_NODE_HAS_NAME(here))
258170784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
258270784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
258352722Sjulian		ni->id = ng_node2ID(here);
258452419Sjulian
258552419Sjulian		/* Cycle through the linked list of hooks */
258652419Sjulian		ni->hooks = 0;
258770784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
258852419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
258952419Sjulian
259052419Sjulian			if (ni->hooks >= nhooks) {
259152419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
259287599Sobrien				    __func__, "hooks");
259352419Sjulian				break;
259452419Sjulian			}
259570784Sjulian			if (NG_HOOK_NOT_VALID(hook))
259652419Sjulian				continue;
259770784Sjulian			strncpy(link->ourhook, NG_HOOK_NAME(hook), NG_HOOKLEN);
259870784Sjulian			strncpy(link->peerhook,
259970784Sjulian				NG_PEER_HOOK_NAME(hook), NG_HOOKLEN);
260070784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
260152419Sjulian				strncpy(link->nodeinfo.name,
260270784Sjulian				    NG_PEER_NODE_NAME(hook), NG_NODELEN);
260352419Sjulian			strncpy(link->nodeinfo.type,
260470784Sjulian			   NG_PEER_NODE(hook)->nd_type->name, NG_TYPELEN);
260570784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
260670784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
260752419Sjulian			ni->hooks++;
260852419Sjulian		}
260952419Sjulian		break;
261052419Sjulian	    }
261152419Sjulian
261252419Sjulian	case NGM_LISTNAMES:
261352419Sjulian	case NGM_LISTNODES:
261452419Sjulian	    {
261552419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
261652419Sjulian		struct namelist *nl;
261752419Sjulian		node_p node;
261852419Sjulian		int num = 0;
261952419Sjulian
262072200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
262152419Sjulian		/* Count number of nodes */
262270784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
262370912Sjulian			if (NG_NODE_IS_VALID(node)
262470912Sjulian			&& (unnamed || NG_NODE_HAS_NAME(node))) {
262552419Sjulian				num++;
262670912Sjulian			}
262752419Sjulian		}
262872200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
262952419Sjulian
263052419Sjulian		/* Get response struct */
263170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*nl)
263270700Sjulian		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
263352419Sjulian		if (resp == NULL) {
263452419Sjulian			error = ENOMEM;
263552419Sjulian			break;
263652419Sjulian		}
263770700Sjulian		nl = (struct namelist *) resp->data;
263852419Sjulian
263952419Sjulian		/* Cycle through the linked list of nodes */
264052419Sjulian		nl->numnames = 0;
264172200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
264270784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
264352419Sjulian			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
264452419Sjulian
264552419Sjulian			if (nl->numnames >= num) {
264652419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
264787599Sobrien				    __func__, "nodes");
264852419Sjulian				break;
264952419Sjulian			}
265070784Sjulian			if (NG_NODE_NOT_VALID(node))
265152419Sjulian				continue;
265270784Sjulian			if (!unnamed && (! NG_NODE_HAS_NAME(node)))
265352419Sjulian				continue;
265470784Sjulian			if (NG_NODE_HAS_NAME(node))
265570784Sjulian				strncpy(np->name, NG_NODE_NAME(node), NG_NODELEN);
265670784Sjulian			strncpy(np->type, node->nd_type->name, NG_TYPELEN);
265752722Sjulian			np->id = ng_node2ID(node);
265870784Sjulian			np->hooks = node->nd_numhooks;
265952419Sjulian			nl->numnames++;
266052419Sjulian		}
266172200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
266252419Sjulian		break;
266352419Sjulian	    }
266452419Sjulian
266552419Sjulian	case NGM_LISTTYPES:
266652419Sjulian	    {
266752419Sjulian		struct typelist *tl;
266852419Sjulian		struct ng_type *type;
266952419Sjulian		int num = 0;
267052419Sjulian
267172200Sbmilekic		mtx_lock(&ng_typelist_mtx);
267252419Sjulian		/* Count number of types */
267370912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
267452419Sjulian			num++;
267570912Sjulian		}
267672200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
267752419Sjulian
267852419Sjulian		/* Get response struct */
267970700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*tl)
268070700Sjulian		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
268152419Sjulian		if (resp == NULL) {
268252419Sjulian			error = ENOMEM;
268352419Sjulian			break;
268452419Sjulian		}
268570700Sjulian		tl = (struct typelist *) resp->data;
268652419Sjulian
268752419Sjulian		/* Cycle through the linked list of types */
268852419Sjulian		tl->numtypes = 0;
268972200Sbmilekic		mtx_lock(&ng_typelist_mtx);
269070700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
269152419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
269252419Sjulian
269352419Sjulian			if (tl->numtypes >= num) {
269452419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
269587599Sobrien				    __func__, "types");
269652419Sjulian				break;
269752419Sjulian			}
269859879Sarchie			strncpy(tp->type_name, type->name, NG_TYPELEN);
269971603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
270052419Sjulian			tl->numtypes++;
270152419Sjulian		}
270272200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
270352419Sjulian		break;
270452419Sjulian	    }
270552419Sjulian
270653913Sarchie	case NGM_BINARY2ASCII:
270753913Sarchie	    {
270864510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
270953913Sarchie		const struct ng_parse_type *argstype;
271053913Sarchie		const struct ng_cmdlist *c;
271170700Sjulian		struct ng_mesg *binary, *ascii;
271253913Sarchie
271353913Sarchie		/* Data area must contain a valid netgraph message */
271453913Sarchie		binary = (struct ng_mesg *)msg->data;
271553913Sarchie		if (msg->header.arglen < sizeof(struct ng_mesg)
271670912Sjulian		    || (msg->header.arglen - sizeof(struct ng_mesg)
271770912Sjulian		      < binary->header.arglen)) {
271871047Sjulian			TRAP_ERROR();
271953913Sarchie			error = EINVAL;
272053913Sarchie			break;
272153913Sarchie		}
272253913Sarchie
272370700Sjulian		/* Get a response message with lots of room */
272470700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
272570159Sjulian		if (resp == NULL) {
272653913Sarchie			error = ENOMEM;
272753913Sarchie			break;
272853913Sarchie		}
272970700Sjulian		ascii = (struct ng_mesg *)resp->data;
273053913Sarchie
273153913Sarchie		/* Copy binary message header to response message payload */
273253913Sarchie		bcopy(binary, ascii, sizeof(*binary));
273353913Sarchie
273453913Sarchie		/* Find command by matching typecookie and command number */
273570784Sjulian		for (c = here->nd_type->cmdlist;
273653913Sarchie		    c != NULL && c->name != NULL; c++) {
273753913Sarchie			if (binary->header.typecookie == c->cookie
273853913Sarchie			    && binary->header.cmd == c->cmd)
273953913Sarchie				break;
274053913Sarchie		}
274153913Sarchie		if (c == NULL || c->name == NULL) {
274253913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
274353913Sarchie				if (binary->header.typecookie == c->cookie
274453913Sarchie				    && binary->header.cmd == c->cmd)
274553913Sarchie					break;
274653913Sarchie			}
274753913Sarchie			if (c->name == NULL) {
274870700Sjulian				NG_FREE_MSG(resp);
274953913Sarchie				error = ENOSYS;
275053913Sarchie				break;
275153913Sarchie			}
275253913Sarchie		}
275353913Sarchie
275453913Sarchie		/* Convert command name to ASCII */
275553913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
275653913Sarchie		    "%s", c->name);
275753913Sarchie
275853913Sarchie		/* Convert command arguments to ASCII */
275953913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
276053913Sarchie		    c->respType : c->mesgType;
276170912Sjulian		if (argstype == NULL) {
276253913Sarchie			*ascii->data = '\0';
276370912Sjulian		} else {
276453913Sarchie			if ((error = ng_unparse(argstype,
276553913Sarchie			    (u_char *)binary->data,
276653913Sarchie			    ascii->data, bufSize)) != 0) {
276770700Sjulian				NG_FREE_MSG(resp);
276853913Sarchie				break;
276953913Sarchie			}
277053913Sarchie		}
277153913Sarchie
277253913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
277353913Sarchie		bufSize = strlen(ascii->data) + 1;
277453913Sarchie		ascii->header.arglen = bufSize;
277570700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
277653913Sarchie		break;
277753913Sarchie	    }
277853913Sarchie
277953913Sarchie	case NGM_ASCII2BINARY:
278053913Sarchie	    {
278153913Sarchie		int bufSize = 2000;	/* XXX hard coded constant */
278253913Sarchie		const struct ng_cmdlist *c;
278353913Sarchie		const struct ng_parse_type *argstype;
278470700Sjulian		struct ng_mesg *ascii, *binary;
278559178Sarchie		int off = 0;
278653913Sarchie
278753913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
278853913Sarchie		ascii = (struct ng_mesg *)msg->data;
278970912Sjulian		if ((msg->header.arglen < sizeof(*ascii) + 1)
279070912Sjulian		    || (ascii->header.arglen < 1)
279170912Sjulian		    || (msg->header.arglen
279270912Sjulian		      < sizeof(*ascii) + ascii->header.arglen)) {
279371047Sjulian			TRAP_ERROR();
279453913Sarchie			error = EINVAL;
279553913Sarchie			break;
279653913Sarchie		}
279753913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
279853913Sarchie
279970700Sjulian		/* Get a response message with lots of room */
280070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
280170159Sjulian		if (resp == NULL) {
280253913Sarchie			error = ENOMEM;
280353913Sarchie			break;
280453913Sarchie		}
280570700Sjulian		binary = (struct ng_mesg *)resp->data;
280653913Sarchie
280753913Sarchie		/* Copy ASCII message header to response message payload */
280853913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
280953913Sarchie
281053913Sarchie		/* Find command by matching ASCII command string */
281170784Sjulian		for (c = here->nd_type->cmdlist;
281253913Sarchie		    c != NULL && c->name != NULL; c++) {
281353913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
281453913Sarchie				break;
281553913Sarchie		}
281653913Sarchie		if (c == NULL || c->name == NULL) {
281753913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
281853913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
281953913Sarchie					break;
282053913Sarchie			}
282153913Sarchie			if (c->name == NULL) {
282270700Sjulian				NG_FREE_MSG(resp);
282353913Sarchie				error = ENOSYS;
282453913Sarchie				break;
282553913Sarchie			}
282653913Sarchie		}
282753913Sarchie
282853913Sarchie		/* Convert command name to binary */
282953913Sarchie		binary->header.cmd = c->cmd;
283053913Sarchie		binary->header.typecookie = c->cookie;
283153913Sarchie
283253913Sarchie		/* Convert command arguments to binary */
283353913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
283453913Sarchie		    c->respType : c->mesgType;
283570912Sjulian		if (argstype == NULL) {
283653913Sarchie			bufSize = 0;
283770912Sjulian		} else {
283853913Sarchie			if ((error = ng_parse(argstype, ascii->data,
283953913Sarchie			    &off, (u_char *)binary->data, &bufSize)) != 0) {
284070700Sjulian				NG_FREE_MSG(resp);
284153913Sarchie				break;
284253913Sarchie			}
284353913Sarchie		}
284453913Sarchie
284553913Sarchie		/* Return the result */
284653913Sarchie		binary->header.arglen = bufSize;
284770700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
284853913Sarchie		break;
284953913Sarchie	    }
285053913Sarchie
285162471Sphk	case NGM_TEXT_CONFIG:
285252419Sjulian	case NGM_TEXT_STATUS:
285352419Sjulian		/*
285452419Sjulian		 * This one is tricky as it passes the command down to the
285552419Sjulian		 * actual node, even though it is a generic type command.
285670700Sjulian		 * This means we must assume that the item/msg is already freed
285752419Sjulian		 * when control passes back to us.
285852419Sjulian		 */
285970784Sjulian		if (here->nd_type->rcvmsg != NULL) {
286070700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
286170784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
286252419Sjulian		}
286352419Sjulian		/* Fall through if rcvmsg not supported */
286452419Sjulian	default:
286571047Sjulian		TRAP_ERROR();
286652419Sjulian		error = EINVAL;
286752419Sjulian	}
286870700Sjulian	/*
286970700Sjulian	 * Sometimes a generic message may be statically allocated
287070700Sjulian	 * to avoid problems with allocating when in tight memeory situations.
287170700Sjulian	 * Don't free it if it is so.
287270700Sjulian	 * I break them appart here, because erros may cause a free if the item
287370700Sjulian	 * in which case we'd be doing it twice.
287470700Sjulian	 * they are kept together above, to simplify freeing.
287570700Sjulian	 */
287670700Sjulianout:
287770700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
287871849Sjulian	if (msg)
287970700Sjulian		NG_FREE_MSG(msg);
288052419Sjulian	return (error);
288152419Sjulian}
288252419Sjulian
288352419Sjulian/*
288459879Sarchie * Copy a 'meta'.
288559879Sarchie *
288659879Sarchie * Returns new meta, or NULL if original meta is NULL or ENOMEM.
288759879Sarchie */
288859879Sarchiemeta_p
288959879Sarchieng_copy_meta(meta_p meta)
289059879Sarchie{
289159879Sarchie	meta_p meta2;
289259879Sarchie
289359879Sarchie	if (meta == NULL)
289459879Sarchie		return (NULL);
289570700Sjulian	MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH_META, M_NOWAIT);
289659879Sarchie	if (meta2 == NULL)
289759879Sarchie		return (NULL);
289859879Sarchie	meta2->allocated_len = meta->used_len;
289959879Sarchie	bcopy(meta, meta2, meta->used_len);
290059879Sarchie	return (meta2);
290159879Sarchie}
290259879Sarchie
290352419Sjulian/************************************************************************
290452419Sjulian			Module routines
290552419Sjulian************************************************************************/
290652419Sjulian
290752419Sjulian/*
290852419Sjulian * Handle the loading/unloading of a netgraph node type module
290952419Sjulian */
291052419Sjulianint
291152419Sjulianng_mod_event(module_t mod, int event, void *data)
291252419Sjulian{
291352419Sjulian	struct ng_type *const type = data;
291452419Sjulian	int s, error = 0;
291552419Sjulian
291652419Sjulian	switch (event) {
291752419Sjulian	case MOD_LOAD:
291852419Sjulian
291952419Sjulian		/* Register new netgraph node type */
292052419Sjulian		s = splnet();
292152419Sjulian		if ((error = ng_newtype(type)) != 0) {
292252419Sjulian			splx(s);
292352419Sjulian			break;
292452419Sjulian		}
292552419Sjulian
292652419Sjulian		/* Call type specific code */
292752419Sjulian		if (type->mod_event != NULL)
292870700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
292972200Sbmilekic				mtx_lock(&ng_typelist_mtx);
293071603Sjulian				type->refs--;	/* undo it */
293152419Sjulian				LIST_REMOVE(type, types);
293272200Sbmilekic				mtx_unlock(&ng_typelist_mtx);
293370700Sjulian			}
293452419Sjulian		splx(s);
293552419Sjulian		break;
293652419Sjulian
293752419Sjulian	case MOD_UNLOAD:
293852419Sjulian		s = splnet();
293971603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
294052419Sjulian			error = EBUSY;
294171603Sjulian		} else {
294271603Sjulian			if (type->refs == 0) {
294371603Sjulian				/* failed load, nothing to undo */
294471603Sjulian				splx(s);
294571603Sjulian				break;
294671603Sjulian			}
294752419Sjulian			if (type->mod_event != NULL) {	/* check with type */
294852419Sjulian				error = (*type->mod_event)(mod, event, data);
294952419Sjulian				if (error != 0) {	/* type refuses.. */
295052419Sjulian					splx(s);
295152419Sjulian					break;
295252419Sjulian				}
295352419Sjulian			}
295472200Sbmilekic			mtx_lock(&ng_typelist_mtx);
295552419Sjulian			LIST_REMOVE(type, types);
295672200Sbmilekic			mtx_unlock(&ng_typelist_mtx);
295752419Sjulian		}
295852419Sjulian		splx(s);
295952419Sjulian		break;
296052419Sjulian
296152419Sjulian	default:
296252419Sjulian		if (type->mod_event != NULL)
296352419Sjulian			error = (*type->mod_event)(mod, event, data);
296452419Sjulian		else
296552419Sjulian			error = 0;		/* XXX ? */
296652419Sjulian		break;
296752419Sjulian	}
296852419Sjulian	return (error);
296952419Sjulian}
297052419Sjulian
297152419Sjulian/*
297252419Sjulian * Handle loading and unloading for this code.
297352419Sjulian * The only thing we need to link into is the NETISR strucure.
297452419Sjulian */
297552419Sjulianstatic int
297652419Sjulianngb_mod_event(module_t mod, int event, void *data)
297752419Sjulian{
297852419Sjulian	int s, error = 0;
297952419Sjulian
298052419Sjulian	switch (event) {
298152419Sjulian	case MOD_LOAD:
298252419Sjulian		/* Register line discipline */
298393818Sjhb		mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_SPIN);
298493818Sjhb		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL, 0);
298593818Sjhb		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 0);
298693818Sjhb		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL, 0);
2987122110Sharti		mtx_init(&ngq_mtx, "netgraph free item list mutex", NULL, 0);
298852419Sjulian		s = splimp();
2989111888Sjlemon		netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL);
299052419Sjulian		splx(s);
299152419Sjulian		break;
299252419Sjulian	case MOD_UNLOAD:
299352419Sjulian		/* You cant unload it because an interface may be using it.  */
299452419Sjulian		error = EBUSY;
299552419Sjulian		break;
299652419Sjulian	default:
299752419Sjulian		error = EOPNOTSUPP;
299852419Sjulian		break;
299952419Sjulian	}
300052419Sjulian	return (error);
300152419Sjulian}
300252419Sjulian
300352419Sjulianstatic moduledata_t netgraph_mod = {
300452419Sjulian	"netgraph",
300552419Sjulian	ngb_mod_event,
300652419Sjulian	(NULL)
300752419Sjulian};
300852419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
300972946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
301072946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
301172946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
301252419Sjulian
301352419Sjulian/************************************************************************
301470700Sjulian			Queue element get/free routines
301552419Sjulian************************************************************************/
301652419Sjulian
301752419Sjulian
301870700Sjulianstatic int			allocated;	/* number of items malloc'd */
3019111749Sharti
302070700Sjulianstatic int			maxalloc = 128;	/* limit the damage of a leak */
3021111749Shartistatic int			ngqfreemax = 64;/* cache at most this many */
3022111749Sharti
3023111749ShartiTUNABLE_INT("net.graph.maxalloc", &maxalloc);
3024121307SsilbySYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
3025111749Sharti    0, "Maximum number of queue items to allocate");
3026111749Sharti
3027111749ShartiTUNABLE_INT("net.graph.ngqfreemax", &ngqfreemax);
3028121307SsilbySYSCTL_INT(_net_graph, OID_AUTO, ngqfreemax, CTLFLAG_RDTUN, &ngqfreemax,
3029111749Sharti    0, "Maximum number of free queue items to cache");
3030111749Sharti
303170700Sjulianstatic const int		ngqfreelow = 4; /* try malloc if free < this */
303270700Sjulianstatic volatile int		ngqfreesize;	/* number of cached entries */
3033122110Shartistatic volatile item_p		ngqfree;	/* free ones */
3034122110Sharti
303570784Sjulian#ifdef	NETGRAPH_DEBUG
303670700Sjulianstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
303770700Sjulian#endif
303852419Sjulian/*
303952419Sjulian * Get a queue entry
304070700Sjulian * This is usually called when a packet first enters netgraph.
304170700Sjulian * By definition, this is usually from an interrupt, or from a user.
304270700Sjulian * Users are not so important, but try be quick for the times that it's
3043122110Sharti * an interrupt.
304470700Sjulian * XXX If reserve is low, we should try to get 2 from malloc as this
304570700Sjulian * would indicate it often fails.
304652419Sjulian */
304770700Sjulianstatic item_p
304852419Sjulianng_getqblk(void)
304952419Sjulian{
305070700Sjulian	item_p item = NULL;
305152419Sjulian
305270700Sjulian	/*
305370700Sjulian	 * Try get a cached queue block, or else allocate a new one
305470700Sjulian	 * If we are less than our reserve, try malloc. If malloc
305570700Sjulian	 * fails, then that's what the reserve is for...
3056122110Sharti	 * We have our little reserve
305770700Sjulian	 * because we use M_NOWAIT for malloc. This just helps us
305870700Sjulian	 * avoid dropping packets while not increasing the time
305971650Sjulian	 * we take to service the interrupt (on average) (I hope).
306070700Sjulian	 */
3061122110Sharti	mtx_lock(&ngq_mtx);
3062122110Sharti
3063122110Sharti	if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) {
3064122110Sharti		if (allocated < maxalloc) {  /* don't leak forever */
3065122110Sharti			MALLOC(item, item_p ,
3066122110Sharti			    sizeof(*item), M_NETGRAPH_ITEM,
3067122110Sharti			    (M_NOWAIT | M_ZERO));
3068122110Sharti			if (item) {
306970784Sjulian#ifdef	NETGRAPH_DEBUG
3070122110Sharti				TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
307170784Sjulian#endif	/* NETGRAPH_DEBUG */
3072122110Sharti				allocated++;
307370700Sjulian			}
307470700Sjulian		}
3075122110Sharti	}
307652419Sjulian
3077122110Sharti	/*
3078122110Sharti	 * We didn't or couldn't malloc.
3079122110Sharti	 * try get one from our cache.
3080122110Sharti	 */
3081122110Sharti	if (item == NULL && (item = ngqfree) != NULL) {
3082122110Sharti		ngqfree = item->el_next;
3083122110Sharti		ngqfreesize--;
3084122110Sharti		item->el_flags &= ~NGQF_FREE;
308552419Sjulian	}
3086122110Sharti
3087122110Sharti	mtx_unlock(&ngq_mtx);
308870700Sjulian	return (item);
308952419Sjulian}
309052419Sjulian
309152419Sjulian/*
309252419Sjulian * Release a queue entry
309352419Sjulian */
309470700Sjulianvoid
309570700Sjulianng_free_item(item_p item)
309652419Sjulian{
309752419Sjulian
309870700Sjulian	/*
309970700Sjulian	 * The item may hold resources on it's own. We need to free
310070700Sjulian	 * these before we can free the item. What they are depends upon
310170700Sjulian	 * what kind of item it is. it is important that nodes zero
310270700Sjulian	 * out pointers to resources that they remove from the item
310370700Sjulian	 * or we release them again here.
310470700Sjulian	 */
310570700Sjulian	if (item->el_flags & NGQF_FREE) {
310670700Sjulian		panic(" Freeing free queue item");
310752419Sjulian	}
310871047Sjulian	switch (item->el_flags & NGQF_TYPE) {
310970700Sjulian	case NGQF_DATA:
311070700Sjulian		/* If we have an mbuf and metadata still attached.. */
311170700Sjulian		NG_FREE_M(_NGI_M(item));
311270700Sjulian		NG_FREE_META(_NGI_META(item));
311370700Sjulian		break;
311470700Sjulian	case NGQF_MESG:
3115102244Sarchie		_NGI_RETADDR(item) = 0;
311670700Sjulian		NG_FREE_MSG(_NGI_MSG(item));
311770700Sjulian		break;
311871047Sjulian	case NGQF_FN:
311971047Sjulian		/* nothing to free really, */
312071047Sjulian		_NGI_FN(item) = NULL;
312171047Sjulian		_NGI_ARG1(item) = NULL;
312271047Sjulian		_NGI_ARG2(item) = 0;
312371047Sjulian	case NGQF_UNDEF:
312497229Speter		break;
312552419Sjulian	}
312671849Sjulian	/* If we still have a node or hook referenced... */
312771849Sjulian	_NGI_CLR_NODE(item);
312871849Sjulian	_NGI_CLR_HOOK(item);
312970700Sjulian	item->el_flags |= NGQF_FREE;
313052419Sjulian
3131122110Sharti	mtx_lock(&ngq_mtx);
3132122110Sharti	if (ngqfreesize < ngqfreemax) {
3133122110Sharti		ngqfreesize++;
3134122110Sharti		item->el_next = ngqfree;
3135122110Sharti		ngqfree = item;
313670700Sjulian	} else {
313770784Sjulian#ifdef	NETGRAPH_DEBUG
313870700Sjulian		TAILQ_REMOVE(&ng_itemlist, item, all);
313970784Sjulian#endif	/* NETGRAPH_DEBUG */
314070700Sjulian		NG_FREE_ITEM_REAL(item);
3141122110Sharti		allocated--;
314270700Sjulian	}
3143122110Sharti	mtx_unlock(&ngq_mtx);
314470700Sjulian}
314552419Sjulian
314670784Sjulian#ifdef	NETGRAPH_DEBUG
314770700Sjulianvoid
314870784Sjuliandumphook (hook_p hook, char *file, int line)
314970784Sjulian{
315070784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
315170784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
315270784Sjulian	printf("	Last active @ %s, line %d\n",
315370784Sjulian		hook->lastfile, hook->lastline);
315470784Sjulian	if (line) {
315570784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
315670784Sjulian	}
315770784Sjulian}
315870784Sjulian
315970784Sjulianvoid
316070784Sjuliandumpnode(node_p node, char *file, int line)
316170784Sjulian{
316270784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
316371047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
316470784Sjulian		node->nd_numhooks, node->nd_flags,
316570784Sjulian		node->nd_refs, node->nd_name);
316670784Sjulian	printf("	Last active @ %s, line %d\n",
316770784Sjulian		node->lastfile, node->lastline);
316870784Sjulian	if (line) {
316970784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
317070784Sjulian	}
317170784Sjulian}
317270784Sjulian
317370784Sjulianvoid
317470700Sjuliandumpitem(item_p item, char *file, int line)
317570700Sjulian{
317670700Sjulian	if (item->el_flags & NGQF_FREE) {
317770700Sjulian		printf(" Free item, freed at %s, line %d\n",
317870700Sjulian			item->lastfile, item->lastline);
317952419Sjulian	} else {
318070700Sjulian		printf(" ACTIVE item, last used at %s, line %d",
318170700Sjulian			item->lastfile, item->lastline);
318271047Sjulian		switch(item->el_flags & NGQF_TYPE) {
318371047Sjulian		case NGQF_DATA:
318471047Sjulian			printf(" - [data]\n");
318571047Sjulian			break;
318671047Sjulian		case NGQF_MESG:
318770700Sjulian			printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
318871047Sjulian			break;
318971047Sjulian		case NGQF_FN:
319071047Sjulian			printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
319171047Sjulian				item->body.fn.fn_fn,
319271849Sjulian				NGI_NODE(item),
319371849Sjulian				NGI_HOOK(item),
319471047Sjulian				item->body.fn.fn_arg1,
319571047Sjulian				item->body.fn.fn_arg2,
319671047Sjulian				item->body.fn.fn_arg2);
319771047Sjulian			break;
319871047Sjulian		case NGQF_UNDEF:
319971047Sjulian			printf(" - UNDEFINED!\n");
320070700Sjulian		}
320152419Sjulian	}
320270784Sjulian	if (line) {
320370784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
320471849Sjulian		if (NGI_NODE(item)) {
320570784Sjulian			printf("node %p ([%x])\n",
320671849Sjulian				NGI_NODE(item), ng_node2ID(NGI_NODE(item)));
320770784Sjulian		}
320870784Sjulian	}
320970700Sjulian}
321052419Sjulian
321170784Sjulianstatic void
321270784Sjulianng_dumpitems(void)
321370784Sjulian{
321470784Sjulian	item_p item;
321570784Sjulian	int i = 1;
321670784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
321770784Sjulian		printf("[%d] ", i++);
321870784Sjulian		dumpitem(item, NULL, 0);
321970784Sjulian	}
322070784Sjulian}
322170784Sjulian
322270784Sjulianstatic void
322370784Sjulianng_dumpnodes(void)
322470784Sjulian{
322570784Sjulian	node_p node;
322670784Sjulian	int i = 1;
322770784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
322870784Sjulian		printf("[%d] ", i++);
322970784Sjulian		dumpnode(node, NULL, 0);
323070784Sjulian	}
323170784Sjulian}
323270784Sjulian
323370784Sjulianstatic void
323470784Sjulianng_dumphooks(void)
323570784Sjulian{
323670784Sjulian	hook_p hook;
323770784Sjulian	int i = 1;
323870784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
323970784Sjulian		printf("[%d] ", i++);
324070784Sjulian		dumphook(hook, NULL, 0);
324170784Sjulian	}
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");		\
337070700Sjulian			Debugger("has node");				\
337171849Sjulian			NGI_CLR_NODE(item);				\
337270700Sjulian		}							\
337371849Sjulian		if (NGI_HOOK(item) ) {					\
337470700Sjulian			printf("item already has hook");		\
337570700Sjulian			Debugger("has hook");				\
337671849Sjulian			NGI_CLR_HOOK(item);				\
337770700Sjulian		}							\
337870700Sjulian	} while (0)
337970700Sjulian#else
338070784Sjulian#define ITEM_DEBUG_CHECKS
338170700Sjulian#endif
338270700Sjulian
338370700Sjulian/*
338470700Sjulian * Put elements 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
339770700Sjulianng_package_data(struct mbuf *m, meta_p meta)
339870700Sjulian{
339970700Sjulian	item_p item;
340070700Sjulian
340170700Sjulian	if ((item = ng_getqblk()) == NULL) {
340270700Sjulian		NG_FREE_M(m);
340370700Sjulian		NG_FREE_META(meta);
340470700Sjulian		return (NULL);
340570700Sjulian	}
340670784Sjulian	ITEM_DEBUG_CHECKS;
340770700Sjulian	item->el_flags = NGQF_DATA;
340870700Sjulian	item->el_next = NULL;
340970700Sjulian	NGI_M(item) = m;
341070700Sjulian	NGI_META(item) = meta;
341170700Sjulian	return (item);
341270700Sjulian}
341370700Sjulian
341470700Sjulian/*
341570700Sjulian * Allocate a queue item and put items into it..
341670700Sjulian * Evaluate the address as this will be needed to queue it and
341770700Sjulian * to work out what some of the fields should be.
341870700Sjulian * Hook and node references will be removed when the item is dequeued.
341970700Sjulian * (or equivalent)
342070700Sjulian */
342170700Sjulianitem_p
342270700Sjulianng_package_msg(struct ng_mesg *msg)
342370700Sjulian{
342470700Sjulian	item_p item;
342570700Sjulian
342670700Sjulian	if ((item = ng_getqblk()) == NULL) {
342771849Sjulian		NG_FREE_MSG(msg);
342870700Sjulian		return (NULL);
342969922Sjulian	}
343070784Sjulian	ITEM_DEBUG_CHECKS;
343170700Sjulian	item->el_flags = NGQF_MESG;
343270700Sjulian	item->el_next = NULL;
343370700Sjulian	/*
343470700Sjulian	 * Set the current lasthook into the queue item
343570700Sjulian	 */
343670700Sjulian	NGI_MSG(item) = msg;
3437102244Sarchie	NGI_RETADDR(item) = 0;
343870700Sjulian	return (item);
343970700Sjulian}
344069922Sjulian
344170700Sjulian
344270700Sjulian
344371849Sjulian#define SET_RETADDR(item, here, retaddr)				\
344471047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
344571047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
344670700Sjulian			if (retaddr) {					\
344770700Sjulian				NGI_RETADDR(item) = retaddr;		\
344870700Sjulian			} else {					\
344970700Sjulian				/*					\
345070700Sjulian				 * The old return address should be ok.	\
345170700Sjulian				 * If there isn't one, use the address	\
345270700Sjulian				 * here.				\
345370700Sjulian				 */					\
345470700Sjulian				if (NGI_RETADDR(item) == 0) {		\
345570700Sjulian					NGI_RETADDR(item)		\
345670700Sjulian						= ng_node2ID(here);	\
345770700Sjulian				}					\
345870700Sjulian			}						\
345970700Sjulian		}							\
346070700Sjulian	} while (0)
346170700Sjulian
346270700Sjulianint
346370700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
346470700Sjulian{
346571849Sjulian	hook_p peer;
346671849Sjulian	node_p peernode;
346770784Sjulian	ITEM_DEBUG_CHECKS;
346870700Sjulian	/*
346970700Sjulian	 * Quick sanity check..
347070784Sjulian	 * Since a hook holds a reference on it's node, once we know
347170784Sjulian	 * that the peer is still connected (even if invalid,) we know
347270784Sjulian	 * that the peer node is present, though maybe invalid.
347370700Sjulian	 */
347470700Sjulian	if ((hook == NULL)
347570784Sjulian	|| NG_HOOK_NOT_VALID(hook)
347670784Sjulian	|| (NG_HOOK_PEER(hook) == NULL)
347770784Sjulian	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
347870784Sjulian	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
347970700Sjulian		NG_FREE_ITEM(item);
348071047Sjulian		TRAP_ERROR();
348173083Sjulian		return (ENETDOWN);
348252419Sjulian	}
348352419Sjulian
348470700Sjulian	/*
348570700Sjulian	 * Transfer our interest to the other (peer) end.
348670700Sjulian	 */
348771849Sjulian	peer = NG_HOOK_PEER(hook);
348871849Sjulian	NG_HOOK_REF(peer);
348971849Sjulian	NGI_SET_HOOK(item, peer);
349071849Sjulian	peernode = NG_PEER_NODE(hook);
349171849Sjulian	NG_NODE_REF(peernode);
349271849Sjulian	NGI_SET_NODE(item, peernode);
349379706Sjulian	SET_RETADDR(item, here, retaddr);
349470700Sjulian	return (0);
349570700Sjulian}
349652419Sjulian
349770700Sjulianint
349870700Sjulianng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
349970700Sjulian{
350070700Sjulian	node_p  dest = NULL;
350170700Sjulian	hook_p	hook = NULL;
350270700Sjulian	int     error;
350370700Sjulian
350470784Sjulian	ITEM_DEBUG_CHECKS;
350570700Sjulian	/*
350670700Sjulian	 * Note that ng_path2noderef increments the reference count
350770700Sjulian	 * on the node for us if it finds one. So we don't have to.
350870700Sjulian	 */
350970700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
351070700Sjulian	if (error) {
351170700Sjulian		NG_FREE_ITEM(item);
351270784Sjulian		return (error);
351352419Sjulian	}
351471849Sjulian	NGI_SET_NODE(item, dest);
351571849Sjulian	if ( hook) {
351670784Sjulian		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
351771849Sjulian		NGI_SET_HOOK(item, hook);
351871849Sjulian	}
351971849Sjulian	SET_RETADDR(item, here, retaddr);
352070700Sjulian	return (0);
352170700Sjulian}
352252419Sjulian
352370700Sjulianint
352470700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
352570700Sjulian{
352670700Sjulian	node_p dest;
352770700Sjulian
352870784Sjulian	ITEM_DEBUG_CHECKS;
352970700Sjulian	/*
353070700Sjulian	 * Find the target node.
353170700Sjulian	 */
353270700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
353370700Sjulian	if (dest == NULL) {
353470700Sjulian		NG_FREE_ITEM(item);
353571047Sjulian		TRAP_ERROR();
353670700Sjulian		return(EINVAL);
353770700Sjulian	}
353870700Sjulian	/* Fill out the contents */
353970700Sjulian	item->el_flags = NGQF_MESG;
354070700Sjulian	item->el_next = NULL;
354171849Sjulian	NGI_SET_NODE(item, dest);
354271849Sjulian	NGI_CLR_HOOK(item);
354371849Sjulian	SET_RETADDR(item, here, retaddr);
354452419Sjulian	return (0);
354552419Sjulian}
354652419Sjulian
354752419Sjulian/*
354870700Sjulian * special case to send a message to self (e.g. destroy node)
354970700Sjulian * Possibly indicate an arrival hook too.
355070700Sjulian * Useful for removing that hook :-)
355152419Sjulian */
355270700Sjulianitem_p
355370700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
355452419Sjulian{
355570700Sjulian	item_p item;
355652419Sjulian
355770700Sjulian	/*
355870700Sjulian	 * Find the target node.
355970700Sjulian	 * If there is a HOOK argument, then use that in preference
356070700Sjulian	 * to the address.
356170700Sjulian	 */
356270700Sjulian	if ((item = ng_getqblk()) == NULL) {
356371849Sjulian		NG_FREE_MSG(msg);
356470700Sjulian		return (NULL);
356552419Sjulian	}
356670700Sjulian
356770700Sjulian	/* Fill out the contents */
356870700Sjulian	item->el_flags = NGQF_MESG;
356970700Sjulian	item->el_next = NULL;
357070784Sjulian	NG_NODE_REF(here);
357171849Sjulian	NGI_SET_NODE(item, here);
357271849Sjulian	if (hook) {
357370784Sjulian		NG_HOOK_REF(hook);
357471849Sjulian		NGI_SET_HOOK(item, hook);
357571849Sjulian	}
357670700Sjulian	NGI_MSG(item) = msg;
357770700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
357870700Sjulian	return (item);
357952419Sjulian}
358052419Sjulian
358171047Sjulianint
358271047Sjulianng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
358371047Sjulian{
358471047Sjulian	item_p item;
358571047Sjulian
358671047Sjulian	if ((item = ng_getqblk()) == NULL) {
358771047Sjulian		return (ENOMEM);
358871047Sjulian	}
358971047Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
359073238Sjulian	NG_NODE_REF(node); /* and one for the item */
359171849Sjulian	NGI_SET_NODE(item, node);
359271849Sjulian	if (hook) {
359371047Sjulian		NG_HOOK_REF(hook);
359471849Sjulian		NGI_SET_HOOK(item, hook);
359571047Sjulian	}
359671047Sjulian	NGI_FN(item) = fn;
359771047Sjulian	NGI_ARG1(item) = arg1;
359871047Sjulian	NGI_ARG2(item) = arg2;
359974078Sjulian	return(ng_snd_item(item, 0));
360071047Sjulian}
360171047Sjulian
360291711Sjulian/*
360391711Sjulian * Official timeout routines for Netgraph nodes.
360491711Sjulian */
360591711Sjulianstatic void
360691711Sjulianng_timeout_trapoline(void *arg)
360791711Sjulian{
360891711Sjulian	item_p item = arg;
360991711Sjulian
361091711Sjulian	ng_snd_item(item, 0);
361191711Sjulian}
361291711Sjulian
361391711Sjulian
361491711Sjulianstruct callout_handle
361591711Sjulianng_timeout(node_p node, hook_p hook, int ticks,
361691711Sjulian    ng_item_fn *fn, void * arg1, int arg2)
361791711Sjulian{
361891711Sjulian	item_p item;
361991711Sjulian
362091711Sjulian	if ((item = ng_getqblk()) == NULL) {
362191711Sjulian		struct callout_handle handle;
362291711Sjulian		handle.callout = NULL;
362391711Sjulian		return (handle);
362491711Sjulian	}
362591711Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
362691711Sjulian	NG_NODE_REF(node);		/* and one for the item */
362791711Sjulian	NGI_SET_NODE(item, node);
362891711Sjulian	if (hook) {
362991711Sjulian		NG_HOOK_REF(hook);
363091711Sjulian		NGI_SET_HOOK(item, hook);
363191711Sjulian	}
363291711Sjulian	NGI_FN(item) = fn;
363391711Sjulian	NGI_ARG1(item) = arg1;
363491711Sjulian	NGI_ARG2(item) = arg2;
363591711Sjulian	return (timeout(&ng_timeout_trapoline, item, ticks));
363691711Sjulian}
363791711Sjulian
363891711Sjulian/* A special modified version of untimeout() */
363991711Sjulianint
364091711Sjulianng_untimeout(struct callout_handle handle, node_p node)
364191711Sjulian{
364291711Sjulian	item_p item;
364391711Sjulian
364491711Sjulian	if (handle.callout == NULL)
364591711Sjulian		return (0);
364691711Sjulian	mtx_lock_spin(&callout_lock);
364791711Sjulian	item = handle.callout->c_arg; /* should be an official way to do this */
364891711Sjulian	if ((handle.callout->c_func == &ng_timeout_trapoline) &&
364991711Sjulian	    (NGI_NODE(item) == node) &&
365091711Sjulian	    (callout_stop(handle.callout))) {
365191711Sjulian		/*
365291711Sjulian		 * We successfully removed it from the queue before it ran
365391711Sjulian		 * So now we need to unreference everything that was
365491711Sjulian		 * given extra references. (NG_FREE_ITEM does this).
365591711Sjulian		 */
365691711Sjulian		mtx_unlock_spin(&callout_lock);
365791711Sjulian		NG_FREE_ITEM(item);
365891711Sjulian		return (1);
365991711Sjulian	}
366091711Sjulian	mtx_unlock_spin(&callout_lock);
366191711Sjulian	return (0);
366291711Sjulian}
366391711Sjulian
366470700Sjulian/*
366570700Sjulian * Set the address, if none given, give the node here.
366670700Sjulian */
366770700Sjulianvoid
366870700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
366970700Sjulian{
367070700Sjulian	if (retaddr) {
367170700Sjulian		NGI_RETADDR(item) = retaddr;
367270700Sjulian	} else {
367370700Sjulian		/*
367470700Sjulian		 * The old return address should be ok.
367570700Sjulian		 * If there isn't one, use the address here.
367670700Sjulian		 */
367770700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
367870700Sjulian	}
367970700Sjulian}
368052419Sjulian
368170700Sjulian#define TESTING
368270700Sjulian#ifdef TESTING
368370700Sjulian/* just test all the macros */
368470700Sjulianvoid
368570700Sjulianng_macro_test(item_p item);
368670700Sjulianvoid
368770700Sjulianng_macro_test(item_p item)
368870700Sjulian{
368970700Sjulian	node_p node = NULL;
369070700Sjulian	hook_p hook = NULL;
369170700Sjulian	struct mbuf *m;
369270700Sjulian	meta_p meta;
369370700Sjulian	struct ng_mesg *msg;
369470700Sjulian	ng_ID_t retaddr;
369570700Sjulian	int	error;
369670700Sjulian
369770700Sjulian	NGI_GET_M(item, m);
369870700Sjulian	NGI_GET_META(item, meta);
369970700Sjulian	NGI_GET_MSG(item, msg);
370070700Sjulian	retaddr = NGI_RETADDR(item);
370170700Sjulian	NG_SEND_DATA(error, hook, m, meta);
370270700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
370370700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
370470784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
370570700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
370670700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
370770700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
370870700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
370970700Sjulian}
371070700Sjulian#endif /* TESTING */
371170700Sjulian
3712