ng_base.c revision 71380
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 71380 2001-01-22 17:51:48Z julian $
4052419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
4152419Sjulian */
4252419Sjulian
4352419Sjulian/*
4452419Sjulian * This file implements the base netgraph code.
4552419Sjulian */
4652419Sjulian
4752419Sjulian#include <sys/param.h>
4852419Sjulian#include <sys/systm.h>
4952419Sjulian#include <sys/errno.h>
5052419Sjulian#include <sys/kernel.h>
5152419Sjulian#include <sys/malloc.h>
5252419Sjulian#include <sys/syslog.h>
5370700Sjulian#include <sys/sysctl.h>
5452419Sjulian#include <sys/linker.h>
5552419Sjulian#include <sys/queue.h>
5652419Sjulian#include <sys/mbuf.h>
5752843Sphk#include <sys/ctype.h>
5852816Sarchie#include <machine/limits.h>
5952419Sjulian
6052419Sjulian#include <net/netisr.h>
6152419Sjulian
6252419Sjulian#include <netgraph/ng_message.h>
6352419Sjulian#include <netgraph/netgraph.h>
6453913Sarchie#include <netgraph/ng_parse.h>
6552419Sjulian
6659756SpeterMODULE_VERSION(netgraph, 1);
6759756Speter
6870784Sjulian/* List of all active nodes */
6970700Sjulianstatic LIST_HEAD(, ng_node) ng_nodelist;
7070700Sjulianstatic struct mtx	ng_nodelist_mtx;
7152419Sjulian
7270784Sjulian#ifdef	NETGRAPH_DEBUG
7370784Sjulian
7470784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
7570784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
7670784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
7770784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
7870784Sjulian
7970784Sjulianstatic void ng_dumpitems(void);
8070784Sjulianstatic void ng_dumpnodes(void);
8170784Sjulianstatic void ng_dumphooks(void);
8270784Sjulian
8370784Sjulian#endif	/* NETGRAPH_DEBUG */
8470935Sjulian/*
8570935Sjulian * DEAD versions of the structures.
8670935Sjulian * In order to avoid races, it is sometimes neccesary to point
8770935Sjulian * at SOMETHING even though theoretically, the current entity is
8870935Sjulian * INVALID. Use these to avoid these races.
8970935Sjulian */
9070935Sjulianstruct ng_type ng_deadtype = {
9170935Sjulian	NG_ABI_VERSION,
9270935Sjulian	"dead",
9370935Sjulian	NULL,	/* modevent */
9470935Sjulian	NULL,	/* constructor */
9570935Sjulian	NULL,	/* rcvmsg */
9670935Sjulian	NULL,	/* shutdown */
9770935Sjulian	NULL,	/* newhook */
9870935Sjulian	NULL,	/* findhook */
9970935Sjulian	NULL,	/* connect */
10070935Sjulian	NULL,	/* rcvdata */
10170935Sjulian	NULL,	/* disconnect */
10270935Sjulian	NULL, 	/* cmdlist */
10370935Sjulian};
10470784Sjulian
10570935Sjulianstruct ng_node ng_deadnode = {
10670935Sjulian	"dead",
10770935Sjulian	&ng_deadtype,
10870935Sjulian	NG_INVALID,
10970935Sjulian	1,	/* refs */
11070935Sjulian	0,	/* numhooks */
11170935Sjulian	NULL,	/* private */
11270935Sjulian	0,	/* ID */
11370935Sjulian	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
11470935Sjulian	{},	/* all_nodes list entry */
11570935Sjulian	{},	/* id hashtable list entry */
11670935Sjulian	{},	/* workqueue entry */
11770935Sjulian	{	0,
11870935Sjulian		{}, /* should never use! (should hang) */
11970935Sjulian		NULL,
12070935Sjulian		&ng_deadnode.nd_input_queue.queue,
12170935Sjulian		&ng_deadnode
12270935Sjulian	},
12370935Sjulian#ifdef	NETGRAPH_DEBUG
12470935Sjulian	ND_MAGIC,
12570935Sjulian	__FILE__,
12670935Sjulian	__LINE__,
12770935Sjulian	{NULL}
12870935Sjulian#endif	/* NETGRAPH_DEBUG */
12970935Sjulian};
13070935Sjulian
13170935Sjulianstruct ng_hook ng_deadhook = {
13270935Sjulian	"dead",
13370935Sjulian	NULL,		/* private */
13470935Sjulian	HK_INVALID | HK_DEAD,
13570935Sjulian	1,		/* refs always >= 1 */
13670935Sjulian	&ng_deadhook,	/* Peer is self */
13770935Sjulian	&ng_deadnode,	/* attached to deadnode */
13870935Sjulian	{},		/* hooks list */
13970935Sjulian#ifdef	NETGRAPH_DEBUG
14070935Sjulian	HK_MAGIC,
14170935Sjulian	__FILE__,
14270935Sjulian	__LINE__,
14370935Sjulian	{NULL}
14470935Sjulian#endif	/* NETGRAPH_DEBUG */
14570935Sjulian};
14670935Sjulian
14770935Sjulian/*
14870935Sjulian * END DEAD STRUCTURES
14970935Sjulian */
15070700Sjulian/* List nodes with unallocated work */
15170700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
15270700Sjulianstatic struct mtx	ng_worklist_mtx;
15370700Sjulian
15452419Sjulian/* List of installed types */
15570700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
15670700Sjulianstatic struct mtx	ng_typelist_mtx;
15752419Sjulian
15870700Sjulian/* Hash related definitions */
15971354Sjulian/* XXX Don't need to initialise them because it's a LIST */
16071354Sjulian#define NG_ID_HASH_SIZE 32 /* most systems wont need even this many */
16171354Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
16270700Sjulianstatic struct mtx	ng_idhash_mtx;
16371354Sjulian/* Method to find a node.. used twice so do it here */
16471354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
16571354Sjulian#define NG_IDHASH_FIND(ID, node)					\
16671354Sjulian	do { 								\
16771354Sjulian		LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)],	\
16871354Sjulian						nd_idnodes) {		\
16971354Sjulian			if (NG_NODE_IS_VALID(node)			\
17071354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
17171354Sjulian				break;					\
17271354Sjulian			}						\
17371354Sjulian		}							\
17471354Sjulian	} while (0)
17552722Sjulian
17670700Sjulian/* Mutex that protects the free queue item list */
17770700Sjulianstatic volatile item_p		ngqfree;	/* free ones */
17870700Sjulianstatic struct mtx	ngq_mtx;
17970700Sjulian
18052419Sjulian/* Internal functions */
18152419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
18270700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
18352722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
18452419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
18570700Sjulianstatic void	ng_worklist_remove(node_p node);
18652419Sjulianstatic void	ngintr(void);
18770700Sjulianstatic int	ng_apply_item(node_p node, item_p item);
18870700Sjulianstatic void	ng_flush_input_queue(struct ng_queue * ngq);
18970700Sjulianstatic void	ng_setisr(node_p node);
19070700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
19171047Sjulianstatic int	ng_con_nodes(node_p node, const char *name, node_p node2,
19271047Sjulian							const char *name2);
19371047Sjulianstatic int	ng_con_part2(node_p node, hook_p hook, void *arg1, int arg2);
19471047Sjulianstatic int	ng_con_part3(node_p node, hook_p hook, void *arg1, int arg2);
19571047Sjulianstatic int	ng_mkpeer(node_p node, const char *name,
19671047Sjulian						const char *name2, char *type);
19752419Sjulian
19870784Sjulian/* imported , these used to be externally visible, some may go back */
19970700Sjulianint	ng_bypass(hook_p hook1, hook_p hook2);
20070700Sjulianvoid	ng_destroy_hook(hook_p hook);
20170700Sjuliannode_p	ng_name2noderef(node_p node, const char *name);
20270700Sjulianint	ng_path2noderef(node_p here, const char *path,
20370700Sjulian	node_p *dest, hook_p *lasthook);
20470700Sjulianstruct	ng_type *ng_findtype(const char *type);
20570700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
20670700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
20770700Sjulianvoid	ng_rmnode(node_p node);
20870784Sjulianvoid	ng_unname(node_p node);
20970700Sjulian
21070700Sjulian
21152419Sjulian/* Our own netgraph malloc type */
21252419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
21370700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
21470700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
21570700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
21670700SjulianMALLOC_DEFINE(M_NETGRAPH_META, "netgraph_meta", "netgraph name storage");
21770700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
21852419Sjulian
21970700Sjulian/* Should not be visible outside this file */
22070784Sjulian
22170784Sjulian#define _NG_ALLOC_HOOK(hook) \
22270784Sjulian	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
22370784Sjulian#define _NG_ALLOC_NODE(node) \
22470784Sjulian	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
22570784Sjulian
22670784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
22770784Sjulian/*
22870784Sjulian * In debug mode:
22970784Sjulian * In an attempt to help track reference count screwups
23070784Sjulian * we do not free objects back to the malloc system, but keep them
23170784Sjulian * in a local cache where we can examine them and keep information safely
23270784Sjulian * after they have been freed.
23370784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
23470784Sjulian */
23570784Sjulianstatic __inline hook_p
23670784Sjulianng_alloc_hook(void)
23770784Sjulian{
23870784Sjulian	hook_p hook;
23970784Sjulian	SLIST_ENTRY(ng_hook) temp;
24070784Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
24170784Sjulian	hook = LIST_FIRST(&ng_freehooks);
24270784Sjulian	if (hook) {
24370784Sjulian		LIST_REMOVE(hook, hk_hooks);
24470784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
24570784Sjulian		bzero(hook, sizeof(struct ng_hook));
24670784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
24770784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
24870784Sjulian		hook->hk_magic = HK_MAGIC;
24970784Sjulian	} else {
25070784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
25170784Sjulian		_NG_ALLOC_HOOK(hook);
25270784Sjulian		if (hook) {
25370784Sjulian			hook->hk_magic = HK_MAGIC;
25470784Sjulian			mtx_enter(&ng_nodelist_mtx, MTX_DEF);
25570784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
25670784Sjulian			mtx_exit(&ng_nodelist_mtx, MTX_DEF);
25770784Sjulian		}
25870784Sjulian	}
25970784Sjulian	return (hook);
26070784Sjulian}
26170784Sjulian
26270784Sjulianstatic __inline node_p
26370784Sjulianng_alloc_node(void)
26470784Sjulian{
26570784Sjulian	node_p node;
26670784Sjulian	SLIST_ENTRY(ng_node) temp;
26770784Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
26870784Sjulian	node = LIST_FIRST(&ng_freenodes);
26970784Sjulian	if (node) {
27070784Sjulian		LIST_REMOVE(node, nd_nodes);
27170784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
27270784Sjulian		bzero(node, sizeof(struct ng_node));
27370784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
27470784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
27570784Sjulian		node->nd_magic = ND_MAGIC;
27670784Sjulian	} else {
27770784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
27870784Sjulian		_NG_ALLOC_NODE(node);
27970784Sjulian		if (node) {
28070784Sjulian			node->nd_magic = ND_MAGIC;
28170784Sjulian			mtx_enter(&ng_nodelist_mtx, MTX_DEF);
28270784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
28370784Sjulian			mtx_exit(&ng_nodelist_mtx, MTX_DEF);
28470784Sjulian		}
28570784Sjulian	}
28670784Sjulian	return (node);
28770784Sjulian}
28870784Sjulian
28970784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
29070784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
29170784Sjulian
29270784Sjulian
29370784Sjulian#define NG_FREE_HOOK(hook)						\
29470784Sjulian	do {								\
29570784Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);			\
29670784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
29770784Sjulian		hook->hk_magic = 0;					\
29870784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);			\
29970784Sjulian	} while (0)
30070784Sjulian
30170784Sjulian#define NG_FREE_NODE(node)						\
30270784Sjulian	do {								\
30370784Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);			\
30470784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
30570784Sjulian		node->nd_magic = 0;					\
30670784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);			\
30770784Sjulian	} while (0)
30870784Sjulian
30970784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
31070784Sjulian
31170784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
31270784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
31370784Sjulian
31470700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
31570700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
31670784Sjulian
31770784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
31870784Sjulian
31970700Sjulian/* Warning: Generally use NG_FREE_ITEM() instead */
32070700Sjulian#define NG_FREE_ITEM_REAL(item) do { FREE((item), M_NETGRAPH_ITEM); } while (0)
32170700Sjulian
32270700Sjulian
32352419Sjulian/* Set this to Debugger("X") to catch all errors as they occur */
32452419Sjulian#ifndef TRAP_ERROR
32571047Sjulian#define TRAP_ERROR()
32652419Sjulian#endif
32752419Sjulian
32852722Sjulianstatic	ng_ID_t nextID = 1;
32952722Sjulian
33053403Sarchie#ifdef INVARIANTS
33153403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
33253403Sarchie		struct mbuf *n;						\
33353403Sarchie		int total;						\
33453403Sarchie									\
33553403Sarchie		if (((m)->m_flags & M_PKTHDR) == 0)			\
33653403Sarchie			panic("%s: !PKTHDR", __FUNCTION__);		\
33753403Sarchie		for (total = 0, n = (m); n != NULL; n = n->m_next)	\
33853403Sarchie			total += n->m_len;				\
33953403Sarchie		if ((m)->m_pkthdr.len != total) {			\
34053403Sarchie			panic("%s: %d != %d",				\
34153403Sarchie			    __FUNCTION__, (m)->m_pkthdr.len, total);	\
34253403Sarchie		}							\
34353403Sarchie	} while (0)
34453403Sarchie#else
34553403Sarchie#define CHECK_DATA_MBUF(m)
34653403Sarchie#endif
34752722Sjulian
34853403Sarchie
34952419Sjulian/************************************************************************
35053913Sarchie	Parse type definitions for generic messages
35153913Sarchie************************************************************************/
35253913Sarchie
35353913Sarchie/* Handy structure parse type defining macro */
35453913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
35553913Sarchiestatic const struct ng_parse_struct_info				\
35653913Sarchie	ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args;	\
35753913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
35853913Sarchie	&ng_parse_struct_type,						\
35953913Sarchie	&ng_ ## lo ## _type_info					\
36053913Sarchie}
36153913Sarchie
36253913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
36353913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
36453913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
36553913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
36653913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
36753913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
36853913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
36953913Sarchie
37053913Sarchie/* Get length of an array when the length is stored as a 32 bit
37153913Sarchie   value immediately preceeding the array -- as with struct namelist
37253913Sarchie   and struct typelist. */
37353913Sarchiestatic int
37453913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
37553913Sarchie	const u_char *start, const u_char *buf)
37653913Sarchie{
37753913Sarchie	return *((const u_int32_t *)(buf - 4));
37853913Sarchie}
37953913Sarchie
38053913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
38153913Sarchiestatic int
38253913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
38353913Sarchie	const u_char *start, const u_char *buf)
38453913Sarchie{
38553913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
38653913Sarchie
38753913Sarchie	return hl->nodeinfo.hooks;
38853913Sarchie}
38953913Sarchie
39053913Sarchie/* Array type for a variable length array of struct namelist */
39153913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
39253913Sarchie	&ng_generic_nodeinfo_type,
39353913Sarchie	&ng_generic_list_getLength
39453913Sarchie};
39553913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
39653913Sarchie	&ng_parse_array_type,
39753913Sarchie	&ng_nodeinfoarray_type_info
39853913Sarchie};
39953913Sarchie
40053913Sarchie/* Array type for a variable length array of struct typelist */
40153913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
40253913Sarchie	&ng_generic_typeinfo_type,
40353913Sarchie	&ng_generic_list_getLength
40453913Sarchie};
40553913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
40653913Sarchie	&ng_parse_array_type,
40753913Sarchie	&ng_typeinfoarray_type_info
40853913Sarchie};
40953913Sarchie
41053913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
41153913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
41253913Sarchie	&ng_generic_linkinfo_type,
41353913Sarchie	&ng_generic_linkinfo_getLength
41453913Sarchie};
41553913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
41653913Sarchie	&ng_parse_array_type,
41753913Sarchie	&ng_generic_linkinfo_array_type_info
41853913Sarchie};
41953913Sarchie
42053913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
42153913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
42253913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
42353913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
42453913Sarchie	(&ng_generic_nodeinfoarray_type));
42553913Sarchie
42653913Sarchie/* List of commands and how to convert arguments to/from ASCII */
42753913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
42853913Sarchie	{
42953913Sarchie	  NGM_GENERIC_COOKIE,
43053913Sarchie	  NGM_SHUTDOWN,
43153913Sarchie	  "shutdown",
43253913Sarchie	  NULL,
43353913Sarchie	  NULL
43453913Sarchie	},
43553913Sarchie	{
43653913Sarchie	  NGM_GENERIC_COOKIE,
43753913Sarchie	  NGM_MKPEER,
43853913Sarchie	  "mkpeer",
43953913Sarchie	  &ng_generic_mkpeer_type,
44053913Sarchie	  NULL
44153913Sarchie	},
44253913Sarchie	{
44353913Sarchie	  NGM_GENERIC_COOKIE,
44453913Sarchie	  NGM_CONNECT,
44553913Sarchie	  "connect",
44653913Sarchie	  &ng_generic_connect_type,
44753913Sarchie	  NULL
44853913Sarchie	},
44953913Sarchie	{
45053913Sarchie	  NGM_GENERIC_COOKIE,
45153913Sarchie	  NGM_NAME,
45253913Sarchie	  "name",
45353913Sarchie	  &ng_generic_name_type,
45453913Sarchie	  NULL
45553913Sarchie	},
45653913Sarchie	{
45753913Sarchie	  NGM_GENERIC_COOKIE,
45853913Sarchie	  NGM_RMHOOK,
45953913Sarchie	  "rmhook",
46053913Sarchie	  &ng_generic_rmhook_type,
46153913Sarchie	  NULL
46253913Sarchie	},
46353913Sarchie	{
46453913Sarchie	  NGM_GENERIC_COOKIE,
46553913Sarchie	  NGM_NODEINFO,
46653913Sarchie	  "nodeinfo",
46753913Sarchie	  NULL,
46853913Sarchie	  &ng_generic_nodeinfo_type
46953913Sarchie	},
47053913Sarchie	{
47153913Sarchie	  NGM_GENERIC_COOKIE,
47253913Sarchie	  NGM_LISTHOOKS,
47353913Sarchie	  "listhooks",
47453913Sarchie	  NULL,
47553913Sarchie	  &ng_generic_hooklist_type
47653913Sarchie	},
47753913Sarchie	{
47853913Sarchie	  NGM_GENERIC_COOKIE,
47953913Sarchie	  NGM_LISTNAMES,
48053913Sarchie	  "listnames",
48153913Sarchie	  NULL,
48253913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
48353913Sarchie	},
48453913Sarchie	{
48553913Sarchie	  NGM_GENERIC_COOKIE,
48653913Sarchie	  NGM_LISTNODES,
48753913Sarchie	  "listnodes",
48853913Sarchie	  NULL,
48953913Sarchie	  &ng_generic_listnodes_type
49053913Sarchie	},
49153913Sarchie	{
49253913Sarchie	  NGM_GENERIC_COOKIE,
49353913Sarchie	  NGM_LISTTYPES,
49453913Sarchie	  "listtypes",
49553913Sarchie	  NULL,
49653913Sarchie	  &ng_generic_typeinfo_type
49753913Sarchie	},
49853913Sarchie	{
49953913Sarchie	  NGM_GENERIC_COOKIE,
50062471Sphk	  NGM_TEXT_CONFIG,
50162471Sphk	  "textconfig",
50262471Sphk	  NULL,
50362471Sphk	  &ng_parse_string_type
50462471Sphk	},
50562471Sphk	{
50662471Sphk	  NGM_GENERIC_COOKIE,
50753913Sarchie	  NGM_TEXT_STATUS,
50853913Sarchie	  "textstatus",
50953913Sarchie	  NULL,
51053913Sarchie	  &ng_parse_string_type
51153913Sarchie	},
51253913Sarchie	{
51353913Sarchie	  NGM_GENERIC_COOKIE,
51453913Sarchie	  NGM_ASCII2BINARY,
51553913Sarchie	  "ascii2binary",
51653913Sarchie	  &ng_parse_ng_mesg_type,
51753913Sarchie	  &ng_parse_ng_mesg_type
51853913Sarchie	},
51953913Sarchie	{
52053913Sarchie	  NGM_GENERIC_COOKIE,
52153913Sarchie	  NGM_BINARY2ASCII,
52253913Sarchie	  "binary2ascii",
52353913Sarchie	  &ng_parse_ng_mesg_type,
52453913Sarchie	  &ng_parse_ng_mesg_type
52553913Sarchie	},
52653913Sarchie	{ 0 }
52753913Sarchie};
52853913Sarchie
52953913Sarchie/************************************************************************
53052419Sjulian			Node routines
53152419Sjulian************************************************************************/
53252419Sjulian
53352419Sjulian/*
53452419Sjulian * Instantiate a node of the requested type
53552419Sjulian */
53652419Sjulianint
53752419Sjulianng_make_node(const char *typename, node_p *nodepp)
53852419Sjulian{
53952419Sjulian	struct ng_type *type;
54070700Sjulian	int	error;
54152419Sjulian
54252419Sjulian	/* Check that the type makes sense */
54352419Sjulian	if (typename == NULL) {
54471047Sjulian		TRAP_ERROR();
54552419Sjulian		return (EINVAL);
54652419Sjulian	}
54752419Sjulian
54852419Sjulian	/* Locate the node type */
54952419Sjulian	if ((type = ng_findtype(typename)) == NULL) {
55059875Speter		char filename[NG_TYPELEN + 4];
55152419Sjulian		linker_file_t lf;
55252419Sjulian		int error;
55352419Sjulian
55452419Sjulian		/* Not found, try to load it as a loadable module */
55569923Sjulian		snprintf(filename, sizeof(filename), "ng_%s", typename);
55659875Speter		error = linker_load_file(filename, &lf);
55752419Sjulian		if (error != 0)
55852419Sjulian			return (error);
55952419Sjulian		lf->userrefs++;		/* pretend loaded by the syscall */
56052419Sjulian
56152419Sjulian		/* Try again, as now the type should have linked itself in */
56252419Sjulian		if ((type = ng_findtype(typename)) == NULL)
56352419Sjulian			return (ENXIO);
56452419Sjulian	}
56552419Sjulian
56670700Sjulian	/*
56770700Sjulian	 * If we have a constructor, then make the node and
56870700Sjulian	 * call the constructor to do type specific initialisation.
56970700Sjulian	 */
57070700Sjulian	if (type->constructor != NULL) {
57170700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
57270700Sjulian			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
57370784Sjulian				NG_NODE_UNREF(*nodepp);
57470700Sjulian			}
57570700Sjulian		}
57670700Sjulian	} else {
57770700Sjulian		/*
57870700Sjulian		 * Node has no constructor. We cannot ask for one
57970700Sjulian		 * to be made. It must be brought into existance by
58070935Sjulian		 * some external agency. The external agency should
58170700Sjulian		 * call ng_make_node_common() directly to get the
58270700Sjulian		 * netgraph part initialised.
58370700Sjulian		 */
58471047Sjulian		TRAP_ERROR();
58570700Sjulian		error = EINVAL;
58670700Sjulian	}
58770700Sjulian	return (error);
58852419Sjulian}
58952419Sjulian
59052419Sjulian/*
59170700Sjulian * Generic node creation. Called by node initialisation for externally
59270700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
59352419Sjulian * The returned node has a reference count of 1.
59452419Sjulian */
59552419Sjulianint
59652419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
59752419Sjulian{
59852419Sjulian	node_p node;
59952419Sjulian
60052419Sjulian	/* Require the node type to have been already installed */
60152419Sjulian	if (ng_findtype(type->name) == NULL) {
60271047Sjulian		TRAP_ERROR();
60352419Sjulian		return (EINVAL);
60452419Sjulian	}
60552419Sjulian
60652419Sjulian	/* Make a node and try attach it to the type */
60770784Sjulian	NG_ALLOC_NODE(node);
60852419Sjulian	if (node == NULL) {
60971047Sjulian		TRAP_ERROR();
61052419Sjulian		return (ENOMEM);
61152419Sjulian	}
61270784Sjulian	node->nd_type = type;
61370784Sjulian	NG_NODE_REF(node);				/* note reference */
61452419Sjulian	type->refs++;
61552419Sjulian
61671380Sjulian	mtx_init(&node->nd_input_queue.q_mtx, "netgraph node mutex", MTX_SPIN);
61770784Sjulian	node->nd_input_queue.queue = NULL;
61870784Sjulian	node->nd_input_queue.last = &node->nd_input_queue.queue;
61970784Sjulian	node->nd_input_queue.q_flags = 0;
62070784Sjulian	node->nd_input_queue.q_node = node;
62152419Sjulian
62252419Sjulian	/* Initialize hook list for new node */
62370784Sjulian	LIST_INIT(&node->nd_hooks);
62452419Sjulian
62570700Sjulian	/* Link us into the node linked list */
62670700Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
62770784Sjulian	LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes);
62870700Sjulian	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
62970700Sjulian
63070700Sjulian
63152722Sjulian	/* get an ID and put us in the hash chain */
63270700Sjulian	mtx_enter(&ng_idhash_mtx, MTX_DEF);
63370784Sjulian	for (;;) { /* wrap protection, even if silly */
63470700Sjulian		node_p node2 = NULL;
63570784Sjulian		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
63671354Sjulian
63770784Sjulian		/* Is there a problem with the new number? */
63871354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
63971354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
64070784Sjulian			break;
64170700Sjulian		}
64270784Sjulian	}
64371354Sjulian	LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
64470784Sjulian							node, nd_idnodes);
64570700Sjulian	mtx_exit(&ng_idhash_mtx, MTX_DEF);
64652722Sjulian
64752419Sjulian	/* Done */
64852419Sjulian	*nodepp = node;
64952419Sjulian	return (0);
65052419Sjulian}
65152419Sjulian
65252419Sjulian/*
65352419Sjulian * Forceably start the shutdown process on a node. Either call
65452419Sjulian * it's shutdown method, or do the default shutdown if there is
65552419Sjulian * no type-specific method.
65652419Sjulian *
65770700Sjulian * We can only be called form a shutdown message, so we know we have
65870939Sjulian * a writer lock, and therefore exclusive access. It also means
65970939Sjulian * that we should not be on the work queue, but we check anyhow.
66070700Sjulian *
66170700Sjulian * Persistent node types must have a type-specific method which
66270939Sjulian * Allocates a new node in which case, this one is irretrievably going away,
66370939Sjulian * or cleans up anything it needs, and just makes the node valid again,
66470939Sjulian * in which case we allow the node to survive.
66570939Sjulian *
66670939Sjulian * XXX We need to think of how to tell a persistant node that we
66770939Sjulian * REALLY need to go away because the hardware has gone or we
66870939Sjulian * are rebooting.... etc.
66952419Sjulian */
67052419Sjulianvoid
67152419Sjulianng_rmnode(node_p node)
67252419Sjulian{
67370939Sjulian	hook_p hook;
67470939Sjulian
67552419Sjulian	/* Check if it's already shutting down */
67670784Sjulian	if ((node->nd_flags & NG_CLOSING) != 0)
67752419Sjulian		return;
67852419Sjulian
67952419Sjulian	/* Add an extra reference so it doesn't go away during this */
68070784Sjulian	NG_NODE_REF(node);
68152419Sjulian
68270784Sjulian	/*
68370784Sjulian	 * Mark it invalid so any newcomers know not to try use it
68470784Sjulian	 * Also add our own mark so we can't recurse
68570784Sjulian	 * note that NG_INVALID does not do this as it's also set during
68670784Sjulian	 * creation
68770784Sjulian	 */
68870784Sjulian	node->nd_flags |= NG_INVALID|NG_CLOSING;
68952419Sjulian
69070939Sjulian	/* Notify all remaining connected nodes to disconnect */
69170939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
69270939Sjulian		ng_destroy_hook(hook);
69370784Sjulian
69470700Sjulian	/*
69570700Sjulian	 * Drain the input queue forceably.
69670784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
69770784Sjulian	 * Theoretically we came here from a queue entry that was added
69870784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
69970700Sjulian	 */
70070784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
70170700Sjulian
70270700Sjulian	/*
70370700Sjulian	 * Take us off the work queue if we are there.
70470784Sjulian	 * We definatly have no work to be done.
70570700Sjulian	 */
70670700Sjulian	ng_worklist_remove(node);
70770700Sjulian
70852419Sjulian	/* Ask the type if it has anything to do in this case */
70970784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
71070784Sjulian		(*node->nd_type->shutdown)(node);
71170700Sjulian	} else {				/* do the default thing */
71270784Sjulian		NG_NODE_UNREF(node);
71352419Sjulian	}
71470784Sjulian	if (NG_NODE_IS_VALID(node)) {
71570784Sjulian		/*
71670784Sjulian		 * Well, blow me down if the node code hasn't declared
71770784Sjulian		 * that it doesn't want to die.
71870784Sjulian		 * Presumably it is a persistant node.
71970784Sjulian		 * XXX we need a way to tell the node
72070784Sjulian		 * "No, really.. the hardware's going away.. REALLY die"
72170784Sjulian		 * We need a way
72270784Sjulian		 */
72370784Sjulian		return;
72470784Sjulian	}
72552419Sjulian
72670784Sjulian	ng_unname(node); /* basically a NOP these days */
72770784Sjulian
72870784Sjulian	/*
72970784Sjulian	 * Remove extra reference, possibly the last
73070784Sjulian	 * Possible other holders of references may include
73170784Sjulian	 * timeout callouts, but theoretically the node's supposed to
73270784Sjulian	 * have cancelled them. Possibly hardware dependencies may
73370784Sjulian	 * force a driver to 'linger' with a reference.
73470784Sjulian	 */
73570784Sjulian	NG_NODE_UNREF(node);
73652419Sjulian}
73752419Sjulian
73852419Sjulian/*
73952419Sjulian * Remove a reference to the node, possibly the last
74052419Sjulian */
74152419Sjulianvoid
74270784Sjulianng_unref_node(node_p node)
74352419Sjulian{
74470784Sjulian	int     v;
74571047Sjulian
74671047Sjulian	if (node == &ng_deadnode) {
74771047Sjulian		return;
74871047Sjulian	}
74971047Sjulian
75070784Sjulian	do {
75170784Sjulian		v = node->nd_refs;
75270784Sjulian	} while (! atomic_cmpset_int(&node->nd_refs, v, v - 1));
75369519Sjulian
75470784Sjulian	if (v == 1) { /* we were the last */
75570700Sjulian
75670700Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
75770784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
75870784Sjulian		LIST_REMOVE(node, nd_nodes);
75970700Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
76070700Sjulian
76170700Sjulian		mtx_enter(&ng_idhash_mtx, MTX_DEF);
76270784Sjulian		LIST_REMOVE(node, nd_idnodes);
76370700Sjulian		mtx_exit(&ng_idhash_mtx, MTX_DEF);
76452419Sjulian
76570791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
76670700Sjulian		NG_FREE_NODE(node);
76752419Sjulian	}
76852419Sjulian}
76952419Sjulian
77052419Sjulian/************************************************************************
77152722Sjulian			Node ID handling
77252722Sjulian************************************************************************/
77352722Sjulianstatic node_p
77470700Sjulianng_ID2noderef(ng_ID_t ID)
77552722Sjulian{
77670784Sjulian	node_p node;
77770700Sjulian	mtx_enter(&ng_idhash_mtx, MTX_DEF);
77871354Sjulian	NG_IDHASH_FIND(ID, node);
77970784Sjulian	if(node)
78070784Sjulian		NG_NODE_REF(node);
78170700Sjulian	mtx_exit(&ng_idhash_mtx, MTX_DEF);
78270784Sjulian	return(node);
78352722Sjulian}
78452722Sjulian
78552722Sjulianng_ID_t
78652722Sjulianng_node2ID(node_p node)
78752722Sjulian{
78870912Sjulian	return (node ? NG_NODE_ID(node) : 0);
78952722Sjulian}
79052722Sjulian
79152722Sjulian/************************************************************************
79252419Sjulian			Node name handling
79352419Sjulian************************************************************************/
79452419Sjulian
79552419Sjulian/*
79652419Sjulian * Assign a node a name. Once assigned, the name cannot be changed.
79752419Sjulian */
79852419Sjulianint
79952419Sjulianng_name_node(node_p node, const char *name)
80052419Sjulian{
80152419Sjulian	int i;
80270700Sjulian	node_p node2;
80352419Sjulian
80452419Sjulian	/* Check the name is valid */
80552419Sjulian	for (i = 0; i < NG_NODELEN + 1; i++) {
80652419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
80752419Sjulian			break;
80852419Sjulian	}
80952419Sjulian	if (i == 0 || name[i] != '\0') {
81071047Sjulian		TRAP_ERROR();
81152419Sjulian		return (EINVAL);
81252419Sjulian	}
81352722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
81471047Sjulian		TRAP_ERROR();
81552419Sjulian		return (EINVAL);
81652419Sjulian	}
81752419Sjulian
81852419Sjulian	/* Check the name isn't already being used */
81970700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
82070784Sjulian		NG_NODE_UNREF(node2);
82171047Sjulian		TRAP_ERROR();
82252419Sjulian		return (EADDRINUSE);
82352419Sjulian	}
82452419Sjulian
82570700Sjulian	/* copy it */
82670784Sjulian	strncpy(NG_NODE_NAME(node), name, NG_NODELEN);
82752419Sjulian
82852419Sjulian	return (0);
82952419Sjulian}
83052419Sjulian
83152419Sjulian/*
83252419Sjulian * Find a node by absolute name. The name should NOT end with ':'
83352419Sjulian * The name "." means "this node" and "[xxx]" means "the node
83452419Sjulian * with ID (ie, at address) xxx".
83552419Sjulian *
83652419Sjulian * Returns the node if found, else NULL.
83770700Sjulian * Eventually should add something faster than a sequential search.
83870784Sjulian * Note it aquires a reference on the node so you can be sure it's still there.
83952419Sjulian */
84052419Sjuliannode_p
84170700Sjulianng_name2noderef(node_p here, const char *name)
84252419Sjulian{
84352722Sjulian	node_p node;
84452722Sjulian	ng_ID_t temp;
84552419Sjulian
84652419Sjulian	/* "." means "this node" */
84770700Sjulian	if (strcmp(name, ".") == 0) {
84870784Sjulian		NG_NODE_REF(here);
84970700Sjulian		return(here);
85070700Sjulian	}
85152419Sjulian
85252419Sjulian	/* Check for name-by-ID */
85352722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
85470700Sjulian		return (ng_ID2noderef(temp));
85552419Sjulian	}
85652419Sjulian
85752419Sjulian	/* Find node by name */
85870700Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
85970784Sjulian	LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
86070912Sjulian		if (NG_NODE_IS_VALID(node)
86170912Sjulian		&& NG_NODE_HAS_NAME(node)
86270912Sjulian		&& (strcmp(NG_NODE_NAME(node), name) == 0)) {
86352419Sjulian			break;
86470912Sjulian		}
86552419Sjulian	}
86670700Sjulian	if (node)
86770784Sjulian		NG_NODE_REF(node);
86870700Sjulian	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
86952419Sjulian	return (node);
87052419Sjulian}
87152419Sjulian
87252419Sjulian/*
87352722Sjulian * Decode a ID name, eg. "[f03034de]". Returns 0 if the
87452722Sjulian * string is not valid, otherwise returns the value.
87552419Sjulian */
87652722Sjulianstatic ng_ID_t
87752419Sjulianng_decodeidname(const char *name)
87852419Sjulian{
87952816Sarchie	const int len = strlen(name);
88053648Sarchie	char *eptr;
88152816Sarchie	u_long val;
88252419Sjulian
88352816Sarchie	/* Check for proper length, brackets, no leading junk */
88470912Sjulian	if ((len < 3)
88570912Sjulian	|| (name[0] != '[')
88670912Sjulian	|| (name[len - 1] != ']')
88770912Sjulian	|| (!isxdigit(name[1]))) {
88870912Sjulian		return ((ng_ID_t)0);
88970912Sjulian	}
89052419Sjulian
89152816Sarchie	/* Decode number */
89252816Sarchie	val = strtoul(name + 1, &eptr, 16);
89370912Sjulian	if ((eptr - name != len - 1)
89470912Sjulian	|| (val == ULONG_MAX)
89570912Sjulian	|| (val == 0)) {
89653042Sjulian		return ((ng_ID_t)0);
89770912Sjulian	}
89852816Sarchie	return (ng_ID_t)val;
89952419Sjulian}
90052419Sjulian
90152419Sjulian/*
90252419Sjulian * Remove a name from a node. This should only be called
90352419Sjulian * when shutting down and removing the node.
90452419Sjulian */
90552419Sjulianvoid
90652419Sjulianng_unname(node_p node)
90752419Sjulian{
90870784Sjulian	bzero(NG_NODE_NAME(node), NG_NODELEN);
90952419Sjulian}
91052419Sjulian
91152419Sjulian/************************************************************************
91252419Sjulian			Hook routines
91352419Sjulian Names are not optional. Hooks are always connected, except for a
91470939Sjulian brief moment within these routines. On invalidation or during creation
91570939Sjulian they are connected to the 'dead' hook.
91652419Sjulian************************************************************************/
91752419Sjulian
91852419Sjulian/*
91952419Sjulian * Remove a hook reference
92052419Sjulian */
92170784Sjulianvoid
92252419Sjulianng_unref_hook(hook_p hook)
92352419Sjulian{
92470784Sjulian	int     v;
92571047Sjulian
92671047Sjulian	if (hook == &ng_deadhook) {
92771047Sjulian		return;
92871047Sjulian	}
92970784Sjulian	do {
93070784Sjulian		v = hook->hk_refs;
93170784Sjulian	} while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1));
93269519Sjulian
93370784Sjulian	if (v == 1) { /* we were the last */
93471047Sjulian		if (_NG_HOOK_NODE(hook)) { /* it'll probably be ng_deadnode */
93571047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
93670784Sjulian			hook->hk_node = NULL;
93770700Sjulian		}
93870700Sjulian		NG_FREE_HOOK(hook);
93970700Sjulian	}
94052419Sjulian}
94152419Sjulian
94252419Sjulian/*
94352419Sjulian * Add an unconnected hook to a node. Only used internally.
94470939Sjulian * Assumes node is locked. (XXX not yet true )
94552419Sjulian */
94652419Sjulianstatic int
94752419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
94852419Sjulian{
94952419Sjulian	hook_p hook;
95052419Sjulian	int error = 0;
95152419Sjulian
95252419Sjulian	/* Check that the given name is good */
95352419Sjulian	if (name == NULL) {
95471047Sjulian		TRAP_ERROR();
95552419Sjulian		return (EINVAL);
95652419Sjulian	}
95754096Sarchie	if (ng_findhook(node, name) != NULL) {
95871047Sjulian		TRAP_ERROR();
95954096Sarchie		return (EEXIST);
96052419Sjulian	}
96152419Sjulian
96252419Sjulian	/* Allocate the hook and link it up */
96370784Sjulian	NG_ALLOC_HOOK(hook);
96452419Sjulian	if (hook == NULL) {
96571047Sjulian		TRAP_ERROR();
96652419Sjulian		return (ENOMEM);
96752419Sjulian	}
96870939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
96970784Sjulian	hook->hk_flags = HK_INVALID;
97070939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
97170784Sjulian	hook->hk_node = node;
97270784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
97352419Sjulian
97470939Sjulian	/* Set hook name */
97570939Sjulian	strncpy(NG_HOOK_NAME(hook), name, NG_HOOKLEN);
97670939Sjulian
97770939Sjulian	/*
97870939Sjulian	 * Check if the node type code has something to say about it
97970939Sjulian	 * If it fails, the unref of the hook will also unref the node.
98070939Sjulian	 */
98170935Sjulian	if (node->nd_type->newhook != NULL) {
98270935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
98370784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
98470700Sjulian			return (error);
98570700Sjulian		}
98670935Sjulian	}
98752419Sjulian	/*
98852419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
98952419Sjulian	 * We'll ask again later when we actually connect the hooks.
99052419Sjulian	 */
99170784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
99270784Sjulian	node->nd_numhooks++;
99370939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
99452419Sjulian
99552419Sjulian	if (hookp)
99652419Sjulian		*hookp = hook;
99770939Sjulian	return (0);
99852419Sjulian}
99952419Sjulian
100052419Sjulian/*
100154096Sarchie * Find a hook
100254096Sarchie *
100354096Sarchie * Node types may supply their own optimized routines for finding
100454096Sarchie * hooks.  If none is supplied, we just do a linear search.
100570939Sjulian * XXX Possibly we should add a reference to the hook?
100654096Sarchie */
100754096Sarchiehook_p
100854096Sarchieng_findhook(node_p node, const char *name)
100954096Sarchie{
101054096Sarchie	hook_p hook;
101154096Sarchie
101270784Sjulian	if (node->nd_type->findhook != NULL)
101370784Sjulian		return (*node->nd_type->findhook)(node, name);
101470784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
101570912Sjulian		if (NG_HOOK_IS_VALID(hook)
101670917Sarchie		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
101754096Sarchie			return (hook);
101854096Sarchie	}
101954096Sarchie	return (NULL);
102054096Sarchie}
102154096Sarchie
102254096Sarchie/*
102352419Sjulian * Destroy a hook
102452419Sjulian *
102552419Sjulian * As hooks are always attached, this really destroys two hooks.
102652419Sjulian * The one given, and the one attached to it. Disconnect the hooks
102770939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
102870939Sjulian * hook so that it can still exist after we depart. We then
102970939Sjulian * send the peer its own destroy message. This ensures that we only
103070939Sjulian * interact with the peer's structures when it is locked processing that
103170939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
103270939Sjulian * the peer hook and node are still going to exist until
103370939Sjulian * we are finished there as the hook holds a ref on the node.
103470939Sjulian * We run this same code again on the peer hook, but that time it is already
103570939Sjulian * attached to the 'dead' hook.
103671047Sjulian *
103771047Sjulian * This routine is called at all stages of hook creation
103871047Sjulian * on error detection and must be able to handle any such stage.
103952419Sjulian */
104052419Sjulianvoid
104152419Sjulianng_destroy_hook(hook_p hook)
104252419Sjulian{
104370784Sjulian	hook_p peer = NG_HOOK_PEER(hook);
104471047Sjulian	node_p node = NG_HOOK_NODE(hook);
104552419Sjulian
104671047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
104771047Sjulian		printf("ng_destroy_hook called on deadhook\n");
104871047Sjulian		return;
104971047Sjulian	}
105070784Sjulian	hook->hk_flags |= HK_INVALID;		/* as soon as possible */
105170939Sjulian	if (peer && (peer != &ng_deadhook)) {
105270939Sjulian		/*
105370939Sjulian		 * Set the peer to point to ng_deadhook
105470939Sjulian		 * from this moment on we are effectively independent it.
105570939Sjulian		 * send it an rmhook message of it's own.
105670939Sjulian		 */
105770939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
105870939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
105971047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
106071047Sjulian			/*
106171047Sjulian			 * If it's already divorced from a node,
106271047Sjulian			 * just free it.
106371047Sjulian			 */
106471047Sjulian			/* nothing */
106571047Sjulian		} else {
106671047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
106771047Sjulian		}
106870942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
106970942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
107052419Sjulian	}
107152419Sjulian
107252419Sjulian	/*
107352419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
107452419Sjulian	 * in case the disconnection results in node shutdown.
107552419Sjulian	 */
107671047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
107771047Sjulian		return;
107871047Sjulian	}
107970784Sjulian	LIST_REMOVE(hook, hk_hooks);
108070784Sjulian	node->nd_numhooks--;
108170784Sjulian	if (node->nd_type->disconnect) {
108252419Sjulian		/*
108371047Sjulian		 * The type handler may elect to destroy the node so don't
108471047Sjulian		 * trust its existance after this point. (except
108571047Sjulian		 * that we still hold a reference on it. (which we
108671047Sjulian		 * inherrited from the hook we are destroying)
108752419Sjulian		 */
108870784Sjulian		(*node->nd_type->disconnect) (hook);
108952419Sjulian	}
109071047Sjulian
109171047Sjulian	/*
109271047Sjulian	 * Note that because we will point to ng_deadnode, the original node
109371047Sjulian	 * is not decremented automatically so we do that manually.
109471047Sjulian	 */
109571047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
109671047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
109771047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
109852419Sjulian}
109952419Sjulian
110052419Sjulian/*
110152419Sjulian * Take two hooks on a node and merge the connection so that the given node
110252419Sjulian * is effectively bypassed.
110352419Sjulian */
110452419Sjulianint
110552419Sjulianng_bypass(hook_p hook1, hook_p hook2)
110652419Sjulian{
110770784Sjulian	if (hook1->hk_node != hook2->hk_node) {
110871047Sjulian		TRAP_ERROR();
110952419Sjulian		return (EINVAL);
111070784Sjulian	}
111170784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
111270784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
111352419Sjulian
111470939Sjulian	hook1->hk_peer = &ng_deadhook;
111570939Sjulian	hook2->hk_peer = &ng_deadhook;
111670939Sjulian
111752419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
111852419Sjulian	ng_destroy_hook(hook1);
111952419Sjulian	ng_destroy_hook(hook2);
112052419Sjulian	return (0);
112152419Sjulian}
112252419Sjulian
112352419Sjulian/*
112452419Sjulian * Install a new netgraph type
112552419Sjulian */
112652419Sjulianint
112752419Sjulianng_newtype(struct ng_type *tp)
112852419Sjulian{
112952419Sjulian	const size_t namelen = strlen(tp->name);
113052419Sjulian
113152419Sjulian	/* Check version and type name fields */
113270159Sjulian	if ((tp->version != NG_ABI_VERSION)
113370159Sjulian	|| (namelen == 0)
113470159Sjulian	|| (namelen > NG_TYPELEN)) {
113571047Sjulian		TRAP_ERROR();
113652419Sjulian		return (EINVAL);
113752419Sjulian	}
113852419Sjulian
113952419Sjulian	/* Check for name collision */
114052419Sjulian	if (ng_findtype(tp->name) != NULL) {
114171047Sjulian		TRAP_ERROR();
114252419Sjulian		return (EEXIST);
114352419Sjulian	}
114452419Sjulian
114570700Sjulian	tp->refs = 0;
114670700Sjulian
114752419Sjulian	/* Link in new type */
114870700Sjulian	mtx_enter(&ng_typelist_mtx, MTX_DEF);
114970700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
115070700Sjulian	mtx_exit(&ng_typelist_mtx, MTX_DEF);
115152419Sjulian	return (0);
115252419Sjulian}
115352419Sjulian
115452419Sjulian/*
115552419Sjulian * Look for a type of the name given
115652419Sjulian */
115752419Sjulianstruct ng_type *
115852419Sjulianng_findtype(const char *typename)
115952419Sjulian{
116052419Sjulian	struct ng_type *type;
116152419Sjulian
116270700Sjulian	mtx_enter(&ng_typelist_mtx, MTX_DEF);
116370700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
116452419Sjulian		if (strcmp(type->name, typename) == 0)
116552419Sjulian			break;
116652419Sjulian	}
116770700Sjulian	mtx_exit(&ng_typelist_mtx, MTX_DEF);
116852419Sjulian	return (type);
116952419Sjulian}
117052419Sjulian
117152419Sjulian/************************************************************************
117252419Sjulian			Composite routines
117352419Sjulian************************************************************************/
117452419Sjulian/*
117571047Sjulian * Connect two nodes using the specified hooks, using queued functions.
117652419Sjulian */
117771047Sjulianstatic int
117871047Sjulianng_con_part3(node_p node, hook_p hook, void *arg1, int arg2)
117952419Sjulian{
118071047Sjulian	int error = 0;
118152419Sjulian
118271047Sjulian	/*
118371047Sjulian	 * When we run, we know that the node 'node' is locked for us.
118471047Sjulian	 * Our caller has a reference on the hook.
118571047Sjulian	 * Our caller has a reference on the node.
118671047Sjulian	 * (In this case our caller is ng_apply_item() ).
118771047Sjulian	 * The peer hook has a reference on the hook.
118871047Sjulian	 */
118971047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
119071047Sjulian		/*
119171047Sjulian		 * The node must have been freed again since we last visited
119271047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
119371047Sjulian		 * We should just release our references and
119471047Sjulian		 * free anything we can think of.
119571047Sjulian		 * Since we know it's been destroyed, and it's our caller
119671047Sjulian		 * that holds the references, just return.
119771047Sjulian		 */
119871047Sjulian		return (0);
119952419Sjulian	}
120071047Sjulian	if (hook->hk_node->nd_type->connect) {
120171047Sjulian		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
120271047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
120371047Sjulian			return (error);
120471047Sjulian		}
120552419Sjulian	}
120671047Sjulian	/*
120771047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
120871047Sjulian	 * to separate out 'create' and 'invalid' flags.
120971047Sjulian	 * should only set flags on hooks we have locked under our node.
121071047Sjulian	 */
121171047Sjulian	hook->hk_flags &= ~HK_INVALID;
121271047Sjulian	return (error);
121371047Sjulian}
121452419Sjulian
121571047Sjulianstatic int
121671047Sjulianng_con_part2(node_p node, hook_p hook, void *arg1, int arg2)
121771047Sjulian{
121871047Sjulian	int error = 0;
121971047Sjulian
122052419Sjulian	/*
122171047Sjulian	 * When we run, we know that the node 'node' is locked for us.
122271047Sjulian	 * Our caller has a reference on the hook.
122371047Sjulian	 * Our caller has a reference on the node.
122471047Sjulian	 * (In this case our caller is ng_apply_item() ).
122571047Sjulian	 * The peer hook has a reference on the hook.
122671047Sjulian	 * our node pointer points to the 'dead' node.
122771047Sjulian	 * First check the hook name is unique.
122852419Sjulian	 */
122971047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
123071047Sjulian		TRAP_ERROR();
123171047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
123271047Sjulian		return (EEXIST);
123371047Sjulian	}
123470939Sjulian	/*
123571047Sjulian	 * Check if the node type code has something to say about it
123671047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
123771047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
123871047Sjulian	 * The peer hook will also be destroyed.
123970939Sjulian	 */
124071047Sjulian	if (node->nd_type->newhook != NULL) {
124171047Sjulian		if ((error =
124271047Sjulian		    (*node->nd_type->newhook)(node, hook, hook->hk_name))) {
124371047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
124471047Sjulian			return (error);
124571047Sjulian		}
124671047Sjulian	}
124771047Sjulian
124871047Sjulian	/*
124971047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
125071047Sjulian	 * We'll ask again later when we actually connect the hooks.
125171047Sjulian	 */
125271047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
125371047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
125471047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
125571047Sjulian	node->nd_numhooks++;
125671047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
125771047Sjulian
125871047Sjulian	/*
125971047Sjulian	 * We now have a symetrical situation, where both hooks have been
126071047Sjulian	 * linked to theur nodes, the newhook methods have been called
126171047Sjulian	 * And the references are all correct. The hooks are still marked
126271047Sjulian	 * as invalid, as we have not called the 'connect' methods
126371047Sjulian	 * yet.
126471047Sjulian	 * We can call the local one immediatly as we have the
126571047Sjulian	 * node locked, but we need to queue the remote one.
126671047Sjulian	 */
126771047Sjulian	if (hook->hk_node->nd_type->connect) {
126871047Sjulian		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
126971047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
127071047Sjulian			return (error);
127171047Sjulian		}
127271047Sjulian	}
127371047Sjulian	error = ng_send_fn(hook->hk_peer->hk_node, hook->hk_peer,
127471047Sjulian			&ng_con_part3, arg1, arg2);
127571047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
127652419Sjulian	return (error);
127752419Sjulian}
127852419Sjulian
127952419Sjulian/*
128071047Sjulian * Connect this node with another node. We assume that this node is
128171047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
128252419Sjulian */
128371047Sjulianstatic int
128452419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
128552419Sjulian{
128652419Sjulian	int     error;
128752419Sjulian	hook_p  hook;
128852419Sjulian	hook_p  hook2;
128952419Sjulian
129070939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
129152419Sjulian		return (error);
129271047Sjulian	/* Allocate the other hook and link it up */
129371047Sjulian	NG_ALLOC_HOOK(hook2);
129471047Sjulian	if (hook == NULL) {
129571047Sjulian		TRAP_ERROR();
129671047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
129771047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
129871047Sjulian		return (ENOMEM);
129971047Sjulian	}
130071047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
130171047Sjulian	hook2->hk_flags = HK_INVALID;
130271047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
130371047Sjulian	hook->hk_peer = hook2;
130471047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
130571047Sjulian	NG_HOOK_REF(hook2);
130671047Sjulian	hook2->hk_node = &ng_deadnode;
130771047Sjulian	strncpy(NG_HOOK_NAME(hook2), name2, NG_HOOKLEN);
130871047Sjulian
130971047Sjulian	/*
131071047Sjulian	 * Queue the function above.
131171047Sjulian	 * Procesing continues in that function in the lock context of
131271047Sjulian	 * the other node.
131371047Sjulian	 */
131471047Sjulian	error = ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0);
131571047Sjulian
131671047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
131771047Sjulian	NG_HOOK_UNREF(hook2);
131871047Sjulian	return (error);
131971047Sjulian}
132071047Sjulian
132171047Sjulian/*
132271047Sjulian * Make a peer and connect.
132371047Sjulian * We assume that the local node is locked.
132471047Sjulian * The new node probably doesn't need a lock until
132571047Sjulian * it has a hook, because it cannot really have any work until then,
132671047Sjulian * but we should think about it a bit more.
132771047Sjulian *
132871047Sjulian * The problem may come if the other node also fires up
132971047Sjulian * some hardware or a timer or some other source of activation,
133071047Sjulian * also it may already get a command msg via it's ID.
133171047Sjulian *
133271047Sjulian * We could use the same method as ng_con_nodes() but we'd have
133371047Sjulian * to add ability to remove the node when failing. (Not hard, just
133471047Sjulian * make arg1 point to the node to remove).
133571047Sjulian * Unless of course we just ignore failure to connect and leave
133671047Sjulian * an unconnected node?
133771047Sjulian */
133871047Sjulianstatic int
133971047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
134071047Sjulian{
134171047Sjulian	node_p  node2;
134271047Sjulian	hook_p  hook1;
134371047Sjulian	hook_p  hook2;
134471047Sjulian	int     error;
134571047Sjulian
134671047Sjulian	if ((error = ng_make_node(type, &node2))) {
134752419Sjulian		return (error);
134852419Sjulian	}
134970939Sjulian
135071047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
135171047Sjulian		ng_rmnode(node2);
135271047Sjulian		return (error);
135371047Sjulian	}
135471047Sjulian
135571047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
135671047Sjulian		ng_rmnode(node2);
135771047Sjulian		ng_destroy_hook(hook1);
135871047Sjulian		NG_HOOK_UNREF(hook1);
135971047Sjulian		return (error);
136071047Sjulian	}
136171047Sjulian
136270939Sjulian	/*
136371047Sjulian	 * Actually link the two hooks together.
136471047Sjulian	 */
136571047Sjulian	hook1->hk_peer = hook2;
136671047Sjulian	hook2->hk_peer = hook1;
136771047Sjulian
136871047Sjulian	/* Each hook is referenced by the other */
136971047Sjulian	NG_HOOK_REF(hook1);
137071047Sjulian	NG_HOOK_REF(hook2);
137171047Sjulian
137271047Sjulian	/* Give each node the opportunity to veto the pending connection */
137371047Sjulian	if (hook1->hk_node->nd_type->connect) {
137471047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
137571047Sjulian	}
137671047Sjulian
137771047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
137871047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
137971047Sjulian
138071047Sjulian	}
138171047Sjulian
138271047Sjulian	/*
138370939Sjulian	 * drop the references we were holding on the two hooks.
138470939Sjulian	 */
138571047Sjulian	if (error) {
138671047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
138771047Sjulian		ng_rmnode(node2);
138871047Sjulian	} else {
138971047Sjulian		/* As a last act, allow the hooks to be used */
139071047Sjulian		hook1->hk_flags &= ~HK_INVALID;
139171047Sjulian		hook2->hk_flags &= ~HK_INVALID;
139271047Sjulian	}
139371047Sjulian	NG_HOOK_UNREF(hook1);
139470939Sjulian	NG_HOOK_UNREF(hook2);
139570939Sjulian	return (error);
139652419Sjulian}
139771047Sjulian
139870700Sjulian/************************************************************************
139970700Sjulian		Utility routines to send self messages
140070700Sjulian************************************************************************/
140170700Sjulian/*
140270700Sjulian * Static version of shutdown message. we don't want to need resources
140370700Sjulian * to shut down (we may be doing it to release resources because we ran out.
140470700Sjulian */
140570700Sjulianstatic struct	ng_mesg  ng_msg_shutdown = {
140670700Sjulian	{NG_VERSION,		/* u_char */
140770700Sjulian	0,			/* u_char spare */
140870700Sjulian	0,			/* u_int16_t arglen */
140970700Sjulian	NGF_STATIC,		/* u_int32_t flags */
141070700Sjulian	0,			/* u_int32_t token */
141170700Sjulian	NGM_GENERIC_COOKIE,	/* u_int32_t */
141270700Sjulian	NGM_SHUTDOWN,		/* u_int32_t */
141370700Sjulian	"shutdown"}		/* u_char[16] */
141470700Sjulian};
141570700Sjulian
141670700Sjulianint
141770700Sjulianng_rmnode_self(node_p here)
141870700Sjulian{
141970700Sjulian	item_p	item;
142070700Sjulian	struct	ng_mesg	*msg;
142152419Sjulian
142270700Sjulian	/*
142370700Sjulian	 * Use the static version to avoid needing
142470700Sjulian	 * memory allocation to succeed.
142570700Sjulian	 * The message is never written to and always the same.
142670700Sjulian	 */
142770700Sjulian	msg = &ng_msg_shutdown;
142870700Sjulian
142970700Sjulian	/*
143070700Sjulian	 * Try get a queue item to send it with.
143170700Sjulian	 * Hopefully since it has a reserve, we can get one.
143270700Sjulian	 * If we can't we are screwed anyhow.
143370700Sjulian	 * Increase the chances by flushing our queue first.
143470700Sjulian	 * We may free an item, (if we were the hog).
143570700Sjulian	 * Work in progress is allowed to complete.
143670700Sjulian	 * We also pretty much ensure that we come straight
143770700Sjulian	 * back in to do the shutdown. It may be a good idea
143870700Sjulian	 * to hold a reference actually to stop it from all
143970700Sjulian	 * going up in smoke.
144070700Sjulian	 */
144170784Sjulian/*	ng_flush_input_queue(&here->nd_input_queue); will mask problem  */
144270700Sjulian	item = ng_package_msg_self(here, NULL, msg);
144370700Sjulian	if (item == NULL) { /* it would have freed the msg except static */
144470700Sjulian		/* try again after flushing our queue */
144570784Sjulian		ng_flush_input_queue(&here->nd_input_queue);
144670700Sjulian		item = ng_package_msg_self(here, NULL, msg);
144770700Sjulian		if (item == NULL) {
144870700Sjulian			printf("failed to free node 0x%x\n", ng_node2ID(here));
144970700Sjulian			return (ENOMEM);
145070700Sjulian		}
145170700Sjulian	}
145270700Sjulian	return (ng_snd_item(item, 0));
145370700Sjulian}
145470700Sjulian
145571047Sjulianstatic int
145671047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
145771047Sjulian{
145871047Sjulian	ng_destroy_hook(hook);
145971047Sjulian	return (0);
146071047Sjulian}
146171047Sjulian
146270935Sjulianint
146370935Sjulianng_rmhook_self(hook_p hook)
146470935Sjulian{
146571047Sjulian	int		error;
146670935Sjulian	node_p node = NG_HOOK_NODE(hook);
146770935Sjulian
146871047Sjulian	if (node == &ng_deadnode)
146971047Sjulian		return (0);
147071047Sjulian
147171047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
147271047Sjulian	return (error);
147370935Sjulian}
147470935Sjulian
147570700Sjulian/***********************************************************************
147652419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
147752419Sjulian *
147852419Sjulian * Such a string can refer to a specific node or a specific hook
147952419Sjulian * on a specific node, depending on how you look at it. In the
148052419Sjulian * latter case, the PATH component must not end in a dot.
148152419Sjulian *
148252419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
148352419Sjulian * of hook names separated by dots. This breaks out the original
148452419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
148552419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
148652419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
148752419Sjulian *
148852419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
148970700Sjulian ***********************************************************************/
149052419Sjulianint
149152419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
149252419Sjulian{
149352419Sjulian	char   *node, *path, *hook;
149452419Sjulian	int     k;
149552419Sjulian
149652419Sjulian	/*
149752419Sjulian	 * Extract absolute NODE, if any
149852419Sjulian	 */
149952419Sjulian	for (path = addr; *path && *path != ':'; path++);
150052419Sjulian	if (*path) {
150152419Sjulian		node = addr;	/* Here's the NODE */
150252419Sjulian		*path++ = '\0';	/* Here's the PATH */
150352419Sjulian
150452419Sjulian		/* Node name must not be empty */
150552419Sjulian		if (!*node)
150652419Sjulian			return -1;
150752419Sjulian
150852419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
150952419Sjulian		if (strcmp(node, ".") != 0) {
151052419Sjulian			for (k = 0; node[k]; k++)
151152419Sjulian				if (node[k] == '.')
151252419Sjulian					return -1;
151352419Sjulian		}
151452419Sjulian	} else {
151552419Sjulian		node = NULL;	/* No absolute NODE */
151652419Sjulian		path = addr;	/* Here's the PATH */
151752419Sjulian	}
151852419Sjulian
151952419Sjulian	/* Snoop for illegal characters in PATH */
152052419Sjulian	for (k = 0; path[k]; k++)
152152419Sjulian		if (path[k] == ':')
152252419Sjulian			return -1;
152352419Sjulian
152452419Sjulian	/* Check for no repeated dots in PATH */
152552419Sjulian	for (k = 0; path[k]; k++)
152652419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
152752419Sjulian			return -1;
152852419Sjulian
152952419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
153052419Sjulian	if (path[0] == '.')
153152419Sjulian		path++;
153252419Sjulian	if (*path && path[strlen(path) - 1] == '.')
153352419Sjulian		path[strlen(path) - 1] = 0;
153452419Sjulian
153552419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
153652419Sjulian	if (*path) {
153752419Sjulian		for (hook = path, k = 0; path[k]; k++)
153852419Sjulian			if (path[k] == '.') {
153952419Sjulian				hook = NULL;
154052419Sjulian				break;
154152419Sjulian			}
154252419Sjulian	} else
154352419Sjulian		path = hook = NULL;
154452419Sjulian
154552419Sjulian	/* Done */
154652419Sjulian	if (nodep)
154752419Sjulian		*nodep = node;
154852419Sjulian	if (pathp)
154952419Sjulian		*pathp = path;
155052419Sjulian	if (hookp)
155152419Sjulian		*hookp = hook;
155252419Sjulian	return (0);
155352419Sjulian}
155452419Sjulian
155552419Sjulian/*
155652419Sjulian * Given a path, which may be absolute or relative, and a starting node,
155770700Sjulian * return the destination node.
155852419Sjulian */
155952419Sjulianint
156070700Sjulianng_path2noderef(node_p here, const char *address,
156170700Sjulian				node_p *destp, hook_p *lasthook)
156252419Sjulian{
156352419Sjulian	char    fullpath[NG_PATHLEN + 1];
156452419Sjulian	char   *nodename, *path, pbuf[2];
156570700Sjulian	node_p  node, oldnode;
156652419Sjulian	char   *cp;
156759728Sjulian	hook_p hook = NULL;
156852419Sjulian
156952419Sjulian	/* Initialize */
157070784Sjulian	if (destp == NULL) {
157171047Sjulian		TRAP_ERROR();
157252419Sjulian		return EINVAL;
157370784Sjulian	}
157452419Sjulian	*destp = NULL;
157552419Sjulian
157652419Sjulian	/* Make a writable copy of address for ng_path_parse() */
157752419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
157852419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
157952419Sjulian
158052419Sjulian	/* Parse out node and sequence of hooks */
158152419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
158271047Sjulian		TRAP_ERROR();
158352419Sjulian		return EINVAL;
158452419Sjulian	}
158552419Sjulian	if (path == NULL) {
158652419Sjulian		pbuf[0] = '.';	/* Needs to be writable */
158752419Sjulian		pbuf[1] = '\0';
158852419Sjulian		path = pbuf;
158952419Sjulian	}
159052419Sjulian
159170700Sjulian	/*
159270700Sjulian	 * For an absolute address, jump to the starting node.
159370700Sjulian	 * Note that this holds a reference on the node for us.
159470700Sjulian	 * Don't forget to drop the reference if we don't need it.
159570700Sjulian	 */
159652419Sjulian	if (nodename) {
159770700Sjulian		node = ng_name2noderef(here, nodename);
159852419Sjulian		if (node == NULL) {
159971047Sjulian			TRAP_ERROR();
160052419Sjulian			return (ENOENT);
160152419Sjulian		}
160270700Sjulian	} else {
160370700Sjulian		if (here == NULL) {
160471047Sjulian			TRAP_ERROR();
160570700Sjulian			return (EINVAL);
160670700Sjulian		}
160752419Sjulian		node = here;
160870784Sjulian		NG_NODE_REF(node);
160970700Sjulian	}
161052419Sjulian
161170700Sjulian	/*
161270700Sjulian	 * Now follow the sequence of hooks
161370700Sjulian	 * XXX
161470700Sjulian	 * We actually cannot guarantee that the sequence
161570700Sjulian	 * is not being demolished as we crawl along it
161670700Sjulian	 * without extra-ordinary locking etc.
161770700Sjulian	 * So this is a bit dodgy to say the least.
161870700Sjulian	 * We can probably hold up some things by holding
161970700Sjulian	 * the nodelist mutex for the time of this
162070700Sjulian	 * crawl if we wanted.. At least that way we wouldn't have to
162170700Sjulian	 * worry about the nodes dissappearing, but the hooks would still
162270700Sjulian	 * be a problem.
162370700Sjulian	 */
162452419Sjulian	for (cp = path; node != NULL && *cp != '\0'; ) {
162552419Sjulian		char *segment;
162652419Sjulian
162752419Sjulian		/*
162852419Sjulian		 * Break out the next path segment. Replace the dot we just
162952419Sjulian		 * found with a NUL; "cp" points to the next segment (or the
163052419Sjulian		 * NUL at the end).
163152419Sjulian		 */
163252419Sjulian		for (segment = cp; *cp != '\0'; cp++) {
163352419Sjulian			if (*cp == '.') {
163452419Sjulian				*cp++ = '\0';
163552419Sjulian				break;
163652419Sjulian			}
163752419Sjulian		}
163852419Sjulian
163952419Sjulian		/* Empty segment */
164052419Sjulian		if (*segment == '\0')
164152419Sjulian			continue;
164252419Sjulian
164352419Sjulian		/* We have a segment, so look for a hook by that name */
164454096Sarchie		hook = ng_findhook(node, segment);
164552419Sjulian
164652419Sjulian		/* Can't get there from here... */
164752419Sjulian		if (hook == NULL
164870784Sjulian		    || NG_HOOK_PEER(hook) == NULL
164970784Sjulian		    || NG_HOOK_NOT_VALID(hook)
165070784Sjulian		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
165171047Sjulian			TRAP_ERROR();
165270784Sjulian			NG_NODE_UNREF(node);
165370784Sjulian#if 0
165470784Sjulian			printf("hooknotvalid %s %s %d %d %d %d ",
165570784Sjulian					path,
165670784Sjulian					segment,
165770784Sjulian					hook == NULL,
165870784Sjulian		     			NG_HOOK_PEER(hook) == NULL,
165970784Sjulian		     			NG_HOOK_NOT_VALID(hook),
166070784Sjulian		     			NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
166170784Sjulian#endif
166252419Sjulian			return (ENOENT);
166352419Sjulian		}
166452419Sjulian
166570700Sjulian		/*
166670700Sjulian		 * Hop on over to the next node
166770700Sjulian		 * XXX
166870700Sjulian		 * Big race conditions here as hooks and nodes go away
166970700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
167070700Sjulian		 * instead of the direct hook in this crawl?
167170700Sjulian		 */
167270700Sjulian		oldnode = node;
167370784Sjulian		if ((node = NG_PEER_NODE(hook)))
167470784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
167570784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
167670784Sjulian		if (NG_NODE_NOT_VALID(node)) {
167770784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
167870700Sjulian			node = NULL;
167970700Sjulian		}
168052419Sjulian	}
168152419Sjulian
168252419Sjulian	/* If node somehow missing, fail here (probably this is not needed) */
168352419Sjulian	if (node == NULL) {
168471047Sjulian		TRAP_ERROR();
168552419Sjulian		return (ENXIO);
168652419Sjulian	}
168752419Sjulian
168852419Sjulian	/* Done */
168952419Sjulian	*destp = node;
169059900Sarchie	if (lasthook != NULL)
169170784Sjulian		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
169252419Sjulian	return (0);
169352419Sjulian}
169452419Sjulian
169570700Sjulian/***************************************************************\
169670700Sjulian* Input queue handling.
169770700Sjulian* All activities are submitted to the node via the input queue
169870700Sjulian* which implements a multiple-reader/single-writer gate.
169970700Sjulian* Items which cannot be handled immeditly are queued.
170070700Sjulian*
170170700Sjulian* read-write queue locking inline functions			*
170270700Sjulian\***************************************************************/
170370700Sjulian
170470700Sjulianstatic __inline item_p ng_dequeue(struct ng_queue * ngq);
170570700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq,
170670700Sjulian					item_p  item);
170770700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq,
170870700Sjulian					item_p  item);
170970700Sjulianstatic __inline void	ng_leave_read(struct ng_queue * ngq);
171070700Sjulianstatic __inline void	ng_leave_write(struct ng_queue * ngq);
171170700Sjulianstatic __inline void	ng_queue_rw(struct ng_queue * ngq,
171270700Sjulian					item_p  item, int rw);
171370700Sjulian
171452419Sjulian/*
171570700Sjulian * Definition of the bits fields in the ng_queue flag word.
171670700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
171770700Sjulian * with them.
171870700Sjulian *
171970700Sjulian * The ordering here is important! don't shuffle these. If you add
172070700Sjulian * READ_PENDING to the word when it has READ_PENDING already set, you
172170700Sjulian * generate a carry into the reader count, this you atomically add a reader,
172270700Sjulian * and remove the pending reader count! Similarly for the pending writer
172370700Sjulian * flag, adding WRITE_PENDING generates a carry and sets the WRITER_ACTIVE
172470700Sjulian * flag, while clearing WRITE_PENDING. When 'SINGLE_THREAD_ONLY' is set, then
172570700Sjulian * it is only permitted to do WRITER operations. Reader operations will
172670700Sjulian * result in errors.
172770700Sjulian * But that "hack" is unnecessary: "cpp" can do the math for us!
172852419Sjulian */
172970700Sjulian/*-
173070700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
173170700Sjulian                       |
173270700Sjulian                       V
173370700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
173470700Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
173570700Sjulian|A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W|S|
173670700Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P|T|
173770700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
173870700Sjulian\_________________________ ____________________________/ | | | |
173970700Sjulian                          V                              | | | |
174070700Sjulian                [active reader count]                    | | | |
174170700Sjulian                                                         | | | |
174270700Sjulian        Read Pending ------------------------------------+ | | |
174370700Sjulian                                                           | | |
174470700Sjulian        Active Writer -------------------------------------+ | |
174570700Sjulian                                                             | |
174670700Sjulian        Write Pending ---------------------------------------+ |
174770700Sjulian                                                               |
174870700Sjulian        Single Threading Only ---------------------------------+
174970700Sjulian*/
175070700Sjulian#define	SINGLE_THREAD_ONLY 0x00000001	/* if set, even reads single thread */
175170700Sjulian#define WRITE_PENDING	0x00000002
175270700Sjulian#define	WRITER_ACTIVE	0x00000004
175370700Sjulian#define READ_PENDING	0x00000008
175470700Sjulian#define	READER_INCREMENT 0x00000010
175570700Sjulian#define	READER_MASK	0xfffffff0	/* Not valid if WRITER_ACTIVE is set */
175670700Sjulian#define SAFETY_BARRIER	0x00100000	/* 64K items queued should be enough */
175770700Sjulian/*
175870700Sjulian * Taking into account the current state of the queue and node, possibly take
175970700Sjulian * the next entry off the queue and return it. Return NULL if there was
176070700Sjulian * nothing we could return, either because there really was nothing there, or
176170700Sjulian * because the node was in a state where it cannot yet process the next item
176270700Sjulian * on the queue.
176370700Sjulian *
176470700Sjulian * This MUST MUST MUST be called with the mutex held.
176570700Sjulian */
176670700Sjulianstatic __inline item_p
176770700Sjulianng_dequeue(struct ng_queue *ngq)
176870700Sjulian{
176970700Sjulian	item_p item;
177070700Sjulian	u_int		add_arg;
177170700Sjulian	/*
177270700Sjulian	 * If there is a writer, then the answer is "no". Everything else
177370700Sjulian	 * stops when there is a WRITER.
177470700Sjulian	 */
177570700Sjulian	if (ngq->q_flags & WRITER_ACTIVE) {
177670700Sjulian		return (NULL);
177770700Sjulian	}
177870700Sjulian	/* Now take a look at what's on the queue and what's running */
177970700Sjulian	if ((ngq->q_flags & ~(READER_MASK | SINGLE_THREAD_ONLY)) == READ_PENDING) {
178070700Sjulian		/*
178170700Sjulian		 * It was a reader and we have no write active. We don't care
178270700Sjulian		 * how many readers are already active. Adjust the count for
178370700Sjulian		 * the item we are about to dequeue. Adding READ_PENDING to
178470700Sjulian		 * the exisiting READ_PENDING clears it and generates a carry
178570700Sjulian		 * into the reader count.
178670700Sjulian		 */
178770700Sjulian		add_arg = READ_PENDING;
178870700Sjulian	} else if ((ngq->q_flags & ~SINGLE_THREAD_ONLY) == WRITE_PENDING) {
178970700Sjulian		/*
179070700Sjulian		 * There is a pending write, no readers and no active writer.
179170700Sjulian		 * This means we can go ahead with the pending writer. Note
179270700Sjulian		 * the fact that we now have a writer, ready for when we take
179370700Sjulian		 * it off the queue.
179470700Sjulian		 *
179570700Sjulian		 * We don't need to worry about a possible collision with the
179670700Sjulian		 * fasttrack reader.
179770700Sjulian		 *
179870700Sjulian		 * The fasttrack thread may take a long time to discover that we
179970700Sjulian		 * are running so we would have an inconsistent state in the
180070700Sjulian		 * flags for a while. Since we ignore the reader count
180170700Sjulian		 * entirely when the WRITER_ACTIVE flag is set, this should
180270700Sjulian		 * not matter (in fact it is defined that way). If it tests
180370700Sjulian		 * the flag before this operation, the WRITE_PENDING flag
180470700Sjulian		 * will make it fail, and if it tests it later, the
180570700Sjulian		 * ACTIVE_WRITER flag will do the same. If it is SO slow that
180670700Sjulian		 * we have actually completed the operation, and neither flag
180770700Sjulian		 * is set (nor the READ_PENDING) by the time that it tests
180870700Sjulian		 * the flags, then it is actually ok for it to continue. If
180970700Sjulian		 * it completes and we've finished and the read pending is
181070700Sjulian		 * set it still fails.
181170700Sjulian		 *
181270700Sjulian		 * So we can just ignore it,  as long as we can ensure that the
181370700Sjulian		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
181470700Sjulian		 * state is atomic.
181570700Sjulian		 *
181670700Sjulian		 * After failing, first it will be held back by the mutex, then
181770700Sjulian		 * when it can proceed, it will queue its request, then it
181870700Sjulian		 * would arrive at this function. Usually it will have to
181970700Sjulian		 * leave empty handed because the ACTIVE WRITER bit wil be
182070700Sjulian		 * set.
182170700Sjulian		 */
182270700Sjulian		/*
182370700Sjulian		 * Adjust the flags for the item we are about to dequeue.
182470700Sjulian		 * Adding WRITE_PENDING to the exisiting WRITE_PENDING clears
182570700Sjulian		 * it and generates a carry into the WRITER_ACTIVE flag, all
182670700Sjulian		 * atomically.
182770700Sjulian		 */
182870700Sjulian		add_arg = WRITE_PENDING;
182970700Sjulian		/*
183070700Sjulian		 * We want to write "active writer, no readers " Now go make
183170700Sjulian		 * it true. In fact there may be a number in the readers
183270700Sjulian		 * count but we know it is not true and will be fixed soon.
183370700Sjulian		 * We will fix the flags for the next pending entry in a
183470700Sjulian		 * moment.
183570700Sjulian		 */
183670700Sjulian	} else {
183770700Sjulian		/*
183870700Sjulian		 * We can't dequeue anything.. return and say so. Probably we
183970700Sjulian		 * have a write pending and the readers count is non zero. If
184070700Sjulian		 * we got here because a reader hit us just at the wrong
184170700Sjulian		 * moment with the fasttrack code, and put us in a strange
184270700Sjulian		 * state, then it will be through in just a moment, (as soon
184370700Sjulian		 * as we release the mutex) and keep things moving.
184470700Sjulian		 */
184570700Sjulian		return (0);
184670700Sjulian	}
184752419Sjulian
184870700Sjulian	/*
184970700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
185070700Sjulian	 * pending flags and the next and last pointers.
185170700Sjulian	 */
185270700Sjulian	item = ngq->queue;
185370700Sjulian	ngq->queue = item->el_next;
185470700Sjulian	if (ngq->last == &(item->el_next)) {
185570700Sjulian		/*
185670700Sjulian		 * that was the last entry in the queue so set the 'last
185770700Sjulian		 * pointer up correctly and make sure the pending flags are
185870700Sjulian		 * clear.
185970700Sjulian		 */
186070700Sjulian		ngq->last = &(ngq->queue);
186170700Sjulian		/*
186270700Sjulian		 * Whatever flag was set is cleared and the carry sets the
186370700Sjulian		 * correct new active state/count. So we don't need to change
186470700Sjulian		 * add_arg.
186570700Sjulian		 */
186670700Sjulian	} else {
186771047Sjulian		if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
186870700Sjulian			/*
186970700Sjulian			 * If we had a READ_PENDING and have another one, we
187070700Sjulian			 * just want to add READ_PENDING twice (the same as
187170700Sjulian			 * adding READER_INCREMENT). If we had WRITE_PENDING,
187270700Sjulian			 * we want to add READ_PENDING + WRITE_PENDING to
187370700Sjulian			 * clear the old WRITE_PENDING, set ACTIVE_WRITER,
187470700Sjulian			 * and set READ_PENDING. Either way we just add
187570700Sjulian			 * READ_PENDING to whatever we already had.
187670700Sjulian			 */
187770700Sjulian			add_arg += READ_PENDING;
187870700Sjulian		} else {
187970700Sjulian			/*
188070700Sjulian			 * If we had a WRITE_PENDING and have another one, we
188170700Sjulian			 * just want to add WRITE_PENDING twice (the same as
188270700Sjulian			 * adding ACTIVE_WRITER). If we had READ_PENDING, we
188370700Sjulian			 * want to add READ_PENDING + WRITE_PENDING to clear
188470700Sjulian			 * the old READ_PENDING, increment the readers, and
188570700Sjulian			 * set WRITE_PENDING. Either way we just add
188670700Sjulian			 * WRITE_PENDING to whatever we already had.
188770700Sjulian			 */
188870700Sjulian			add_arg += WRITE_PENDING;
188970700Sjulian		}
189070700Sjulian	}
189170700Sjulian	atomic_add_long(&ngq->q_flags, add_arg);
189270700Sjulian	/*
189370700Sjulian	 * We have successfully cleared the old pending flag, set the new one
189470700Sjulian	 * if it is needed, and incremented the appropriate active field.
189570700Sjulian	 * (all in one atomic addition.. wow)
189670700Sjulian	 */
189770700Sjulian	return (item);
189870700Sjulian}
189952419Sjulian
190070700Sjulian/*
190170700Sjulian * Queue a packet to be picked up by someone else.
190270700Sjulian * We really don't care who, but we can't or don't want to hang around
190370700Sjulian * to process it ourselves. We are probably an interrupt routine..
190470700Sjulian * 1 = writer, 0 = reader
190570700Sjulian * We should set something to indicate NETISR requested
190670700Sjulian * If it's the first item queued.
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);
195870700Sjulian	if ((ngq->q_flags & (~READER_MASK)) == 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 ######### */
196670700Sjulian	mtx_enter((&ngq->q_mtx), MTX_SPIN);
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	 */
197370700Sjulian	if ((ngq->q_flags & (~READER_MASK)) == 0) {
197470700Sjulian		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
197570700Sjulian		mtx_exit((&ngq->q_mtx), MTX_SPIN);
197670700Sjulian		return (item);
197770700Sjulian	}
197870700Sjulian
197970700Sjulian	/*
198070700Sjulian	 * Quick check that we are doing things right.
198170700Sjulian	 */
198270700Sjulian	if (ngq->q_flags & SINGLE_THREAD_ONLY) {
198370700Sjulian		panic("Calling single treaded queue incorrectly");
198470700Sjulian	}
198570700Sjulian
198670700Sjulian	/*
198770700Sjulian	 * and queue the request for later.
198870700Sjulian	 */
198971047Sjulian	item->el_flags |= NGQF_READER;
199070700Sjulian	ng_queue_rw(ngq, item, NGQRW_R);
199170700Sjulian
199270700Sjulian	/*
199370700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
199470700Sjulian	 * see if we can dequeue something to run instead.
199570700Sjulian	 */
199670700Sjulian	item = ng_dequeue(ngq);
199770700Sjulian	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
199870700Sjulian	return (item);
199970700Sjulian}
200070700Sjulian
200170700Sjulianstatic __inline item_p
200270700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item)
200370700Sjulian{
200470700Sjulianrestart:
200570700Sjulian	mtx_enter(&(ngq->q_mtx), MTX_SPIN);
200670700Sjulian	/*
200770700Sjulian	 * If there are no readers, no writer, and no pending packets, then
200870700Sjulian	 * we can just go ahead. In all other situations we need to queue the
200970700Sjulian	 * request
201070700Sjulian	 */
201170700Sjulian	if ((ngq->q_flags & (~SINGLE_THREAD_ONLY)) == 0) {
201270700Sjulian		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
201370700Sjulian		mtx_exit((&ngq->q_mtx), MTX_SPIN);
201470700Sjulian		if (ngq->q_flags & READER_MASK) {
201570700Sjulian			/* Collision with fast-track reader */
201670700Sjulian			atomic_add_long(&ngq->q_flags, -WRITER_ACTIVE);
201770700Sjulian			goto restart;
201869922Sjulian		}
201970700Sjulian
202070700Sjulian		return (item);
202152419Sjulian	}
202252419Sjulian
202370700Sjulian	/*
202470700Sjulian	 * and queue the request for later.
202570700Sjulian	 */
202671047Sjulian	item->el_flags &= ~NGQF_RW;
202770700Sjulian	ng_queue_rw(ngq, item, NGQRW_W);
202870700Sjulian
202970700Sjulian	/*
203070700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
203170700Sjulian	 * see if we can dequeue something to run instead.
203270700Sjulian	 */
203370700Sjulian	item = ng_dequeue(ngq);
203470700Sjulian	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
203570700Sjulian	return (item);
203670700Sjulian}
203770700Sjulian
203870700Sjulianstatic __inline void
203970700Sjulianng_leave_read(struct ng_queue *ngq)
204070700Sjulian{
204170700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
204270700Sjulian}
204370700Sjulian
204470700Sjulianstatic __inline void
204570700Sjulianng_leave_write(struct ng_queue *ngq)
204670700Sjulian{
204770700Sjulian	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
204870700Sjulian}
204970700Sjulian
205070700Sjulianstatic void
205170700Sjulianng_flush_input_queue(struct ng_queue * ngq)
205270700Sjulian{
205370700Sjulian	item_p item;
205470700Sjulian	u_int		add_arg;
205570700Sjulian	mtx_enter(&ngq->q_mtx, MTX_SPIN);
205670700Sjulian	for (;;) {
205770700Sjulian		/* Now take a look at what's on the queue */
205870700Sjulian		if (ngq->q_flags & READ_PENDING) {
205970700Sjulian			add_arg = -READ_PENDING;
206070700Sjulian		} else if (ngq->q_flags & WRITE_PENDING) {
206170700Sjulian			add_arg = -WRITE_PENDING;
206270700Sjulian		} else {
206370700Sjulian			break;
206470700Sjulian		}
206570700Sjulian
206670700Sjulian		item = ngq->queue;
206770700Sjulian		ngq->queue = item->el_next;
206870700Sjulian		if (ngq->last == &(item->el_next)) {
206970700Sjulian			ngq->last = &(ngq->queue);
207070700Sjulian		} else {
207171047Sjulian			if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) {
207270700Sjulian				add_arg += READ_PENDING;
207370700Sjulian			} else {
207470700Sjulian				add_arg += WRITE_PENDING;
207570700Sjulian			}
207670700Sjulian		}
207770700Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
207870700Sjulian
207970700Sjulian		mtx_exit(&ngq->q_mtx, MTX_SPIN);
208070700Sjulian		NG_FREE_ITEM(item);
208170700Sjulian		mtx_enter(&ngq->q_mtx, MTX_SPIN);
208270700Sjulian	}
208370700Sjulian	mtx_exit(&ngq->q_mtx, MTX_SPIN);
208470700Sjulian}
208570700Sjulian
208670700Sjulian/***********************************************************************
208770700Sjulian* Externally visible method for sending or queueing messages or data.
208870700Sjulian***********************************************************************/
208970700Sjulian
209070700Sjulian/*
209170700Sjulian * MACRO WILL DO THE JOB OF CALLING ng_package_msg IN CALLER
209270700Sjulian * before we are called. The user code should have filled out the item
209370700Sjulian * correctly by this stage:
209470700Sjulian * Common:
209570700Sjulian *    reference to destination node.
209670700Sjulian *    Reference to destination rcv hook if relevant.
209770700Sjulian * Data:
209870700Sjulian *    pointer to mbuf
209970700Sjulian *    pointer to metadata
210070700Sjulian * Control_Message:
210170700Sjulian *    pointer to msg.
210270700Sjulian *    ID of original sender node. (return address)
210370700Sjulian *
210470700Sjulian * The nodes have several routines and macros to help with this task:
210570700Sjulian * ng_package_msg()
210670700Sjulian * ng_package_data() do much of the work.
210770700Sjulian * ng_retarget_msg
210870700Sjulian * ng_retarget_data
210970700Sjulian */
211070700Sjulian
211170700Sjulianint
211270700Sjulianng_snd_item(item_p item, int queue)
211370700Sjulian{
211470700Sjulian	hook_p hook = item->el_hook;
211570700Sjulian	node_p dest = item->el_dest;
211670700Sjulian	int rw;
211770700Sjulian	int error = 0, ierror;
211870700Sjulian	item_p	oitem;
211970784Sjulian	struct ng_queue * ngq = &dest->nd_input_queue;
212070700Sjulian
212170784Sjulian#ifdef	NETGRAPH_DEBUG
212270700Sjulian        _ngi_check(item, __FILE__, __LINE__);
212370700Sjulian#endif
212470700Sjulian
212570700Sjulian	if (item == NULL) {
212671047Sjulian		TRAP_ERROR();
212770700Sjulian		return (EINVAL);	/* failed to get queue element */
212870700Sjulian	}
212970700Sjulian	if (dest == NULL) {
213070700Sjulian		NG_FREE_ITEM(item);
213171047Sjulian		TRAP_ERROR();
213270700Sjulian		return (EINVAL);	/* No address */
213370700Sjulian	}
213471047Sjulian	switch(item->el_flags & NGQF_TYPE) {
213571047Sjulian	case NGQF_DATA:
213669922Sjulian		/*
213770700Sjulian		 * DATA MESSAGE
213870700Sjulian		 * Delivered to a node via a non-optional hook.
213970700Sjulian		 * Both should be present in the item even though
214070700Sjulian		 * the node is derivable from the hook.
214170700Sjulian		 * References are held on both by the item.
214269922Sjulian		 */
214370784Sjulian#ifdef	NETGRAPH_DEBUG
214470700Sjulian        _ngi_check(item, __FILE__, __LINE__);
214570700Sjulian#endif
214670700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
214770700Sjulian		if (hook == NULL) {
214870700Sjulian			NG_FREE_ITEM(item);
214971047Sjulian			TRAP_ERROR();
215070700Sjulian			return(EINVAL);
215170700Sjulian		}
215270784Sjulian		if ((NG_HOOK_NOT_VALID(hook))
215370784Sjulian		|| (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) {
215470700Sjulian			NG_FREE_ITEM(item);
215570700Sjulian			return (ENOTCONN);
215669922Sjulian		}
215770784Sjulian		if ((hook->hk_flags & HK_QUEUE)) {
215870700Sjulian			queue = 1;
215970700Sjulian		}
216070700Sjulian		/* By default data is a reader in the locking scheme */
216170700Sjulian		item->el_flags |= NGQF_READER;
216270700Sjulian		rw = NGQRW_R;
216371047Sjulian		break;
216471047Sjulian	case NGQF_MESG:
216570700Sjulian		/*
216670700Sjulian		 * CONTROL MESSAGE
216770700Sjulian		 * Delivered to a node.
216870700Sjulian		 * Hook is optional.
216970700Sjulian		 * References are held by the item on the node and
217070700Sjulian		 * the hook if it is present.
217170700Sjulian		 */
217270784Sjulian		if (hook && (hook->hk_flags & HK_QUEUE)) {
217370700Sjulian			queue = 1;
217470700Sjulian		}
217570700Sjulian		/* Data messages count as writers unles explicitly exempted */
217670700Sjulian		if (NGI_MSG(item)->header.cmd & NGM_READONLY) {
217770700Sjulian			item->el_flags |= NGQF_READER;
217870700Sjulian			rw = NGQRW_R;
217970700Sjulian		} else {
218071047Sjulian			item->el_flags &= ~NGQF_RW;
218170700Sjulian			rw = NGQRW_W;
218270700Sjulian		}
218371047Sjulian		break;
218471047Sjulian	case NGQF_FN:
218571047Sjulian		item->el_flags &= ~NGQF_RW;
218671047Sjulian		rw = NGQRW_W;
218771047Sjulian		break;
218871047Sjulian	default:
218971047Sjulian		NG_FREE_ITEM(item);
219071047Sjulian		TRAP_ERROR();
219171047Sjulian		return (EINVAL);
219269922Sjulian	}
219370700Sjulian	/*
219470700Sjulian	 * If the node specifies single threading, force writer semantics
219570700Sjulian	 * Similarly the node may say one hook always produces writers.
219670700Sjulian	 * These are over-rides.
219770700Sjulian	 */
219870700Sjulian	if ((ngq->q_flags & SINGLE_THREAD_ONLY)
219970784Sjulian	|| (dest->nd_flags & NG_FORCE_WRITER)
220070784Sjulian	|| (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
220170700Sjulian			rw = NGQRW_W;
220271047Sjulian			item->el_flags &= ~NGQF_READER;
220370700Sjulian	}
220470700Sjulian	if (queue) {
220570700Sjulian		/* Put it on the queue for that node*/
220670784Sjulian#ifdef	NETGRAPH_DEBUG
220770700Sjulian        _ngi_check(item, __FILE__, __LINE__);
220870700Sjulian#endif
220970700Sjulian		mtx_enter(&(ngq->q_mtx), MTX_SPIN);
221070700Sjulian		ng_queue_rw(ngq, item, rw);
221170700Sjulian		mtx_exit(&(ngq->q_mtx), MTX_SPIN);
221270700Sjulian		/*
221370700Sjulian		 * If there are active elements then we can rely on
221470700Sjulian		 * them. if not we should not rely on another packet
221570700Sjulian		 * coming here by another path,
221670700Sjulian		 * so it is best to put us in the netisr list.
221770700Sjulian		 */
221870700Sjulian		if ((ngq->q_flags & (READER_MASK|WRITER_ACTIVE)) == 0) {
221970700Sjulian			ng_setisr(ngq->q_node);
222070700Sjulian		}
222170700Sjulian		return (0);
222270700Sjulian	}
222370700Sjulian	/*
222470700Sjulian	 * Take a queue item and a node and see if we can apply the item to
222570700Sjulian	 * the node. We may end up getting a different item to apply instead.
222670700Sjulian	 * Will allow for a piggyback reply only in the case where
222770700Sjulian	 * there is no queueing.
222870700Sjulian	 */
222969922Sjulian
223070700Sjulian	oitem = item;
223170700Sjulian	/*
223270700Sjulian	 * We already decided how we will be queueud or treated.
223370700Sjulian	 * Try get the appropriate operating permission.
223470700Sjulian	 */
223570700Sjulian 	if (rw == NGQRW_R) {
223670700Sjulian		item = ng_acquire_read(ngq, item);
223770700Sjulian	} else {
223870700Sjulian		item = ng_acquire_write(ngq, item);
223970700Sjulian	}
224052419Sjulian
224170700Sjulian	/*
224270700Sjulian	 * May have come back with a different item.
224370700Sjulian	 * or maybe none at all. The one we started with will
224470700Sjulian	 * have been queued in thises cases.
224570700Sjulian	 */
224670700Sjulian	if (item == NULL) {
224770700Sjulian		return (0);
224870700Sjulian	}
224952419Sjulian
225070784Sjulian#ifdef	NETGRAPH_DEBUG
225170700Sjulian        _ngi_check(item, __FILE__, __LINE__);
225270700Sjulian#endif
225370700Sjulian	ierror = ng_apply_item(dest, item); /* drops r/w lock when done */
225452419Sjulian
225570700Sjulian	/* only return an error if it was our initial item.. (compat hack) */
225670700Sjulian	if (oitem == item) {
225770700Sjulian		error = ierror;
225870700Sjulian	}
225970700Sjulian
226052419Sjulian	/*
226170700Sjulian	 * Now we've handled the packet we brought, (or a friend of it) let's
226270700Sjulian	 * look for any other packets that may have been queued up. We hold
226370700Sjulian	 * no locks, so if someone puts something in the queue after
226470700Sjulian	 * we check that it is empty, it is their problem
226570700Sjulian	 * to ensure it is processed. If we have the netisr thread cme in here
226670700Sjulian	 * while we still say we have stuff to do, we may get a boost
226770700Sjulian	 * in SMP systems. :-)
226852419Sjulian	 */
226970700Sjulian	for (;;) {
227070700Sjulian		/* quick hack to save all that mutex stuff */
227170700Sjulian		if ((ngq->q_flags & (WRITE_PENDING | READ_PENDING)) == 0) {
227270784Sjulian			if (dest->nd_flags & NG_WORKQ)
227370700Sjulian				ng_worklist_remove(dest);
227470700Sjulian			return (0);
227570700Sjulian		}
227670700Sjulian		/*
227770700Sjulian		 * dequeue acquires and adjusts the input_queue as it dequeues
227870700Sjulian		 * packets. It acquires the rw lock as needed.
227970700Sjulian		 */
228070700Sjulian		mtx_enter(&ngq->q_mtx, MTX_SPIN);
228170700Sjulian		item = ng_dequeue(ngq);
228270700Sjulian		mtx_exit(&ngq->q_mtx, MTX_SPIN);
228370700Sjulian		if (!item) {
228470700Sjulian			/*
228570700Sjulian			 * If we have no work to do
228670700Sjulian			 * then we certainly don't need to be
228770700Sjulian			 * on the worklist.
228870700Sjulian			 */
228970784Sjulian			if (dest->nd_flags & NG_WORKQ)
229070700Sjulian				ng_worklist_remove(dest);
229170700Sjulian			return (0);
229270700Sjulian		}
229370784Sjulian#ifdef	NETGRAPH_DEBUG
229470700Sjulian        _ngi_check(item, __FILE__, __LINE__);
229570700Sjulian#endif
229670700Sjulian
229770700Sjulian		/*
229870700Sjulian		 * We have the appropriate lock, so run the item.
229970700Sjulian		 * When finished it will drop the lock accordingly
230070700Sjulian		 */
230170700Sjulian
230270700Sjulian		ierror = ng_apply_item(dest, item);
230370700Sjulian		/*
230470700Sjulian		 * only return an error if it was our initial
230570700Sjulian		 * item.. (compat hack)
230670700Sjulian		 */
230770700Sjulian		if (oitem == item) {
230870700Sjulian			error = ierror;
230970700Sjulian		}
231070700Sjulian	}
231170700Sjulian	return (0);
231252419Sjulian}
231352419Sjulian
231452419Sjulian/*
231570700Sjulian * We have an item that was possibly queued somewhere.
231670700Sjulian * It should contain all the information needed
231770700Sjulian * to run it on the appropriate node/hook.
231852419Sjulian */
231952419Sjulianstatic int
232070700Sjulianng_apply_item(node_p node, item_p item)
232152419Sjulian{
232270700Sjulian	hook_p  hook;
232371047Sjulian	int was_reader = ((item->el_flags & NGQF_RW));
232452419Sjulian	int error = 0;
232570700Sjulian	ng_rcvdata_t *rcvdata;
232652419Sjulian
232770700Sjulian	hook = item->el_hook;
232870784Sjulian	item->el_hook = NULL;	/* so NG_FREE_ITEM doesn't NG_HOOK_UNREF() */
232970700Sjulian	/* We already have the node.. assume responsibility */
233070700Sjulian	/* And the reference */
233170700Sjulian	/* node = item->el_dest; */
233270700Sjulian	item->el_dest = NULL;	/* same as for the hook above */
233370784Sjulian#ifdef	NETGRAPH_DEBUG
233470700Sjulian        _ngi_check(item, __FILE__, __LINE__);
233570700Sjulian#endif
233671047Sjulian	switch (item->el_flags & NGQF_TYPE) {
233770700Sjulian	case NGQF_DATA:
233870700Sjulian		/*
233970700Sjulian		 * Check things are still ok as when we were queued.
234070700Sjulian		 */
234170700Sjulian
234270700Sjulian		if ((hook == NULL)
234370784Sjulian		|| NG_HOOK_NOT_VALID(hook)
234471047Sjulian		|| NG_NODE_NOT_VALID(node)
234570784Sjulian		|| ((rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata) == NULL)) {
234670700Sjulian			error = EIO;
234770700Sjulian			NG_FREE_ITEM(item);
234870700Sjulian		} else {
234970700Sjulian			error = (*rcvdata)(hook, item);
235070700Sjulian		}
235170700Sjulian		break;
235270700Sjulian	case NGQF_MESG:
235370700Sjulian		if (hook) {
235470700Sjulian			item->el_hook = NULL;
235570784Sjulian			if (NG_HOOK_NOT_VALID(hook)) {
235670700Sjulian			/*
235770700Sjulian			 * If the hook has been zapped then we can't use it.
235870700Sjulian			 * Immediatly drop its reference.
235970700Sjulian			 * The message may not need it.
236070700Sjulian			 */
236170784Sjulian				NG_HOOK_UNREF(hook);
236270700Sjulian				hook = NULL;
236370700Sjulian			}
236470700Sjulian		}
236570700Sjulian		/*
236670700Sjulian		 * Similarly, if the node is a zombie there is
236770700Sjulian		 * nothing we can do with it, drop everything.
236870700Sjulian		 */
236970784Sjulian		if (NG_NODE_NOT_VALID(node)) {
237071047Sjulian			TRAP_ERROR();
237170700Sjulian			error = EINVAL;
237270700Sjulian			NG_FREE_ITEM(item);
237370700Sjulian		} else {
237470700Sjulian			/*
237570700Sjulian			 * Call the appropriate message handler for the object.
237670700Sjulian			 * It is up to the message handler to free the message.
237770700Sjulian			 * If it's a generic message, handle it generically,
237870700Sjulian			 * otherwise call the type's message handler
237970700Sjulian			 * (if it exists)
238070700Sjulian			 * XXX (race). Remember that a queued message may
238170700Sjulian			 * reference a node or hook that has just been
238270700Sjulian			 * invalidated. It will exist as the queue code
238370700Sjulian			 * is holding a reference, but..
238470700Sjulian			 */
238570700Sjulian
238670700Sjulian			struct ng_mesg *msg = NGI_MSG(item);
238770700Sjulian
238870700Sjulian			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
238970700Sjulian			&& ((msg->header.flags & NGF_RESP) == 0)) {
239070700Sjulian				error = ng_generic_msg(node, item, hook);
239170700Sjulian			} else {
239270784Sjulian				if ((node)->nd_type->rcvmsg != NULL) {
239370784Sjulian					error = (*(node)->nd_type->rcvmsg)((node),
239470700Sjulian						(item), (hook));
239570700Sjulian				} else {
239671047Sjulian					TRAP_ERROR();
239770700Sjulian					error = EINVAL; /* XXX */
239870700Sjulian					NG_FREE_ITEM(item);
239970700Sjulian				}
240070700Sjulian			}
240170700Sjulian			/* item is now invalid */
240270700Sjulian		}
240370700Sjulian		break;
240471047Sjulian	case NGQF_FN:
240571047Sjulian		/*
240671047Sjulian		 *  We have to implicitly trust the hook,
240771047Sjulian		 * as some of these are used for system purposes
240871047Sjulian		 * where the hook is invalid.
240971047Sjulian		 */
241071047Sjulian		if (NG_NODE_NOT_VALID(node)) {
241171047Sjulian			TRAP_ERROR();
241271047Sjulian			error = EINVAL;
241371047Sjulian			break;
241471047Sjulian		}
241571047Sjulian		error =
241671047Sjulian		    (*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item));
241771047Sjulian
241871047Sjulian		NG_FREE_ITEM(item);
241971047Sjulian		break;
242071047Sjulian
242170700Sjulian	}
242270700Sjulian	/*
242370700Sjulian	 * We held references on some of the resources
242470700Sjulian	 * that we took from the item. Now that we have
242570700Sjulian	 * finished doing everything, drop those references.
242670700Sjulian	 */
242770700Sjulian	if (hook) {
242870784Sjulian		NG_HOOK_UNREF(hook);
242970700Sjulian	}
243070700Sjulian
243170700Sjulian	if (was_reader) {
243270784Sjulian		ng_leave_read(&node->nd_input_queue);
243370700Sjulian	} else {
243470784Sjulian		ng_leave_write(&node->nd_input_queue);
243570700Sjulian	}
243670784Sjulian	NG_NODE_UNREF(node);
243770700Sjulian	return (error);
243870700Sjulian}
243970700Sjulian
244070700Sjulian/***********************************************************************
244170700Sjulian * Implement the 'generic' control messages
244270700Sjulian ***********************************************************************/
244370700Sjulianstatic int
244470700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
244570700Sjulian{
244670700Sjulian	int error = 0;
244770700Sjulian	struct ng_mesg *msg;
244870700Sjulian	struct ng_mesg *resp = NULL;
244970700Sjulian
245070700Sjulian	NGI_GET_MSG(item, msg);
245152419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
245271047Sjulian		TRAP_ERROR();
245370700Sjulian		error = EINVAL;
245470700Sjulian		goto out;
245552419Sjulian	}
245652419Sjulian	switch (msg->header.cmd) {
245752419Sjulian	case NGM_SHUTDOWN:
245852419Sjulian		ng_rmnode(here);
245952419Sjulian		break;
246052419Sjulian	case NGM_MKPEER:
246152419Sjulian	    {
246252419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
246352419Sjulian
246452419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
246571047Sjulian			TRAP_ERROR();
246670700Sjulian			error = EINVAL;
246770700Sjulian			break;
246852419Sjulian		}
246952419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
247052419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
247152419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
247252419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
247352419Sjulian		break;
247452419Sjulian	    }
247552419Sjulian	case NGM_CONNECT:
247652419Sjulian	    {
247752419Sjulian		struct ngm_connect *const con =
247852419Sjulian			(struct ngm_connect *) msg->data;
247952419Sjulian		node_p node2;
248052419Sjulian
248152419Sjulian		if (msg->header.arglen != sizeof(*con)) {
248271047Sjulian			TRAP_ERROR();
248370700Sjulian			error = EINVAL;
248470700Sjulian			break;
248552419Sjulian		}
248652419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
248752419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
248852419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
248970700Sjulian		/* Don't forget we get a reference.. */
249070700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
249152419Sjulian		if (error)
249252419Sjulian			break;
249352419Sjulian		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
249470784Sjulian		NG_NODE_UNREF(node2);
249552419Sjulian		break;
249652419Sjulian	    }
249752419Sjulian	case NGM_NAME:
249852419Sjulian	    {
249952419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
250052419Sjulian
250152419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
250271047Sjulian			TRAP_ERROR();
250370700Sjulian			error = EINVAL;
250470700Sjulian			break;
250552419Sjulian		}
250652419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
250752419Sjulian		error = ng_name_node(here, nam->name);
250852419Sjulian		break;
250952419Sjulian	    }
251052419Sjulian	case NGM_RMHOOK:
251152419Sjulian	    {
251252419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
251352419Sjulian		hook_p hook;
251452419Sjulian
251552419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
251671047Sjulian			TRAP_ERROR();
251770700Sjulian			error = EINVAL;
251870700Sjulian			break;
251952419Sjulian		}
252052419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
252154096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
252252419Sjulian			ng_destroy_hook(hook);
252352419Sjulian		break;
252452419Sjulian	    }
252552419Sjulian	case NGM_NODEINFO:
252652419Sjulian	    {
252752419Sjulian		struct nodeinfo *ni;
252852419Sjulian
252970700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
253052419Sjulian		if (resp == NULL) {
253152419Sjulian			error = ENOMEM;
253252419Sjulian			break;
253352419Sjulian		}
253452419Sjulian
253552419Sjulian		/* Fill in node info */
253670700Sjulian		ni = (struct nodeinfo *) resp->data;
253770784Sjulian		if (NG_NODE_HAS_NAME(here))
253870784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
253970784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
254052722Sjulian		ni->id = ng_node2ID(here);
254170784Sjulian		ni->hooks = here->nd_numhooks;
254252419Sjulian		break;
254352419Sjulian	    }
254452419Sjulian	case NGM_LISTHOOKS:
254552419Sjulian	    {
254670784Sjulian		const int nhooks = here->nd_numhooks;
254752419Sjulian		struct hooklist *hl;
254852419Sjulian		struct nodeinfo *ni;
254952419Sjulian		hook_p hook;
255052419Sjulian
255152419Sjulian		/* Get response struct */
255270700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*hl)
255370700Sjulian		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
255452419Sjulian		if (resp == NULL) {
255552419Sjulian			error = ENOMEM;
255652419Sjulian			break;
255752419Sjulian		}
255870700Sjulian		hl = (struct hooklist *) resp->data;
255952419Sjulian		ni = &hl->nodeinfo;
256052419Sjulian
256152419Sjulian		/* Fill in node info */
256270784Sjulian		if (NG_NODE_HAS_NAME(here))
256370784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
256470784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
256552722Sjulian		ni->id = ng_node2ID(here);
256652419Sjulian
256752419Sjulian		/* Cycle through the linked list of hooks */
256852419Sjulian		ni->hooks = 0;
256970784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
257052419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
257152419Sjulian
257252419Sjulian			if (ni->hooks >= nhooks) {
257352419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
257452419Sjulian				    __FUNCTION__, "hooks");
257552419Sjulian				break;
257652419Sjulian			}
257770784Sjulian			if (NG_HOOK_NOT_VALID(hook))
257852419Sjulian				continue;
257970784Sjulian			strncpy(link->ourhook, NG_HOOK_NAME(hook), NG_HOOKLEN);
258070784Sjulian			strncpy(link->peerhook,
258170784Sjulian				NG_PEER_HOOK_NAME(hook), NG_HOOKLEN);
258270784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
258352419Sjulian				strncpy(link->nodeinfo.name,
258470784Sjulian				    NG_PEER_NODE_NAME(hook), NG_NODELEN);
258552419Sjulian			strncpy(link->nodeinfo.type,
258670784Sjulian			   NG_PEER_NODE(hook)->nd_type->name, NG_TYPELEN);
258770784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
258870784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
258952419Sjulian			ni->hooks++;
259052419Sjulian		}
259152419Sjulian		break;
259252419Sjulian	    }
259352419Sjulian
259452419Sjulian	case NGM_LISTNAMES:
259552419Sjulian	case NGM_LISTNODES:
259652419Sjulian	    {
259752419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
259852419Sjulian		struct namelist *nl;
259952419Sjulian		node_p node;
260052419Sjulian		int num = 0;
260152419Sjulian
260270700Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
260352419Sjulian		/* Count number of nodes */
260470784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
260570912Sjulian			if (NG_NODE_IS_VALID(node)
260670912Sjulian			&& (unnamed || NG_NODE_HAS_NAME(node))) {
260752419Sjulian				num++;
260870912Sjulian			}
260952419Sjulian		}
261070700Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
261152419Sjulian
261252419Sjulian		/* Get response struct */
261370700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*nl)
261470700Sjulian		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
261552419Sjulian		if (resp == NULL) {
261652419Sjulian			error = ENOMEM;
261752419Sjulian			break;
261852419Sjulian		}
261970700Sjulian		nl = (struct namelist *) resp->data;
262052419Sjulian
262152419Sjulian		/* Cycle through the linked list of nodes */
262252419Sjulian		nl->numnames = 0;
262370700Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
262470784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
262552419Sjulian			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
262652419Sjulian
262752419Sjulian			if (nl->numnames >= num) {
262852419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
262952419Sjulian				    __FUNCTION__, "nodes");
263052419Sjulian				break;
263152419Sjulian			}
263270784Sjulian			if (NG_NODE_NOT_VALID(node))
263352419Sjulian				continue;
263470784Sjulian			if (!unnamed && (! NG_NODE_HAS_NAME(node)))
263552419Sjulian				continue;
263670784Sjulian			if (NG_NODE_HAS_NAME(node))
263770784Sjulian				strncpy(np->name, NG_NODE_NAME(node), NG_NODELEN);
263870784Sjulian			strncpy(np->type, node->nd_type->name, NG_TYPELEN);
263952722Sjulian			np->id = ng_node2ID(node);
264070784Sjulian			np->hooks = node->nd_numhooks;
264152419Sjulian			nl->numnames++;
264252419Sjulian		}
264370700Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
264452419Sjulian		break;
264552419Sjulian	    }
264652419Sjulian
264752419Sjulian	case NGM_LISTTYPES:
264852419Sjulian	    {
264952419Sjulian		struct typelist *tl;
265052419Sjulian		struct ng_type *type;
265152419Sjulian		int num = 0;
265252419Sjulian
265370700Sjulian		mtx_enter(&ng_typelist_mtx, MTX_DEF);
265452419Sjulian		/* Count number of types */
265570912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
265652419Sjulian			num++;
265770912Sjulian		}
265870700Sjulian		mtx_exit(&ng_typelist_mtx, MTX_DEF);
265952419Sjulian
266052419Sjulian		/* Get response struct */
266170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*tl)
266270700Sjulian		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
266352419Sjulian		if (resp == NULL) {
266452419Sjulian			error = ENOMEM;
266552419Sjulian			break;
266652419Sjulian		}
266770700Sjulian		tl = (struct typelist *) resp->data;
266852419Sjulian
266952419Sjulian		/* Cycle through the linked list of types */
267052419Sjulian		tl->numtypes = 0;
267170700Sjulian		mtx_enter(&ng_typelist_mtx, MTX_DEF);
267270700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
267352419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
267452419Sjulian
267552419Sjulian			if (tl->numtypes >= num) {
267652419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
267752419Sjulian				    __FUNCTION__, "types");
267852419Sjulian				break;
267952419Sjulian			}
268059879Sarchie			strncpy(tp->type_name, type->name, NG_TYPELEN);
268152419Sjulian			tp->numnodes = type->refs;
268252419Sjulian			tl->numtypes++;
268352419Sjulian		}
268470700Sjulian		mtx_exit(&ng_typelist_mtx, MTX_DEF);
268552419Sjulian		break;
268652419Sjulian	    }
268752419Sjulian
268853913Sarchie	case NGM_BINARY2ASCII:
268953913Sarchie	    {
269064510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
269153913Sarchie		const struct ng_parse_type *argstype;
269253913Sarchie		const struct ng_cmdlist *c;
269370700Sjulian		struct ng_mesg *binary, *ascii;
269453913Sarchie
269553913Sarchie		/* Data area must contain a valid netgraph message */
269653913Sarchie		binary = (struct ng_mesg *)msg->data;
269753913Sarchie		if (msg->header.arglen < sizeof(struct ng_mesg)
269870912Sjulian		    || (msg->header.arglen - sizeof(struct ng_mesg)
269970912Sjulian		      < binary->header.arglen)) {
270071047Sjulian			TRAP_ERROR();
270153913Sarchie			error = EINVAL;
270253913Sarchie			break;
270353913Sarchie		}
270453913Sarchie
270570700Sjulian		/* Get a response message with lots of room */
270670700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
270770159Sjulian		if (resp == NULL) {
270853913Sarchie			error = ENOMEM;
270953913Sarchie			break;
271053913Sarchie		}
271170700Sjulian		ascii = (struct ng_mesg *)resp->data;
271253913Sarchie
271353913Sarchie		/* Copy binary message header to response message payload */
271453913Sarchie		bcopy(binary, ascii, sizeof(*binary));
271553913Sarchie
271653913Sarchie		/* Find command by matching typecookie and command number */
271770784Sjulian		for (c = here->nd_type->cmdlist;
271853913Sarchie		    c != NULL && c->name != NULL; c++) {
271953913Sarchie			if (binary->header.typecookie == c->cookie
272053913Sarchie			    && binary->header.cmd == c->cmd)
272153913Sarchie				break;
272253913Sarchie		}
272353913Sarchie		if (c == NULL || c->name == NULL) {
272453913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
272553913Sarchie				if (binary->header.typecookie == c->cookie
272653913Sarchie				    && binary->header.cmd == c->cmd)
272753913Sarchie					break;
272853913Sarchie			}
272953913Sarchie			if (c->name == NULL) {
273070700Sjulian				NG_FREE_MSG(resp);
273153913Sarchie				error = ENOSYS;
273253913Sarchie				break;
273353913Sarchie			}
273453913Sarchie		}
273553913Sarchie
273653913Sarchie		/* Convert command name to ASCII */
273753913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
273853913Sarchie		    "%s", c->name);
273953913Sarchie
274053913Sarchie		/* Convert command arguments to ASCII */
274153913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
274253913Sarchie		    c->respType : c->mesgType;
274370912Sjulian		if (argstype == NULL) {
274453913Sarchie			*ascii->data = '\0';
274570912Sjulian		} else {
274653913Sarchie			if ((error = ng_unparse(argstype,
274753913Sarchie			    (u_char *)binary->data,
274853913Sarchie			    ascii->data, bufSize)) != 0) {
274970700Sjulian				NG_FREE_MSG(resp);
275053913Sarchie				break;
275153913Sarchie			}
275253913Sarchie		}
275353913Sarchie
275453913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
275553913Sarchie		bufSize = strlen(ascii->data) + 1;
275653913Sarchie		ascii->header.arglen = bufSize;
275770700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
275853913Sarchie		break;
275953913Sarchie	    }
276053913Sarchie
276153913Sarchie	case NGM_ASCII2BINARY:
276253913Sarchie	    {
276353913Sarchie		int bufSize = 2000;	/* XXX hard coded constant */
276453913Sarchie		const struct ng_cmdlist *c;
276553913Sarchie		const struct ng_parse_type *argstype;
276670700Sjulian		struct ng_mesg *ascii, *binary;
276759178Sarchie		int off = 0;
276853913Sarchie
276953913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
277053913Sarchie		ascii = (struct ng_mesg *)msg->data;
277170912Sjulian		if ((msg->header.arglen < sizeof(*ascii) + 1)
277270912Sjulian		    || (ascii->header.arglen < 1)
277370912Sjulian		    || (msg->header.arglen
277470912Sjulian		      < sizeof(*ascii) + ascii->header.arglen)) {
277571047Sjulian			TRAP_ERROR();
277653913Sarchie			error = EINVAL;
277753913Sarchie			break;
277853913Sarchie		}
277953913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
278053913Sarchie
278170700Sjulian		/* Get a response message with lots of room */
278270700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
278370159Sjulian		if (resp == NULL) {
278453913Sarchie			error = ENOMEM;
278553913Sarchie			break;
278653913Sarchie		}
278770700Sjulian		binary = (struct ng_mesg *)resp->data;
278853913Sarchie
278953913Sarchie		/* Copy ASCII message header to response message payload */
279053913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
279153913Sarchie
279253913Sarchie		/* Find command by matching ASCII command string */
279370784Sjulian		for (c = here->nd_type->cmdlist;
279453913Sarchie		    c != NULL && c->name != NULL; c++) {
279553913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
279653913Sarchie				break;
279753913Sarchie		}
279853913Sarchie		if (c == NULL || c->name == NULL) {
279953913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
280053913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
280153913Sarchie					break;
280253913Sarchie			}
280353913Sarchie			if (c->name == NULL) {
280470700Sjulian				NG_FREE_MSG(resp);
280553913Sarchie				error = ENOSYS;
280653913Sarchie				break;
280753913Sarchie			}
280853913Sarchie		}
280953913Sarchie
281053913Sarchie		/* Convert command name to binary */
281153913Sarchie		binary->header.cmd = c->cmd;
281253913Sarchie		binary->header.typecookie = c->cookie;
281353913Sarchie
281453913Sarchie		/* Convert command arguments to binary */
281553913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
281653913Sarchie		    c->respType : c->mesgType;
281770912Sjulian		if (argstype == NULL) {
281853913Sarchie			bufSize = 0;
281970912Sjulian		} else {
282053913Sarchie			if ((error = ng_parse(argstype, ascii->data,
282153913Sarchie			    &off, (u_char *)binary->data, &bufSize)) != 0) {
282270700Sjulian				NG_FREE_MSG(resp);
282353913Sarchie				break;
282453913Sarchie			}
282553913Sarchie		}
282653913Sarchie
282753913Sarchie		/* Return the result */
282853913Sarchie		binary->header.arglen = bufSize;
282970700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
283053913Sarchie		break;
283153913Sarchie	    }
283253913Sarchie
283362471Sphk	case NGM_TEXT_CONFIG:
283452419Sjulian	case NGM_TEXT_STATUS:
283552419Sjulian		/*
283652419Sjulian		 * This one is tricky as it passes the command down to the
283752419Sjulian		 * actual node, even though it is a generic type command.
283870700Sjulian		 * This means we must assume that the item/msg is already freed
283952419Sjulian		 * when control passes back to us.
284052419Sjulian		 */
284170784Sjulian		if (here->nd_type->rcvmsg != NULL) {
284270700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
284370784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
284452419Sjulian		}
284552419Sjulian		/* Fall through if rcvmsg not supported */
284652419Sjulian	default:
284771047Sjulian		TRAP_ERROR();
284852419Sjulian		error = EINVAL;
284952419Sjulian	}
285070700Sjulian	/*
285170700Sjulian	 * Sometimes a generic message may be statically allocated
285270700Sjulian	 * to avoid problems with allocating when in tight memeory situations.
285370700Sjulian	 * Don't free it if it is so.
285470700Sjulian	 * I break them appart here, because erros may cause a free if the item
285570700Sjulian	 * in which case we'd be doing it twice.
285670700Sjulian	 * they are kept together above, to simplify freeing.
285770700Sjulian	 */
285870700Sjulianout:
285970700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
286070700Sjulian	if ( msg && ((msg->header.flags & NGF_STATIC) == 0))
286170700Sjulian		NG_FREE_MSG(msg);
286252419Sjulian	return (error);
286352419Sjulian}
286452419Sjulian
286552419Sjulian/*
286659879Sarchie * Copy a 'meta'.
286759879Sarchie *
286859879Sarchie * Returns new meta, or NULL if original meta is NULL or ENOMEM.
286959879Sarchie */
287059879Sarchiemeta_p
287159879Sarchieng_copy_meta(meta_p meta)
287259879Sarchie{
287359879Sarchie	meta_p meta2;
287459879Sarchie
287559879Sarchie	if (meta == NULL)
287659879Sarchie		return (NULL);
287770700Sjulian	MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH_META, M_NOWAIT);
287859879Sarchie	if (meta2 == NULL)
287959879Sarchie		return (NULL);
288059879Sarchie	meta2->allocated_len = meta->used_len;
288159879Sarchie	bcopy(meta, meta2, meta->used_len);
288259879Sarchie	return (meta2);
288359879Sarchie}
288459879Sarchie
288552419Sjulian/************************************************************************
288652419Sjulian			Module routines
288752419Sjulian************************************************************************/
288852419Sjulian
288952419Sjulian/*
289052419Sjulian * Handle the loading/unloading of a netgraph node type module
289152419Sjulian */
289252419Sjulianint
289352419Sjulianng_mod_event(module_t mod, int event, void *data)
289452419Sjulian{
289552419Sjulian	struct ng_type *const type = data;
289652419Sjulian	int s, error = 0;
289752419Sjulian
289852419Sjulian	switch (event) {
289952419Sjulian	case MOD_LOAD:
290052419Sjulian
290152419Sjulian		/* Register new netgraph node type */
290252419Sjulian		s = splnet();
290352419Sjulian		if ((error = ng_newtype(type)) != 0) {
290452419Sjulian			splx(s);
290552419Sjulian			break;
290652419Sjulian		}
290752419Sjulian
290852419Sjulian		/* Call type specific code */
290952419Sjulian		if (type->mod_event != NULL)
291070700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
291170700Sjulian				mtx_enter(&ng_typelist_mtx, MTX_DEF);
291252419Sjulian				LIST_REMOVE(type, types);
291370700Sjulian				mtx_exit(&ng_typelist_mtx, MTX_DEF);
291470700Sjulian			}
291552419Sjulian		splx(s);
291652419Sjulian		break;
291752419Sjulian
291852419Sjulian	case MOD_UNLOAD:
291952419Sjulian		s = splnet();
292052419Sjulian		if (type->refs != 0)		/* make sure no nodes exist! */
292152419Sjulian			error = EBUSY;
292252419Sjulian		else {
292352419Sjulian			if (type->mod_event != NULL) {	/* check with type */
292452419Sjulian				error = (*type->mod_event)(mod, event, data);
292552419Sjulian				if (error != 0) {	/* type refuses.. */
292652419Sjulian					splx(s);
292752419Sjulian					break;
292852419Sjulian				}
292952419Sjulian			}
293070700Sjulian			mtx_enter(&ng_typelist_mtx, MTX_DEF);
293152419Sjulian			LIST_REMOVE(type, types);
293270700Sjulian			mtx_exit(&ng_typelist_mtx, MTX_DEF);
293352419Sjulian		}
293452419Sjulian		splx(s);
293552419Sjulian		break;
293652419Sjulian
293752419Sjulian	default:
293852419Sjulian		if (type->mod_event != NULL)
293952419Sjulian			error = (*type->mod_event)(mod, event, data);
294052419Sjulian		else
294152419Sjulian			error = 0;		/* XXX ? */
294252419Sjulian		break;
294352419Sjulian	}
294452419Sjulian	return (error);
294552419Sjulian}
294652419Sjulian
294752419Sjulian/*
294852419Sjulian * Handle loading and unloading for this code.
294952419Sjulian * The only thing we need to link into is the NETISR strucure.
295052419Sjulian */
295152419Sjulianstatic int
295252419Sjulianngb_mod_event(module_t mod, int event, void *data)
295352419Sjulian{
295452419Sjulian	int s, error = 0;
295552419Sjulian
295652419Sjulian	switch (event) {
295752419Sjulian	case MOD_LOAD:
295852419Sjulian		/* Register line discipline */
295970700Sjulian		mtx_init(&ng_worklist_mtx, "netgraph worklist mutex", 0);
296070700Sjulian		mtx_init(&ng_typelist_mtx, "netgraph types mutex", 0);
296170700Sjulian		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", 0);
296270700Sjulian		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", 0);
296370700Sjulian		mtx_init(&ngq_mtx, "netgraph netisr mutex", 0);
296452419Sjulian		s = splimp();
296552419Sjulian		error = register_netisr(NETISR_NETGRAPH, ngintr);
296652419Sjulian		splx(s);
296752419Sjulian		break;
296852419Sjulian	case MOD_UNLOAD:
296952419Sjulian		/* You cant unload it because an interface may be using it.  */
297052419Sjulian		error = EBUSY;
297152419Sjulian		break;
297252419Sjulian	default:
297352419Sjulian		error = EOPNOTSUPP;
297452419Sjulian		break;
297552419Sjulian	}
297652419Sjulian	return (error);
297752419Sjulian}
297852419Sjulian
297952419Sjulianstatic moduledata_t netgraph_mod = {
298052419Sjulian	"netgraph",
298152419Sjulian	ngb_mod_event,
298252419Sjulian	(NULL)
298352419Sjulian};
298452419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
298552419Sjulian
298652419Sjulian/************************************************************************
298770700Sjulian			Queue element get/free routines
298852419Sjulian************************************************************************/
298952419Sjulian
299052419Sjulian
299170700Sjulianstatic int			allocated;	/* number of items malloc'd */
299270700Sjulianstatic int			maxalloc = 128;	/* limit the damage of a leak */
299370700Sjulianstatic const int		ngqfreemax = 64;/* cache at most this many */
299470700Sjulianstatic const int		ngqfreelow = 4; /* try malloc if free < this */
299570700Sjulianstatic volatile int		ngqfreesize;	/* number of cached entries */
299670784Sjulian#ifdef	NETGRAPH_DEBUG
299770700Sjulianstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
299870700Sjulian#endif
299952419Sjulian/*
300052419Sjulian * Get a queue entry
300170700Sjulian * This is usually called when a packet first enters netgraph.
300270700Sjulian * By definition, this is usually from an interrupt, or from a user.
300370700Sjulian * Users are not so important, but try be quick for the times that it's
300470700Sjulian * an interrupt. Use atomic operations to cope with collisions
300570700Sjulian * with interrupts and other processors. Assumes MALLOC is SMP safe.
300670700Sjulian * XXX If reserve is low, we should try to get 2 from malloc as this
300770700Sjulian * would indicate it often fails.
300852419Sjulian */
300970700Sjulianstatic item_p
301052419Sjulianng_getqblk(void)
301152419Sjulian{
301270700Sjulian	item_p item = NULL;
301352419Sjulian
301470700Sjulian	/*
301570700Sjulian	 * Try get a cached queue block, or else allocate a new one
301670700Sjulian	 * If we are less than our reserve, try malloc. If malloc
301770700Sjulian	 * fails, then that's what the reserve is for...
301870700Sjulian	 * Don't completely trust ngqfreesize, as it is subject
301970700Sjulian	 * to races.. (it'll eventually catch up but may be out by one or two
302070700Sjulian	 * for brief moments(under SMP or interrupts).
302170700Sjulian	 * ngqfree is the final arbiter. We have our little reserve
302270700Sjulian	 * because we use M_NOWAIT for malloc. This just helps us
302370700Sjulian	 * avoid dropping packets while not increasing the time
302470700Sjulian	 * we take to service the interrupt (on average) (we hope).
302570700Sjulian	 */
302670700Sjulian	for (;;) {
302770700Sjulian		if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) {
302870700Sjulian			if (allocated < maxalloc) {  /* don't leak forever */
302970700Sjulian				MALLOC(item, item_p ,
303070700Sjulian				    sizeof(*item), M_NETGRAPH_ITEM,
303170700Sjulian				    (M_NOWAIT | M_ZERO));
303270700Sjulian				if (item) {
303370784Sjulian#ifdef	NETGRAPH_DEBUG
303470700Sjulian					TAILQ_INSERT_TAIL(&ng_itemlist,
303570700Sjulian								item, all);
303670784Sjulian#endif	/* NETGRAPH_DEBUG */
303770700Sjulian					atomic_add_int(&allocated, 1);
303870700Sjulian					break;
303970700Sjulian				}
304070700Sjulian			}
304170700Sjulian		}
304252419Sjulian
304370700Sjulian		/*
304470700Sjulian		 * We didn't or couldn't malloc.
304570700Sjulian		 * try get one from our cache.
304670700Sjulian		 * item must be NULL to get here.
304770700Sjulian		 */
304870700Sjulian		if ((item = ngqfree) != NULL) {
304970700Sjulian			/*
305070700Sjulian			 * Atomically try grab the first item
305170700Sjulian			 * and put it's successor in its place.
305270700Sjulian			 * If we fail, just try again.. someone else
305370700Sjulian			 * beat us to this one or freed one.
305470700Sjulian			 * Don't worry about races with ngqfreesize.
305570700Sjulian			 * Close enough is good enough..
305670700Sjulian			 */
305770700Sjulian			if (atomic_cmpset_ptr(&ngqfree, item, item->el_next)) {
305870700Sjulian				atomic_subtract_int(&ngqfreesize, 1);
305970700Sjulian				break;
306070700Sjulian			}
306170700Sjulian			item = NULL;
306270700Sjulian		} else {
306370700Sjulian			/* We really ran out */
306470700Sjulian			break;
306552419Sjulian		}
306652419Sjulian	}
306770700Sjulian	item->el_flags &= ~NGQF_FREE;
306870700Sjulian	return (item);
306952419Sjulian}
307052419Sjulian
307152419Sjulian/*
307252419Sjulian * Release a queue entry
307352419Sjulian */
307470700Sjulianvoid
307570700Sjulianng_free_item(item_p item)
307652419Sjulian{
307752419Sjulian
307870700Sjulian	/*
307970700Sjulian	 * The item may hold resources on it's own. We need to free
308070700Sjulian	 * these before we can free the item. What they are depends upon
308170700Sjulian	 * what kind of item it is. it is important that nodes zero
308270700Sjulian	 * out pointers to resources that they remove from the item
308370700Sjulian	 * or we release them again here.
308470700Sjulian	 */
308570700Sjulian	if (item->el_flags & NGQF_FREE) {
308670700Sjulian		panic(" Freeing free queue item");
308752419Sjulian	}
308871047Sjulian	switch (item->el_flags & NGQF_TYPE) {
308970700Sjulian	case NGQF_DATA:
309070700Sjulian		/* If we have an mbuf and metadata still attached.. */
309170700Sjulian		NG_FREE_M(_NGI_M(item));
309270700Sjulian		NG_FREE_META(_NGI_META(item));
309370700Sjulian		break;
309470700Sjulian	case NGQF_MESG:
309570700Sjulian		_NGI_RETADDR(item) = NULL;
309670700Sjulian		NG_FREE_MSG(_NGI_MSG(item));
309770700Sjulian		break;
309871047Sjulian	case NGQF_FN:
309971047Sjulian		/* nothing to free really, */
310071047Sjulian		_NGI_FN(item) = NULL;
310171047Sjulian		_NGI_ARG1(item) = NULL;
310271047Sjulian		_NGI_ARG2(item) = 0;
310371047Sjulian	case NGQF_UNDEF:
310452419Sjulian	}
310570700Sjulian		/* If we still have a node or hook referenced... */
310670700Sjulian	if (item->el_dest) {
310770784Sjulian		NG_NODE_UNREF(item->el_dest);
310870700Sjulian		item->el_dest = NULL;
310970700Sjulian	}
311070700Sjulian	if (item->el_hook) {
311170784Sjulian		NG_HOOK_UNREF(item->el_hook);
311270700Sjulian		item->el_hook = NULL;
311370700Sjulian	}
311470700Sjulian	item->el_flags |= NGQF_FREE;
311552419Sjulian
311670700Sjulian	/*
311770700Sjulian	 * We have freed any resources held by the item.
311870700Sjulian	 * now we can free the item itself.
311970700Sjulian	 */
312070700Sjulian	if (ngqfreesize < ngqfreemax) { /* don't worry about races */
312170700Sjulian		for (;;) {
312270700Sjulian			item->el_next = ngqfree;
312370700Sjulian			if (atomic_cmpset_ptr(&ngqfree, item->el_next, item)) {
312470700Sjulian				break;
312570700Sjulian			}
312670700Sjulian		}
312770700Sjulian		atomic_add_int(&ngqfreesize, 1);
312870700Sjulian	} else {
312970700Sjulian		/* This is the only place that should use this Macro */
313070784Sjulian#ifdef	NETGRAPH_DEBUG
313170700Sjulian		TAILQ_REMOVE(&ng_itemlist, item, all);
313270784Sjulian#endif	/* NETGRAPH_DEBUG */
313370700Sjulian		NG_FREE_ITEM_REAL(item);
313470700Sjulian		atomic_subtract_int(&allocated, 1);
313570700Sjulian	}
313670700Sjulian}
313752419Sjulian
313870784Sjulian#ifdef	NETGRAPH_DEBUG
313970700Sjulianvoid
314070784Sjuliandumphook (hook_p hook, char *file, int line)
314170784Sjulian{
314270784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
314370784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
314470784Sjulian	printf("	Last active @ %s, line %d\n",
314570784Sjulian		hook->lastfile, hook->lastline);
314670784Sjulian	if (line) {
314770784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
314870784Sjulian	}
314970784Sjulian}
315070784Sjulian
315170784Sjulianvoid
315270784Sjuliandumpnode(node_p node, char *file, int line)
315370784Sjulian{
315470784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
315571047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
315670784Sjulian		node->nd_numhooks, node->nd_flags,
315770784Sjulian		node->nd_refs, node->nd_name);
315870784Sjulian	printf("	Last active @ %s, line %d\n",
315970784Sjulian		node->lastfile, node->lastline);
316070784Sjulian	if (line) {
316170784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
316270784Sjulian	}
316370784Sjulian}
316470784Sjulian
316570784Sjulianvoid
316670700Sjuliandumpitem(item_p item, char *file, int line)
316770700Sjulian{
316870700Sjulian	if (item->el_flags & NGQF_FREE) {
316970700Sjulian		printf(" Free item, freed at %s, line %d\n",
317070700Sjulian			item->lastfile, item->lastline);
317152419Sjulian	} else {
317270700Sjulian		printf(" ACTIVE item, last used at %s, line %d",
317370700Sjulian			item->lastfile, item->lastline);
317471047Sjulian		switch(item->el_flags & NGQF_TYPE) {
317571047Sjulian		case NGQF_DATA:
317671047Sjulian			printf(" - [data]\n");
317771047Sjulian			break;
317871047Sjulian		case NGQF_MESG:
317970700Sjulian			printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
318071047Sjulian			break;
318171047Sjulian		case NGQF_FN:
318271047Sjulian			printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
318371047Sjulian				item->body.fn.fn_fn,
318471047Sjulian				item->el_dest,
318571047Sjulian				item->el_hook,
318671047Sjulian				item->body.fn.fn_arg1,
318771047Sjulian				item->body.fn.fn_arg2,
318871047Sjulian				item->body.fn.fn_arg2);
318971047Sjulian			break;
319071047Sjulian		case NGQF_UNDEF:
319171047Sjulian			printf(" - UNDEFINED!\n");
319270700Sjulian		}
319352419Sjulian	}
319470784Sjulian	if (line) {
319570784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
319670784Sjulian		if (item->el_dest) {
319770784Sjulian			printf("node %p ([%x])\n",
319870784Sjulian				item->el_dest, ng_node2ID(item->el_dest));
319970784Sjulian		}
320070784Sjulian	}
320170700Sjulian}
320252419Sjulian
320370784Sjulianstatic void
320470784Sjulianng_dumpitems(void)
320570784Sjulian{
320670784Sjulian	item_p item;
320770784Sjulian	int i = 1;
320870784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
320970784Sjulian		printf("[%d] ", i++);
321070784Sjulian		dumpitem(item, NULL, 0);
321170784Sjulian	}
321270784Sjulian}
321370784Sjulian
321470784Sjulianstatic void
321570784Sjulianng_dumpnodes(void)
321670784Sjulian{
321770784Sjulian	node_p node;
321870784Sjulian	int i = 1;
321970784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
322070784Sjulian		printf("[%d] ", i++);
322170784Sjulian		dumpnode(node, NULL, 0);
322270784Sjulian	}
322370784Sjulian}
322470784Sjulian
322570784Sjulianstatic void
322670784Sjulianng_dumphooks(void)
322770784Sjulian{
322870784Sjulian	hook_p hook;
322970784Sjulian	int i = 1;
323070784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
323170784Sjulian		printf("[%d] ", i++);
323270784Sjulian		dumphook(hook, NULL, 0);
323370784Sjulian	}
323470784Sjulian}
323570784Sjulian
323670700Sjulianstatic int
323770700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
323870700Sjulian{
323970700Sjulian	int error;
324070700Sjulian	int val;
324170700Sjulian	int i;
324270700Sjulian
324370700Sjulian	val = allocated;
324470700Sjulian	i = 1;
324570700Sjulian	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
324671047Sjulian	if (error != 0 || req->newptr == NULL)
324771047Sjulian		return (error);
324871047Sjulian	if (val == 42) {
324970784Sjulian		ng_dumpitems();
325070784Sjulian		ng_dumpnodes();
325170784Sjulian		ng_dumphooks();
325270700Sjulian	}
325371047Sjulian	return (0);
325452419Sjulian}
325552419Sjulian
325671047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
325771047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
325870784Sjulian#endif	/* NETGRAPH_DEBUG */
325970700Sjulian
326070700Sjulian
326170700Sjulian/***********************************************************************
326270700Sjulian* Worklist routines
326370700Sjulian**********************************************************************/
326470700Sjulian/* NETISR thread enters here */
326552419Sjulian/*
326670700Sjulian * Pick a node off the list of nodes with work,
326770700Sjulian * try get an item to process off it.
326870700Sjulian * If there are no more, remove the node from the list.
326952419Sjulian */
327070700Sjulianstatic void
327170700Sjulianngintr(void)
327252419Sjulian{
327370700Sjulian	item_p item;
327470700Sjulian	node_p  node = NULL;
327552419Sjulian
327670700Sjulian	for (;;) {
327770700Sjulian		mtx_enter(&ng_worklist_mtx, MTX_SPIN);
327870700Sjulian		node = TAILQ_FIRST(&ng_worklist);
327970700Sjulian		if (!node) {
328070700Sjulian			mtx_exit(&ng_worklist_mtx, MTX_SPIN);
328170700Sjulian			break;
328269922Sjulian		}
328370784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
328470700Sjulian		mtx_exit(&ng_worklist_mtx, MTX_SPIN);
328570700Sjulian		/*
328670700Sjulian		 * We have the node. We also take over the reference
328770700Sjulian		 * that the list had on it.
328870700Sjulian		 * Now process as much as you can, until it won't
328970700Sjulian		 * let you have another item off the queue.
329070700Sjulian		 * All this time, keep the reference
329170700Sjulian		 * that lets us be sure that the node still exists.
329270700Sjulian		 * Let the reference go at the last minute.
329370700Sjulian		 */
329470700Sjulian		for (;;) {
329570784Sjulian			mtx_enter(&node->nd_input_queue.q_mtx, MTX_SPIN);
329670784Sjulian			item = ng_dequeue(&node->nd_input_queue);
329770700Sjulian			if (item == NULL) {
329870700Sjulian				/*
329970700Sjulian				 * Say we are on the queue as long as
330070700Sjulian				 * we are processing it here.
330170700Sjulian				 * it probably wouldn't come here while we
330270700Sjulian				 * are processing anyhow.
330370700Sjulian				 */
330470784Sjulian				node->nd_flags &= ~NG_WORKQ;
330570784Sjulian				mtx_exit(&node->nd_input_queue.q_mtx, MTX_SPIN);
330670784Sjulian				NG_NODE_UNREF(node);
330770700Sjulian				break; /* go look for another node */
330870700Sjulian			} else {
330970784Sjulian				mtx_exit(&node->nd_input_queue.q_mtx, MTX_SPIN);
331070784Sjulian#ifdef	NETGRAPH_DEBUG
331170700Sjulian        _ngi_check(item, __FILE__, __LINE__);
331270700Sjulian#endif
331370700Sjulian				ng_apply_item(node, item);
331470700Sjulian			}
331570700Sjulian		}
331652419Sjulian	}
331770700Sjulian}
331869922Sjulian
331970700Sjulianstatic void
332070700Sjulianng_worklist_remove(node_p node)
332170700Sjulian{
332270700Sjulian	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
332370784Sjulian	if (node->nd_flags & NG_WORKQ) {
332470784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
332570784Sjulian		NG_NODE_UNREF(node);
332670700Sjulian	}
332770784Sjulian	node->nd_flags &= ~NG_WORKQ;
332870700Sjulian	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
332970700Sjulian}
333070700Sjulian
333170700Sjulianstatic void
333270700Sjulianng_setisr(node_p node)
333370700Sjulian{
333470700Sjulian	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
333570784Sjulian	if ((node->nd_flags & NG_WORKQ) == 0) {
333669922Sjulian		/*
333770700Sjulian		 * If we are not already on the work queue,
333870700Sjulian		 * then put us on.
333969922Sjulian		 */
334070784Sjulian		node->nd_flags |= NG_WORKQ;
334170784Sjulian		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
334270784Sjulian		NG_NODE_REF(node);
334370700Sjulian	}
334470700Sjulian	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
334570700Sjulian	schednetisr(NETISR_NETGRAPH);
334670700Sjulian}
334770700Sjulian
334870700Sjulian
334970700Sjulian/***********************************************************************
335070700Sjulian* Externally useable functions to set up a queue item ready for sending
335170700Sjulian***********************************************************************/
335270700Sjulian
335370784Sjulian#ifdef	NETGRAPH_DEBUG
335470784Sjulian#define	ITEM_DEBUG_CHECKS						\
335570700Sjulian	do {								\
335670700Sjulian		if (item->el_dest ) {					\
335770700Sjulian			printf("item already has node");		\
335870700Sjulian			Debugger("has node");				\
335970784Sjulian			NG_NODE_UNREF(item->el_dest);			\
336070700Sjulian			item->el_dest = NULL;				\
336170700Sjulian		}							\
336270700Sjulian		if (item->el_hook ) {					\
336370700Sjulian			printf("item already has hook");		\
336470700Sjulian			Debugger("has hook");				\
336570784Sjulian			NG_HOOK_UNREF(item->el_hook);			\
336670700Sjulian			item->el_hook = NULL;				\
336770700Sjulian		}							\
336870700Sjulian	} while (0)
336970700Sjulian#else
337070784Sjulian#define ITEM_DEBUG_CHECKS
337170700Sjulian#endif
337270700Sjulian
337370700Sjulian/*
337470700Sjulian * Put elements into the item.
337570700Sjulian * Hook and node references will be removed when the item is dequeued.
337670700Sjulian * (or equivalent)
337770700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
337870700Sjulian * remote node might go away in this timescale.
337970700Sjulian * We know the hooks can't go away because that would require getting
338070700Sjulian * a writer item on both nodes and we must have at least a  reader
338170700Sjulian * here to eb able to do this.
338270700Sjulian * Note that the hook loaded is the REMOTE hook.
338370700Sjulian *
338470700Sjulian * This is possibly in the critical path for new data.
338570700Sjulian */
338670700Sjulianitem_p
338770700Sjulianng_package_data(struct mbuf *m, meta_p meta)
338870700Sjulian{
338970700Sjulian	item_p item;
339070700Sjulian
339170700Sjulian	if ((item = ng_getqblk()) == NULL) {
339270700Sjulian		NG_FREE_M(m);
339370700Sjulian		NG_FREE_META(meta);
339470700Sjulian		return (NULL);
339570700Sjulian	}
339670784Sjulian	ITEM_DEBUG_CHECKS;
339770700Sjulian	item->el_flags = NGQF_DATA;
339870700Sjulian	item->el_next = NULL;
339970700Sjulian	NGI_M(item) = m;
340070700Sjulian	NGI_META(item) = meta;
340170700Sjulian	return (item);
340270700Sjulian}
340370700Sjulian
340470700Sjulian/*
340570700Sjulian * Allocate a queue item and put items into it..
340670700Sjulian * Evaluate the address as this will be needed to queue it and
340770700Sjulian * to work out what some of the fields should be.
340870700Sjulian * Hook and node references will be removed when the item is dequeued.
340970700Sjulian * (or equivalent)
341070700Sjulian */
341170700Sjulianitem_p
341270700Sjulianng_package_msg(struct ng_mesg *msg)
341370700Sjulian{
341470700Sjulian	item_p item;
341570700Sjulian
341670700Sjulian	if ((item = ng_getqblk()) == NULL) {
341770700Sjulian		if ((msg->header.flags & NGF_STATIC) == 0) {
341870700Sjulian			NG_FREE_MSG(msg);
341969922Sjulian		}
342070700Sjulian		return (NULL);
342169922Sjulian	}
342270784Sjulian	ITEM_DEBUG_CHECKS;
342370700Sjulian	item->el_flags = NGQF_MESG;
342470700Sjulian	item->el_next = NULL;
342570700Sjulian	/*
342670700Sjulian	 * Set the current lasthook into the queue item
342770700Sjulian	 */
342870700Sjulian	NGI_MSG(item) = msg;
342970700Sjulian	NGI_RETADDR(item) = NULL;
343070700Sjulian	return (item);
343170700Sjulian}
343269922Sjulian
343370700Sjulian
343470700Sjulian
343570700Sjulian#define SET_RETADDR							\
343671047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
343771047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
343870700Sjulian			if (retaddr) {					\
343970700Sjulian				NGI_RETADDR(item) = retaddr;		\
344070700Sjulian			} else {					\
344170700Sjulian				/*					\
344270700Sjulian				 * The old return address should be ok.	\
344370700Sjulian				 * If there isn't one, use the address	\
344470700Sjulian				 * here.				\
344570700Sjulian				 */					\
344670700Sjulian				if (NGI_RETADDR(item) == 0) {		\
344770700Sjulian					NGI_RETADDR(item)		\
344870700Sjulian						= ng_node2ID(here);	\
344970700Sjulian				}					\
345070700Sjulian			}						\
345170700Sjulian		}							\
345270700Sjulian	} while (0)
345370700Sjulian
345470700Sjulianint
345570700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
345670700Sjulian{
345770784Sjulian	ITEM_DEBUG_CHECKS;
345870700Sjulian	/*
345970700Sjulian	 * Quick sanity check..
346070784Sjulian	 * Since a hook holds a reference on it's node, once we know
346170784Sjulian	 * that the peer is still connected (even if invalid,) we know
346270784Sjulian	 * that the peer node is present, though maybe invalid.
346370700Sjulian	 */
346470700Sjulian	if ((hook == NULL)
346570784Sjulian	|| NG_HOOK_NOT_VALID(hook)
346670784Sjulian	|| (NG_HOOK_PEER(hook) == NULL)
346770784Sjulian	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
346870784Sjulian	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
346970700Sjulian		NG_FREE_ITEM(item);
347071047Sjulian		TRAP_ERROR();
347170700Sjulian		return (EINVAL);
347252419Sjulian	}
347352419Sjulian
347470700Sjulian	/*
347570700Sjulian	 * Transfer our interest to the other (peer) end.
347670700Sjulian	 */
347770784Sjulian	item->el_hook = NG_HOOK_PEER(hook);
347870784Sjulian	NG_HOOK_REF(item->el_hook); /* Don't let it go while on the queue */
347970784Sjulian	item->el_dest = NG_PEER_NODE(hook);
348070784Sjulian	NG_NODE_REF(item->el_dest); /* Nor this */
348170700Sjulian	SET_RETADDR;
348270700Sjulian	return (0);
348370700Sjulian}
348452419Sjulian
348570700Sjulianint
348670700Sjulianng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
348770700Sjulian{
348870700Sjulian	node_p  dest = NULL;
348970700Sjulian	hook_p	hook = NULL;
349070700Sjulian	int     error;
349170700Sjulian
349270784Sjulian	ITEM_DEBUG_CHECKS;
349370700Sjulian	/*
349470700Sjulian	 * Note that ng_path2noderef increments the reference count
349570700Sjulian	 * on the node for us if it finds one. So we don't have to.
349670700Sjulian	 */
349770700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
349870700Sjulian	if (error) {
349970700Sjulian		NG_FREE_ITEM(item);
350070784Sjulian		return (error);
350152419Sjulian	}
350270700Sjulian	item->el_dest = dest;
350370700Sjulian	if (( item->el_hook = hook))
350470784Sjulian		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
350570700Sjulian	SET_RETADDR;
350670700Sjulian	return (0);
350770700Sjulian}
350852419Sjulian
350970700Sjulianint
351070700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
351170700Sjulian{
351270700Sjulian	node_p dest;
351370700Sjulian
351470784Sjulian	ITEM_DEBUG_CHECKS;
351570700Sjulian	/*
351670700Sjulian	 * Find the target node.
351770700Sjulian	 */
351870700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
351970700Sjulian	if (dest == NULL) {
352070700Sjulian		NG_FREE_ITEM(item);
352171047Sjulian		TRAP_ERROR();
352270700Sjulian		return(EINVAL);
352370700Sjulian	}
352470700Sjulian	/* Fill out the contents */
352570700Sjulian	item->el_flags = NGQF_MESG;
352670700Sjulian	item->el_next = NULL;
352770700Sjulian	item->el_dest = dest;
352870700Sjulian	item->el_hook = NULL;
352970700Sjulian	SET_RETADDR;
353052419Sjulian	return (0);
353152419Sjulian}
353252419Sjulian
353352419Sjulian/*
353470700Sjulian * special case to send a message to self (e.g. destroy node)
353570700Sjulian * Possibly indicate an arrival hook too.
353670700Sjulian * Useful for removing that hook :-)
353752419Sjulian */
353870700Sjulianitem_p
353970700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
354052419Sjulian{
354170700Sjulian	item_p item;
354252419Sjulian
354370700Sjulian	/*
354470700Sjulian	 * Find the target node.
354570700Sjulian	 * If there is a HOOK argument, then use that in preference
354670700Sjulian	 * to the address.
354770700Sjulian	 */
354870700Sjulian	if ((item = ng_getqblk()) == NULL) {
354970700Sjulian		if ((msg->header.flags & NGF_STATIC) == 0) {
355070700Sjulian			NG_FREE_MSG(msg);
355152419Sjulian		}
355270700Sjulian		return (NULL);
355352419Sjulian	}
355470700Sjulian
355570700Sjulian	/* Fill out the contents */
355670700Sjulian	item->el_flags = NGQF_MESG;
355770700Sjulian	item->el_next = NULL;
355870700Sjulian	item->el_dest = here;
355970784Sjulian	NG_NODE_REF(here);
356070700Sjulian	item->el_hook = hook;
356170700Sjulian	if (hook)
356270784Sjulian		NG_HOOK_REF(hook);
356370700Sjulian	NGI_MSG(item) = msg;
356470700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
356570700Sjulian	return (item);
356652419Sjulian}
356752419Sjulian
356871047Sjulianint
356971047Sjulianng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
357071047Sjulian{
357171047Sjulian	item_p item;
357271047Sjulian
357371047Sjulian	if ((item = ng_getqblk()) == NULL) {
357471047Sjulian		return (ENOMEM);
357571047Sjulian	}
357671047Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
357771047Sjulian	item->el_dest = node;
357871047Sjulian	NG_NODE_REF(node);
357971047Sjulian	if ((item->el_hook = hook)) {
358071047Sjulian		NG_HOOK_REF(hook);
358171047Sjulian	}
358271047Sjulian	NGI_FN(item) = fn;
358371047Sjulian	NGI_ARG1(item) = arg1;
358471047Sjulian	NGI_ARG2(item) = arg2;
358571047Sjulian	return (ng_snd_item(item, 0));
358671047Sjulian}
358771047Sjulian
358870700Sjulian/*
358970700Sjulian * Set the address, if none given, give the node here.
359070700Sjulian */
359170700Sjulianvoid
359270700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
359370700Sjulian{
359470700Sjulian	if (retaddr) {
359570700Sjulian		NGI_RETADDR(item) = retaddr;
359670700Sjulian	} else {
359770700Sjulian		/*
359870700Sjulian		 * The old return address should be ok.
359970700Sjulian		 * If there isn't one, use the address here.
360070700Sjulian		 */
360170700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
360270700Sjulian	}
360370700Sjulian}
360452419Sjulian
360570700Sjulian#define TESTING
360670700Sjulian#ifdef TESTING
360770700Sjulian/* just test all the macros */
360870700Sjulianvoid
360970700Sjulianng_macro_test(item_p item);
361070700Sjulianvoid
361170700Sjulianng_macro_test(item_p item)
361270700Sjulian{
361370700Sjulian	node_p node = NULL;
361470700Sjulian	hook_p hook = NULL;
361570700Sjulian	struct mbuf *m;
361670700Sjulian	meta_p meta;
361770700Sjulian	struct ng_mesg *msg;
361870700Sjulian	ng_ID_t retaddr;
361970700Sjulian	int	error;
362070700Sjulian
362170700Sjulian	NGI_GET_M(item, m);
362270700Sjulian	NGI_GET_META(item, meta);
362370700Sjulian	NGI_GET_MSG(item, msg);
362470700Sjulian	retaddr = NGI_RETADDR(item);
362570700Sjulian	NG_SEND_DATA(error, hook, m, meta);
362670700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
362770700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
362870784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
362970700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
363070700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
363170700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
363270700Sjulian	NG_QUEUE_MSG(error, node, msg, ".:", retaddr);
363370700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
363470700Sjulian}
363570700Sjulian#endif /* TESTING */
363670700Sjulian
3637