ng_base.c revision 177953
152419Sjulian/*
252419Sjulian * ng_base.c
3139823Simp */
4139823Simp
5139823Simp/*-
652419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
752419Sjulian * All rights reserved.
870700Sjulian *
952419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
1052419Sjulian * redistribution of this software, in source or object code forms, with or
1152419Sjulian * without modifications are expressly permitted by Whistle Communications;
1252419Sjulian * provided, however, that:
1352419Sjulian * 1. Any and all reproductions of the source or object code must include the
1452419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1552419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1652419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1752419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1852419Sjulian *    such appears in the above copyright notice or in the software.
1970700Sjulian *
2052419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2152419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2252419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2352419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2452419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2552419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2652419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2752419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2852419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2952419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3052419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3152419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3252419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3352419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3452419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3552419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3652419Sjulian * OF SUCH DAMAGE.
3752419Sjulian *
3867506Sjulian * Authors: Julian Elischer <julian@freebsd.org>
3967506Sjulian *          Archie Cobbs <archie@freebsd.org>
4052419Sjulian *
4152419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 177953 2008-04-06 15:26:32Z mav $
4252419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
4352419Sjulian */
4452419Sjulian
4552419Sjulian/*
4652419Sjulian * This file implements the base netgraph code.
4752419Sjulian */
4852419Sjulian
4952419Sjulian#include <sys/param.h>
50139236Sglebius#include <sys/systm.h>
51139235Sglebius#include <sys/ctype.h>
5252419Sjulian#include <sys/errno.h>
53131933Smarcel#include <sys/kdb.h>
5452419Sjulian#include <sys/kernel.h>
55154225Sglebius#include <sys/ktr.h>
56114216Skan#include <sys/limits.h>
5752419Sjulian#include <sys/malloc.h>
58139235Sglebius#include <sys/mbuf.h>
5952419Sjulian#include <sys/queue.h>
6072946Sjulian#include <sys/sysctl.h>
61139235Sglebius#include <sys/syslog.h>
62172806Smav#include <sys/refcount.h>
63175847Smav#include <sys/proc.h>
64177953Smav#include <machine/cpu.h>
6552419Sjulian
6652419Sjulian#include <net/netisr.h>
6752419Sjulian
6852419Sjulian#include <netgraph/ng_message.h>
6952419Sjulian#include <netgraph/netgraph.h>
7053913Sarchie#include <netgraph/ng_parse.h>
7152419Sjulian
7272053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION);
7359756Speter
74151974Sglebius/* Mutex to protect topology events. */
75151974Sglebiusstatic struct mtx	ng_topo_mtx;
76151974Sglebius
7770784Sjulian#ifdef	NETGRAPH_DEBUG
78176802Smavstatic struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
79152451Sglebiusstatic struct mtx	ngq_mtx;	/* protects the queue item list */
8070784Sjulian
8170784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
8270784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8370784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
8470784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8570784Sjulian
8670784Sjulianstatic void ng_dumpitems(void);
8770784Sjulianstatic void ng_dumpnodes(void);
8870784Sjulianstatic void ng_dumphooks(void);
8970784Sjulian
9070784Sjulian#endif	/* NETGRAPH_DEBUG */
9170935Sjulian/*
92152451Sglebius * DEAD versions of the structures.
9370935Sjulian * In order to avoid races, it is sometimes neccesary to point
94152451Sglebius * at SOMETHING even though theoretically, the current entity is
9570935Sjulian * INVALID. Use these to avoid these races.
9670935Sjulian */
9770935Sjulianstruct ng_type ng_deadtype = {
9870935Sjulian	NG_ABI_VERSION,
9970935Sjulian	"dead",
10070935Sjulian	NULL,	/* modevent */
10170935Sjulian	NULL,	/* constructor */
10270935Sjulian	NULL,	/* rcvmsg */
10370935Sjulian	NULL,	/* shutdown */
10470935Sjulian	NULL,	/* newhook */
10570935Sjulian	NULL,	/* findhook */
10670935Sjulian	NULL,	/* connect */
10770935Sjulian	NULL,	/* rcvdata */
10870935Sjulian	NULL,	/* disconnect */
10970935Sjulian	NULL, 	/* cmdlist */
11070935Sjulian};
11170784Sjulian
11270935Sjulianstruct ng_node ng_deadnode = {
11370935Sjulian	"dead",
11470935Sjulian	&ng_deadtype,
115132464Sjulian	NGF_INVALID,
11670935Sjulian	1,	/* refs */
11770935Sjulian	0,	/* numhooks */
11870935Sjulian	NULL,	/* private */
11970935Sjulian	0,	/* ID */
12070935Sjulian	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
12170935Sjulian	{},	/* all_nodes list entry */
12270935Sjulian	{},	/* id hashtable list entry */
12370935Sjulian	{},	/* workqueue entry */
12470935Sjulian	{	0,
12570935Sjulian		{}, /* should never use! (should hang) */
12670935Sjulian		NULL,
12770935Sjulian		&ng_deadnode.nd_input_queue.queue,
12870935Sjulian		&ng_deadnode
12970935Sjulian	},
13070935Sjulian#ifdef	NETGRAPH_DEBUG
13170935Sjulian	ND_MAGIC,
13270935Sjulian	__FILE__,
13370935Sjulian	__LINE__,
13470935Sjulian	{NULL}
13570935Sjulian#endif	/* NETGRAPH_DEBUG */
13670935Sjulian};
13770935Sjulian
13870935Sjulianstruct ng_hook ng_deadhook = {
13970935Sjulian	"dead",
14070935Sjulian	NULL,		/* private */
14170935Sjulian	HK_INVALID | HK_DEAD,
14270935Sjulian	1,		/* refs always >= 1 */
143148261Sglebius	0,		/* undefined data link type */
14470935Sjulian	&ng_deadhook,	/* Peer is self */
14570935Sjulian	&ng_deadnode,	/* attached to deadnode */
14670935Sjulian	{},		/* hooks list */
14771885Sjulian	NULL,		/* override rcvmsg() */
14871885Sjulian	NULL,		/* override rcvdata() */
14970935Sjulian#ifdef	NETGRAPH_DEBUG
15070935Sjulian	HK_MAGIC,
15170935Sjulian	__FILE__,
15270935Sjulian	__LINE__,
15370935Sjulian	{NULL}
15470935Sjulian#endif	/* NETGRAPH_DEBUG */
15570935Sjulian};
15670935Sjulian
15770935Sjulian/*
15870935Sjulian * END DEAD STRUCTURES
15970935Sjulian */
16070700Sjulian/* List nodes with unallocated work */
16170700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
16271902Sjulianstatic struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
16370700Sjulian
16452419Sjulian/* List of installed types */
16570700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
16670700Sjulianstatic struct mtx	ng_typelist_mtx;
16752419Sjulian
16870700Sjulian/* Hash related definitions */
16971354Sjulian/* XXX Don't need to initialise them because it's a LIST */
170176802Smav#define NG_ID_HASH_SIZE 128 /* most systems wont need even this many */
17171354Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
17270700Sjulianstatic struct mtx	ng_idhash_mtx;
17371354Sjulian/* Method to find a node.. used twice so do it here */
17471354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
17571354Sjulian#define NG_IDHASH_FIND(ID, node)					\
17671354Sjulian	do { 								\
177131008Srwatson		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
17871354Sjulian		LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)],	\
17971354Sjulian						nd_idnodes) {		\
18071354Sjulian			if (NG_NODE_IS_VALID(node)			\
18171354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
18271354Sjulian				break;					\
18371354Sjulian			}						\
18471354Sjulian		}							\
18571354Sjulian	} while (0)
18652722Sjulian
187176802Smav#define NG_NAME_HASH_SIZE 128 /* most systems wont need even this many */
188176802Smavstatic LIST_HEAD(, ng_node) ng_name_hash[NG_NAME_HASH_SIZE];
189176802Smavstatic struct mtx	ng_namehash_mtx;
190176802Smav#define NG_NAMEHASH(NAME, HASH)				\
191176802Smav	do {						\
192176802Smav		u_char	h = 0;				\
193176802Smav		const u_char	*c;			\
194176802Smav		for (c = (const u_char*)(NAME); *c; c++)\
195176802Smav			h += *c;			\
196176802Smav		(HASH) = h % (NG_NAME_HASH_SIZE);	\
197176802Smav	} while (0)
19870700Sjulian
199176802Smav
20052419Sjulian/* Internal functions */
20152419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
20270700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
20352722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
20452419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
205177953Smavstatic void	ng_worklist_add(node_p node);
20652419Sjulianstatic void	ngintr(void);
207170180Sglebiusstatic int	ng_apply_item(node_p node, item_p item, int rw);
20870700Sjulianstatic void	ng_flush_input_queue(struct ng_queue * ngq);
20970700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
210172806Smavstatic int	ng_con_nodes(item_p item, node_p node, const char *name,
211172806Smav		    node_p node2, const char *name2);
212172806Smavstatic int	ng_con_part2(node_p node, item_p item, hook_p hook);
213172806Smavstatic int	ng_con_part3(node_p node, item_p item, hook_p hook);
21471047Sjulianstatic int	ng_mkpeer(node_p node, const char *name,
21571047Sjulian						const char *name2, char *type);
21652419Sjulian
217152451Sglebius/* Imported, these used to be externally visible, some may go back. */
21870700Sjulianvoid	ng_destroy_hook(hook_p hook);
21970700Sjuliannode_p	ng_name2noderef(node_p node, const char *name);
22070700Sjulianint	ng_path2noderef(node_p here, const char *path,
22170700Sjulian	node_p *dest, hook_p *lasthook);
22270700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
22370700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
22471849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
22570784Sjulianvoid	ng_unname(node_p node);
22670700Sjulian
22770700Sjulian
22852419Sjulian/* Our own netgraph malloc type */
22952419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
23070700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
23170700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
23270700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
23370700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
23452419Sjulian
23570700Sjulian/* Should not be visible outside this file */
23670784Sjulian
23770784Sjulian#define _NG_ALLOC_HOOK(hook) \
23870784Sjulian	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
23970784Sjulian#define _NG_ALLOC_NODE(node) \
24070784Sjulian	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
24170784Sjulian
242168049Swkoszek#define	NG_QUEUE_LOCK_INIT(n)			\
243168137Swkoszek	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
244168049Swkoszek#define	NG_QUEUE_LOCK(n)			\
245168137Swkoszek	mtx_lock(&(n)->q_mtx)
246168049Swkoszek#define	NG_QUEUE_UNLOCK(n)			\
247168137Swkoszek	mtx_unlock(&(n)->q_mtx)
248168049Swkoszek#define	NG_WORKLIST_LOCK_INIT()			\
249168137Swkoszek	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
250168049Swkoszek#define	NG_WORKLIST_LOCK()			\
251168137Swkoszek	mtx_lock(&ng_worklist_mtx)
252168049Swkoszek#define	NG_WORKLIST_UNLOCK()			\
253168137Swkoszek	mtx_unlock(&ng_worklist_mtx)
254168049Swkoszek
25570784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
25670784Sjulian/*
25770784Sjulian * In debug mode:
25870784Sjulian * In an attempt to help track reference count screwups
25970784Sjulian * we do not free objects back to the malloc system, but keep them
26070784Sjulian * in a local cache where we can examine them and keep information safely
26170784Sjulian * after they have been freed.
26270784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
26370784Sjulian */
26470784Sjulianstatic __inline hook_p
26570784Sjulianng_alloc_hook(void)
26670784Sjulian{
26770784Sjulian	hook_p hook;
26870784Sjulian	SLIST_ENTRY(ng_hook) temp;
26972200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
27070784Sjulian	hook = LIST_FIRST(&ng_freehooks);
27170784Sjulian	if (hook) {
27270784Sjulian		LIST_REMOVE(hook, hk_hooks);
27370784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
27470784Sjulian		bzero(hook, sizeof(struct ng_hook));
27570784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
27672200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
27770784Sjulian		hook->hk_magic = HK_MAGIC;
27870784Sjulian	} else {
27972200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
28070784Sjulian		_NG_ALLOC_HOOK(hook);
28170784Sjulian		if (hook) {
28270784Sjulian			hook->hk_magic = HK_MAGIC;
28372200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
28470784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
28572200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
28670784Sjulian		}
28770784Sjulian	}
28870784Sjulian	return (hook);
28970784Sjulian}
29070784Sjulian
29170784Sjulianstatic __inline node_p
29270784Sjulianng_alloc_node(void)
29370784Sjulian{
29470784Sjulian	node_p node;
29570784Sjulian	SLIST_ENTRY(ng_node) temp;
29672200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
29770784Sjulian	node = LIST_FIRST(&ng_freenodes);
29870784Sjulian	if (node) {
29970784Sjulian		LIST_REMOVE(node, nd_nodes);
30070784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
30170784Sjulian		bzero(node, sizeof(struct ng_node));
30270784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
30372200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
30470784Sjulian		node->nd_magic = ND_MAGIC;
30570784Sjulian	} else {
30672200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
30770784Sjulian		_NG_ALLOC_NODE(node);
30870784Sjulian		if (node) {
30970784Sjulian			node->nd_magic = ND_MAGIC;
31072200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
31170784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
31272200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
31370784Sjulian		}
31470784Sjulian	}
31570784Sjulian	return (node);
31670784Sjulian}
31770784Sjulian
31870784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
31970784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
32070784Sjulian
32170784Sjulian
32270784Sjulian#define NG_FREE_HOOK(hook)						\
32370784Sjulian	do {								\
32472200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
32570784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
32670784Sjulian		hook->hk_magic = 0;					\
32772200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
32870784Sjulian	} while (0)
32970784Sjulian
33070784Sjulian#define NG_FREE_NODE(node)						\
33170784Sjulian	do {								\
33272200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
33370784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
33470784Sjulian		node->nd_magic = 0;					\
33572200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
33670784Sjulian	} while (0)
33770784Sjulian
33870784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
33970784Sjulian
34070784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
34170784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
34270784Sjulian
34370700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
34470700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
34570784Sjulian
34670784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
34770784Sjulian
348131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */
34952419Sjulian#ifndef TRAP_ERROR
35071047Sjulian#define TRAP_ERROR()
35152419Sjulian#endif
35252419Sjulian
35352722Sjulianstatic	ng_ID_t nextID = 1;
35452722Sjulian
35553403Sarchie#ifdef INVARIANTS
35653403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
35753403Sarchie		struct mbuf *n;						\
35853403Sarchie		int total;						\
35953403Sarchie									\
360113255Sdes		M_ASSERTPKTHDR(m);					\
361149818Sglebius		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
36253403Sarchie			total += n->m_len;				\
363149818Sglebius			if (n->m_nextpkt != NULL)			\
364149818Sglebius				panic("%s: m_nextpkt", __func__);	\
365149818Sglebius		}							\
366149827Sglebius									\
36753403Sarchie		if ((m)->m_pkthdr.len != total) {			\
36853403Sarchie			panic("%s: %d != %d",				\
36987599Sobrien			    __func__, (m)->m_pkthdr.len, total);	\
37053403Sarchie		}							\
37153403Sarchie	} while (0)
37253403Sarchie#else
37353403Sarchie#define CHECK_DATA_MBUF(m)
37453403Sarchie#endif
37552722Sjulian
376172806Smav#define ERROUT(x)	do { error = (x); goto done; } while (0)
37753403Sarchie
37852419Sjulian/************************************************************************
37953913Sarchie	Parse type definitions for generic messages
38053913Sarchie************************************************************************/
38153913Sarchie
38253913Sarchie/* Handy structure parse type defining macro */
38353913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
38497685Sarchiestatic const struct ng_parse_struct_field				\
38597685Sarchie	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
38653913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
38753913Sarchie	&ng_parse_struct_type,						\
38897685Sarchie	&ng_ ## lo ## _type_fields					\
38953913Sarchie}
39053913Sarchie
39153913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
39253913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
39353913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
39453913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
39553913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
39653913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
39753913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
39853913Sarchie
39953913Sarchie/* Get length of an array when the length is stored as a 32 bit
40072645Sasmodai   value immediately preceding the array -- as with struct namelist
40153913Sarchie   and struct typelist. */
40253913Sarchiestatic int
40353913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
40453913Sarchie	const u_char *start, const u_char *buf)
40553913Sarchie{
40653913Sarchie	return *((const u_int32_t *)(buf - 4));
40753913Sarchie}
40853913Sarchie
40953913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
41053913Sarchiestatic int
41153913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
41253913Sarchie	const u_char *start, const u_char *buf)
41353913Sarchie{
41453913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
41553913Sarchie
41653913Sarchie	return hl->nodeinfo.hooks;
41753913Sarchie}
41853913Sarchie
41953913Sarchie/* Array type for a variable length array of struct namelist */
42053913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
42153913Sarchie	&ng_generic_nodeinfo_type,
42253913Sarchie	&ng_generic_list_getLength
42353913Sarchie};
42453913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
42553913Sarchie	&ng_parse_array_type,
42653913Sarchie	&ng_nodeinfoarray_type_info
42753913Sarchie};
42853913Sarchie
42953913Sarchie/* Array type for a variable length array of struct typelist */
43053913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
43153913Sarchie	&ng_generic_typeinfo_type,
43253913Sarchie	&ng_generic_list_getLength
43353913Sarchie};
43453913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
43553913Sarchie	&ng_parse_array_type,
43653913Sarchie	&ng_typeinfoarray_type_info
43753913Sarchie};
43853913Sarchie
43953913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
44053913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
44153913Sarchie	&ng_generic_linkinfo_type,
44253913Sarchie	&ng_generic_linkinfo_getLength
44353913Sarchie};
44453913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
44553913Sarchie	&ng_parse_array_type,
44653913Sarchie	&ng_generic_linkinfo_array_type_info
44753913Sarchie};
44853913Sarchie
44953913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
45053913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
45153913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
45253913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
45353913Sarchie	(&ng_generic_nodeinfoarray_type));
45453913Sarchie
45553913Sarchie/* List of commands and how to convert arguments to/from ASCII */
45653913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
45753913Sarchie	{
45853913Sarchie	  NGM_GENERIC_COOKIE,
45953913Sarchie	  NGM_SHUTDOWN,
46053913Sarchie	  "shutdown",
46153913Sarchie	  NULL,
46253913Sarchie	  NULL
46353913Sarchie	},
46453913Sarchie	{
46553913Sarchie	  NGM_GENERIC_COOKIE,
46653913Sarchie	  NGM_MKPEER,
46753913Sarchie	  "mkpeer",
46853913Sarchie	  &ng_generic_mkpeer_type,
46953913Sarchie	  NULL
47053913Sarchie	},
47153913Sarchie	{
47253913Sarchie	  NGM_GENERIC_COOKIE,
47353913Sarchie	  NGM_CONNECT,
47453913Sarchie	  "connect",
47553913Sarchie	  &ng_generic_connect_type,
47653913Sarchie	  NULL
47753913Sarchie	},
47853913Sarchie	{
47953913Sarchie	  NGM_GENERIC_COOKIE,
48053913Sarchie	  NGM_NAME,
48153913Sarchie	  "name",
48253913Sarchie	  &ng_generic_name_type,
48353913Sarchie	  NULL
48453913Sarchie	},
48553913Sarchie	{
48653913Sarchie	  NGM_GENERIC_COOKIE,
48753913Sarchie	  NGM_RMHOOK,
48853913Sarchie	  "rmhook",
48953913Sarchie	  &ng_generic_rmhook_type,
49053913Sarchie	  NULL
49153913Sarchie	},
49253913Sarchie	{
49353913Sarchie	  NGM_GENERIC_COOKIE,
49453913Sarchie	  NGM_NODEINFO,
49553913Sarchie	  "nodeinfo",
49653913Sarchie	  NULL,
49753913Sarchie	  &ng_generic_nodeinfo_type
49853913Sarchie	},
49953913Sarchie	{
50053913Sarchie	  NGM_GENERIC_COOKIE,
50153913Sarchie	  NGM_LISTHOOKS,
50253913Sarchie	  "listhooks",
50353913Sarchie	  NULL,
50453913Sarchie	  &ng_generic_hooklist_type
50553913Sarchie	},
50653913Sarchie	{
50753913Sarchie	  NGM_GENERIC_COOKIE,
50853913Sarchie	  NGM_LISTNAMES,
50953913Sarchie	  "listnames",
51053913Sarchie	  NULL,
51153913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
51253913Sarchie	},
51353913Sarchie	{
51453913Sarchie	  NGM_GENERIC_COOKIE,
51553913Sarchie	  NGM_LISTNODES,
51653913Sarchie	  "listnodes",
51753913Sarchie	  NULL,
51853913Sarchie	  &ng_generic_listnodes_type
51953913Sarchie	},
52053913Sarchie	{
52153913Sarchie	  NGM_GENERIC_COOKIE,
52253913Sarchie	  NGM_LISTTYPES,
52353913Sarchie	  "listtypes",
52453913Sarchie	  NULL,
52553913Sarchie	  &ng_generic_typeinfo_type
52653913Sarchie	},
52753913Sarchie	{
52853913Sarchie	  NGM_GENERIC_COOKIE,
52962471Sphk	  NGM_TEXT_CONFIG,
53062471Sphk	  "textconfig",
53162471Sphk	  NULL,
53262471Sphk	  &ng_parse_string_type
53362471Sphk	},
53462471Sphk	{
53562471Sphk	  NGM_GENERIC_COOKIE,
53653913Sarchie	  NGM_TEXT_STATUS,
53753913Sarchie	  "textstatus",
53853913Sarchie	  NULL,
53953913Sarchie	  &ng_parse_string_type
54053913Sarchie	},
54153913Sarchie	{
54253913Sarchie	  NGM_GENERIC_COOKIE,
54353913Sarchie	  NGM_ASCII2BINARY,
54453913Sarchie	  "ascii2binary",
54553913Sarchie	  &ng_parse_ng_mesg_type,
54653913Sarchie	  &ng_parse_ng_mesg_type
54753913Sarchie	},
54853913Sarchie	{
54953913Sarchie	  NGM_GENERIC_COOKIE,
55053913Sarchie	  NGM_BINARY2ASCII,
55153913Sarchie	  "binary2ascii",
55253913Sarchie	  &ng_parse_ng_mesg_type,
55353913Sarchie	  &ng_parse_ng_mesg_type
55453913Sarchie	},
55553913Sarchie	{ 0 }
55653913Sarchie};
55753913Sarchie
55853913Sarchie/************************************************************************
55952419Sjulian			Node routines
56052419Sjulian************************************************************************/
56152419Sjulian
56252419Sjulian/*
56352419Sjulian * Instantiate a node of the requested type
56452419Sjulian */
56552419Sjulianint
56652419Sjulianng_make_node(const char *typename, node_p *nodepp)
56752419Sjulian{
56852419Sjulian	struct ng_type *type;
56970700Sjulian	int	error;
57052419Sjulian
57152419Sjulian	/* Check that the type makes sense */
57252419Sjulian	if (typename == NULL) {
57371047Sjulian		TRAP_ERROR();
57452419Sjulian		return (EINVAL);
57552419Sjulian	}
57652419Sjulian
577132705Sglebius	/* Locate the node type. If we fail we return. Do not try to load
578132705Sglebius	 * module.
579132705Sglebius	 */
580132705Sglebius	if ((type = ng_findtype(typename)) == NULL)
581132705Sglebius		return (ENXIO);
58252419Sjulian
58370700Sjulian	/*
58470700Sjulian	 * If we have a constructor, then make the node and
58570700Sjulian	 * call the constructor to do type specific initialisation.
58670700Sjulian	 */
58770700Sjulian	if (type->constructor != NULL) {
58870700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
58970700Sjulian			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
59070784Sjulian				NG_NODE_UNREF(*nodepp);
59170700Sjulian			}
59270700Sjulian		}
59370700Sjulian	} else {
59470700Sjulian		/*
59570700Sjulian		 * Node has no constructor. We cannot ask for one
596167677Srwatson		 * to be made. It must be brought into existence by
59770935Sjulian		 * some external agency. The external agency should
59870700Sjulian		 * call ng_make_node_common() directly to get the
59970700Sjulian		 * netgraph part initialised.
60070700Sjulian		 */
60171047Sjulian		TRAP_ERROR();
60270700Sjulian		error = EINVAL;
60370700Sjulian	}
60470700Sjulian	return (error);
60552419Sjulian}
60652419Sjulian
60752419Sjulian/*
60870700Sjulian * Generic node creation. Called by node initialisation for externally
60970700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
61052419Sjulian * The returned node has a reference count of 1.
61152419Sjulian */
61252419Sjulianint
61352419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
61452419Sjulian{
61552419Sjulian	node_p node;
61652419Sjulian
61752419Sjulian	/* Require the node type to have been already installed */
61852419Sjulian	if (ng_findtype(type->name) == NULL) {
61971047Sjulian		TRAP_ERROR();
62052419Sjulian		return (EINVAL);
62152419Sjulian	}
62252419Sjulian
62352419Sjulian	/* Make a node and try attach it to the type */
62470784Sjulian	NG_ALLOC_NODE(node);
62552419Sjulian	if (node == NULL) {
62671047Sjulian		TRAP_ERROR();
62752419Sjulian		return (ENOMEM);
62852419Sjulian	}
62970784Sjulian	node->nd_type = type;
63070784Sjulian	NG_NODE_REF(node);				/* note reference */
63152419Sjulian	type->refs++;
63252419Sjulian
633168049Swkoszek	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
63470784Sjulian	node->nd_input_queue.queue = NULL;
63570784Sjulian	node->nd_input_queue.last = &node->nd_input_queue.queue;
63670784Sjulian	node->nd_input_queue.q_flags = 0;
63770784Sjulian	node->nd_input_queue.q_node = node;
63852419Sjulian
63952419Sjulian	/* Initialize hook list for new node */
64070784Sjulian	LIST_INIT(&node->nd_hooks);
64152419Sjulian
642176802Smav	/* Link us into the name hash. */
643176802Smav	mtx_lock(&ng_namehash_mtx);
644176802Smav	LIST_INSERT_HEAD(&ng_name_hash[0], node, nd_nodes);
645176802Smav	mtx_unlock(&ng_namehash_mtx);
64670700Sjulian
64752722Sjulian	/* get an ID and put us in the hash chain */
64872200Sbmilekic	mtx_lock(&ng_idhash_mtx);
64970784Sjulian	for (;;) { /* wrap protection, even if silly */
65070700Sjulian		node_p node2 = NULL;
65170784Sjulian		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
65271354Sjulian
65370784Sjulian		/* Is there a problem with the new number? */
65471354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
65571354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
65670784Sjulian			break;
65770700Sjulian		}
65870784Sjulian	}
65971354Sjulian	LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
66070784Sjulian							node, nd_idnodes);
66172200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
66252722Sjulian
66352419Sjulian	/* Done */
66452419Sjulian	*nodepp = node;
66552419Sjulian	return (0);
66652419Sjulian}
66752419Sjulian
66852419Sjulian/*
66952419Sjulian * Forceably start the shutdown process on a node. Either call
670167677Srwatson * its shutdown method, or do the default shutdown if there is
67152419Sjulian * no type-specific method.
67252419Sjulian *
673167677Srwatson * We can only be called from a shutdown message, so we know we have
67470939Sjulian * a writer lock, and therefore exclusive access. It also means
67570939Sjulian * that we should not be on the work queue, but we check anyhow.
67670700Sjulian *
67770700Sjulian * Persistent node types must have a type-specific method which
678167677Srwatson * allocates a new node in which case, this one is irretrievably going away,
67970939Sjulian * or cleans up anything it needs, and just makes the node valid again,
680152451Sglebius * in which case we allow the node to survive.
68170939Sjulian *
682167677Srwatson * XXX We need to think of how to tell a persistent node that we
68370939Sjulian * REALLY need to go away because the hardware has gone or we
68470939Sjulian * are rebooting.... etc.
68552419Sjulian */
68652419Sjulianvoid
68771849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
68852419Sjulian{
68970939Sjulian	hook_p hook;
69070939Sjulian
69152419Sjulian	/* Check if it's already shutting down */
692132464Sjulian	if ((node->nd_flags & NGF_CLOSING) != 0)
69352419Sjulian		return;
69452419Sjulian
69571849Sjulian	if (node == &ng_deadnode) {
69671849Sjulian		printf ("shutdown called on deadnode\n");
69771849Sjulian		return;
69871849Sjulian	}
69971849Sjulian
70052419Sjulian	/* Add an extra reference so it doesn't go away during this */
70170784Sjulian	NG_NODE_REF(node);
70252419Sjulian
70370784Sjulian	/*
70470784Sjulian	 * Mark it invalid so any newcomers know not to try use it
70570784Sjulian	 * Also add our own mark so we can't recurse
706132464Sjulian	 * note that NGF_INVALID does not do this as it's also set during
70770784Sjulian	 * creation
70870784Sjulian	 */
709132464Sjulian	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
71052419Sjulian
711129836Sjulian	/* If node has its pre-shutdown method, then call it first*/
712129836Sjulian	if (node->nd_type && node->nd_type->close)
713129836Sjulian		(*node->nd_type->close)(node);
714129836Sjulian
71570939Sjulian	/* Notify all remaining connected nodes to disconnect */
71670939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
71770939Sjulian		ng_destroy_hook(hook);
71870784Sjulian
71970700Sjulian	/*
72070700Sjulian	 * Drain the input queue forceably.
72170784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
72270784Sjulian	 * Theoretically we came here from a queue entry that was added
72370784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
72471902Sjulian	 * Also removes us from worklist if needed.
72570700Sjulian	 */
72670784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
72770700Sjulian
72852419Sjulian	/* Ask the type if it has anything to do in this case */
72970784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
73070784Sjulian		(*node->nd_type->shutdown)(node);
73171849Sjulian		if (NG_NODE_IS_VALID(node)) {
73271849Sjulian			/*
73371849Sjulian			 * Well, blow me down if the node code hasn't declared
73471849Sjulian			 * that it doesn't want to die.
73571849Sjulian			 * Presumably it is a persistant node.
73671849Sjulian			 * If we REALLY want it to go away,
73771849Sjulian			 *  e.g. hardware going away,
738132464Sjulian			 * Our caller should set NGF_REALLY_DIE in nd_flags.
739152451Sglebius			 */
740132464Sjulian			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
74171849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
74271849Sjulian			return;
74371849Sjulian		}
74470700Sjulian	} else {				/* do the default thing */
74570784Sjulian		NG_NODE_UNREF(node);
74652419Sjulian	}
74752419Sjulian
74870784Sjulian	ng_unname(node); /* basically a NOP these days */
74970784Sjulian
75070784Sjulian	/*
75170784Sjulian	 * Remove extra reference, possibly the last
75270784Sjulian	 * Possible other holders of references may include
75370784Sjulian	 * timeout callouts, but theoretically the node's supposed to
75470784Sjulian	 * have cancelled them. Possibly hardware dependencies may
75570784Sjulian	 * force a driver to 'linger' with a reference.
75670784Sjulian	 */
75770784Sjulian	NG_NODE_UNREF(node);
75852419Sjulian}
75952419Sjulian
76052419Sjulian/*
76174078Sjulian * Remove a reference to the node, possibly the last.
76274078Sjulian * deadnode always acts as it it were the last.
76352419Sjulian */
76474078Sjulianint
76570784Sjulianng_unref_node(node_p node)
76652419Sjulian{
767152451Sglebius	int v;
76871047Sjulian
76971047Sjulian	if (node == &ng_deadnode) {
77074078Sjulian		return (0);
77171047Sjulian	}
77271047Sjulian
773177728Smav	v = atomic_fetchadd_int(&node->nd_refs, -1);
77469519Sjulian
775177728Smav	if (v == 1) { /* we were the last */
77670700Sjulian
777176802Smav		mtx_lock(&ng_namehash_mtx);
77870784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
77970784Sjulian		LIST_REMOVE(node, nd_nodes);
780176802Smav		mtx_unlock(&ng_namehash_mtx);
78170700Sjulian
78272200Sbmilekic		mtx_lock(&ng_idhash_mtx);
78370784Sjulian		LIST_REMOVE(node, nd_idnodes);
78472200Sbmilekic		mtx_unlock(&ng_idhash_mtx);
78552419Sjulian
78670791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
78770700Sjulian		NG_FREE_NODE(node);
78852419Sjulian	}
789177728Smav	return (v - 1);
79052419Sjulian}
79152419Sjulian
79252419Sjulian/************************************************************************
79352722Sjulian			Node ID handling
79452722Sjulian************************************************************************/
79552722Sjulianstatic node_p
79670700Sjulianng_ID2noderef(ng_ID_t ID)
79752722Sjulian{
79870784Sjulian	node_p node;
79972200Sbmilekic	mtx_lock(&ng_idhash_mtx);
80071354Sjulian	NG_IDHASH_FIND(ID, node);
80170784Sjulian	if(node)
80270784Sjulian		NG_NODE_REF(node);
80372200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
80470784Sjulian	return(node);
80552722Sjulian}
80652722Sjulian
80752722Sjulianng_ID_t
80852722Sjulianng_node2ID(node_p node)
80952722Sjulian{
81070912Sjulian	return (node ? NG_NODE_ID(node) : 0);
81152722Sjulian}
81252722Sjulian
81352722Sjulian/************************************************************************
81452419Sjulian			Node name handling
81552419Sjulian************************************************************************/
81652419Sjulian
81752419Sjulian/*
81852419Sjulian * Assign a node a name. Once assigned, the name cannot be changed.
81952419Sjulian */
82052419Sjulianint
82152419Sjulianng_name_node(node_p node, const char *name)
82252419Sjulian{
823176802Smav	int i, hash;
82470700Sjulian	node_p node2;
82552419Sjulian
82652419Sjulian	/* Check the name is valid */
827125028Sharti	for (i = 0; i < NG_NODESIZ; i++) {
82852419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
82952419Sjulian			break;
83052419Sjulian	}
83152419Sjulian	if (i == 0 || name[i] != '\0') {
83271047Sjulian		TRAP_ERROR();
83352419Sjulian		return (EINVAL);
83452419Sjulian	}
83552722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
83671047Sjulian		TRAP_ERROR();
83752419Sjulian		return (EINVAL);
83852419Sjulian	}
83952419Sjulian
84052419Sjulian	/* Check the name isn't already being used */
84170700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
84270784Sjulian		NG_NODE_UNREF(node2);
84371047Sjulian		TRAP_ERROR();
84452419Sjulian		return (EADDRINUSE);
84552419Sjulian	}
84652419Sjulian
84770700Sjulian	/* copy it */
848125028Sharti	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
84952419Sjulian
850176802Smav	/* Update name hash. */
851176802Smav	NG_NAMEHASH(name, hash);
852176802Smav	mtx_lock(&ng_namehash_mtx);
853176802Smav	LIST_REMOVE(node, nd_nodes);
854176802Smav	LIST_INSERT_HEAD(&ng_name_hash[hash], node, nd_nodes);
855176802Smav	mtx_unlock(&ng_namehash_mtx);
856176802Smav
85752419Sjulian	return (0);
85852419Sjulian}
85952419Sjulian
86052419Sjulian/*
86152419Sjulian * Find a node by absolute name. The name should NOT end with ':'
86252419Sjulian * The name "." means "this node" and "[xxx]" means "the node
86352419Sjulian * with ID (ie, at address) xxx".
86452419Sjulian *
86552419Sjulian * Returns the node if found, else NULL.
86670700Sjulian * Eventually should add something faster than a sequential search.
867170035Srwatson * Note it acquires a reference on the node so you can be sure it's still
868170035Srwatson * there.
86952419Sjulian */
87052419Sjuliannode_p
87170700Sjulianng_name2noderef(node_p here, const char *name)
87252419Sjulian{
87352722Sjulian	node_p node;
87452722Sjulian	ng_ID_t temp;
875176802Smav	int	hash;
87652419Sjulian
87752419Sjulian	/* "." means "this node" */
87870700Sjulian	if (strcmp(name, ".") == 0) {
87970784Sjulian		NG_NODE_REF(here);
88070700Sjulian		return(here);
88170700Sjulian	}
88252419Sjulian
88352419Sjulian	/* Check for name-by-ID */
88452722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
88570700Sjulian		return (ng_ID2noderef(temp));
88652419Sjulian	}
88752419Sjulian
88852419Sjulian	/* Find node by name */
889176802Smav	NG_NAMEHASH(name, hash);
890176802Smav	mtx_lock(&ng_namehash_mtx);
891176802Smav	LIST_FOREACH(node, &ng_name_hash[hash], nd_nodes) {
892176802Smav		if (NG_NODE_IS_VALID(node) &&
893176802Smav		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
89452419Sjulian			break;
89570912Sjulian		}
89652419Sjulian	}
89770700Sjulian	if (node)
89870784Sjulian		NG_NODE_REF(node);
899176802Smav	mtx_unlock(&ng_namehash_mtx);
90052419Sjulian	return (node);
90152419Sjulian}
90252419Sjulian
90352419Sjulian/*
904108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the
90552722Sjulian * string is not valid, otherwise returns the value.
90652419Sjulian */
90752722Sjulianstatic ng_ID_t
90852419Sjulianng_decodeidname(const char *name)
90952419Sjulian{
91052816Sarchie	const int len = strlen(name);
91153648Sarchie	char *eptr;
91252816Sarchie	u_long val;
91352419Sjulian
91452816Sarchie	/* Check for proper length, brackets, no leading junk */
91570912Sjulian	if ((len < 3)
91670912Sjulian	|| (name[0] != '[')
91770912Sjulian	|| (name[len - 1] != ']')
91870912Sjulian	|| (!isxdigit(name[1]))) {
91970912Sjulian		return ((ng_ID_t)0);
92070912Sjulian	}
92152419Sjulian
92252816Sarchie	/* Decode number */
92352816Sarchie	val = strtoul(name + 1, &eptr, 16);
92470912Sjulian	if ((eptr - name != len - 1)
92570912Sjulian	|| (val == ULONG_MAX)
92670912Sjulian	|| (val == 0)) {
92753042Sjulian		return ((ng_ID_t)0);
92870912Sjulian	}
92952816Sarchie	return (ng_ID_t)val;
93052419Sjulian}
93152419Sjulian
93252419Sjulian/*
93352419Sjulian * Remove a name from a node. This should only be called
93452419Sjulian * when shutting down and removing the node.
935167677Srwatson * IF we allow name changing this may be more resurrected.
93652419Sjulian */
93752419Sjulianvoid
93852419Sjulianng_unname(node_p node)
93952419Sjulian{
94052419Sjulian}
94152419Sjulian
94252419Sjulian/************************************************************************
94352419Sjulian			Hook routines
94452419Sjulian Names are not optional. Hooks are always connected, except for a
94570939Sjulian brief moment within these routines. On invalidation or during creation
94670939Sjulian they are connected to the 'dead' hook.
94752419Sjulian************************************************************************/
94852419Sjulian
94952419Sjulian/*
95052419Sjulian * Remove a hook reference
95152419Sjulian */
95270784Sjulianvoid
95352419Sjulianng_unref_hook(hook_p hook)
95452419Sjulian{
955152451Sglebius	int v;
95671047Sjulian
95771047Sjulian	if (hook == &ng_deadhook) {
95871047Sjulian		return;
95971047Sjulian	}
96069519Sjulian
961177728Smav	v = atomic_fetchadd_int(&hook->hk_refs, -1);
962177728Smav
96370784Sjulian	if (v == 1) { /* we were the last */
964177722Smav		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
96571047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
96670700Sjulian		NG_FREE_HOOK(hook);
96770700Sjulian	}
96852419Sjulian}
96952419Sjulian
97052419Sjulian/*
97152419Sjulian * Add an unconnected hook to a node. Only used internally.
97270939Sjulian * Assumes node is locked. (XXX not yet true )
97352419Sjulian */
97452419Sjulianstatic int
97552419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
97652419Sjulian{
97752419Sjulian	hook_p hook;
97852419Sjulian	int error = 0;
97952419Sjulian
98052419Sjulian	/* Check that the given name is good */
98152419Sjulian	if (name == NULL) {
98271047Sjulian		TRAP_ERROR();
98352419Sjulian		return (EINVAL);
98452419Sjulian	}
98554096Sarchie	if (ng_findhook(node, name) != NULL) {
98671047Sjulian		TRAP_ERROR();
98754096Sarchie		return (EEXIST);
98852419Sjulian	}
98952419Sjulian
99052419Sjulian	/* Allocate the hook and link it up */
99170784Sjulian	NG_ALLOC_HOOK(hook);
99252419Sjulian	if (hook == NULL) {
99371047Sjulian		TRAP_ERROR();
99452419Sjulian		return (ENOMEM);
99552419Sjulian	}
99670939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
99770784Sjulian	hook->hk_flags = HK_INVALID;
99870939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
99970784Sjulian	hook->hk_node = node;
100070784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
100152419Sjulian
100270939Sjulian	/* Set hook name */
1003125028Sharti	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
100470939Sjulian
100570939Sjulian	/*
100670939Sjulian	 * Check if the node type code has something to say about it
100770939Sjulian	 * If it fails, the unref of the hook will also unref the node.
100870939Sjulian	 */
100970935Sjulian	if (node->nd_type->newhook != NULL) {
101070935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
101170784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
101270700Sjulian			return (error);
101370700Sjulian		}
101470935Sjulian	}
101552419Sjulian	/*
101652419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
101752419Sjulian	 * We'll ask again later when we actually connect the hooks.
101852419Sjulian	 */
101970784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
102070784Sjulian	node->nd_numhooks++;
102170939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
102252419Sjulian
102352419Sjulian	if (hookp)
102452419Sjulian		*hookp = hook;
102570939Sjulian	return (0);
102652419Sjulian}
102752419Sjulian
102852419Sjulian/*
102954096Sarchie * Find a hook
103054096Sarchie *
103154096Sarchie * Node types may supply their own optimized routines for finding
103254096Sarchie * hooks.  If none is supplied, we just do a linear search.
103370939Sjulian * XXX Possibly we should add a reference to the hook?
103454096Sarchie */
103554096Sarchiehook_p
103654096Sarchieng_findhook(node_p node, const char *name)
103754096Sarchie{
103854096Sarchie	hook_p hook;
103954096Sarchie
104070784Sjulian	if (node->nd_type->findhook != NULL)
104170784Sjulian		return (*node->nd_type->findhook)(node, name);
104270784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
104370912Sjulian		if (NG_HOOK_IS_VALID(hook)
104470917Sarchie		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
104554096Sarchie			return (hook);
104654096Sarchie	}
104754096Sarchie	return (NULL);
104854096Sarchie}
104954096Sarchie
105054096Sarchie/*
105152419Sjulian * Destroy a hook
105252419Sjulian *
105352419Sjulian * As hooks are always attached, this really destroys two hooks.
105452419Sjulian * The one given, and the one attached to it. Disconnect the hooks
105570939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
105670939Sjulian * hook so that it can still exist after we depart. We then
105770939Sjulian * send the peer its own destroy message. This ensures that we only
1058152451Sglebius * interact with the peer's structures when it is locked processing that
105970939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
106070939Sjulian * the peer hook and node are still going to exist until
106170939Sjulian * we are finished there as the hook holds a ref on the node.
1062152451Sglebius * We run this same code again on the peer hook, but that time it is already
1063152451Sglebius * attached to the 'dead' hook.
106471047Sjulian *
1065152451Sglebius * This routine is called at all stages of hook creation
106671047Sjulian * on error detection and must be able to handle any such stage.
106752419Sjulian */
106852419Sjulianvoid
106952419Sjulianng_destroy_hook(hook_p hook)
107052419Sjulian{
1071151974Sglebius	hook_p peer;
1072151974Sglebius	node_p node;
107352419Sjulian
107471047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
107571047Sjulian		printf("ng_destroy_hook called on deadhook\n");
107671047Sjulian		return;
107771047Sjulian	}
1078151974Sglebius
1079151974Sglebius	/*
1080151974Sglebius	 * Protect divorce process with mutex, to avoid races on
1081151974Sglebius	 * simultaneous disconnect.
1082151974Sglebius	 */
1083151974Sglebius	mtx_lock(&ng_topo_mtx);
1084151974Sglebius
1085151974Sglebius	hook->hk_flags |= HK_INVALID;
1086151974Sglebius
1087151974Sglebius	peer = NG_HOOK_PEER(hook);
1088151974Sglebius	node = NG_HOOK_NODE(hook);
1089151974Sglebius
109070939Sjulian	if (peer && (peer != &ng_deadhook)) {
109170939Sjulian		/*
109270939Sjulian		 * Set the peer to point to ng_deadhook
109370939Sjulian		 * from this moment on we are effectively independent it.
109470939Sjulian		 * send it an rmhook message of it's own.
109570939Sjulian		 */
109670939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
109770939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
109871047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
1099152451Sglebius			/*
110071047Sjulian			 * If it's already divorced from a node,
110171047Sjulian			 * just free it.
110271047Sjulian			 */
1103151974Sglebius			mtx_unlock(&ng_topo_mtx);
110471047Sjulian		} else {
1105151974Sglebius			mtx_unlock(&ng_topo_mtx);
110671047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
110771047Sjulian		}
110870942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
110970942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
1110151974Sglebius	} else
1111151974Sglebius		mtx_unlock(&ng_topo_mtx);
111252419Sjulian
1113151974Sglebius	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
1114151974Sglebius
111552419Sjulian	/*
111652419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
111752419Sjulian	 * in case the disconnection results in node shutdown.
111852419Sjulian	 */
111971047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
112071047Sjulian		return;
112171047Sjulian	}
112270784Sjulian	LIST_REMOVE(hook, hk_hooks);
112370784Sjulian	node->nd_numhooks--;
112470784Sjulian	if (node->nd_type->disconnect) {
112552419Sjulian		/*
112671047Sjulian		 * The type handler may elect to destroy the node so don't
1127167677Srwatson		 * trust its existence after this point. (except
112871047Sjulian		 * that we still hold a reference on it. (which we
112971047Sjulian		 * inherrited from the hook we are destroying)
113052419Sjulian		 */
113170784Sjulian		(*node->nd_type->disconnect) (hook);
113252419Sjulian	}
113371047Sjulian
113471047Sjulian	/*
113571047Sjulian	 * Note that because we will point to ng_deadnode, the original node
113671047Sjulian	 * is not decremented automatically so we do that manually.
113771047Sjulian	 */
113871047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
113971047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
114071047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
114152419Sjulian}
114252419Sjulian
114352419Sjulian/*
114452419Sjulian * Take two hooks on a node and merge the connection so that the given node
114552419Sjulian * is effectively bypassed.
114652419Sjulian */
114752419Sjulianint
114852419Sjulianng_bypass(hook_p hook1, hook_p hook2)
114952419Sjulian{
115070784Sjulian	if (hook1->hk_node != hook2->hk_node) {
115171047Sjulian		TRAP_ERROR();
115252419Sjulian		return (EINVAL);
115370784Sjulian	}
115470784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
115570784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
115652419Sjulian
115770939Sjulian	hook1->hk_peer = &ng_deadhook;
115870939Sjulian	hook2->hk_peer = &ng_deadhook;
115970939Sjulian
1160163244Sglebius	NG_HOOK_UNREF(hook1);
1161163244Sglebius	NG_HOOK_UNREF(hook2);
1162163244Sglebius
116352419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
116452419Sjulian	ng_destroy_hook(hook1);
116552419Sjulian	ng_destroy_hook(hook2);
116652419Sjulian	return (0);
116752419Sjulian}
116852419Sjulian
116952419Sjulian/*
117052419Sjulian * Install a new netgraph type
117152419Sjulian */
117252419Sjulianint
117352419Sjulianng_newtype(struct ng_type *tp)
117452419Sjulian{
117552419Sjulian	const size_t namelen = strlen(tp->name);
117652419Sjulian
117752419Sjulian	/* Check version and type name fields */
117870159Sjulian	if ((tp->version != NG_ABI_VERSION)
117970159Sjulian	|| (namelen == 0)
1180125028Sharti	|| (namelen >= NG_TYPESIZ)) {
118171047Sjulian		TRAP_ERROR();
1182131374Sjulian		if (tp->version != NG_ABI_VERSION) {
1183131374Sjulian			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
1184131374Sjulian		}
118552419Sjulian		return (EINVAL);
118652419Sjulian	}
118752419Sjulian
118852419Sjulian	/* Check for name collision */
118952419Sjulian	if (ng_findtype(tp->name) != NULL) {
119071047Sjulian		TRAP_ERROR();
119152419Sjulian		return (EEXIST);
119252419Sjulian	}
119352419Sjulian
119470700Sjulian
119552419Sjulian	/* Link in new type */
119672200Sbmilekic	mtx_lock(&ng_typelist_mtx);
119770700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
119871603Sjulian	tp->refs = 1;	/* first ref is linked list */
119972200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
120052419Sjulian	return (0);
120152419Sjulian}
120252419Sjulian
120352419Sjulian/*
120480222Sjulian * unlink a netgraph type
120580222Sjulian * If no examples exist
120680222Sjulian */
120780222Sjulianint
120880222Sjulianng_rmtype(struct ng_type *tp)
120980222Sjulian{
121080222Sjulian	/* Check for name collision */
121180222Sjulian	if (tp->refs != 1) {
121280222Sjulian		TRAP_ERROR();
121380222Sjulian		return (EBUSY);
121480222Sjulian	}
121580222Sjulian
121680222Sjulian	/* Unlink type */
121780222Sjulian	mtx_lock(&ng_typelist_mtx);
121880222Sjulian	LIST_REMOVE(tp, types);
121980222Sjulian	mtx_unlock(&ng_typelist_mtx);
122080222Sjulian	return (0);
122180222Sjulian}
122280222Sjulian
122380222Sjulian/*
122452419Sjulian * Look for a type of the name given
122552419Sjulian */
122652419Sjulianstruct ng_type *
122752419Sjulianng_findtype(const char *typename)
122852419Sjulian{
122952419Sjulian	struct ng_type *type;
123052419Sjulian
123172200Sbmilekic	mtx_lock(&ng_typelist_mtx);
123270700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
123352419Sjulian		if (strcmp(type->name, typename) == 0)
123452419Sjulian			break;
123552419Sjulian	}
123672200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
123752419Sjulian	return (type);
123852419Sjulian}
123952419Sjulian
124052419Sjulian/************************************************************************
124152419Sjulian			Composite routines
124252419Sjulian************************************************************************/
124352419Sjulian/*
124471047Sjulian * Connect two nodes using the specified hooks, using queued functions.
124552419Sjulian */
1246172806Smavstatic int
1247172806Smavng_con_part3(node_p node, item_p item, hook_p hook)
124852419Sjulian{
1249172806Smav	int	error = 0;
125052419Sjulian
125171047Sjulian	/*
125271047Sjulian	 * When we run, we know that the node 'node' is locked for us.
125371047Sjulian	 * Our caller has a reference on the hook.
125471047Sjulian	 * Our caller has a reference on the node.
125571047Sjulian	 * (In this case our caller is ng_apply_item() ).
125671047Sjulian	 * The peer hook has a reference on the hook.
125771849Sjulian	 * We are all set up except for the final call to the node, and
125871849Sjulian	 * the clearing of the INVALID flag.
125971047Sjulian	 */
126071047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
126171047Sjulian		/*
126271047Sjulian		 * The node must have been freed again since we last visited
126371047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
126471047Sjulian		 * We should just release our references and
126571047Sjulian		 * free anything we can think of.
126671047Sjulian		 * Since we know it's been destroyed, and it's our caller
126771047Sjulian		 * that holds the references, just return.
126871047Sjulian		 */
1269172806Smav		ERROUT(ENOENT);
127052419Sjulian	}
127171047Sjulian	if (hook->hk_node->nd_type->connect) {
1272172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
127371047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
127471849Sjulian			printf("failed in ng_con_part3()\n");
1275172806Smav			ERROUT(error);
127671047Sjulian		}
127752419Sjulian	}
127871047Sjulian	/*
127971047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
128071047Sjulian	 * to separate out 'create' and 'invalid' flags.
128171047Sjulian	 * should only set flags on hooks we have locked under our node.
128271047Sjulian	 */
128371047Sjulian	hook->hk_flags &= ~HK_INVALID;
1284172806Smavdone:
1285172806Smav	NG_FREE_ITEM(item);
1286172806Smav	return (error);
128771047Sjulian}
128852419Sjulian
1289172806Smavstatic int
1290172806Smavng_con_part2(node_p node, item_p item, hook_p hook)
129171047Sjulian{
1292172806Smav	hook_p	peer;
1293172806Smav	int	error = 0;
129471047Sjulian
129552419Sjulian	/*
129671047Sjulian	 * When we run, we know that the node 'node' is locked for us.
129771047Sjulian	 * Our caller has a reference on the hook.
129871047Sjulian	 * Our caller has a reference on the node.
129971047Sjulian	 * (In this case our caller is ng_apply_item() ).
130071047Sjulian	 * The peer hook has a reference on the hook.
130171047Sjulian	 * our node pointer points to the 'dead' node.
130271047Sjulian	 * First check the hook name is unique.
130371849Sjulian	 * Should not happen because we checked before queueing this.
130452419Sjulian	 */
130571047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
130671047Sjulian		TRAP_ERROR();
130771047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
130871849Sjulian		printf("failed in ng_con_part2()\n");
1309172806Smav		ERROUT(EEXIST);
131071047Sjulian	}
131170939Sjulian	/*
131271047Sjulian	 * Check if the node type code has something to say about it
131371047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
131471047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
131571047Sjulian	 * The peer hook will also be destroyed.
131670939Sjulian	 */
131771047Sjulian	if (node->nd_type->newhook != NULL) {
1318172806Smav		if ((error = (*node->nd_type->newhook)(node, hook,
1319172806Smav		    hook->hk_name))) {
132071047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
132171849Sjulian			printf("failed in ng_con_part2()\n");
1322172806Smav			ERROUT(error);
132371047Sjulian		}
132471047Sjulian	}
132571047Sjulian
132671047Sjulian	/*
132771047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
132871047Sjulian	 * We'll ask again later when we actually connect the hooks.
132971047Sjulian	 */
133071047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
133171047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
133271047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
133371047Sjulian	node->nd_numhooks++;
133471047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
133571047Sjulian
133671047Sjulian	/*
1337167677Srwatson	 * We now have a symmetrical situation, where both hooks have been
133874078Sjulian	 * linked to their nodes, the newhook methods have been called
133971047Sjulian	 * And the references are all correct. The hooks are still marked
134071047Sjulian	 * as invalid, as we have not called the 'connect' methods
134171047Sjulian	 * yet.
1342167677Srwatson	 * We can call the local one immediately as we have the
134371047Sjulian	 * node locked, but we need to queue the remote one.
134471047Sjulian	 */
134571047Sjulian	if (hook->hk_node->nd_type->connect) {
1346172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
134771047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
134871849Sjulian			printf("failed in ng_con_part2(A)\n");
1349172806Smav			ERROUT(error);
135071047Sjulian		}
135171047Sjulian	}
1352151974Sglebius
1353151974Sglebius	/*
1354151974Sglebius	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1355151974Sglebius	 */
1356151974Sglebius	mtx_lock(&ng_topo_mtx);
1357151974Sglebius	peer = hook->hk_peer;
1358151974Sglebius	if (peer == &ng_deadhook) {
1359151974Sglebius		mtx_unlock(&ng_topo_mtx);
1360151974Sglebius		printf("failed in ng_con_part2(B)\n");
1361151974Sglebius		ng_destroy_hook(hook);
1362172806Smav		ERROUT(ENOENT);
1363151974Sglebius	}
1364151974Sglebius	mtx_unlock(&ng_topo_mtx);
1365151974Sglebius
1366173605Sglebius	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1367173605Sglebius	    NULL, 0, NG_REUSE_ITEM))) {
1368151974Sglebius		printf("failed in ng_con_part2(C)\n");
136971849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
1370172806Smav		return (error);		/* item was consumed. */
137171849Sjulian	}
137271047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1373172806Smav	return (0);			/* item was consumed. */
1374172806Smavdone:
1375172806Smav	NG_FREE_ITEM(item);
1376172806Smav	return (error);
137752419Sjulian}
137852419Sjulian
137952419Sjulian/*
1380152451Sglebius * Connect this node with another node. We assume that this node is
138171047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
138252419Sjulian */
138371047Sjulianstatic int
1384172806Smavng_con_nodes(item_p item, node_p node, const char *name,
1385172806Smav    node_p node2, const char *name2)
138652419Sjulian{
1387152451Sglebius	int	error;
1388152451Sglebius	hook_p	hook;
1389152451Sglebius	hook_p	hook2;
139052419Sjulian
139171849Sjulian	if (ng_findhook(node2, name2) != NULL) {
139271849Sjulian		return(EEXIST);
139371849Sjulian	}
139470939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
139552419Sjulian		return (error);
139671047Sjulian	/* Allocate the other hook and link it up */
139771047Sjulian	NG_ALLOC_HOOK(hook2);
1398140737Sglebius	if (hook2 == NULL) {
139971047Sjulian		TRAP_ERROR();
140071047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
140171047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
140271047Sjulian		return (ENOMEM);
140371047Sjulian	}
140471047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
140571047Sjulian	hook2->hk_flags = HK_INVALID;
140671047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
140771047Sjulian	hook->hk_peer = hook2;
140871047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
140971047Sjulian	NG_HOOK_REF(hook2);
1410152451Sglebius	hook2->hk_node = &ng_deadnode;
1411125028Sharti	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
141271047Sjulian
141371047Sjulian	/*
141471047Sjulian	 * Queue the function above.
141571047Sjulian	 * Procesing continues in that function in the lock context of
141671047Sjulian	 * the other node.
141771047Sjulian	 */
1418173605Sglebius	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1419173605Sglebius	    NG_NOFLAGS))) {
1420171885Smav		printf("failed in ng_con_nodes(): %d\n", error);
1421171885Smav		ng_destroy_hook(hook);	/* also zaps peer */
1422171885Smav	}
142371047Sjulian
142471047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
142571047Sjulian	NG_HOOK_UNREF(hook2);
1426171885Smav	return (error);
142771047Sjulian}
142871047Sjulian
142971047Sjulian/*
143071047Sjulian * Make a peer and connect.
143171047Sjulian * We assume that the local node is locked.
143271047Sjulian * The new node probably doesn't need a lock until
143371047Sjulian * it has a hook, because it cannot really have any work until then,
143471047Sjulian * but we should think about it a bit more.
143571047Sjulian *
143671047Sjulian * The problem may come if the other node also fires up
143771047Sjulian * some hardware or a timer or some other source of activation,
143871047Sjulian * also it may already get a command msg via it's ID.
143971047Sjulian *
144071047Sjulian * We could use the same method as ng_con_nodes() but we'd have
1441152451Sglebius * to add ability to remove the node when failing. (Not hard, just
144271047Sjulian * make arg1 point to the node to remove).
144371047Sjulian * Unless of course we just ignore failure to connect and leave
144471047Sjulian * an unconnected node?
144571047Sjulian */
144671047Sjulianstatic int
144771047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
144871047Sjulian{
1449152451Sglebius	node_p	node2;
1450152451Sglebius	hook_p	hook1, hook2;
1451152451Sglebius	int	error;
145271047Sjulian
145371047Sjulian	if ((error = ng_make_node(type, &node2))) {
145452419Sjulian		return (error);
145552419Sjulian	}
145670939Sjulian
145771047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
145871849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
145971047Sjulian		return (error);
146071047Sjulian	}
146171047Sjulian
146271047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
146371849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
146471047Sjulian		ng_destroy_hook(hook1);
146571047Sjulian		NG_HOOK_UNREF(hook1);
146671047Sjulian		return (error);
146771047Sjulian	}
146871047Sjulian
146970939Sjulian	/*
147071047Sjulian	 * Actually link the two hooks together.
147171047Sjulian	 */
147271047Sjulian	hook1->hk_peer = hook2;
147371047Sjulian	hook2->hk_peer = hook1;
147471047Sjulian
147571047Sjulian	/* Each hook is referenced by the other */
147671047Sjulian	NG_HOOK_REF(hook1);
147771047Sjulian	NG_HOOK_REF(hook2);
147871047Sjulian
147971047Sjulian	/* Give each node the opportunity to veto the pending connection */
148071047Sjulian	if (hook1->hk_node->nd_type->connect) {
148171047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
148271047Sjulian	}
148371047Sjulian
148471047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
148571047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
148671047Sjulian
148771047Sjulian	}
148871047Sjulian
148971047Sjulian	/*
149070939Sjulian	 * drop the references we were holding on the two hooks.
149170939Sjulian	 */
149271047Sjulian	if (error) {
149371047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
149471849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
149571047Sjulian	} else {
149671047Sjulian		/* As a last act, allow the hooks to be used */
149771047Sjulian		hook1->hk_flags &= ~HK_INVALID;
149871047Sjulian		hook2->hk_flags &= ~HK_INVALID;
149971047Sjulian	}
150071047Sjulian	NG_HOOK_UNREF(hook1);
150170939Sjulian	NG_HOOK_UNREF(hook2);
150270939Sjulian	return (error);
150352419Sjulian}
150471047Sjulian
150570700Sjulian/************************************************************************
150670700Sjulian		Utility routines to send self messages
150770700Sjulian************************************************************************/
150870700Sjulian
150971849Sjulian/* Shut this node down as soon as everyone is clear of it */
1510167677Srwatson/* Should add arg "immediately" to jump the queue */
151170700Sjulianint
151271849Sjulianng_rmnode_self(node_p node)
151370700Sjulian{
151471849Sjulian	int		error;
151552419Sjulian
151671849Sjulian	if (node == &ng_deadnode)
151771849Sjulian		return (0);
1518132464Sjulian	node->nd_flags |= NGF_INVALID;
1519132464Sjulian	if (node->nd_flags & NGF_CLOSING)
152071849Sjulian		return (0);
152170700Sjulian
152271849Sjulian	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
152371849Sjulian	return (error);
152470700Sjulian}
152570700Sjulian
152671849Sjulianstatic void
152771047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
152871047Sjulian{
152971047Sjulian	ng_destroy_hook(hook);
153071849Sjulian	return ;
153171047Sjulian}
153271047Sjulian
153370935Sjulianint
153470935Sjulianng_rmhook_self(hook_p hook)
153570935Sjulian{
153671047Sjulian	int		error;
153770935Sjulian	node_p node = NG_HOOK_NODE(hook);
153870935Sjulian
153971047Sjulian	if (node == &ng_deadnode)
154071047Sjulian		return (0);
154171047Sjulian
154271047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
154371047Sjulian	return (error);
154470935Sjulian}
154570935Sjulian
154670700Sjulian/***********************************************************************
154752419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
154852419Sjulian *
154952419Sjulian * Such a string can refer to a specific node or a specific hook
155052419Sjulian * on a specific node, depending on how you look at it. In the
155152419Sjulian * latter case, the PATH component must not end in a dot.
155252419Sjulian *
155352419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
155452419Sjulian * of hook names separated by dots. This breaks out the original
155552419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
155652419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
155752419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
155852419Sjulian *
155952419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
156070700Sjulian ***********************************************************************/
156152419Sjulianint
156252419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
156352419Sjulian{
1564152451Sglebius	char	*node, *path, *hook;
1565152451Sglebius	int	k;
156652419Sjulian
156752419Sjulian	/*
156852419Sjulian	 * Extract absolute NODE, if any
156952419Sjulian	 */
157052419Sjulian	for (path = addr; *path && *path != ':'; path++);
157152419Sjulian	if (*path) {
157252419Sjulian		node = addr;	/* Here's the NODE */
157352419Sjulian		*path++ = '\0';	/* Here's the PATH */
157452419Sjulian
157552419Sjulian		/* Node name must not be empty */
157652419Sjulian		if (!*node)
157752419Sjulian			return -1;
157852419Sjulian
157952419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
158052419Sjulian		if (strcmp(node, ".") != 0) {
158152419Sjulian			for (k = 0; node[k]; k++)
158252419Sjulian				if (node[k] == '.')
158352419Sjulian					return -1;
158452419Sjulian		}
158552419Sjulian	} else {
158652419Sjulian		node = NULL;	/* No absolute NODE */
158752419Sjulian		path = addr;	/* Here's the PATH */
158852419Sjulian	}
158952419Sjulian
159052419Sjulian	/* Snoop for illegal characters in PATH */
159152419Sjulian	for (k = 0; path[k]; k++)
159252419Sjulian		if (path[k] == ':')
159352419Sjulian			return -1;
159452419Sjulian
159552419Sjulian	/* Check for no repeated dots in PATH */
159652419Sjulian	for (k = 0; path[k]; k++)
159752419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
159852419Sjulian			return -1;
159952419Sjulian
160052419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
160152419Sjulian	if (path[0] == '.')
160252419Sjulian		path++;
160352419Sjulian	if (*path && path[strlen(path) - 1] == '.')
160452419Sjulian		path[strlen(path) - 1] = 0;
160552419Sjulian
160652419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
160752419Sjulian	if (*path) {
160852419Sjulian		for (hook = path, k = 0; path[k]; k++)
160952419Sjulian			if (path[k] == '.') {
161052419Sjulian				hook = NULL;
161152419Sjulian				break;
161252419Sjulian			}
161352419Sjulian	} else
161452419Sjulian		path = hook = NULL;
161552419Sjulian
161652419Sjulian	/* Done */
161752419Sjulian	if (nodep)
161852419Sjulian		*nodep = node;
161952419Sjulian	if (pathp)
162052419Sjulian		*pathp = path;
162152419Sjulian	if (hookp)
162252419Sjulian		*hookp = hook;
162352419Sjulian	return (0);
162452419Sjulian}
162552419Sjulian
162652419Sjulian/*
162752419Sjulian * Given a path, which may be absolute or relative, and a starting node,
162870700Sjulian * return the destination node.
162952419Sjulian */
163052419Sjulianint
163170700Sjulianng_path2noderef(node_p here, const char *address,
163270700Sjulian				node_p *destp, hook_p *lasthook)
163352419Sjulian{
1634125028Sharti	char    fullpath[NG_PATHSIZ];
163552419Sjulian	char   *nodename, *path, pbuf[2];
163670700Sjulian	node_p  node, oldnode;
163752419Sjulian	char   *cp;
163859728Sjulian	hook_p hook = NULL;
163952419Sjulian
164052419Sjulian	/* Initialize */
164170784Sjulian	if (destp == NULL) {
164271047Sjulian		TRAP_ERROR();
164352419Sjulian		return EINVAL;
164470784Sjulian	}
164552419Sjulian	*destp = NULL;
164652419Sjulian
164752419Sjulian	/* Make a writable copy of address for ng_path_parse() */
164852419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
164952419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
165052419Sjulian
165152419Sjulian	/* Parse out node and sequence of hooks */
165252419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
165371047Sjulian		TRAP_ERROR();
165452419Sjulian		return EINVAL;
165552419Sjulian	}
165652419Sjulian	if (path == NULL) {
165752419Sjulian		pbuf[0] = '.';	/* Needs to be writable */
165852419Sjulian		pbuf[1] = '\0';
165952419Sjulian		path = pbuf;
166052419Sjulian	}
166152419Sjulian
166270700Sjulian	/*
166370700Sjulian	 * For an absolute address, jump to the starting node.
166470700Sjulian	 * Note that this holds a reference on the node for us.
166570700Sjulian	 * Don't forget to drop the reference if we don't need it.
166670700Sjulian	 */
166752419Sjulian	if (nodename) {
166870700Sjulian		node = ng_name2noderef(here, nodename);
166952419Sjulian		if (node == NULL) {
167071047Sjulian			TRAP_ERROR();
167152419Sjulian			return (ENOENT);
167252419Sjulian		}
167370700Sjulian	} else {
167470700Sjulian		if (here == NULL) {
167571047Sjulian			TRAP_ERROR();
167670700Sjulian			return (EINVAL);
167770700Sjulian		}
167852419Sjulian		node = here;
167970784Sjulian		NG_NODE_REF(node);
168070700Sjulian	}
168152419Sjulian
168270700Sjulian	/*
1683152451Sglebius	 * Now follow the sequence of hooks
168470700Sjulian	 * XXX
168570700Sjulian	 * We actually cannot guarantee that the sequence
168670700Sjulian	 * is not being demolished as we crawl along it
168770700Sjulian	 * without extra-ordinary locking etc.
168870700Sjulian	 * So this is a bit dodgy to say the least.
168970700Sjulian	 * We can probably hold up some things by holding
169070700Sjulian	 * the nodelist mutex for the time of this
169170700Sjulian	 * crawl if we wanted.. At least that way we wouldn't have to
1692167677Srwatson	 * worry about the nodes disappearing, but the hooks would still
169370700Sjulian	 * be a problem.
169470700Sjulian	 */
169552419Sjulian	for (cp = path; node != NULL && *cp != '\0'; ) {
169652419Sjulian		char *segment;
169752419Sjulian
169852419Sjulian		/*
169952419Sjulian		 * Break out the next path segment. Replace the dot we just
170052419Sjulian		 * found with a NUL; "cp" points to the next segment (or the
170152419Sjulian		 * NUL at the end).
170252419Sjulian		 */
170352419Sjulian		for (segment = cp; *cp != '\0'; cp++) {
170452419Sjulian			if (*cp == '.') {
170552419Sjulian				*cp++ = '\0';
170652419Sjulian				break;
170752419Sjulian			}
170852419Sjulian		}
170952419Sjulian
171052419Sjulian		/* Empty segment */
171152419Sjulian		if (*segment == '\0')
171252419Sjulian			continue;
171352419Sjulian
171452419Sjulian		/* We have a segment, so look for a hook by that name */
171554096Sarchie		hook = ng_findhook(node, segment);
171652419Sjulian
171752419Sjulian		/* Can't get there from here... */
171852419Sjulian		if (hook == NULL
171970784Sjulian		    || NG_HOOK_PEER(hook) == NULL
172070784Sjulian		    || NG_HOOK_NOT_VALID(hook)
172170784Sjulian		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
172271047Sjulian			TRAP_ERROR();
172370784Sjulian			NG_NODE_UNREF(node);
1724152451Sglebius#if 0
172570784Sjulian			printf("hooknotvalid %s %s %d %d %d %d ",
172670784Sjulian					path,
172770784Sjulian					segment,
172870784Sjulian					hook == NULL,
1729152451Sglebius					NG_HOOK_PEER(hook) == NULL,
1730152451Sglebius					NG_HOOK_NOT_VALID(hook),
1731152451Sglebius					NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
173270784Sjulian#endif
173352419Sjulian			return (ENOENT);
173452419Sjulian		}
173552419Sjulian
173670700Sjulian		/*
1737152451Sglebius		 * Hop on over to the next node
173870700Sjulian		 * XXX
1739152451Sglebius		 * Big race conditions here as hooks and nodes go away
174070700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
174170700Sjulian		 * instead of the direct hook in this crawl?
174270700Sjulian		 */
174370700Sjulian		oldnode = node;
174470784Sjulian		if ((node = NG_PEER_NODE(hook)))
174570784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
174670784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
174770784Sjulian		if (NG_NODE_NOT_VALID(node)) {
174870784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
174970700Sjulian			node = NULL;
175070700Sjulian		}
175152419Sjulian	}
175252419Sjulian
175352419Sjulian	/* If node somehow missing, fail here (probably this is not needed) */
175452419Sjulian	if (node == NULL) {
175571047Sjulian		TRAP_ERROR();
175652419Sjulian		return (ENXIO);
175752419Sjulian	}
175852419Sjulian
175952419Sjulian	/* Done */
176052419Sjulian	*destp = node;
176159900Sarchie	if (lasthook != NULL)
176270784Sjulian		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
176352419Sjulian	return (0);
176452419Sjulian}
176552419Sjulian
176670700Sjulian/***************************************************************\
176770700Sjulian* Input queue handling.
176870700Sjulian* All activities are submitted to the node via the input queue
176970700Sjulian* which implements a multiple-reader/single-writer gate.
1770167677Srwatson* Items which cannot be handled immediately are queued.
177170700Sjulian*
177270700Sjulian* read-write queue locking inline functions			*
177370700Sjulian\***************************************************************/
177470700Sjulian
1775151238Sglebiusstatic __inline item_p ng_dequeue(struct ng_queue * ngq, int *rw);
177670700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq,
177770700Sjulian					item_p  item);
177870700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq,
177970700Sjulian					item_p  item);
178070700Sjulianstatic __inline void	ng_leave_read(struct ng_queue * ngq);
178170700Sjulianstatic __inline void	ng_leave_write(struct ng_queue * ngq);
178270700Sjulianstatic __inline void	ng_queue_rw(struct ng_queue * ngq,
178370700Sjulian					item_p  item, int rw);
178470700Sjulian
178552419Sjulian/*
178670700Sjulian * Definition of the bits fields in the ng_queue flag word.
178770700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
178870700Sjulian * with them.
178970700Sjulian *
179071902Sjulian * The ordering here may be important! don't shuffle these.
179152419Sjulian */
179270700Sjulian/*-
179370700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
179470700Sjulian                       |
179570700Sjulian                       V
179670700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1797151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1798151973Sglebius  | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
1799151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
180070700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1801151973Sglebius  \___________________________ ____________________________/ | |
1802151973Sglebius                            V                                | |
1803151973Sglebius                  [active reader count]                      | |
180470700Sjulian                                                             | |
1805151973Sglebius            Operation Pending -------------------------------+ |
180670700Sjulian                                                               |
1807151973Sglebius          Active Writer ---------------------------------------+
180871902Sjulian
180971902Sjulian
181070700Sjulian*/
1811151973Sglebius#define WRITER_ACTIVE	0x00000001
1812151973Sglebius#define OP_PENDING	0x00000002
1813151973Sglebius#define READER_INCREMENT 0x00000004
1814151973Sglebius#define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
1815151973Sglebius#define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
181671902Sjulian
181771902Sjulian/* Defines of more elaborate states on the queue */
1818151973Sglebius/* Mask of bits a new read cares about */
1819151973Sglebius#define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
182071902Sjulian
1821151973Sglebius/* Mask of bits a new write cares about */
182271902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
182371902Sjulian
1824151973Sglebius/* Test to decide if there is something on the queue. */
1825151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
182671902Sjulian
1827151973Sglebius/* How to decide what the next queued item is. */
1828151973Sglebius#define HEAD_IS_READER(QP)  NGI_QUEUED_READER((QP)->queue)
1829151973Sglebius#define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER((QP)->queue) /* notused */
1830151973Sglebius
1831151973Sglebius/* Read the status to decide if the next item on the queue can now run. */
1832151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP)			\
1833151973Sglebius		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
1834151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP)			\
1835151973Sglebius		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1836151973Sglebius
183771902Sjulian/* Is there a chance of getting ANY work off the queue? */
1838151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
1839151973Sglebius	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1840177953Smav				QUEUED_WRITER_CAN_PROCEED(QP))
184171902Sjulian
1842151973Sglebius
1843151238Sglebius#define NGQRW_R 0
1844151238Sglebius#define NGQRW_W 1
1845151238Sglebius
184670700Sjulian/*
184770700Sjulian * Taking into account the current state of the queue and node, possibly take
184870700Sjulian * the next entry off the queue and return it. Return NULL if there was
184970700Sjulian * nothing we could return, either because there really was nothing there, or
185070700Sjulian * because the node was in a state where it cannot yet process the next item
185170700Sjulian * on the queue.
185270700Sjulian *
185370700Sjulian * This MUST MUST MUST be called with the mutex held.
185470700Sjulian */
185570700Sjulianstatic __inline item_p
1856151238Sglebiusng_dequeue(struct ng_queue *ngq, int *rw)
185770700Sjulian{
185870700Sjulian	item_p item;
185971902Sjulian
1860139039Sglebius	mtx_assert(&ngq->q_mtx, MA_OWNED);
1861152451Sglebius	/*
1862151973Sglebius	 * If there is nothing queued, then just return.
1863151973Sglebius	 * No point in continuing.
1864151973Sglebius	 */
1865151973Sglebius	if (!QUEUE_ACTIVE(ngq)) {
1866154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
1867154275Sglebius		    "queue flags 0x%lx", __func__,
1868154275Sglebius		    ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags);
1869151973Sglebius		return (NULL);
1870151973Sglebius	}
1871139039Sglebius
1872151973Sglebius	/*
1873151973Sglebius	 * From here, we can assume there is a head item.
1874151973Sglebius	 * We need to find out what it is and if it can be dequeued, given
1875151973Sglebius	 * the current state of the node.
1876151973Sglebius	 */
1877151973Sglebius	if (HEAD_IS_READER(ngq)) {
1878177953Smav		while (1) {
1879177953Smav			long t = ngq->q_flags;
1880177953Smav			if (t & WRITER_ACTIVE) {
1881177953Smav				/* It's a reader but we can't use it. */
1882177953Smav				CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
1883177953Smav				    "can't proceed; queue flags 0x%lx", __func__,
1884177953Smav				    ngq->q_node->nd_ID, ngq->q_node, t);
1885177953Smav				return (NULL);
1886177953Smav			}
1887177953Smav			if (atomic_cmpset_long(&ngq->q_flags, t,
1888177953Smav			    t + READER_INCREMENT))
1889177953Smav				break;
1890177953Smav			cpu_spinwait();
1891151973Sglebius		}
1892151238Sglebius		*rw = NGQRW_R;
1893177953Smav	} else if (atomic_cmpset_long(&ngq->q_flags, OP_PENDING,
1894177953Smav	    OP_PENDING + WRITER_ACTIVE)) {
1895151238Sglebius		*rw = NGQRW_W;
189670700Sjulian	} else {
1897177953Smav		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer "
1898177953Smav		    "can't proceed; queue flags 0x%lx", __func__,
1899154275Sglebius		    ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags);
1900151973Sglebius		return (NULL);
190170700Sjulian	}
190252419Sjulian
190370700Sjulian	/*
190470700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
190570700Sjulian	 * pending flags and the next and last pointers.
190670700Sjulian	 */
190770700Sjulian	item = ngq->queue;
190870700Sjulian	ngq->queue = item->el_next;
190970700Sjulian	if (ngq->last == &(item->el_next)) {
191070700Sjulian		ngq->last = &(ngq->queue);
1911177953Smav		atomic_clear_long(&ngq->q_flags, OP_PENDING);
191270700Sjulian	}
1913154275Sglebius	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
1914154275Sglebius	    "queue flags 0x%lx", __func__,
1915154275Sglebius	    ngq->q_node->nd_ID, ngq->q_node, item, *rw ? "WRITER" : "READER" ,
1916154225Sglebius	    ngq->q_flags);
191770700Sjulian	return (item);
191870700Sjulian}
191952419Sjulian
192070700Sjulian/*
192170700Sjulian * Queue a packet to be picked up by someone else.
192270700Sjulian * We really don't care who, but we can't or don't want to hang around
192370700Sjulian * to process it ourselves. We are probably an interrupt routine..
1924151973Sglebius * If the queue could be run, flag the netisr handler to start.
192570700Sjulian */
192670700Sjulianstatic __inline void
192770700Sjulianng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
192870700Sjulian{
1929151973Sglebius	if (rw == NGQRW_W)
1930151973Sglebius		NGI_SET_WRITER(item);
1931151973Sglebius	else
1932151973Sglebius		NGI_SET_READER(item);
193370700Sjulian	item->el_next = NULL;	/* maybe not needed */
1934177953Smav
1935177953Smav	NG_QUEUE_LOCK(ngq);
1936177953Smav	/* Set OP_PENDING flag and enqueue the item. */
1937177953Smav	atomic_set_long(&ngq->q_flags, OP_PENDING);
193870700Sjulian	*ngq->last = item;
1939177953Smav	ngq->last = &(item->el_next);
1940177953Smav
1941154275Sglebius	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
1942154275Sglebius	    ngq->q_node->nd_ID, ngq->q_node, item, rw ? "WRITER" : "READER" );
1943177953Smav
194470700Sjulian	/*
1945151973Sglebius	 * We can take the worklist lock with the node locked
1946151973Sglebius	 * BUT NOT THE REVERSE!
1947151973Sglebius	 */
1948151973Sglebius	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
1949177953Smav		ng_worklist_add(ngq->q_node);
1950177953Smav	NG_QUEUE_UNLOCK(ngq);
195170700Sjulian}
195252419Sjulian
195370700Sjulianstatic __inline item_p
195470700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item)
195552419Sjulian{
1956151974Sglebius	KASSERT(ngq != &ng_deadnode.nd_input_queue,
1957151974Sglebius	    ("%s: working on deadnode", __func__));
195852419Sjulian
1959177953Smav	/* Reader needs node without writer and pending items. */
1960177953Smav	while (1) {
1961177953Smav		long t = ngq->q_flags;
1962177953Smav		if (t & NGQ_RMASK)
1963177953Smav			break; /* Node is not ready for reader. */
1964177953Smav		if (atomic_cmpset_long(&ngq->q_flags, t, t + READER_INCREMENT)) {
1965177953Smav	    		/* Successfully grabbed node */
1966177953Smav			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
1967177953Smav			    __func__, ngq->q_node->nd_ID, ngq->q_node, item);
1968177953Smav			return (item);
1969177953Smav		}
1970177953Smav		cpu_spinwait();
1971177953Smav	};
197270700Sjulian
1973177953Smav	/* Queue the request for later. */
197470700Sjulian	ng_queue_rw(ngq, item, NGQRW_R);
197570700Sjulian
1976148236Sglebius	return (NULL);
197770700Sjulian}
197870700Sjulian
197970700Sjulianstatic __inline item_p
198070700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item)
198170700Sjulian{
1982151974Sglebius	KASSERT(ngq != &ng_deadnode.nd_input_queue,
1983151974Sglebius	    ("%s: working on deadnode", __func__));
1984151974Sglebius
1985177953Smav	/* Writer needs completely idle node. */
1986177953Smav	if (atomic_cmpset_long(&ngq->q_flags, 0, WRITER_ACTIVE)) {
1987177953Smav	    	/* Successfully grabbed node */
1988154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
1989154275Sglebius		    __func__, ngq->q_node->nd_ID, ngq->q_node, item);
199070700Sjulian		return (item);
199152419Sjulian	}
199252419Sjulian
1993177953Smav	/* Queue the request for later. */
199470700Sjulian	ng_queue_rw(ngq, item, NGQRW_W);
199570700Sjulian
1996148236Sglebius	return (NULL);
199770700Sjulian}
199870700Sjulian
1999167385Sjulian#if 0
2000167385Sjulianstatic __inline item_p
2001167385Sjulianng_upgrade_write(struct ng_queue *ngq, item_p item)
2002167385Sjulian{
2003167385Sjulian	KASSERT(ngq != &ng_deadnode.nd_input_queue,
2004167385Sjulian	    ("%s: working on deadnode", __func__));
2005167385Sjulian
2006167385Sjulian	NGI_SET_WRITER(item);
2007167385Sjulian
2008167385Sjulian	mtx_lock_spin(&(ngq->q_mtx));
2009167385Sjulian
2010167385Sjulian	/*
2011167385Sjulian	 * There will never be no readers as we are there ourselves.
2012167385Sjulian	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2013167385Sjulian	 * The caller we are running from will call ng_leave_read()
2014167385Sjulian	 * soon, so we must account for that. We must leave again with the
2015167385Sjulian	 * READER lock. If we find other readers, then
2016167385Sjulian	 * queue the request for later. However "later" may be rignt now
2017167385Sjulian	 * if there are no readers. We don't really care if there are queued
2018167385Sjulian	 * items as we will bypass them anyhow.
2019167385Sjulian	 */
2020167385Sjulian	atomic_add_long(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
2021167385Sjulian	if (ngq->q_flags & (NGQ_WMASK & ~OP_PENDING) == WRITER_ACTIVE) {
2022167385Sjulian		mtx_unlock_spin(&(ngq->q_mtx));
2023167385Sjulian
2024167385Sjulian		/* It's just us, act on the item. */
2025167385Sjulian		/* will NOT drop writer lock when done */
2026167385Sjulian		ng_apply_item(node, item, 0);
2027167385Sjulian
2028167385Sjulian		/*
2029167385Sjulian		 * Having acted on the item, atomically
2030167385Sjulian		 * down grade back to READER and finish up
2031167385Sjulian	 	 */
2032167385Sjulian		atomic_add_long(&ngq->q_flags,
2033167385Sjulian		    READER_INCREMENT - WRITER_ACTIVE);
2034167385Sjulian
2035167385Sjulian		/* Our caller will call ng_leave_read() */
2036167385Sjulian		return;
2037167385Sjulian	}
2038167385Sjulian	/*
2039167385Sjulian	 * It's not just us active, so queue us AT THE HEAD.
2040167385Sjulian	 * "Why?" I hear you ask.
2041167385Sjulian	 * Put us at the head of the queue as we've already been
2042167385Sjulian	 * through it once. If there is nothing else waiting,
2043167385Sjulian	 * set the correct flags.
2044167385Sjulian	 */
2045167385Sjulian	if ((item->el_next = ngq->queue) == NULL) {
2046167385Sjulian		/*
2047167385Sjulian		 * Set up the "last" pointer.
2048167385Sjulian		 * We are the only (and thus last) item
2049167385Sjulian		 */
2050167385Sjulian		ngq->last = &(item->el_next);
2051167385Sjulian
2052167385Sjulian		/* We've gone from, 0 to 1 item in the queue */
2053177953Smav		atomic_set_long(&ngq->q_flags, OP_PENDING);
2054167385Sjulian
2055167385Sjulian		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
2056167385Sjulian		    ngq->q_node->nd_ID, ngq->q_node);
2057167385Sjulian	};
2058167385Sjulian	ngq->queue = item;
2059167385Sjulian	CTR5(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
2060167385Sjulian	    __func__, ngq->q_node->nd_ID, ngq->q_node, item );
2061167385Sjulian
2062167385Sjulian	/* Reverse what we did above. That downgrades us back to reader */
2063167385Sjulian	atomic_add_long(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2064177953Smav	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2065177953Smav		ng_worklist_add(ngq->q_node);
2066167385Sjulian	mtx_unlock_spin(&(ngq->q_mtx));
2067167385Sjulian
2068167385Sjulian	return;
2069167385Sjulian}
2070167385Sjulian
2071167385Sjulian#endif
2072167385Sjulian
207370700Sjulianstatic __inline void
207470700Sjulianng_leave_read(struct ng_queue *ngq)
207570700Sjulian{
207670700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
207770700Sjulian}
207870700Sjulian
207970700Sjulianstatic __inline void
208070700Sjulianng_leave_write(struct ng_queue *ngq)
208170700Sjulian{
2082177953Smav	atomic_clear_long(&ngq->q_flags, WRITER_ACTIVE);
208370700Sjulian}
208470700Sjulian
208570700Sjulianstatic void
208670700Sjulianng_flush_input_queue(struct ng_queue * ngq)
208770700Sjulian{
208870700Sjulian	item_p item;
2089151973Sglebius
2090168049Swkoszek	NG_QUEUE_LOCK(ngq);
2091151973Sglebius	while (ngq->queue) {
209270700Sjulian		item = ngq->queue;
209370700Sjulian		ngq->queue = item->el_next;
209470700Sjulian		if (ngq->last == &(item->el_next)) {
209570700Sjulian			ngq->last = &(ngq->queue);
2096177953Smav			atomic_clear_long(&ngq->q_flags, OP_PENDING);
209770700Sjulian		}
2098168049Swkoszek		NG_QUEUE_UNLOCK(ngq);
209970700Sjulian
2100151973Sglebius		/* If the item is supplying a callback, call it with an error */
2101177071Smav		if (item->apply != NULL) {
2102177071Smav			if (item->depth == 1)
2103177071Smav				item->apply->error = ENOENT;
2104177071Smav			if (refcount_release(&item->apply->refs)) {
2105177071Smav				(*item->apply->apply)(item->apply->context,
2106177071Smav				    item->apply->error);
2107177071Smav			}
2108151283Sglebius		}
2109132369Sjulian		NG_FREE_ITEM(item);
2110168049Swkoszek		NG_QUEUE_LOCK(ngq);
211170700Sjulian	}
2112168049Swkoszek	NG_QUEUE_UNLOCK(ngq);
211370700Sjulian}
211470700Sjulian
211570700Sjulian/***********************************************************************
211670700Sjulian* Externally visible method for sending or queueing messages or data.
211770700Sjulian***********************************************************************/
211870700Sjulian
211970700Sjulian/*
212071849Sjulian * The module code should have filled out the item correctly by this stage:
212170700Sjulian * Common:
212270700Sjulian *    reference to destination node.
212370700Sjulian *    Reference to destination rcv hook if relevant.
2124172806Smav *    apply pointer must be or NULL or reference valid struct ng_apply_info.
212570700Sjulian * Data:
212670700Sjulian *    pointer to mbuf
212770700Sjulian * Control_Message:
212870700Sjulian *    pointer to msg.
212970700Sjulian *    ID of original sender node. (return address)
213071849Sjulian * Function:
213171849Sjulian *    Function pointer
213271849Sjulian *    void * argument
213371849Sjulian *    integer argument
213470700Sjulian *
213570700Sjulian * The nodes have several routines and macros to help with this task:
213670700Sjulian */
213770700Sjulian
213870700Sjulianint
2139146281Sglebiusng_snd_item(item_p item, int flags)
214070700Sjulian{
2141172806Smav	hook_p hook;
2142172806Smav	node_p node;
2143146281Sglebius	int queue, rw;
2144172806Smav	struct ng_queue *ngq;
2145151256Sglebius	int error = 0;
214670700Sjulian
2147176046Smav	/* We are sending item, so it must be present! */
2148176046Smav	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2149172806Smav
215070784Sjulian#ifdef	NETGRAPH_DEBUG
2151152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
215270700Sjulian#endif
215370700Sjulian
2154176046Smav	/* Item was sent once more, postpone apply() call. */
2155172806Smav	if (item->apply)
2156172806Smav		refcount_acquire(&item->apply->refs);
2157146281Sglebius
2158172806Smav	node = NGI_NODE(item);
2159176046Smav	/* Node is never optional. */
2160176046Smav	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2161172806Smav
2162175850Smav	hook = NGI_HOOK(item);
2163176046Smav	/* Valid hook and mbuf are mandatory for data. */
2164176046Smav	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2165176046Smav		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
2166131123Sjulian		if (NGI_M(item) == NULL)
2167172806Smav			ERROUT(EINVAL);
216870700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
216969922Sjulian	}
2170149505Sglebius
217170700Sjulian	/*
2172176046Smav	 * If the item or the node specifies single threading, force
2173176046Smav	 * writer semantics. Similarly, the node may say one hook always
2174176046Smav	 * produces writers. These are overrides.
217570700Sjulian	 */
2176176567Smav	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2177176046Smav	    (node->nd_flags & NGF_FORCE_WRITER) ||
2178176046Smav	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2179176046Smav		rw = NGQRW_W;
2180176046Smav	} else {
2181176046Smav		rw = NGQRW_R;
2182176046Smav	}
2183149505Sglebius
2184175847Smav	/*
2185175847Smav	 * If sender or receiver requests queued delivery or stack usage
2186175847Smav	 * level is dangerous - enqueue message.
2187175847Smav	 */
2188175847Smav	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
2189175847Smav		queue = 1;
2190176046Smav	} else {
2191176046Smav		queue = 0;
2192175847Smav#ifdef GET_STACK_USAGE
2193175868Smav		/*
2194175871Smarck		 * Most of netgraph nodes have small stack consumption and
2195176046Smav		 * for them 25% of free stack space is more than enough.
2196175868Smav		 * Nodes/hooks with higher stack usage should be marked as
2197175889Smarck		 * HI_STACK. For them 50% of stack will be guaranteed then.
2198176046Smav		 * XXX: Values 25% and 50% are completely empirical.
2199175868Smav		 */
2200176046Smav		size_t	st, su, sl;
2201175847Smav		GET_STACK_USAGE(st, su);
2202176046Smav		sl = st - su;
2203176046Smav		if ((sl * 4 < st) ||
2204176046Smav		    ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) ||
2205175847Smav		      (hook && (hook->hk_flags & HK_HI_STACK))))) {
2206175847Smav			queue = 1;
2207175847Smav		}
2208176046Smav#endif
2209175847Smav	}
2210175847Smav
2211175850Smav	ngq = &node->nd_input_queue;
221270700Sjulian	if (queue) {
2213177953Smav		item->depth = 1;
221470700Sjulian		/* Put it on the queue for that node*/
221570700Sjulian		ng_queue_rw(ngq, item, rw);
2216176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
221770700Sjulian	}
221869922Sjulian
221970700Sjulian	/*
222070700Sjulian	 * We already decided how we will be queueud or treated.
222170700Sjulian	 * Try get the appropriate operating permission.
222270700Sjulian	 */
2223151256Sglebius 	if (rw == NGQRW_R)
222470700Sjulian		item = ng_acquire_read(ngq, item);
2225151256Sglebius	else
222670700Sjulian		item = ng_acquire_write(ngq, item);
222752419Sjulian
2228176046Smav	/* Item was queued while trying to get permission. */
2229176046Smav	if (item == NULL)
2230176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
223152419Sjulian
223274078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
223352419Sjulian
2234177071Smav	item->depth++;
2235170180Sglebius	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
223674078Sjulian
2237177953Smav	/* If something is waiting on queue and ready, schedule it. */
2238177953Smav	if (QUEUE_ACTIVE(ngq)) {
2239177953Smav		NG_QUEUE_LOCK(ngq);
2240177953Smav		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2241177953Smav			ng_worklist_add(ngq->q_node);
2242177953Smav		NG_QUEUE_UNLOCK(ngq);
2243177953Smav	}
2244177953Smav
224552419Sjulian	/*
2246177953Smav	 * Node may go away as soon as we remove the reference.
2247177953Smav	 * Whatever we do, DO NOT access the node again!
224874078Sjulian	 */
2249177953Smav	NG_NODE_UNREF(node);
225074078Sjulian
225171849Sjulian	return (error);
2252172806Smav
2253172806Smavdone:
2254176046Smav	/* If was not sent, apply callback here. */
2255177071Smav	if (item->apply != NULL) {
2256177071Smav		if (item->depth == 0 && error != 0)
2257177071Smav			item->apply->error = error;
2258177071Smav		if (refcount_release(&item->apply->refs)) {
2259177071Smav			(*item->apply->apply)(item->apply->context,
2260177071Smav			    item->apply->error);
2261177071Smav		}
2262177071Smav	}
2263175850Smav
2264172806Smav	NG_FREE_ITEM(item);
2265172806Smav	return (error);
226652419Sjulian}
226752419Sjulian
226852419Sjulian/*
226970700Sjulian * We have an item that was possibly queued somewhere.
227070700Sjulian * It should contain all the information needed
227170700Sjulian * to run it on the appropriate node/hook.
2272172806Smav * If there is apply pointer and we own the last reference, call apply().
227352419Sjulian */
2274170180Sglebiusstatic int
2275151238Sglebiusng_apply_item(node_p node, item_p item, int rw)
227652419Sjulian{
227770700Sjulian	hook_p  hook;
227870700Sjulian	ng_rcvdata_t *rcvdata;
227971885Sjulian	ng_rcvmsg_t *rcvmsg;
2280172806Smav	struct ng_apply_info *apply;
2281177071Smav	int	error = 0, depth;
228252419Sjulian
2283176046Smav	/* Node and item are never optional. */
2284176046Smav	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2285176046Smav	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2286176046Smav
228771849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
228870784Sjulian#ifdef	NETGRAPH_DEBUG
2289152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
229070700Sjulian#endif
2291147774Sglebius
2292172806Smav	apply = item->apply;
2293177071Smav	depth = item->depth;
2294147774Sglebius
229571047Sjulian	switch (item->el_flags & NGQF_TYPE) {
229670700Sjulian	case NGQF_DATA:
229770700Sjulian		/*
229870700Sjulian		 * Check things are still ok as when we were queued.
229970700Sjulian		 */
2300176046Smav		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2301176046Smav		if (NG_HOOK_NOT_VALID(hook) ||
2302176046Smav		    NG_NODE_NOT_VALID(node)) {
2303167402Sjulian			error = EIO;
230470700Sjulian			NG_FREE_ITEM(item);
230571885Sjulian			break;
230670700Sjulian		}
230771885Sjulian		/*
230871885Sjulian		 * If no receive method, just silently drop it.
230971885Sjulian		 * Give preference to the hook over-ride method
231071885Sjulian		 */
2311152451Sglebius		if ((!(rcvdata = hook->hk_rcvdata))
231271885Sjulian		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
231371885Sjulian			error = 0;
231471885Sjulian			NG_FREE_ITEM(item);
231571885Sjulian			break;
231671885Sjulian		}
2317167402Sjulian		error = (*rcvdata)(hook, item);
231870700Sjulian		break;
231970700Sjulian	case NGQF_MESG:
2320175850Smav		if (hook && NG_HOOK_NOT_VALID(hook)) {
2321175850Smav			/*
2322175850Smav			 * The hook has been zapped then we can't use it.
2323175850Smav			 * Immediately drop its reference.
2324175850Smav			 * The message may not need it.
2325175850Smav			 */
2326175850Smav			NG_HOOK_UNREF(hook);
2327175850Smav			hook = NULL;
232870700Sjulian		}
232970700Sjulian		/*
233070700Sjulian		 * Similarly, if the node is a zombie there is
233170700Sjulian		 * nothing we can do with it, drop everything.
233270700Sjulian		 */
233370784Sjulian		if (NG_NODE_NOT_VALID(node)) {
233471047Sjulian			TRAP_ERROR();
2335167402Sjulian			error = EINVAL;
233670700Sjulian			NG_FREE_ITEM(item);
2337175850Smav			break;
233870700Sjulian		}
2339175850Smav		/*
2340175850Smav		 * Call the appropriate message handler for the object.
2341175850Smav		 * It is up to the message handler to free the message.
2342175850Smav		 * If it's a generic message, handle it generically,
2343175850Smav		 * otherwise call the type's message handler (if it exists).
2344175850Smav		 * XXX (race). Remember that a queued message may
2345175850Smav		 * reference a node or hook that has just been
2346175850Smav		 * invalidated. It will exist as the queue code
2347175850Smav		 * is holding a reference, but..
2348175850Smav		 */
2349175850Smav		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2350175850Smav		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2351175850Smav			error = ng_generic_msg(node, item, hook);
2352175850Smav			break;
2353175850Smav		}
2354175850Smav		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2355175850Smav		    (!(rcvmsg = node->nd_type->rcvmsg))) {
2356175850Smav			TRAP_ERROR();
2357175850Smav			error = 0;
2358175850Smav			NG_FREE_ITEM(item);
2359175850Smav			break;
2360175850Smav		}
2361175850Smav		error = (*rcvmsg)(node, item, hook);
236270700Sjulian		break;
236371047Sjulian	case NGQF_FN:
2364173605Sglebius	case NGQF_FN2:
236571047Sjulian		/*
236671047Sjulian		 *  We have to implicitly trust the hook,
236771047Sjulian		 * as some of these are used for system purposes
236871849Sjulian		 * where the hook is invalid. In the case of
236971849Sjulian		 * the shutdown message we allow it to hit
237071849Sjulian		 * even if the node is invalid.
237171047Sjulian		 */
2372152451Sglebius		if ((NG_NODE_NOT_VALID(node))
237371849Sjulian		&& (NGI_FN(item) != &ng_rmnode)) {
237471047Sjulian			TRAP_ERROR();
2375167402Sjulian			error = EINVAL;
2376143384Sglebius			NG_FREE_ITEM(item);
237771047Sjulian			break;
237871047Sjulian		}
2379173605Sglebius		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2380173605Sglebius			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2381173605Sglebius			    NGI_ARG2(item));
2382172806Smav			NG_FREE_ITEM(item);
2383173605Sglebius		} else	/* it is NGQF_FN2 */
2384173605Sglebius			error = (*NGI_FN2(item))(node, item, hook);
2385172806Smav		break;
238670700Sjulian	}
238770700Sjulian	/*
238870700Sjulian	 * We held references on some of the resources
238970700Sjulian	 * that we took from the item. Now that we have
239070700Sjulian	 * finished doing everything, drop those references.
239170700Sjulian	 */
2392175850Smav	if (hook)
239370784Sjulian		NG_HOOK_UNREF(hook);
239470700Sjulian
2395176046Smav 	if (rw == NGQRW_R)
239670784Sjulian		ng_leave_read(&node->nd_input_queue);
2397176046Smav	else
239870784Sjulian		ng_leave_write(&node->nd_input_queue);
2399147774Sglebius
2400147774Sglebius	/* Apply callback. */
2401177071Smav	if (apply != NULL) {
2402177071Smav		if (depth == 1 && error != 0)
2403177071Smav			apply->error = error;
2404177071Smav		if (refcount_release(&apply->refs))
2405177071Smav			(*apply->apply)(apply->context, apply->error);
2406177071Smav	}
2407147774Sglebius
2408170180Sglebius	return (error);
240970700Sjulian}
241070700Sjulian
241170700Sjulian/***********************************************************************
241270700Sjulian * Implement the 'generic' control messages
241370700Sjulian ***********************************************************************/
241470700Sjulianstatic int
241570700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
241670700Sjulian{
241770700Sjulian	int error = 0;
241870700Sjulian	struct ng_mesg *msg;
241970700Sjulian	struct ng_mesg *resp = NULL;
242070700Sjulian
242170700Sjulian	NGI_GET_MSG(item, msg);
242252419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
242371047Sjulian		TRAP_ERROR();
242470700Sjulian		error = EINVAL;
242570700Sjulian		goto out;
242652419Sjulian	}
242752419Sjulian	switch (msg->header.cmd) {
242852419Sjulian	case NGM_SHUTDOWN:
242971849Sjulian		ng_rmnode(here, NULL, NULL, 0);
243052419Sjulian		break;
243152419Sjulian	case NGM_MKPEER:
243252419Sjulian	    {
243352419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
243452419Sjulian
243552419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
243671047Sjulian			TRAP_ERROR();
243770700Sjulian			error = EINVAL;
243870700Sjulian			break;
243952419Sjulian		}
244052419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
244152419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
244252419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
244352419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
244452419Sjulian		break;
244552419Sjulian	    }
244652419Sjulian	case NGM_CONNECT:
244752419Sjulian	    {
244852419Sjulian		struct ngm_connect *const con =
244952419Sjulian			(struct ngm_connect *) msg->data;
245052419Sjulian		node_p node2;
245152419Sjulian
245252419Sjulian		if (msg->header.arglen != sizeof(*con)) {
245371047Sjulian			TRAP_ERROR();
245470700Sjulian			error = EINVAL;
245570700Sjulian			break;
245652419Sjulian		}
245752419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
245852419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
245952419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
246070700Sjulian		/* Don't forget we get a reference.. */
246170700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
246252419Sjulian		if (error)
246352419Sjulian			break;
2464172806Smav		error = ng_con_nodes(item, here, con->ourhook,
2465172806Smav		    node2, con->peerhook);
246670784Sjulian		NG_NODE_UNREF(node2);
246752419Sjulian		break;
246852419Sjulian	    }
246952419Sjulian	case NGM_NAME:
247052419Sjulian	    {
247152419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
247252419Sjulian
247352419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
247471047Sjulian			TRAP_ERROR();
247570700Sjulian			error = EINVAL;
247670700Sjulian			break;
247752419Sjulian		}
247852419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
247952419Sjulian		error = ng_name_node(here, nam->name);
248052419Sjulian		break;
248152419Sjulian	    }
248252419Sjulian	case NGM_RMHOOK:
248352419Sjulian	    {
248452419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
248552419Sjulian		hook_p hook;
248652419Sjulian
248752419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
248871047Sjulian			TRAP_ERROR();
248970700Sjulian			error = EINVAL;
249070700Sjulian			break;
249152419Sjulian		}
249252419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
249354096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
249452419Sjulian			ng_destroy_hook(hook);
249552419Sjulian		break;
249652419Sjulian	    }
249752419Sjulian	case NGM_NODEINFO:
249852419Sjulian	    {
249952419Sjulian		struct nodeinfo *ni;
250052419Sjulian
250170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
250252419Sjulian		if (resp == NULL) {
250352419Sjulian			error = ENOMEM;
250452419Sjulian			break;
250552419Sjulian		}
250652419Sjulian
250752419Sjulian		/* Fill in node info */
250870700Sjulian		ni = (struct nodeinfo *) resp->data;
250970784Sjulian		if (NG_NODE_HAS_NAME(here))
2510125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2511125028Sharti		strcpy(ni->type, here->nd_type->name);
251252722Sjulian		ni->id = ng_node2ID(here);
251370784Sjulian		ni->hooks = here->nd_numhooks;
251452419Sjulian		break;
251552419Sjulian	    }
251652419Sjulian	case NGM_LISTHOOKS:
251752419Sjulian	    {
251870784Sjulian		const int nhooks = here->nd_numhooks;
251952419Sjulian		struct hooklist *hl;
252052419Sjulian		struct nodeinfo *ni;
252152419Sjulian		hook_p hook;
252252419Sjulian
252352419Sjulian		/* Get response struct */
252470700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*hl)
252570700Sjulian		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
252652419Sjulian		if (resp == NULL) {
252752419Sjulian			error = ENOMEM;
252852419Sjulian			break;
252952419Sjulian		}
253070700Sjulian		hl = (struct hooklist *) resp->data;
253152419Sjulian		ni = &hl->nodeinfo;
253252419Sjulian
253352419Sjulian		/* Fill in node info */
253470784Sjulian		if (NG_NODE_HAS_NAME(here))
2535125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2536125028Sharti		strcpy(ni->type, here->nd_type->name);
253752722Sjulian		ni->id = ng_node2ID(here);
253852419Sjulian
253952419Sjulian		/* Cycle through the linked list of hooks */
254052419Sjulian		ni->hooks = 0;
254170784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
254252419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
254352419Sjulian
254452419Sjulian			if (ni->hooks >= nhooks) {
254552419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
254687599Sobrien				    __func__, "hooks");
254752419Sjulian				break;
254852419Sjulian			}
254970784Sjulian			if (NG_HOOK_NOT_VALID(hook))
255052419Sjulian				continue;
2551125028Sharti			strcpy(link->ourhook, NG_HOOK_NAME(hook));
2552125028Sharti			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
255370784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
2554125028Sharti				strcpy(link->nodeinfo.name,
2555125028Sharti				    NG_PEER_NODE_NAME(hook));
2556125028Sharti			strcpy(link->nodeinfo.type,
2557125028Sharti			   NG_PEER_NODE(hook)->nd_type->name);
255870784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
255970784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
256052419Sjulian			ni->hooks++;
256152419Sjulian		}
256252419Sjulian		break;
256352419Sjulian	    }
256452419Sjulian
256552419Sjulian	case NGM_LISTNAMES:
256652419Sjulian	case NGM_LISTNODES:
256752419Sjulian	    {
256852419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
256952419Sjulian		struct namelist *nl;
257052419Sjulian		node_p node;
2571176802Smav		int num = 0, i;
257252419Sjulian
2573176802Smav		mtx_lock(&ng_namehash_mtx);
257452419Sjulian		/* Count number of nodes */
2575176802Smav		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2576176802Smav			LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) {
2577176802Smav				if (NG_NODE_IS_VALID(node) &&
2578176802Smav				    (unnamed || NG_NODE_HAS_NAME(node))) {
2579176802Smav					num++;
2580176802Smav				}
258170912Sjulian			}
258252419Sjulian		}
2583176802Smav		mtx_unlock(&ng_namehash_mtx);
258452419Sjulian
258552419Sjulian		/* Get response struct */
258670700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*nl)
258770700Sjulian		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
258852419Sjulian		if (resp == NULL) {
258952419Sjulian			error = ENOMEM;
259052419Sjulian			break;
259152419Sjulian		}
259270700Sjulian		nl = (struct namelist *) resp->data;
259352419Sjulian
259452419Sjulian		/* Cycle through the linked list of nodes */
259552419Sjulian		nl->numnames = 0;
2596176802Smav		mtx_lock(&ng_namehash_mtx);
2597176802Smav		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2598176802Smav			LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) {
2599176802Smav				struct nodeinfo *const np =
2600176802Smav				    &nl->nodeinfo[nl->numnames];
260152419Sjulian
2602176802Smav				if (NG_NODE_NOT_VALID(node))
2603176802Smav					continue;
2604176802Smav				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2605176802Smav					continue;
2606176802Smav				if (nl->numnames >= num) {
2607176802Smav					log(LOG_ERR, "%s: number of nodes changed\n",
2608176802Smav					    __func__);
2609176802Smav					break;
2610176802Smav				}
2611176802Smav				if (NG_NODE_HAS_NAME(node))
2612176802Smav					strcpy(np->name, NG_NODE_NAME(node));
2613176802Smav				strcpy(np->type, node->nd_type->name);
2614176802Smav				np->id = ng_node2ID(node);
2615176802Smav				np->hooks = node->nd_numhooks;
2616176802Smav				nl->numnames++;
261752419Sjulian			}
261852419Sjulian		}
2619176802Smav		mtx_unlock(&ng_namehash_mtx);
262052419Sjulian		break;
262152419Sjulian	    }
262252419Sjulian
262352419Sjulian	case NGM_LISTTYPES:
262452419Sjulian	    {
262552419Sjulian		struct typelist *tl;
262652419Sjulian		struct ng_type *type;
262752419Sjulian		int num = 0;
262852419Sjulian
262972200Sbmilekic		mtx_lock(&ng_typelist_mtx);
263052419Sjulian		/* Count number of types */
263170912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
263252419Sjulian			num++;
263370912Sjulian		}
263472200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
263552419Sjulian
263652419Sjulian		/* Get response struct */
263770700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*tl)
263870700Sjulian		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
263952419Sjulian		if (resp == NULL) {
264052419Sjulian			error = ENOMEM;
264152419Sjulian			break;
264252419Sjulian		}
264370700Sjulian		tl = (struct typelist *) resp->data;
264452419Sjulian
264552419Sjulian		/* Cycle through the linked list of types */
264652419Sjulian		tl->numtypes = 0;
264772200Sbmilekic		mtx_lock(&ng_typelist_mtx);
264870700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
264952419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
265052419Sjulian
265152419Sjulian			if (tl->numtypes >= num) {
265252419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
265387599Sobrien				    __func__, "types");
265452419Sjulian				break;
265552419Sjulian			}
2656125028Sharti			strcpy(tp->type_name, type->name);
265771603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
265852419Sjulian			tl->numtypes++;
265952419Sjulian		}
266072200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
266152419Sjulian		break;
266252419Sjulian	    }
266352419Sjulian
266453913Sarchie	case NGM_BINARY2ASCII:
266553913Sarchie	    {
266664510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
266753913Sarchie		const struct ng_parse_type *argstype;
266853913Sarchie		const struct ng_cmdlist *c;
266970700Sjulian		struct ng_mesg *binary, *ascii;
267053913Sarchie
267153913Sarchie		/* Data area must contain a valid netgraph message */
267253913Sarchie		binary = (struct ng_mesg *)msg->data;
2673152451Sglebius		if (msg->header.arglen < sizeof(struct ng_mesg) ||
2674152451Sglebius		    (msg->header.arglen - sizeof(struct ng_mesg) <
2675152451Sglebius		    binary->header.arglen)) {
267671047Sjulian			TRAP_ERROR();
267753913Sarchie			error = EINVAL;
267853913Sarchie			break;
267953913Sarchie		}
268053913Sarchie
268170700Sjulian		/* Get a response message with lots of room */
268270700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
268370159Sjulian		if (resp == NULL) {
268453913Sarchie			error = ENOMEM;
268553913Sarchie			break;
268653913Sarchie		}
268770700Sjulian		ascii = (struct ng_mesg *)resp->data;
268853913Sarchie
268953913Sarchie		/* Copy binary message header to response message payload */
269053913Sarchie		bcopy(binary, ascii, sizeof(*binary));
269153913Sarchie
269253913Sarchie		/* Find command by matching typecookie and command number */
269370784Sjulian		for (c = here->nd_type->cmdlist;
269453913Sarchie		    c != NULL && c->name != NULL; c++) {
269553913Sarchie			if (binary->header.typecookie == c->cookie
269653913Sarchie			    && binary->header.cmd == c->cmd)
269753913Sarchie				break;
269853913Sarchie		}
269953913Sarchie		if (c == NULL || c->name == NULL) {
270053913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
270153913Sarchie				if (binary->header.typecookie == c->cookie
270253913Sarchie				    && binary->header.cmd == c->cmd)
270353913Sarchie					break;
270453913Sarchie			}
270553913Sarchie			if (c->name == NULL) {
270670700Sjulian				NG_FREE_MSG(resp);
270753913Sarchie				error = ENOSYS;
270853913Sarchie				break;
270953913Sarchie			}
271053913Sarchie		}
271153913Sarchie
271253913Sarchie		/* Convert command name to ASCII */
271353913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
271453913Sarchie		    "%s", c->name);
271553913Sarchie
271653913Sarchie		/* Convert command arguments to ASCII */
271753913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
271853913Sarchie		    c->respType : c->mesgType;
271970912Sjulian		if (argstype == NULL) {
272053913Sarchie			*ascii->data = '\0';
272170912Sjulian		} else {
272253913Sarchie			if ((error = ng_unparse(argstype,
272353913Sarchie			    (u_char *)binary->data,
272453913Sarchie			    ascii->data, bufSize)) != 0) {
272570700Sjulian				NG_FREE_MSG(resp);
272653913Sarchie				break;
272753913Sarchie			}
272853913Sarchie		}
272953913Sarchie
273053913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
273153913Sarchie		bufSize = strlen(ascii->data) + 1;
273253913Sarchie		ascii->header.arglen = bufSize;
273370700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
273453913Sarchie		break;
273553913Sarchie	    }
273653913Sarchie
273753913Sarchie	case NGM_ASCII2BINARY:
273853913Sarchie	    {
273953913Sarchie		int bufSize = 2000;	/* XXX hard coded constant */
274053913Sarchie		const struct ng_cmdlist *c;
274153913Sarchie		const struct ng_parse_type *argstype;
274270700Sjulian		struct ng_mesg *ascii, *binary;
274359178Sarchie		int off = 0;
274453913Sarchie
274553913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
274653913Sarchie		ascii = (struct ng_mesg *)msg->data;
2747152451Sglebius		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
2748152451Sglebius		    (ascii->header.arglen < 1) ||
2749152451Sglebius		    (msg->header.arglen < sizeof(*ascii) +
2750152451Sglebius		    ascii->header.arglen)) {
275171047Sjulian			TRAP_ERROR();
275253913Sarchie			error = EINVAL;
275353913Sarchie			break;
275453913Sarchie		}
275553913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
275653913Sarchie
275770700Sjulian		/* Get a response message with lots of room */
275870700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
275970159Sjulian		if (resp == NULL) {
276053913Sarchie			error = ENOMEM;
276153913Sarchie			break;
276253913Sarchie		}
276370700Sjulian		binary = (struct ng_mesg *)resp->data;
276453913Sarchie
276553913Sarchie		/* Copy ASCII message header to response message payload */
276653913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
276753913Sarchie
276853913Sarchie		/* Find command by matching ASCII command string */
276970784Sjulian		for (c = here->nd_type->cmdlist;
277053913Sarchie		    c != NULL && c->name != NULL; c++) {
277153913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
277253913Sarchie				break;
277353913Sarchie		}
277453913Sarchie		if (c == NULL || c->name == NULL) {
277553913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
277653913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
277753913Sarchie					break;
277853913Sarchie			}
277953913Sarchie			if (c->name == NULL) {
278070700Sjulian				NG_FREE_MSG(resp);
278153913Sarchie				error = ENOSYS;
278253913Sarchie				break;
278353913Sarchie			}
278453913Sarchie		}
278553913Sarchie
278653913Sarchie		/* Convert command name to binary */
278753913Sarchie		binary->header.cmd = c->cmd;
278853913Sarchie		binary->header.typecookie = c->cookie;
278953913Sarchie
279053913Sarchie		/* Convert command arguments to binary */
279153913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
279253913Sarchie		    c->respType : c->mesgType;
279370912Sjulian		if (argstype == NULL) {
279453913Sarchie			bufSize = 0;
279570912Sjulian		} else {
279653913Sarchie			if ((error = ng_parse(argstype, ascii->data,
279753913Sarchie			    &off, (u_char *)binary->data, &bufSize)) != 0) {
279870700Sjulian				NG_FREE_MSG(resp);
279953913Sarchie				break;
280053913Sarchie			}
280153913Sarchie		}
280253913Sarchie
280353913Sarchie		/* Return the result */
280453913Sarchie		binary->header.arglen = bufSize;
280570700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
280653913Sarchie		break;
280753913Sarchie	    }
280853913Sarchie
280962471Sphk	case NGM_TEXT_CONFIG:
281052419Sjulian	case NGM_TEXT_STATUS:
281152419Sjulian		/*
281252419Sjulian		 * This one is tricky as it passes the command down to the
281352419Sjulian		 * actual node, even though it is a generic type command.
281470700Sjulian		 * This means we must assume that the item/msg is already freed
281552419Sjulian		 * when control passes back to us.
281652419Sjulian		 */
281770784Sjulian		if (here->nd_type->rcvmsg != NULL) {
281870700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
281970784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
282052419Sjulian		}
282152419Sjulian		/* Fall through if rcvmsg not supported */
282252419Sjulian	default:
282371047Sjulian		TRAP_ERROR();
282452419Sjulian		error = EINVAL;
282552419Sjulian	}
282670700Sjulian	/*
282770700Sjulian	 * Sometimes a generic message may be statically allocated
282870700Sjulian	 * to avoid problems with allocating when in tight memeory situations.
282970700Sjulian	 * Don't free it if it is so.
283070700Sjulian	 * I break them appart here, because erros may cause a free if the item
283170700Sjulian	 * in which case we'd be doing it twice.
283270700Sjulian	 * they are kept together above, to simplify freeing.
283370700Sjulian	 */
283470700Sjulianout:
283570700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
283671849Sjulian	if (msg)
283770700Sjulian		NG_FREE_MSG(msg);
283852419Sjulian	return (error);
283952419Sjulian}
284052419Sjulian
284152419Sjulian/************************************************************************
2842146213Sglebius			Queue element get/free routines
2843146213Sglebius************************************************************************/
2844146213Sglebius
2845146213Sglebiusuma_zone_t			ng_qzone;
2846176849Smavstatic int			maxalloc = 4096;/* limit the damage of a leak */
2847176849Smavstatic int			maxdata = 512;	/* limit the damage of a DoS */
2848176849Smavstatic int			useddata = 0;
2849146213Sglebius
2850146213SglebiusTUNABLE_INT("net.graph.maxalloc", &maxalloc);
2851146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
2852146213Sglebius    0, "Maximum number of queue items to allocate");
2853176849SmavTUNABLE_INT("net.graph.maxdata", &maxdata);
2854176849SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RW | CTLFLAG_TUN, &maxdata,
2855176849Smav    0, "Maximum number of queue data items to allocate");
2856146213Sglebius
2857146213Sglebius#ifdef	NETGRAPH_DEBUG
2858146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2859146213Sglebiusstatic int			allocated;	/* number of items malloc'd */
2860146213Sglebius#endif
2861146213Sglebius
2862146213Sglebius/*
2863146213Sglebius * Get a queue entry.
2864146213Sglebius * This is usually called when a packet first enters netgraph.
2865146213Sglebius * By definition, this is usually from an interrupt, or from a user.
2866146213Sglebius * Users are not so important, but try be quick for the times that it's
2867146213Sglebius * an interrupt.
2868146213Sglebius */
2869146213Sglebiusstatic __inline item_p
2870146281Sglebiusng_getqblk(int flags)
2871146213Sglebius{
2872146213Sglebius	item_p item = NULL;
2873146281Sglebius	int wait;
2874146213Sglebius
2875146281Sglebius	wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT;
2876146213Sglebius
2877146281Sglebius	item = uma_zalloc(ng_qzone, wait | M_ZERO);
2878146281Sglebius
2879146213Sglebius#ifdef	NETGRAPH_DEBUG
2880146213Sglebius	if (item) {
2881146213Sglebius			mtx_lock(&ngq_mtx);
2882146213Sglebius			TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
2883146213Sglebius			allocated++;
2884146213Sglebius			mtx_unlock(&ngq_mtx);
2885146213Sglebius	}
2886146213Sglebius#endif
2887146213Sglebius
2888146213Sglebius	return (item);
2889146213Sglebius}
2890146213Sglebius
2891146213Sglebius/*
2892146213Sglebius * Release a queue entry
2893146213Sglebius */
2894146213Sglebiusvoid
2895146213Sglebiusng_free_item(item_p item)
2896146213Sglebius{
2897146213Sglebius	/*
2898146213Sglebius	 * The item may hold resources on it's own. We need to free
2899146213Sglebius	 * these before we can free the item. What they are depends upon
2900146213Sglebius	 * what kind of item it is. it is important that nodes zero
2901146213Sglebius	 * out pointers to resources that they remove from the item
2902146213Sglebius	 * or we release them again here.
2903146213Sglebius	 */
2904146213Sglebius	switch (item->el_flags & NGQF_TYPE) {
2905146213Sglebius	case NGQF_DATA:
2906176849Smav		atomic_subtract_int(&useddata, 1);
2907146213Sglebius		/* If we have an mbuf still attached.. */
2908146213Sglebius		NG_FREE_M(_NGI_M(item));
2909146213Sglebius		break;
2910146213Sglebius	case NGQF_MESG:
2911146213Sglebius		_NGI_RETADDR(item) = 0;
2912146213Sglebius		NG_FREE_MSG(_NGI_MSG(item));
2913146213Sglebius		break;
2914146213Sglebius	case NGQF_FN:
2915172806Smav	case NGQF_FN2:
2916146213Sglebius		/* nothing to free really, */
2917146213Sglebius		_NGI_FN(item) = NULL;
2918146213Sglebius		_NGI_ARG1(item) = NULL;
2919146213Sglebius		_NGI_ARG2(item) = 0;
2920146213Sglebius		break;
2921146213Sglebius	}
2922146213Sglebius	/* If we still have a node or hook referenced... */
2923146213Sglebius	_NGI_CLR_NODE(item);
2924146213Sglebius	_NGI_CLR_HOOK(item);
2925146213Sglebius
2926146213Sglebius#ifdef	NETGRAPH_DEBUG
2927146213Sglebius	mtx_lock(&ngq_mtx);
2928146213Sglebius	TAILQ_REMOVE(&ng_itemlist, item, all);
2929146213Sglebius	allocated--;
2930146213Sglebius	mtx_unlock(&ngq_mtx);
2931146213Sglebius#endif
2932146213Sglebius	uma_zfree(ng_qzone, item);
2933146213Sglebius}
2934146213Sglebius
2935146213Sglebius/************************************************************************
293652419Sjulian			Module routines
293752419Sjulian************************************************************************/
293852419Sjulian
293952419Sjulian/*
294052419Sjulian * Handle the loading/unloading of a netgraph node type module
294152419Sjulian */
294252419Sjulianint
294352419Sjulianng_mod_event(module_t mod, int event, void *data)
294452419Sjulian{
294552419Sjulian	struct ng_type *const type = data;
294652419Sjulian	int s, error = 0;
294752419Sjulian
294852419Sjulian	switch (event) {
294952419Sjulian	case MOD_LOAD:
295052419Sjulian
295152419Sjulian		/* Register new netgraph node type */
295252419Sjulian		s = splnet();
295352419Sjulian		if ((error = ng_newtype(type)) != 0) {
295452419Sjulian			splx(s);
295552419Sjulian			break;
295652419Sjulian		}
295752419Sjulian
295852419Sjulian		/* Call type specific code */
295952419Sjulian		if (type->mod_event != NULL)
296070700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
296172200Sbmilekic				mtx_lock(&ng_typelist_mtx);
296271603Sjulian				type->refs--;	/* undo it */
296352419Sjulian				LIST_REMOVE(type, types);
296472200Sbmilekic				mtx_unlock(&ng_typelist_mtx);
296570700Sjulian			}
296652419Sjulian		splx(s);
296752419Sjulian		break;
296852419Sjulian
296952419Sjulian	case MOD_UNLOAD:
297052419Sjulian		s = splnet();
297171603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
297252419Sjulian			error = EBUSY;
297371603Sjulian		} else {
297471603Sjulian			if (type->refs == 0) {
297571603Sjulian				/* failed load, nothing to undo */
297671603Sjulian				splx(s);
297771603Sjulian				break;
297871603Sjulian			}
297952419Sjulian			if (type->mod_event != NULL) {	/* check with type */
298052419Sjulian				error = (*type->mod_event)(mod, event, data);
298152419Sjulian				if (error != 0) {	/* type refuses.. */
298252419Sjulian					splx(s);
298352419Sjulian					break;
298452419Sjulian				}
298552419Sjulian			}
298672200Sbmilekic			mtx_lock(&ng_typelist_mtx);
298752419Sjulian			LIST_REMOVE(type, types);
298872200Sbmilekic			mtx_unlock(&ng_typelist_mtx);
298952419Sjulian		}
299052419Sjulian		splx(s);
299152419Sjulian		break;
299252419Sjulian
299352419Sjulian	default:
299452419Sjulian		if (type->mod_event != NULL)
299552419Sjulian			error = (*type->mod_event)(mod, event, data);
299652419Sjulian		else
2997132199Sphk			error = EOPNOTSUPP;		/* XXX ? */
299852419Sjulian		break;
299952419Sjulian	}
300052419Sjulian	return (error);
300152419Sjulian}
300252419Sjulian
300352419Sjulian/*
300452419Sjulian * Handle loading and unloading for this code.
300552419Sjulian * The only thing we need to link into is the NETISR strucure.
300652419Sjulian */
300752419Sjulianstatic int
300852419Sjulianngb_mod_event(module_t mod, int event, void *data)
300952419Sjulian{
3010146212Sglebius	int error = 0;
301152419Sjulian
301252419Sjulian	switch (event) {
301352419Sjulian	case MOD_LOAD:
3014146212Sglebius		/* Initialize everything. */
3015168049Swkoszek		NG_WORKLIST_LOCK_INIT();
3016123278Struckman		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3017123278Struckman		    MTX_DEF);
3018123278Struckman		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3019123278Struckman		    MTX_DEF);
3020176802Smav		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3021176802Smav		    MTX_DEF);
3022151974Sglebius		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3023151974Sglebius		    MTX_DEF);
3024146212Sglebius#ifdef	NETGRAPH_DEBUG
3025176802Smav		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3026176802Smav		    MTX_DEF);
3027146212Sglebius		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3028123278Struckman		    MTX_DEF);
3029146212Sglebius#endif
3030146212Sglebius		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
3031146212Sglebius		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
3032146212Sglebius		uma_zone_set_max(ng_qzone, maxalloc);
3033141719Sglebius		netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL,
3034141719Sglebius		    NETISR_MPSAFE);
303552419Sjulian		break;
303652419Sjulian	case MOD_UNLOAD:
3037167677Srwatson		/* You can't unload it because an interface may be using it. */
303852419Sjulian		error = EBUSY;
303952419Sjulian		break;
304052419Sjulian	default:
304152419Sjulian		error = EOPNOTSUPP;
304252419Sjulian		break;
304352419Sjulian	}
304452419Sjulian	return (error);
304552419Sjulian}
304652419Sjulian
304752419Sjulianstatic moduledata_t netgraph_mod = {
304852419Sjulian	"netgraph",
304952419Sjulian	ngb_mod_event,
305052419Sjulian	(NULL)
305152419Sjulian};
3052139774SemaxDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
305372946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
305472946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
305572946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
305652419Sjulian
305770784Sjulian#ifdef	NETGRAPH_DEBUG
305870700Sjulianvoid
305970784Sjuliandumphook (hook_p hook, char *file, int line)
306070784Sjulian{
306170784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
306270784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
306370784Sjulian	printf("	Last active @ %s, line %d\n",
306470784Sjulian		hook->lastfile, hook->lastline);
306570784Sjulian	if (line) {
306670784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
306770784Sjulian	}
306870784Sjulian}
306970784Sjulian
307070784Sjulianvoid
307170784Sjuliandumpnode(node_p node, char *file, int line)
307270784Sjulian{
307370784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
307471047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
307570784Sjulian		node->nd_numhooks, node->nd_flags,
307670784Sjulian		node->nd_refs, node->nd_name);
307770784Sjulian	printf("	Last active @ %s, line %d\n",
307870784Sjulian		node->lastfile, node->lastline);
307970784Sjulian	if (line) {
308070784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
308170784Sjulian	}
308270784Sjulian}
308370784Sjulian
308470784Sjulianvoid
308570700Sjuliandumpitem(item_p item, char *file, int line)
308670700Sjulian{
3087146212Sglebius	printf(" ACTIVE item, last used at %s, line %d",
3088146212Sglebius		item->lastfile, item->lastline);
3089146212Sglebius	switch(item->el_flags & NGQF_TYPE) {
3090146212Sglebius	case NGQF_DATA:
3091146212Sglebius		printf(" - [data]\n");
3092146212Sglebius		break;
3093146212Sglebius	case NGQF_MESG:
3094146212Sglebius		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
3095146212Sglebius		break;
3096146212Sglebius	case NGQF_FN:
3097172820Sru		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3098172820Sru			_NGI_FN(item),
3099172820Sru			_NGI_NODE(item),
3100172820Sru			_NGI_HOOK(item),
3101172820Sru			item->body.fn.fn_arg1,
3102172820Sru			item->body.fn.fn_arg2,
3103172820Sru			item->body.fn.fn_arg2);
3104172820Sru		break;
3105172806Smav	case NGQF_FN2:
3106173110Smav		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3107172820Sru			_NGI_FN2(item),
3108149735Sglebius			_NGI_NODE(item),
3109149735Sglebius			_NGI_HOOK(item),
3110146212Sglebius			item->body.fn.fn_arg1,
3111146212Sglebius			item->body.fn.fn_arg2,
3112146212Sglebius			item->body.fn.fn_arg2);
3113146212Sglebius		break;
311452419Sjulian	}
311570784Sjulian	if (line) {
311670784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3117149735Sglebius		if (_NGI_NODE(item)) {
311870784Sjulian			printf("node %p ([%x])\n",
3119149735Sglebius				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
312070784Sjulian		}
312170784Sjulian	}
312270700Sjulian}
312352419Sjulian
312470784Sjulianstatic void
312570784Sjulianng_dumpitems(void)
312670784Sjulian{
312770784Sjulian	item_p item;
312870784Sjulian	int i = 1;
312970784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
313070784Sjulian		printf("[%d] ", i++);
313170784Sjulian		dumpitem(item, NULL, 0);
313270784Sjulian	}
313370784Sjulian}
313470784Sjulian
313570784Sjulianstatic void
313670784Sjulianng_dumpnodes(void)
313770784Sjulian{
313870784Sjulian	node_p node;
313970784Sjulian	int i = 1;
3140131008Srwatson	mtx_lock(&ng_nodelist_mtx);
314170784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
314270784Sjulian		printf("[%d] ", i++);
314370784Sjulian		dumpnode(node, NULL, 0);
314470784Sjulian	}
3145131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
314670784Sjulian}
314770784Sjulian
314870784Sjulianstatic void
314970784Sjulianng_dumphooks(void)
315070784Sjulian{
315170784Sjulian	hook_p hook;
315270784Sjulian	int i = 1;
3153131008Srwatson	mtx_lock(&ng_nodelist_mtx);
315470784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
315570784Sjulian		printf("[%d] ", i++);
315670784Sjulian		dumphook(hook, NULL, 0);
315770784Sjulian	}
3158131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
315970784Sjulian}
316070784Sjulian
316170700Sjulianstatic int
316270700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
316370700Sjulian{
316470700Sjulian	int error;
316570700Sjulian	int val;
316670700Sjulian	int i;
316770700Sjulian
316870700Sjulian	val = allocated;
316970700Sjulian	i = 1;
3170170289Sdwmalone	error = sysctl_handle_int(oidp, &val, 0, req);
317171047Sjulian	if (error != 0 || req->newptr == NULL)
317271047Sjulian		return (error);
317371047Sjulian	if (val == 42) {
317470784Sjulian		ng_dumpitems();
317570784Sjulian		ng_dumpnodes();
317670784Sjulian		ng_dumphooks();
317770700Sjulian	}
317871047Sjulian	return (0);
317952419Sjulian}
318052419Sjulian
318171047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
318271047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
318370784Sjulian#endif	/* NETGRAPH_DEBUG */
318470700Sjulian
318570700Sjulian
318670700Sjulian/***********************************************************************
318770700Sjulian* Worklist routines
318870700Sjulian**********************************************************************/
318970700Sjulian/* NETISR thread enters here */
319052419Sjulian/*
319170700Sjulian * Pick a node off the list of nodes with work,
319270700Sjulian * try get an item to process off it.
319370700Sjulian * If there are no more, remove the node from the list.
319452419Sjulian */
319570700Sjulianstatic void
319670700Sjulianngintr(void)
319752419Sjulian{
3198177953Smav	for (;;) {
3199177953Smav		node_p  node;
320052419Sjulian
3201177953Smav		/* Get node from the worklist. */
3202168049Swkoszek		NG_WORKLIST_LOCK();
320370700Sjulian		node = TAILQ_FIRST(&ng_worklist);
320470700Sjulian		if (!node) {
3205168049Swkoszek			NG_WORKLIST_UNLOCK();
320670700Sjulian			break;
320769922Sjulian		}
320870784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
3209168049Swkoszek		NG_WORKLIST_UNLOCK();
3210154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
3211154275Sglebius		    __func__, node->nd_ID, node);
321270700Sjulian		/*
321370700Sjulian		 * We have the node. We also take over the reference
321470700Sjulian		 * that the list had on it.
321570700Sjulian		 * Now process as much as you can, until it won't
321670700Sjulian		 * let you have another item off the queue.
321770700Sjulian		 * All this time, keep the reference
321870700Sjulian		 * that lets us be sure that the node still exists.
321970700Sjulian		 * Let the reference go at the last minute.
322070700Sjulian		 */
322170700Sjulian		for (;;) {
3222177953Smav			item_p item;
3223151238Sglebius			int rw;
3224151238Sglebius
3225168049Swkoszek			NG_QUEUE_LOCK(&node->nd_input_queue);
3226151238Sglebius			item = ng_dequeue(&node->nd_input_queue, &rw);
322770700Sjulian			if (item == NULL) {
3228177953Smav				atomic_clear_int(&node->nd_flags, NGF_WORKQ);
3229168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
323070700Sjulian				break; /* go look for another node */
323170700Sjulian			} else {
3232168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
323374078Sjulian				NGI_GET_NODE(item, node); /* zaps stored node */
3234151238Sglebius				ng_apply_item(node, item, rw);
323574078Sjulian				NG_NODE_UNREF(node);
323670700Sjulian			}
323770700Sjulian		}
323873238Sjulian		NG_NODE_UNREF(node);
323952419Sjulian	}
324070700Sjulian}
324169922Sjulian
324272979Sjulian/*
324372979Sjulian * XXX
324472979Sjulian * It's posible that a debugging NG_NODE_REF may need
324572979Sjulian * to be outside the mutex zone
324672979Sjulian */
324770700Sjulianstatic void
3248177953Smavng_worklist_add(node_p node)
324970700Sjulian{
3250148236Sglebius
3251148266Sglebius	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3252148236Sglebius
3253132464Sjulian	if ((node->nd_flags & NGF_WORKQ) == 0) {
325469922Sjulian		/*
325570700Sjulian		 * If we are not already on the work queue,
325670700Sjulian		 * then put us on.
325769922Sjulian		 */
3258177953Smav		atomic_set_int(&node->nd_flags, NGF_WORKQ);
3259177953Smav		NG_NODE_REF(node); /* XXX fafe in mutex? */
3260168049Swkoszek		NG_WORKLIST_LOCK();
326170784Sjulian		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
3262168049Swkoszek		NG_WORKLIST_UNLOCK();
3263177953Smav		schednetisr(NETISR_NETGRAPH);
3264154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
3265154275Sglebius		    node->nd_ID, node);
3266177953Smav	} else {
3267154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
3268154275Sglebius		    __func__, node->nd_ID, node);
3269177953Smav	}
327070700Sjulian}
327170700Sjulian
327270700Sjulian
327370700Sjulian/***********************************************************************
327470700Sjulian* Externally useable functions to set up a queue item ready for sending
327570700Sjulian***********************************************************************/
327670700Sjulian
327770784Sjulian#ifdef	NETGRAPH_DEBUG
327870784Sjulian#define	ITEM_DEBUG_CHECKS						\
327970700Sjulian	do {								\
328071849Sjulian		if (NGI_NODE(item) ) {					\
328170700Sjulian			printf("item already has node");		\
3282174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
328371849Sjulian			NGI_CLR_NODE(item);				\
328470700Sjulian		}							\
328571849Sjulian		if (NGI_HOOK(item) ) {					\
328670700Sjulian			printf("item already has hook");		\
3287174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
328871849Sjulian			NGI_CLR_HOOK(item);				\
328970700Sjulian		}							\
329070700Sjulian	} while (0)
329170700Sjulian#else
329270784Sjulian#define ITEM_DEBUG_CHECKS
329370700Sjulian#endif
329470700Sjulian
329570700Sjulian/*
3296131374Sjulian * Put mbuf into the item.
329770700Sjulian * Hook and node references will be removed when the item is dequeued.
329870700Sjulian * (or equivalent)
329970700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
330070700Sjulian * remote node might go away in this timescale.
330170700Sjulian * We know the hooks can't go away because that would require getting
330270700Sjulian * a writer item on both nodes and we must have at least a  reader
3303151973Sglebius * here to be able to do this.
330470700Sjulian * Note that the hook loaded is the REMOTE hook.
330570700Sjulian *
330670700Sjulian * This is possibly in the critical path for new data.
330770700Sjulian */
330870700Sjulianitem_p
3309146281Sglebiusng_package_data(struct mbuf *m, int flags)
331070700Sjulian{
331170700Sjulian	item_p item;
331270700Sjulian
3313176849Smav	if (atomic_fetchadd_int(&useddata, 1) >= maxdata) {
3314176849Smav		atomic_subtract_int(&useddata, 1);
3315176849Smav		NG_FREE_M(m);
3316176849Smav		return (NULL);
3317176849Smav	}
3318146281Sglebius	if ((item = ng_getqblk(flags)) == NULL) {
331970700Sjulian		NG_FREE_M(m);
332070700Sjulian		return (NULL);
332170700Sjulian	}
332270784Sjulian	ITEM_DEBUG_CHECKS;
3323149505Sglebius	item->el_flags = NGQF_DATA | NGQF_READER;
332470700Sjulian	NGI_M(item) = m;
332570700Sjulian	return (item);
332670700Sjulian}
332770700Sjulian
332870700Sjulian/*
332970700Sjulian * Allocate a queue item and put items into it..
333070700Sjulian * Evaluate the address as this will be needed to queue it and
333170700Sjulian * to work out what some of the fields should be.
333270700Sjulian * Hook and node references will be removed when the item is dequeued.
333370700Sjulian * (or equivalent)
333470700Sjulian */
333570700Sjulianitem_p
3336146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags)
333770700Sjulian{
333870700Sjulian	item_p item;
333970700Sjulian
3340146281Sglebius	if ((item = ng_getqblk(flags)) == NULL) {
334171849Sjulian		NG_FREE_MSG(msg);
334270700Sjulian		return (NULL);
334369922Sjulian	}
334470784Sjulian	ITEM_DEBUG_CHECKS;
3345149505Sglebius	/* Messages items count as writers unless explicitly exempted. */
3346149505Sglebius	if (msg->header.cmd & NGM_READONLY)
3347149505Sglebius		item->el_flags = NGQF_MESG | NGQF_READER;
3348149505Sglebius	else
3349149505Sglebius		item->el_flags = NGQF_MESG | NGQF_WRITER;
335070700Sjulian	/*
335170700Sjulian	 * Set the current lasthook into the queue item
335270700Sjulian	 */
335370700Sjulian	NGI_MSG(item) = msg;
3354102244Sarchie	NGI_RETADDR(item) = 0;
335570700Sjulian	return (item);
335670700Sjulian}
335769922Sjulian
335870700Sjulian
335970700Sjulian
336071849Sjulian#define SET_RETADDR(item, here, retaddr)				\
336171047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
336271047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
336370700Sjulian			if (retaddr) {					\
336470700Sjulian				NGI_RETADDR(item) = retaddr;		\
336570700Sjulian			} else {					\
336670700Sjulian				/*					\
336770700Sjulian				 * The old return address should be ok.	\
336870700Sjulian				 * If there isn't one, use the address	\
336970700Sjulian				 * here.				\
337070700Sjulian				 */					\
337170700Sjulian				if (NGI_RETADDR(item) == 0) {		\
337270700Sjulian					NGI_RETADDR(item)		\
337370700Sjulian						= ng_node2ID(here);	\
337470700Sjulian				}					\
337570700Sjulian			}						\
337670700Sjulian		}							\
337770700Sjulian	} while (0)
337870700Sjulian
337970700Sjulianint
338070700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
338170700Sjulian{
338271849Sjulian	hook_p peer;
338371849Sjulian	node_p peernode;
338470784Sjulian	ITEM_DEBUG_CHECKS;
338570700Sjulian	/*
338670700Sjulian	 * Quick sanity check..
338770784Sjulian	 * Since a hook holds a reference on it's node, once we know
338870784Sjulian	 * that the peer is still connected (even if invalid,) we know
338970784Sjulian	 * that the peer node is present, though maybe invalid.
339070700Sjulian	 */
339170700Sjulian	if ((hook == NULL)
339270784Sjulian	|| NG_HOOK_NOT_VALID(hook)
339370784Sjulian	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
339470784Sjulian	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
339570700Sjulian		NG_FREE_ITEM(item);
339671047Sjulian		TRAP_ERROR();
339773083Sjulian		return (ENETDOWN);
339852419Sjulian	}
339952419Sjulian
340070700Sjulian	/*
340170700Sjulian	 * Transfer our interest to the other (peer) end.
340270700Sjulian	 */
340371849Sjulian	peer = NG_HOOK_PEER(hook);
340471849Sjulian	NG_HOOK_REF(peer);
340571849Sjulian	NGI_SET_HOOK(item, peer);
340671849Sjulian	peernode = NG_PEER_NODE(hook);
340771849Sjulian	NG_NODE_REF(peernode);
340871849Sjulian	NGI_SET_NODE(item, peernode);
340979706Sjulian	SET_RETADDR(item, here, retaddr);
341070700Sjulian	return (0);
341170700Sjulian}
341252419Sjulian
341370700Sjulianint
3414152451Sglebiusng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
341570700Sjulian{
3416152451Sglebius	node_p	dest = NULL;
341770700Sjulian	hook_p	hook = NULL;
3418152451Sglebius	int	error;
341970700Sjulian
342070784Sjulian	ITEM_DEBUG_CHECKS;
342170700Sjulian	/*
342270700Sjulian	 * Note that ng_path2noderef increments the reference count
342370700Sjulian	 * on the node for us if it finds one. So we don't have to.
342470700Sjulian	 */
342570700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
342670700Sjulian	if (error) {
342770700Sjulian		NG_FREE_ITEM(item);
342870784Sjulian		return (error);
342952419Sjulian	}
343071849Sjulian	NGI_SET_NODE(item, dest);
343171849Sjulian	if ( hook) {
343270784Sjulian		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
343371849Sjulian		NGI_SET_HOOK(item, hook);
343471849Sjulian	}
343571849Sjulian	SET_RETADDR(item, here, retaddr);
343670700Sjulian	return (0);
343770700Sjulian}
343852419Sjulian
343970700Sjulianint
344070700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
344170700Sjulian{
344270700Sjulian	node_p dest;
344370700Sjulian
344470784Sjulian	ITEM_DEBUG_CHECKS;
344570700Sjulian	/*
344670700Sjulian	 * Find the target node.
344770700Sjulian	 */
344870700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
344970700Sjulian	if (dest == NULL) {
345070700Sjulian		NG_FREE_ITEM(item);
345171047Sjulian		TRAP_ERROR();
345270700Sjulian		return(EINVAL);
345370700Sjulian	}
345470700Sjulian	/* Fill out the contents */
345571849Sjulian	NGI_SET_NODE(item, dest);
345671849Sjulian	NGI_CLR_HOOK(item);
345771849Sjulian	SET_RETADDR(item, here, retaddr);
345852419Sjulian	return (0);
345952419Sjulian}
346052419Sjulian
346152419Sjulian/*
346270700Sjulian * special case to send a message to self (e.g. destroy node)
346370700Sjulian * Possibly indicate an arrival hook too.
346470700Sjulian * Useful for removing that hook :-)
346552419Sjulian */
346670700Sjulianitem_p
346770700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
346852419Sjulian{
346970700Sjulian	item_p item;
347052419Sjulian
347170700Sjulian	/*
347270700Sjulian	 * Find the target node.
347370700Sjulian	 * If there is a HOOK argument, then use that in preference
347470700Sjulian	 * to the address.
347570700Sjulian	 */
3476146281Sglebius	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) {
347771849Sjulian		NG_FREE_MSG(msg);
347870700Sjulian		return (NULL);
347952419Sjulian	}
348070700Sjulian
348170700Sjulian	/* Fill out the contents */
3482149505Sglebius	item->el_flags = NGQF_MESG | NGQF_WRITER;
348370784Sjulian	NG_NODE_REF(here);
348471849Sjulian	NGI_SET_NODE(item, here);
348571849Sjulian	if (hook) {
348670784Sjulian		NG_HOOK_REF(hook);
348771849Sjulian		NGI_SET_HOOK(item, hook);
348871849Sjulian	}
348970700Sjulian	NGI_MSG(item) = msg;
349070700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
349170700Sjulian	return (item);
349252419Sjulian}
349352419Sjulian
3494172806Smav/*
3495172806Smav * Send ng_item_fn function call to the specified node.
3496172806Smav */
3497172806Smav
3498146281Sglebiusint
3499173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
350071047Sjulian{
350171047Sjulian
3502173605Sglebius	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
350371047Sjulian}
350471047Sjulian
3505172806Smavint
3506173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
3507172806Smav	int flags)
3508172806Smav{
3509172806Smav	item_p item;
3510172806Smav
3511172806Smav	if ((item = ng_getqblk(flags)) == NULL) {
3512172806Smav		return (ENOMEM);
3513172806Smav	}
3514173605Sglebius	item->el_flags = NGQF_FN | NGQF_WRITER;
3515172806Smav	NG_NODE_REF(node); /* and one for the item */
3516172806Smav	NGI_SET_NODE(item, node);
3517172806Smav	if (hook) {
3518172806Smav		NG_HOOK_REF(hook);
3519172806Smav		NGI_SET_HOOK(item, hook);
3520172806Smav	}
3521173605Sglebius	NGI_FN(item) = fn;
3522172806Smav	NGI_ARG1(item) = arg1;
3523172806Smav	NGI_ARG2(item) = arg2;
3524172806Smav	return(ng_snd_item(item, flags));
3525172806Smav}
3526172806Smav
3527172806Smav/*
3528173605Sglebius * Send ng_item_fn2 function call to the specified node.
3529173605Sglebius *
3530173605Sglebius * If an optional pitem parameter is supplied, its apply
3531173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM
3532173605Sglebius * flag is set, no new item will be allocated, but pitem will
3533173605Sglebius * be used.
3534172806Smav */
3535172806Smavint
3536173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3537173605Sglebius	int arg2, int flags)
3538172806Smav{
3539172806Smav	item_p item;
3540172806Smav
3541173605Sglebius	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3542173605Sglebius	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3543172806Smav
3544173605Sglebius	/*
3545173605Sglebius	 * Allocate a new item if no supplied or
3546173605Sglebius	 * if we can't use supplied one.
3547173605Sglebius	 */
3548173605Sglebius	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
3549173605Sglebius		if ((item = ng_getqblk(flags)) == NULL)
3550173605Sglebius			return (ENOMEM);
3551176849Smav	} else {
3552176849Smav		if ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA)
3553176849Smav			atomic_subtract_int(&useddata, 1);
3554173605Sglebius		item = pitem;
3555176849Smav	}
3556172806Smav
3557172806Smav	item->el_flags = NGQF_FN2 | NGQF_WRITER;
3558172806Smav	NG_NODE_REF(node); /* and one for the item */
3559172806Smav	NGI_SET_NODE(item, node);
3560172806Smav	if (hook) {
3561172806Smav		NG_HOOK_REF(hook);
3562172806Smav		NGI_SET_HOOK(item, hook);
3563172806Smav	}
3564172806Smav	NGI_FN2(item) = fn;
3565172806Smav	NGI_ARG1(item) = arg1;
3566172806Smav	NGI_ARG2(item) = arg2;
3567173605Sglebius	if (pitem != NULL && (flags & NG_REUSE_ITEM) == 0)
3568173605Sglebius		item->apply = pitem->apply;
3569172806Smav	return(ng_snd_item(item, flags));
3570172806Smav}
3571172806Smav
3572172806Smav/*
357391711Sjulian * Official timeout routines for Netgraph nodes.
357491711Sjulian */
357591711Sjulianstatic void
3576140852Sglebiusng_callout_trampoline(void *arg)
357791711Sjulian{
357891711Sjulian	item_p item = arg;
357991711Sjulian
358091711Sjulian	ng_snd_item(item, 0);
358191711Sjulian}
358291711Sjulian
358391711Sjulian
3584137138Sglebiusint
3585138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
358691711Sjulian    ng_item_fn *fn, void * arg1, int arg2)
358791711Sjulian{
3588149881Sglebius	item_p item, oitem;
358991711Sjulian
3590146281Sglebius	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL)
3591137138Sglebius		return (ENOMEM);
3592137138Sglebius
359391711Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
359491711Sjulian	NG_NODE_REF(node);		/* and one for the item */
359591711Sjulian	NGI_SET_NODE(item, node);
359691711Sjulian	if (hook) {
359791711Sjulian		NG_HOOK_REF(hook);
359891711Sjulian		NGI_SET_HOOK(item, hook);
359991711Sjulian	}
360091711Sjulian	NGI_FN(item) = fn;
360191711Sjulian	NGI_ARG1(item) = arg1;
360291711Sjulian	NGI_ARG2(item) = arg2;
3603149881Sglebius	oitem = c->c_arg;
3604149881Sglebius	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
3605149881Sglebius	    oitem != NULL)
3606149881Sglebius		NG_FREE_ITEM(oitem);
3607137138Sglebius	return (0);
360891711Sjulian}
360991711Sjulian
361091711Sjulian/* A special modified version of untimeout() */
3611152451Sglebiusint
3612138268Sglebiusng_uncallout(struct callout *c, node_p node)
361391711Sjulian{
361491711Sjulian	item_p item;
3615137138Sglebius	int rval;
3616149357Sglebius
3617149357Sglebius	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
3618149357Sglebius	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
3619149357Sglebius
3620137230Sglebius	rval = callout_stop(c);
3621137138Sglebius	item = c->c_arg;
3622137138Sglebius	/* Do an extra check */
3623140852Sglebius	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
3624137138Sglebius	    (NGI_NODE(item) == node)) {
362591711Sjulian		/*
362691711Sjulian		 * We successfully removed it from the queue before it ran
3627152451Sglebius		 * So now we need to unreference everything that was
362891711Sjulian		 * given extra references. (NG_FREE_ITEM does this).
362991711Sjulian		 */
363091711Sjulian		NG_FREE_ITEM(item);
363191711Sjulian	}
3632149881Sglebius	c->c_arg = NULL;
3633137138Sglebius
3634137138Sglebius	return (rval);
363591711Sjulian}
363691711Sjulian
363770700Sjulian/*
363870700Sjulian * Set the address, if none given, give the node here.
363970700Sjulian */
364070700Sjulianvoid
364170700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
364270700Sjulian{
364370700Sjulian	if (retaddr) {
364470700Sjulian		NGI_RETADDR(item) = retaddr;
364570700Sjulian	} else {
364670700Sjulian		/*
364770700Sjulian		 * The old return address should be ok.
364870700Sjulian		 * If there isn't one, use the address here.
364970700Sjulian		 */
365070700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
365170700Sjulian	}
365270700Sjulian}
365352419Sjulian
365470700Sjulian#define TESTING
365570700Sjulian#ifdef TESTING
365670700Sjulian/* just test all the macros */
365770700Sjulianvoid
365870700Sjulianng_macro_test(item_p item);
365970700Sjulianvoid
366070700Sjulianng_macro_test(item_p item)
366170700Sjulian{
366270700Sjulian	node_p node = NULL;
366370700Sjulian	hook_p hook = NULL;
366470700Sjulian	struct mbuf *m;
366570700Sjulian	struct ng_mesg *msg;
366670700Sjulian	ng_ID_t retaddr;
366770700Sjulian	int	error;
366870700Sjulian
366970700Sjulian	NGI_GET_M(item, m);
367070700Sjulian	NGI_GET_MSG(item, msg);
367170700Sjulian	retaddr = NGI_RETADDR(item);
3672131374Sjulian	NG_SEND_DATA(error, hook, m, NULL);
367370700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
367470700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
367570784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
367670700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
367770700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
367870700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
367970700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
368070700Sjulian}
368170700Sjulian#endif /* TESTING */
368270700Sjulian
3683